diff options
197 files changed, 4279 insertions, 3899 deletions
diff --git a/AconfigFlags.bp b/AconfigFlags.bp index 214e0402c8fc..b1c091bfa946 100644 --- a/AconfigFlags.bp +++ b/AconfigFlags.bp @@ -220,17 +220,6 @@ java_aconfig_library { defaults: ["framework-minus-apex-aconfig-java-defaults"], } -java_aconfig_library { - name: "telephony_flags_core_java_exported_lib", - aconfig_declarations: "telephony_flags", - mode: "exported", - min_sdk_version: "30", - apex_available: [ - "com.android.wifi", - ], - defaults: ["framework-minus-apex-aconfig-java-defaults"], -} - cc_aconfig_library { name: "telephony_flags_c_lib", aconfig_declarations: "telephony_flags", diff --git a/apct-tests/perftests/aconfig/src/android/os/flagging/AconfigPackagePerfTest.java b/apct-tests/perftests/aconfig/src/android/os/flagging/AconfigPackagePerfTest.java index df6e3c836256..e790874ebc61 100644 --- a/apct-tests/perftests/aconfig/src/android/os/flagging/AconfigPackagePerfTest.java +++ b/apct-tests/perftests/aconfig/src/android/os/flagging/AconfigPackagePerfTest.java @@ -43,7 +43,7 @@ public class AconfigPackagePerfTest { @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter(); - @Parameterized.Parameters(name = "isPlatform={0}") + @Parameterized.Parameters(name = "isPlatform_{0}") public static Collection<Object[]> data() { return Arrays.asList(new Object[][] {{false}, {true}}); } @@ -60,10 +60,9 @@ public class AconfigPackagePerfTest { } } - @Parameterized.Parameter(0) - // if this variable is true, then the test query flags from system/product/vendor // if this variable is false, then the test query flags from updatable partitions + @Parameterized.Parameter(0) public boolean mIsPlatform; @Test diff --git a/core/api/system-current.txt b/core/api/system-current.txt index f82aecbd6d44..1286851a6918 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -1290,7 +1290,6 @@ package android.app { public class WallpaperManager { method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public void clearWallpaper(int, int); - method @FlaggedApi("android.app.customization_packs_apis") @NonNull @RequiresPermission(android.Manifest.permission.READ_WALLPAPER_INTERNAL) public android.util.SparseArray<android.graphics.Rect> getBitmapCrops(int); method @FlaggedApi("android.app.customization_packs_apis") public static int getOrientation(@NonNull android.graphics.Point); method @FloatRange(from=0.0f, to=1.0f) @RequiresPermission(android.Manifest.permission.SET_WALLPAPER_DIM_AMOUNT) public float getWallpaperDimAmount(); method @FlaggedApi("android.app.customization_packs_apis") @Nullable public android.os.ParcelFileDescriptor getWallpaperFile(int, boolean); diff --git a/core/api/test-current.txt b/core/api/test-current.txt index cd2cc07b8cc3..33a06165c77a 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -536,6 +536,7 @@ package android.app { method @Nullable public android.graphics.Bitmap getBitmap(); method @Nullable public android.graphics.Bitmap getBitmapAsUser(int, boolean, int); method @FlaggedApi("com.android.window.flags.multi_crop") @NonNull @RequiresPermission(android.Manifest.permission.READ_WALLPAPER_INTERNAL) public java.util.List<android.graphics.Rect> getBitmapCrops(@NonNull java.util.List<android.graphics.Point>, int, boolean); + method @FlaggedApi("android.app.customization_packs_apis") @NonNull @RequiresPermission(android.Manifest.permission.READ_WALLPAPER_INTERNAL) public android.util.SparseArray<android.graphics.Rect> getBitmapCrops(int); method @FlaggedApi("com.android.window.flags.multi_crop") @NonNull public java.util.List<android.graphics.Rect> getBitmapCrops(@NonNull android.graphics.Point, @NonNull java.util.List<android.graphics.Point>, @Nullable java.util.Map<android.graphics.Point,android.graphics.Rect>); method public boolean isLockscreenLiveWallpaperEnabled(); method @Nullable public android.graphics.Rect peekBitmapDimensions(); diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 717a2acb4b4a..1f3e6559a695 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -105,6 +105,7 @@ import android.content.pm.ServiceInfo; import android.content.res.AssetManager; import android.content.res.CompatibilityInfo; import android.content.res.Configuration; +import android.content.res.ResourceTimer; import android.content.res.Resources; import android.content.res.ResourcesImpl; import android.content.res.loader.ResourcesLoader; @@ -5253,6 +5254,7 @@ public final class ActivityThread extends ClientTransactionHandler Resources.dumpHistory(pw, ""); pw.flush(); + ResourceTimer.dumpTimers(info.fd.getFileDescriptor(), "-refresh"); if (info.finishCallback != null) { info.finishCallback.sendResult(null); } diff --git a/core/java/android/app/UiModeManager.java b/core/java/android/app/UiModeManager.java index 2e6f3e1c7f0a..57549847f05d 100644 --- a/core/java/android/app/UiModeManager.java +++ b/core/java/android/app/UiModeManager.java @@ -753,7 +753,7 @@ public class UiModeManager { * <p> * The mode can be one of: * <ul> - * <li><em>{@link #MODE_NIGHT_NO}<em> sets the device into + * <li><em>{@link #MODE_NIGHT_NO}</em> sets the device into * {@code notnight} mode</li> * <li><em>{@link #MODE_NIGHT_YES}</em> sets the device into * {@code night} mode</li> @@ -889,7 +889,7 @@ public class UiModeManager { * <p> * The mode can be one of: * <ul> - * <li><em>{@link #MODE_NIGHT_NO}<em> sets the device into + * <li><em>{@link #MODE_NIGHT_NO}</em> sets the device into * {@code notnight} mode</li> * <li><em>{@link #MODE_NIGHT_YES}</em> sets the device into * {@code night} mode</li> diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java index 360376da618c..73ecc7199686 100644 --- a/core/java/android/app/WallpaperManager.java +++ b/core/java/android/app/WallpaperManager.java @@ -1690,7 +1690,7 @@ public class WallpaperManager { * @hide */ @FlaggedApi(FLAG_CUSTOMIZATION_PACKS_APIS) - @SystemApi + @TestApi @RequiresPermission(READ_WALLPAPER_INTERNAL) @NonNull public SparseArray<Rect> getBitmapCrops(@SetWallpaperFlags int which) { diff --git a/core/java/android/companion/virtual/flags.aconfig b/core/java/android/companion/virtual/flags.aconfig index 46da4a3d99bc..f31e7d4c61cd 100644 --- a/core/java/android/companion/virtual/flags.aconfig +++ b/core/java/android/companion/virtual/flags.aconfig @@ -11,14 +11,6 @@ package: "android.companion.virtual.flags" container: "system" flag { - name: "enable_native_vdm" - namespace: "virtual_devices" - description: "Enable native VDM service" - bug: "303535376" - is_fixed_read_only: true -} - -flag { name: "dynamic_policy" is_exported: true namespace: "virtual_devices" diff --git a/core/java/android/content/pm/RegisteredServicesCache.java b/core/java/android/content/pm/RegisteredServicesCache.java index 0333942b7f3e..9d11710a2cad 100644 --- a/core/java/android/content/pm/RegisteredServicesCache.java +++ b/core/java/android/content/pm/RegisteredServicesCache.java @@ -17,6 +17,7 @@ package android.content.pm; import android.Manifest; +import android.annotation.NonNull; import android.compat.annotation.UnsupportedAppUsage; import android.content.BroadcastReceiver; import android.content.ComponentName; @@ -30,6 +31,7 @@ import android.os.Environment; import android.os.Handler; import android.os.UserHandle; import android.os.UserManager; +import android.util.ArrayMap; import android.util.AtomicFile; import android.util.AttributeSet; import android.util.IntArray; @@ -45,11 +47,11 @@ import com.android.internal.util.ArrayUtils; import com.android.modules.utils.TypedXmlPullParser; import com.android.modules.utils.TypedXmlSerializer; -import libcore.io.IoUtils; - import com.google.android.collect.Lists; import com.google.android.collect.Maps; +import libcore.io.IoUtils; + import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -94,6 +96,9 @@ public abstract class RegisteredServicesCache<V> { @GuardedBy("mServicesLock") private final SparseArray<UserServices<V>> mUserServices = new SparseArray<UserServices<V>>(2); + @GuardedBy("mServicesLock") + private final ArrayMap<String, ServiceInfo<V>> mServiceInfoCaches = new ArrayMap<>(); + private static class UserServices<V> { @GuardedBy("mServicesLock") final Map<V, Integer> persistentServices = Maps.newHashMap(); @@ -323,13 +328,16 @@ public abstract class RegisteredServicesCache<V> { public final ComponentName componentName; @UnsupportedAppUsage public final int uid; + public final long lastUpdateTime; /** @hide */ - public ServiceInfo(V type, ComponentInfo componentInfo, ComponentName componentName) { + public ServiceInfo(V type, ComponentInfo componentInfo, ComponentName componentName, + long lastUpdateTime) { this.type = type; this.componentInfo = componentInfo; this.componentName = componentName; this.uid = (componentInfo != null) ? componentInfo.applicationInfo.uid : -1; + this.lastUpdateTime = lastUpdateTime; } @Override @@ -490,7 +498,7 @@ public abstract class RegisteredServicesCache<V> { final List<ResolveInfo> resolveInfos = queryIntentServices(userId); for (ResolveInfo resolveInfo : resolveInfos) { try { - ServiceInfo<V> info = parseServiceInfo(resolveInfo); + ServiceInfo<V> info = parseServiceInfo(resolveInfo, userId); if (info == null) { Log.w(TAG, "Unable to load service info " + resolveInfo.toString()); continue; @@ -638,13 +646,31 @@ public abstract class RegisteredServicesCache<V> { } @VisibleForTesting - protected ServiceInfo<V> parseServiceInfo(ResolveInfo service) + protected ServiceInfo<V> parseServiceInfo(ResolveInfo service, int userId) throws XmlPullParserException, IOException { android.content.pm.ServiceInfo si = service.serviceInfo; ComponentName componentName = new ComponentName(si.packageName, si.name); PackageManager pm = mContext.getPackageManager(); + // Check if the service has been in the service cache. + long lastUpdateTime = -1; + if (Flags.optimizeParsingInRegisteredServicesCache()) { + try { + PackageInfo packageInfo = pm.getPackageInfoAsUser(si.packageName, + PackageManager.MATCH_DIRECT_BOOT_AWARE + | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userId); + lastUpdateTime = packageInfo.lastUpdateTime; + + ServiceInfo<V> serviceInfo = getServiceInfoFromServiceCache(si, lastUpdateTime); + if (serviceInfo != null) { + return serviceInfo; + } + } catch (NameNotFoundException | SecurityException e) { + Slog.d(TAG, "Fail to get the PackageInfo in parseServiceInfo: " + e); + } + } + XmlResourceParser parser = null; try { parser = si.loadXmlMetaData(pm, mMetaDataName); @@ -670,8 +696,13 @@ public abstract class RegisteredServicesCache<V> { if (v == null) { return null; } - final android.content.pm.ServiceInfo serviceInfo = service.serviceInfo; - return new ServiceInfo<V>(v, serviceInfo, componentName); + ServiceInfo<V> serviceInfo = new ServiceInfo<V>(v, si, componentName, lastUpdateTime); + if (Flags.optimizeParsingInRegisteredServicesCache()) { + synchronized (mServicesLock) { + mServiceInfoCaches.put(getServiceCacheKey(si), serviceInfo); + } + } + return serviceInfo; } catch (NameNotFoundException e) { throw new XmlPullParserException( "Unable to load resources for pacakge " + si.packageName); @@ -841,4 +872,28 @@ public abstract class RegisteredServicesCache<V> { mContext.unregisterReceiver(mExternalReceiver); mContext.unregisterReceiver(mUserRemovedReceiver); } + + private static String getServiceCacheKey(@NonNull android.content.pm.ServiceInfo serviceInfo) { + StringBuilder sb = new StringBuilder(serviceInfo.packageName); + sb.append('-'); + sb.append(serviceInfo.name); + return sb.toString(); + } + + private ServiceInfo<V> getServiceInfoFromServiceCache( + @NonNull android.content.pm.ServiceInfo serviceInfo, long lastUpdateTime) { + String serviceCacheKey = getServiceCacheKey(serviceInfo); + synchronized (mServicesLock) { + ServiceInfo<V> serviceCache = mServiceInfoCaches.get(serviceCacheKey); + if (serviceCache == null) { + return null; + } + if (serviceCache.lastUpdateTime == lastUpdateTime) { + return serviceCache; + } + // The service is not latest, remove it from the cache. + mServiceInfoCaches.remove(serviceCacheKey); + return null; + } + } } diff --git a/core/java/android/content/pm/flags.aconfig b/core/java/android/content/pm/flags.aconfig index 7bba06c87813..e4b8c90d381d 100644 --- a/core/java/android/content/pm/flags.aconfig +++ b/core/java/android/content/pm/flags.aconfig @@ -383,3 +383,11 @@ flag { bug: "334024639" description: "Feature flag to check whether a given UID can access a content provider" } + +flag { + name: "optimize_parsing_in_registered_services_cache" + namespace: "package_manager_service" + description: "Feature flag to optimize RegisteredServicesCache ServiceInfo parsing by using caches." + bug: "319137634" + is_fixed_read_only: true +} diff --git a/core/java/android/content/res/ApkAssets.java b/core/java/android/content/res/ApkAssets.java index 908999b64961..b938aac811fd 100644 --- a/core/java/android/content/res/ApkAssets.java +++ b/core/java/android/content/res/ApkAssets.java @@ -25,6 +25,7 @@ import android.content.res.loader.ResourcesProvider; import android.ravenwood.annotation.RavenwoodClassLoadHook; import android.ravenwood.annotation.RavenwoodKeepWholeClass; import android.text.TextUtils; +import android.util.Log; import com.android.internal.annotations.GuardedBy; @@ -50,6 +51,7 @@ import java.util.Objects; @RavenwoodKeepWholeClass @RavenwoodClassLoadHook(RavenwoodClassLoadHook.LIBANDROID_LOADING_HOOK) public final class ApkAssets { + private static final boolean DEBUG = false; /** * The apk assets contains framework resource values specified by the system. @@ -134,6 +136,17 @@ public final class ApkAssets { @Nullable private final AssetsProvider mAssets; + @NonNull + private String mName; + + private static final int UPTODATE_FALSE = 0; + private static final int UPTODATE_TRUE = 1; + private static final int UPTODATE_ALWAYS_TRUE = 2; + + // Start with the only value that may change later and would force a native call to + // double check it. + private int mPreviousUpToDateResult = UPTODATE_TRUE; + /** * Creates a new ApkAssets instance from the given path on disk. * @@ -304,7 +317,7 @@ public final class ApkAssets { private ApkAssets(@FormatType int format, @NonNull String path, @PropertyFlags int flags, @Nullable AssetsProvider assets) throws IOException { - this(format, flags, assets); + this(format, flags, assets, path); Objects.requireNonNull(path, "path"); mNativePtr = nativeLoad(format, path, flags, assets); mStringBlock = new StringBlock(nativeGetStringBlock(mNativePtr), true /*useSparse*/); @@ -313,7 +326,7 @@ public final class ApkAssets { private ApkAssets(@FormatType int format, @NonNull FileDescriptor fd, @NonNull String friendlyName, @PropertyFlags int flags, @Nullable AssetsProvider assets) throws IOException { - this(format, flags, assets); + this(format, flags, assets, friendlyName); Objects.requireNonNull(fd, "fd"); Objects.requireNonNull(friendlyName, "friendlyName"); mNativePtr = nativeLoadFd(format, fd, friendlyName, flags, assets); @@ -323,7 +336,7 @@ public final class ApkAssets { private ApkAssets(@FormatType int format, @NonNull FileDescriptor fd, @NonNull String friendlyName, long offset, long length, @PropertyFlags int flags, @Nullable AssetsProvider assets) throws IOException { - this(format, flags, assets); + this(format, flags, assets, friendlyName); Objects.requireNonNull(fd, "fd"); Objects.requireNonNull(friendlyName, "friendlyName"); mNativePtr = nativeLoadFdOffsets(format, fd, friendlyName, offset, length, flags, assets); @@ -331,16 +344,17 @@ public final class ApkAssets { } private ApkAssets(@PropertyFlags int flags, @Nullable AssetsProvider assets) { - this(FORMAT_APK, flags, assets); + this(FORMAT_APK, flags, assets, "empty"); mNativePtr = nativeLoadEmpty(flags, assets); mStringBlock = null; } private ApkAssets(@FormatType int format, @PropertyFlags int flags, - @Nullable AssetsProvider assets) { + @Nullable AssetsProvider assets, @NonNull String name) { mFlags = flags; mAssets = assets; mIsOverlay = format == FORMAT_IDMAP; + if (DEBUG) mName = name; } @UnsupportedAppUsage @@ -421,13 +435,41 @@ public final class ApkAssets { } } + private static double intervalMs(long beginNs, long endNs) { + return (endNs - beginNs) / 1000000.0; + } + /** * Returns false if the underlying APK was changed since this ApkAssets was loaded. */ public boolean isUpToDate() { + // This function is performance-critical - it's called multiple times on every Resources + // object creation, and on few other cache accesses - so it's important to avoid the native + // call when we know for sure what it will return (which is the case for both ALWAYS_TRUE + // and FALSE). + if (mPreviousUpToDateResult != UPTODATE_TRUE) { + return mPreviousUpToDateResult == UPTODATE_ALWAYS_TRUE; + } + final long beforeTs, afterLockTs, afterNativeTs, afterUnlockTs; + if (DEBUG) beforeTs = System.nanoTime(); + final int res; synchronized (this) { - return nativeIsUpToDate(mNativePtr); + if (DEBUG) afterLockTs = System.nanoTime(); + res = nativeIsUpToDate(mNativePtr); + if (DEBUG) afterNativeTs = System.nanoTime(); + } + if (DEBUG) { + afterUnlockTs = System.nanoTime(); + if (afterUnlockTs - beforeTs >= 10L * 1000000) { + Log.d("ApkAssets", "isUpToDate(" + mName + ") took " + + intervalMs(beforeTs, afterUnlockTs) + + " ms: " + intervalMs(beforeTs, afterLockTs) + + " / " + intervalMs(afterLockTs, afterNativeTs) + + " / " + intervalMs(afterNativeTs, afterUnlockTs)); + } } + mPreviousUpToDateResult = res; + return res != UPTODATE_FALSE; } public boolean isSystem() { @@ -487,7 +529,7 @@ public final class ApkAssets { private static native @NonNull String nativeGetAssetPath(long ptr); private static native @NonNull String nativeGetDebugName(long ptr); private static native long nativeGetStringBlock(long ptr); - @CriticalNative private static native boolean nativeIsUpToDate(long ptr); + @CriticalNative private static native int nativeIsUpToDate(long ptr); private static native long nativeOpenXml(long ptr, @NonNull String fileName) throws IOException; private static native @Nullable OverlayableInfo nativeGetOverlayableInfo(long ptr, String overlayableName) throws IOException; diff --git a/core/java/android/content/res/ResourceTimer.java b/core/java/android/content/res/ResourceTimer.java index d51f64ce8106..2d1bf4d9d296 100644 --- a/core/java/android/content/res/ResourceTimer.java +++ b/core/java/android/content/res/ResourceTimer.java @@ -17,13 +17,10 @@ package android.content.res; import android.annotation.NonNull; -import android.annotation.Nullable; - import android.app.AppProtoEnums; import android.os.Handler; import android.os.Looper; import android.os.Message; -import android.os.ParcelFileDescriptor; import android.os.Process; import android.os.SystemClock; import android.text.TextUtils; @@ -33,6 +30,7 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.util.FastPrintWriter; import com.android.internal.util.FrameworkStatsLog; +import java.io.FileDescriptor; import java.io.FileOutputStream; import java.io.PrintWriter; import java.util.Arrays; @@ -277,38 +275,40 @@ public final class ResourceTimer { * Update the metrics information and dump it. * @hide */ - public static void dumpTimers(@NonNull ParcelFileDescriptor pfd, @Nullable String[] args) { - FileOutputStream fout = new FileOutputStream(pfd.getFileDescriptor()); - PrintWriter pw = new FastPrintWriter(fout); - synchronized (sLock) { - if (!sEnabled || (sConfig == null)) { + public static void dumpTimers(@NonNull FileDescriptor fd, String... args) { + try (PrintWriter pw = new FastPrintWriter(new FileOutputStream(fd))) { + pw.println("\nDumping ResourceTimers"); + + final boolean enabled; + synchronized (sLock) { + enabled = sEnabled && sConfig != null; + } + if (!enabled) { pw.println(" Timers are not enabled in this process"); - pw.flush(); return; } - } - // Look for the --refresh switch. If the switch is present, then sTimers is updated. - // Otherwise, the current value of sTimers is displayed. - boolean refresh = Arrays.asList(args).contains("-refresh"); - - synchronized (sLock) { - update(refresh); - long runtime = sLastUpdated - sProcessStart; - pw.format(" config runtime=%d proc=%s\n", runtime, Process.myProcessName()); - for (int i = 0; i < sTimers.length; i++) { - Timer t = sTimers[i]; - if (t.count != 0) { - String name = sConfig.timers[i]; - pw.format(" stats timer=%s cnt=%d avg=%d min=%d max=%d pval=%s " - + "largest=%s\n", - name, t.count, t.total / t.count, t.mintime, t.maxtime, - packedString(t.percentile), - packedString(t.largest)); + // Look for the --refresh switch. If the switch is present, then sTimers is updated. + // Otherwise, the current value of sTimers is displayed. + boolean refresh = Arrays.asList(args).contains("-refresh"); + + synchronized (sLock) { + update(refresh); + long runtime = sLastUpdated - sProcessStart; + pw.format(" config runtime=%d proc=%s\n", runtime, Process.myProcessName()); + for (int i = 0; i < sTimers.length; i++) { + Timer t = sTimers[i]; + if (t.count != 0) { + String name = sConfig.timers[i]; + pw.format(" stats timer=%s cnt=%d avg=%d min=%d max=%d pval=%s " + + "largest=%s\n", + name, t.count, t.total / t.count, t.mintime, t.maxtime, + packedString(t.percentile), + packedString(t.largest)); + } } } } - pw.flush(); } // Enable (or disabled) the runtime timers. Note that timers are disabled by default. This diff --git a/core/java/android/hardware/SystemSensorManager.java b/core/java/android/hardware/SystemSensorManager.java index 2d3d25217357..868429c30631 100644 --- a/core/java/android/hardware/SystemSensorManager.java +++ b/core/java/android/hardware/SystemSensorManager.java @@ -16,8 +16,6 @@ package android.hardware; -import static android.companion.virtual.VirtualDeviceManager.ACTION_VIRTUAL_DEVICE_REMOVED; -import static android.companion.virtual.VirtualDeviceManager.EXTRA_VIRTUAL_DEVICE_ID; import static android.companion.virtual.VirtualDeviceParams.DEVICE_POLICY_DEFAULT; import static android.companion.virtual.VirtualDeviceParams.POLICY_TYPE_SENSORS; import static android.content.Context.DEVICE_ID_DEFAULT; @@ -164,11 +162,7 @@ public class SystemSensorManager extends SensorManager { // initialize the sensor list for (int index = 0;; ++index) { Sensor sensor = new Sensor(); - if (android.companion.virtual.flags.Flags.enableNativeVdm()) { - if (!nativeGetDefaultDeviceSensorAtIndex(mNativeInstance, sensor, index)) break; - } else { - if (!nativeGetSensorAtIndex(mNativeInstance, sensor, index)) break; - } + if (!nativeGetDefaultDeviceSensorAtIndex(mNativeInstance, sensor, index)) break; mFullSensorsList.add(sensor); mHandleToSensor.put(sensor.getHandle(), sensor); } @@ -555,11 +549,7 @@ public class SystemSensorManager extends SensorManager { } private List<Sensor> createRuntimeSensorListLocked(int deviceId) { - if (android.companion.virtual.flags.Flags.vdmPublicApis()) { - setupVirtualDeviceListener(); - } else { - setupRuntimeSensorBroadcastReceiver(); - } + setupVirtualDeviceListener(); List<Sensor> list = new ArrayList<>(); nativeGetRuntimeSensors(mNativeInstance, deviceId, list); mFullRuntimeSensorListByDevice.put(deviceId, list); @@ -570,35 +560,6 @@ public class SystemSensorManager extends SensorManager { return list; } - private void setupRuntimeSensorBroadcastReceiver() { - if (mRuntimeSensorBroadcastReceiver == null) { - mRuntimeSensorBroadcastReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - if (intent.getAction().equals(ACTION_VIRTUAL_DEVICE_REMOVED)) { - synchronized (mFullRuntimeSensorListByDevice) { - final int deviceId = intent.getIntExtra( - EXTRA_VIRTUAL_DEVICE_ID, DEVICE_ID_DEFAULT); - List<Sensor> removedSensors = - mFullRuntimeSensorListByDevice.removeReturnOld(deviceId); - if (removedSensors != null) { - for (Sensor s : removedSensors) { - cleanupSensorConnection(s); - } - } - mRuntimeSensorListByDeviceByType.remove(deviceId); - } - } - } - }; - - IntentFilter filter = new IntentFilter("virtual_device_removed"); - filter.addAction(ACTION_VIRTUAL_DEVICE_REMOVED); - mContext.registerReceiver(mRuntimeSensorBroadcastReceiver, filter, - Context.RECEIVER_NOT_EXPORTED); - } - } - private void setupVirtualDeviceListener() { if (mVirtualDeviceListener != null) { return; diff --git a/core/java/android/hardware/display/DisplayTopology.java b/core/java/android/hardware/display/DisplayTopology.java index 0e2c05f92e7c..1d2f133ee759 100644 --- a/core/java/android/hardware/display/DisplayTopology.java +++ b/core/java/android/hardware/display/DisplayTopology.java @@ -679,8 +679,7 @@ public final class DisplayTopology implements Parcelable { } /** - * Tests whether two brightness float values are within a small enough tolerance - * of each other. + * Tests whether two float values are within a small enough tolerance of each other. * @param a first float to compare * @param b second float to compare * @return whether the two values are within a small enough tolerance value diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index c94526bcdcd7..0fe18a647f95 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -17395,13 +17395,6 @@ public final class Settings { /** - * Whether back preview animations are played when user does a back gesture or presses - * the back button. - * @hide - */ - public static final String ENABLE_BACK_ANIMATION = "enable_back_animation"; - - /** * An allow list of packages for which the user has granted the permission to communicate * across profiles. * diff --git a/core/java/android/view/InputEventConsistencyVerifier.java b/core/java/android/view/InputEventConsistencyVerifier.java index 5c38a1597433..195896dc8edf 100644 --- a/core/java/android/view/InputEventConsistencyVerifier.java +++ b/core/java/android/view/InputEventConsistencyVerifier.java @@ -81,7 +81,7 @@ public final class InputEventConsistencyVerifier { // Bitfield of pointer ids that are currently down. // Assumes that the largest possible pointer id is 31, which is potentially subject to change. - // (See MAX_POINTER_ID in frameworks/base/include/ui/Input.h) + // (See MAX_POINTER_ID in frameworks/native/include/input/input.h) private int mTouchEventStreamPointers; // The device id and source of the current stream of touch events. diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java index 833f2d98554e..e665c08c63e4 100644 --- a/core/java/android/view/SurfaceControl.java +++ b/core/java/android/view/SurfaceControl.java @@ -160,6 +160,10 @@ public final class SurfaceControl implements Parcelable { float l, float t, float r, float b); private static native void nativeSetCornerRadius(long transactionObj, long nativeObject, float cornerRadius); + private static native void nativeSetClientDrawnCornerRadius(long transactionObj, + long nativeObject, float clientDrawnCornerRadius); + private static native void nativeSetClientDrawnShadows(long transactionObj, + long nativeObject, float clientDrawnShadows); private static native void nativeSetBackgroundBlurRadius(long transactionObj, long nativeObject, int blurRadius); private static native void nativeSetLayerStack(long transactionObj, long nativeObject, @@ -3654,6 +3658,66 @@ public final class SurfaceControl implements Parcelable { return this; } + + /** + * Disables corner radius of a {@link SurfaceControl}. When the radius set by + * {@link Transaction#setCornerRadius(SurfaceControl, float)} is equal to + * clientDrawnCornerRadius the corner radius drawn by SurfaceFlinger is disabled. + * + * @param sc SurfaceControl + * @param clientDrawnCornerRadius Corner radius drawn by the client + * @return Itself. + * @hide + */ + @NonNull + public Transaction setClientDrawnCornerRadius(@NonNull SurfaceControl sc, + float clientDrawnCornerRadius) { + checkPreconditions(sc); + if (SurfaceControlRegistry.sCallStackDebuggingEnabled) { + SurfaceControlRegistry.getProcessInstance().checkCallStackDebugging( + "setClientDrawnCornerRadius", this, sc, "clientDrawnCornerRadius=" + + clientDrawnCornerRadius); + } + if (Flags.ignoreCornerRadiusAndShadows()) { + nativeSetClientDrawnCornerRadius(mNativeObject, sc.mNativeObject, + clientDrawnCornerRadius); + } else { + Log.w(TAG, "setClientDrawnCornerRadius was called but" + + "ignore_corner_radius_and_shadows flag is disabled"); + } + + return this; + } + + /** + * Disables shadows of a {@link SurfaceControl}. When the radius set by + * {@link Transaction#setClientDrawnShadows(SurfaceControl, float)} is equal to + * clientDrawnShadowRadius the shadows drawn by SurfaceFlinger is disabled. + * + * @param sc SurfaceControl + * @param clientDrawnShadowRadius Shadow radius drawn by the client + * @return Itself. + * @hide + */ + @NonNull + public Transaction setClientDrawnShadows(@NonNull SurfaceControl sc, + float clientDrawnShadowRadius) { + checkPreconditions(sc); + if (SurfaceControlRegistry.sCallStackDebuggingEnabled) { + SurfaceControlRegistry.getProcessInstance().checkCallStackDebugging( + "setClientDrawnShadows", this, sc, + "clientDrawnShadowRadius=" + clientDrawnShadowRadius); + } + if (Flags.ignoreCornerRadiusAndShadows()) { + nativeSetClientDrawnShadows(mNativeObject, sc.mNativeObject, + clientDrawnShadowRadius); + } else { + Log.w(TAG, "setClientDrawnShadows was called but" + + "ignore_corner_radius_and_shadows flag is disabled"); + } + return this; + } + /** * Sets the background blur radius of the {@link SurfaceControl}. * diff --git a/core/java/android/view/WindowManagerPolicyConstants.java b/core/java/android/view/WindowManagerPolicyConstants.java index 1f341caa8ed3..6d2c0d0061dd 100644 --- a/core/java/android/view/WindowManagerPolicyConstants.java +++ b/core/java/android/view/WindowManagerPolicyConstants.java @@ -30,7 +30,8 @@ import java.lang.annotation.RetentionPolicy; * @hide */ public interface WindowManagerPolicyConstants { - // Policy flags. These flags are also defined in frameworks/base/include/ui/Input.h and + // Policy flags. These flags are also defined in + // frameworks/native/include/input/Input.h and // frameworks/native/libs/input/android/os/IInputConstants.aidl int FLAG_WAKE = 0x00000001; int FLAG_VIRTUAL = 0x00000002; diff --git a/core/java/android/view/contentprotection/flags/content_protection_flags.aconfig b/core/java/android/view/contentprotection/flags/content_protection_flags.aconfig index b3bd92b37357..c871d568e625 100644 --- a/core/java/android/view/contentprotection/flags/content_protection_flags.aconfig +++ b/core/java/android/view/contentprotection/flags/content_protection_flags.aconfig @@ -45,3 +45,10 @@ flag { description: "If true, the APIs to manage content protection device policy will be enabled." bug: "319477846" } + +flag { + name: "exported_settings_activity_enabled" + namespace: "content_protection" + description: "If true, the content protection Settings Activity will be exported for launching externally." + bug: "385310141" +} diff --git a/core/java/android/window/flags/window_surfaces.aconfig b/core/java/android/window/flags/window_surfaces.aconfig index bb4770768cb1..8ff2e6aebdd0 100644 --- a/core/java/android/window/flags/window_surfaces.aconfig +++ b/core/java/android/window/flags/window_surfaces.aconfig @@ -91,6 +91,14 @@ flag { } flag { + name: "ignore_corner_radius_and_shadows" + namespace: "window_surfaces" + description: "Ignore the corner radius and shadows of a SurfaceControl" + bug: "375624570" + is_fixed_read_only: true +} # ignore_corner_radius_and_shadows + +flag { name: "jank_api" namespace: "window_surfaces" description: "Adds the jank data listener to AttachedSurfaceControl" diff --git a/core/java/android/window/flags/windowing_frontend.aconfig b/core/java/android/window/flags/windowing_frontend.aconfig index a8641326b1f2..de3e0d3faf43 100644 --- a/core/java/android/window/flags/windowing_frontend.aconfig +++ b/core/java/android/window/flags/windowing_frontend.aconfig @@ -113,13 +113,6 @@ flag { } flag { - name: "predictive_back_system_anims" - namespace: "systemui" - description: "Predictive back for system animations" - bug: "320510464" -} - -flag { name: "remove_activity_starter_dream_callback" namespace: "windowing_frontend" description: "Avoid a race with DreamManagerService callbacks for isDreaming by checking Activity state directly" diff --git a/core/java/com/android/internal/policy/IKeyguardService.aidl b/core/java/com/android/internal/policy/IKeyguardService.aidl index d62c8f378af0..73c2265d5f6e 100644 --- a/core/java/com/android/internal/policy/IKeyguardService.aidl +++ b/core/java/com/android/internal/policy/IKeyguardService.aidl @@ -53,21 +53,21 @@ oneway interface IKeyguardService { * * @param pmSleepReason One of PowerManager.GO_TO_SLEEP_REASON_*, detailing the specific reason * we're going to sleep, such as GO_TO_SLEEP_REASON_POWER_BUTTON or GO_TO_SLEEP_REASON_TIMEOUT. - * @param cameraGestureTriggered whether the camera gesture was triggered between - * {@link #onStartedGoingToSleep} and this method; if it's been - * triggered, we shouldn't lock the device. + * @param powerButtonLaunchGestureTriggered whether the power button double tap gesture was + * triggered between {@link #onStartedGoingToSleep} and this + * method; if it's been triggered, we shouldn't lock the device. */ - void onFinishedGoingToSleep(int pmSleepReason, boolean cameraGestureTriggered); + void onFinishedGoingToSleep(int pmSleepReason, boolean powerButtonLaunchGestureTriggered); /** * Called when the device has started waking up. * @param pmWakeReason One of PowerManager.WAKE_REASON_*, detailing the reason we're waking up, * such as WAKE_REASON_POWER_BUTTON or WAKE_REASON_GESTURE. - * @param cameraGestureTriggered Whether we're waking up due to a power button double tap - * gesture. + * @param powerButtonLaunchGestureTriggered Whether we're waking up due to a power button + * double tap gesture. */ - void onStartedWakingUp(int pmWakeReason, boolean cameraGestureTriggered); + void onStartedWakingUp(int pmWakeReason, boolean powerButtonLaunchGestureTriggered); /** * Called when the device has finished waking up. diff --git a/core/jni/android_content_res_ApkAssets.cpp b/core/jni/android_content_res_ApkAssets.cpp index 1e7bfe32ba79..e6364a96bd9f 100644 --- a/core/jni/android_content_res_ApkAssets.cpp +++ b/core/jni/android_content_res_ApkAssets.cpp @@ -111,9 +111,8 @@ static void DeleteGuardedApkAssets(Guarded<AssetManager2::ApkAssetsPtr>& apk_ass class LoaderAssetsProvider : public AssetsProvider { public: static std::unique_ptr<AssetsProvider> Create(JNIEnv* env, jobject assets_provider) { - return (!assets_provider) ? EmptyAssetsProvider::Create() - : std::unique_ptr<AssetsProvider>(new LoaderAssetsProvider( - env, assets_provider)); + return std::unique_ptr<AssetsProvider>{ + assets_provider ? new LoaderAssetsProvider(env, assets_provider) : nullptr}; } bool ForEachFile(const std::string& /* root_path */, @@ -129,8 +128,8 @@ class LoaderAssetsProvider : public AssetsProvider { return debug_name_; } - bool IsUpToDate() const override { - return true; + UpToDate IsUpToDate() const override { + return UpToDate::Always; } ~LoaderAssetsProvider() override { @@ -212,7 +211,7 @@ class LoaderAssetsProvider : public AssetsProvider { auto string_result = static_cast<jstring>(env->CallObjectMethod( assets_provider_, gAssetsProviderOffsets.toString)); ScopedUtfChars str(env, string_result); - debug_name_ = std::string(str.c_str(), str.size()); + debug_name_ = std::string(str.c_str()); } // The global reference to the AssetsProvider @@ -243,10 +242,10 @@ static jlong NativeLoad(JNIEnv* env, jclass /*clazz*/, const format_type_t forma apk_assets = ApkAssets::LoadOverlay(path.c_str(), property_flags); break; case FORMAT_ARSC: - apk_assets = ApkAssets::LoadTable(AssetsProvider::CreateAssetFromFile(path.c_str()), - std::move(loader_assets), - property_flags); - break; + apk_assets = ApkAssets::LoadTable(AssetsProvider::CreateAssetFromFile(path.c_str()), + MultiAssetsProvider::Create(std::move(loader_assets)), + property_flags); + break; case FORMAT_DIRECTORY: { auto assets = MultiAssetsProvider::Create(std::move(loader_assets), DirectoryAssetsProvider::Create(path.c_str())); @@ -316,10 +315,11 @@ static jlong NativeLoadFromFd(JNIEnv* env, jclass /*clazz*/, const format_type_t break; } case FORMAT_ARSC: - apk_assets = ApkAssets::LoadTable( - AssetsProvider::CreateAssetFromFd(std::move(dup_fd), nullptr /* path */), - std::move(loader_assets), property_flags); - break; + apk_assets = ApkAssets::LoadTable(AssetsProvider::CreateAssetFromFd(std::move(dup_fd), + nullptr /* path */), + MultiAssetsProvider::Create(std::move(loader_assets)), + property_flags); + break; default: const std::string error_msg = base::StringPrintf("Unsupported format type %d", format); jniThrowException(env, "java/lang/IllegalArgumentException", error_msg.c_str()); @@ -386,12 +386,15 @@ static jlong NativeLoadFromFdOffset(JNIEnv* env, jclass /*clazz*/, const format_ break; } case FORMAT_ARSC: - apk_assets = ApkAssets::LoadTable( - AssetsProvider::CreateAssetFromFd(std::move(dup_fd), nullptr /* path */, - static_cast<off64_t>(offset), - static_cast<off64_t>(length)), - std::move(loader_assets), property_flags); - break; + apk_assets = + ApkAssets::LoadTable(AssetsProvider::CreateAssetFromFd(std::move(dup_fd), + nullptr /* path */, + static_cast<off64_t>(offset), + static_cast<off64_t>( + length)), + MultiAssetsProvider::Create(std::move(loader_assets)), + property_flags); + break; default: const std::string error_msg = base::StringPrintf("Unsupported format type %d", format); jniThrowException(env, "java/lang/IllegalArgumentException", error_msg.c_str()); @@ -408,13 +411,16 @@ static jlong NativeLoadFromFdOffset(JNIEnv* env, jclass /*clazz*/, const format_ } static jlong NativeLoadEmpty(JNIEnv* env, jclass /*clazz*/, jint flags, jobject assets_provider) { - auto apk_assets = ApkAssets::Load(LoaderAssetsProvider::Create(env, assets_provider), flags); - if (apk_assets == nullptr) { - const std::string error_msg = - base::StringPrintf("Failed to load empty assets with provider %p", (void*)assets_provider); - jniThrowException(env, "java/io/IOException", error_msg.c_str()); - return 0; - } + auto apk_assets = ApkAssets::Load(MultiAssetsProvider::Create( + LoaderAssetsProvider::Create(env, assets_provider)), + flags); + if (apk_assets == nullptr) { + const std::string error_msg = + base::StringPrintf("Failed to load empty assets with provider %p", + (void*)assets_provider); + jniThrowException(env, "java/io/IOException", error_msg.c_str()); + return 0; + } return CreateGuardedApkAssets(std::move(apk_assets)); } @@ -443,10 +449,10 @@ static jlong NativeGetStringBlock(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) return reinterpret_cast<jlong>(apk_assets->GetLoadedArsc()->GetStringPool()); } -static jboolean NativeIsUpToDate(CRITICAL_JNI_PARAMS_COMMA jlong ptr) { +static jint NativeIsUpToDate(CRITICAL_JNI_PARAMS_COMMA jlong ptr) { auto scoped_apk_assets = ScopedLock(ApkAssetsFromLong(ptr)); auto apk_assets = scoped_apk_assets->get(); - return apk_assets->IsUpToDate() ? JNI_TRUE : JNI_FALSE; + return (jint)apk_assets->IsUpToDate(); } static jlong NativeOpenXml(JNIEnv* env, jclass /*clazz*/, jlong ptr, jstring file_name) { @@ -558,7 +564,7 @@ static const JNINativeMethod gApkAssetsMethods[] = { {"nativeGetDebugName", "(J)Ljava/lang/String;", (void*)NativeGetDebugName}, {"nativeGetStringBlock", "(J)J", (void*)NativeGetStringBlock}, // @CriticalNative - {"nativeIsUpToDate", "(J)Z", (void*)NativeIsUpToDate}, + {"nativeIsUpToDate", "(J)I", (void*)NativeIsUpToDate}, {"nativeOpenXml", "(JLjava/lang/String;)J", (void*)NativeOpenXml}, {"nativeGetOverlayableInfo", "(JLjava/lang/String;)Landroid/content/om/OverlayableInfo;", (void*)NativeGetOverlayableInfo}, diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp index 0c243d1dc185..6f69e4005b80 100644 --- a/core/jni/android_view_SurfaceControl.cpp +++ b/core/jni/android_view_SurfaceControl.cpp @@ -1113,6 +1113,22 @@ static void nativeSetCornerRadius(JNIEnv* env, jclass clazz, jlong transactionOb transaction->setCornerRadius(ctrl, cornerRadius); } +static void nativeSetClientDrawnCornerRadius(JNIEnv* env, jclass clazz, jlong transactionObj, + jlong nativeObject, jfloat clientDrawnCornerRadius) { + auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj); + + SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl*>(nativeObject); + transaction->setClientDrawnCornerRadius(ctrl, clientDrawnCornerRadius); +} + +static void nativeSetClientDrawnShadows(JNIEnv* env, jclass clazz, jlong transactionObj, + jlong nativeObject, jfloat clientDrawnShadowRadius) { + auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj); + + SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl*>(nativeObject); + transaction->setClientDrawnShadowRadius(ctrl, clientDrawnShadowRadius); +} + static void nativeSetBackgroundBlurRadius(JNIEnv* env, jclass clazz, jlong transactionObj, jlong nativeObject, jint blurRadius) { auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj); @@ -2547,6 +2563,10 @@ static const JNINativeMethod sSurfaceControlMethods[] = { (void*)nativeSetCrop }, {"nativeSetCornerRadius", "(JJF)V", (void*)nativeSetCornerRadius }, + {"nativeSetClientDrawnCornerRadius", "(JJF)V", + (void*) nativeSetClientDrawnCornerRadius }, + {"nativeSetClientDrawnShadows", "(JJF)V", + (void*) nativeSetClientDrawnShadows }, {"nativeSetBackgroundBlurRadius", "(JJI)V", (void*)nativeSetBackgroundBlurRadius }, {"nativeSetLayerStack", "(JJI)V", diff --git a/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheTest.java b/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheTest.java index 37ef6cba8814..939bf2ec4b0a 100644 --- a/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheTest.java +++ b/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheTest.java @@ -207,7 +207,8 @@ public class RegisteredServicesCacheTest extends AndroidTestCase { final ComponentInfo info = new ComponentInfo(); info.applicationInfo = new ApplicationInfo(); info.applicationInfo.uid = uid; - return new RegisteredServicesCache.ServiceInfo<>(type, info, null); + return new RegisteredServicesCache.ServiceInfo<>(type, info, null /* componentName */, + 0 /* lastUpdateTime */); } private void assertNotEmptyFileCreated(TestServicesCache cache, int userId) { @@ -301,7 +302,7 @@ public class RegisteredServicesCacheTest extends AndroidTestCase { @Override protected ServiceInfo<TestServiceType> parseServiceInfo( - ResolveInfo resolveInfo) throws XmlPullParserException, IOException { + ResolveInfo resolveInfo, int userId) throws XmlPullParserException, IOException { int size = mServices.size(); for (int i = 0; i < size; i++) { Map<ResolveInfo, ServiceInfo<TestServiceType>> map = mServices.valueAt(i); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java index e96bc02c1198..cacfbfaac954 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java @@ -28,7 +28,6 @@ import static android.window.TransitionInfo.FLAG_MOVED_TO_TOP; import static android.window.TransitionInfo.FLAG_SHOW_WALLPAPER; import static com.android.internal.jank.InteractionJankMonitor.CUJ_PREDICTIVE_BACK_HOME; -import static com.android.window.flags.Flags.predictiveBackSystemAnims; import static com.android.window.flags.Flags.unifyBackNavigationTransition; import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_BACK_PREVIEW; @@ -40,23 +39,17 @@ import android.app.ActivityTaskManager; import android.app.IActivityTaskManager; import android.app.TaskInfo; import android.content.ComponentName; -import android.content.ContentResolver; import android.content.Context; import android.content.res.Configuration; -import android.database.ContentObserver; import android.graphics.Point; import android.graphics.Rect; import android.hardware.input.InputManager; -import android.net.Uri; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.RemoteCallback; import android.os.RemoteException; import android.os.SystemClock; -import android.os.SystemProperties; -import android.os.UserHandle; -import android.provider.Settings.Global; import android.util.Log; import android.view.IRemoteAnimationRunner; import android.view.InputDevice; @@ -92,7 +85,6 @@ import com.android.wm.shell.common.ExternalInterfaceBinder; import com.android.wm.shell.common.RemoteCallable; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.shared.TransitionUtil; -import com.android.wm.shell.shared.annotations.ShellBackgroundThread; import com.android.wm.shell.shared.annotations.ShellMainThread; import com.android.wm.shell.sysui.ConfigurationChangeListener; import com.android.wm.shell.sysui.ShellCommandHandler; @@ -102,7 +94,6 @@ import com.android.wm.shell.transition.Transitions; import java.io.PrintWriter; import java.util.ArrayList; -import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Predicate; /** @@ -111,14 +102,7 @@ import java.util.function.Predicate; public class BackAnimationController implements RemoteCallable<BackAnimationController>, ConfigurationChangeListener { private static final String TAG = "ShellBackPreview"; - private static final int SETTING_VALUE_OFF = 0; - private static final int SETTING_VALUE_ON = 1; - public static final boolean IS_ENABLED = - SystemProperties.getInt("persist.wm.debug.predictive_back", - SETTING_VALUE_ON) == SETTING_VALUE_ON; - - /** Predictive back animation developer option */ - private final AtomicBoolean mEnableAnimations = new AtomicBoolean(false); + /** * Max duration to wait for an animation to finish before triggering the real back. */ @@ -148,11 +132,9 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont private boolean mReceivedNullNavigationInfo = false; private final IActivityTaskManager mActivityTaskManager; private final Context mContext; - private final ContentResolver mContentResolver; private final ShellController mShellController; private final ShellCommandHandler mShellCommandHandler; private final ShellExecutor mShellExecutor; - private final Handler mBgHandler; private final WindowManager mWindowManager; private final Transitions mTransitions; @VisibleForTesting @@ -245,7 +227,6 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont @NonNull ShellInit shellInit, @NonNull ShellController shellController, @NonNull @ShellMainThread ShellExecutor shellExecutor, - @NonNull @ShellBackgroundThread Handler backgroundHandler, Context context, @NonNull BackAnimationBackground backAnimationBackground, ShellBackAnimationRegistry shellBackAnimationRegistry, @@ -256,10 +237,8 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont shellInit, shellController, shellExecutor, - backgroundHandler, ActivityTaskManager.getService(), context, - context.getContentResolver(), backAnimationBackground, shellBackAnimationRegistry, shellCommandHandler, @@ -272,10 +251,8 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont @NonNull ShellInit shellInit, @NonNull ShellController shellController, @NonNull @ShellMainThread ShellExecutor shellExecutor, - @NonNull @ShellBackgroundThread Handler bgHandler, @NonNull IActivityTaskManager activityTaskManager, Context context, - ContentResolver contentResolver, @NonNull BackAnimationBackground backAnimationBackground, ShellBackAnimationRegistry shellBackAnimationRegistry, ShellCommandHandler shellCommandHandler, @@ -285,10 +262,8 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont mShellExecutor = shellExecutor; mActivityTaskManager = activityTaskManager; mContext = context; - mContentResolver = contentResolver; mRequirePointerPilfer = context.getResources().getBoolean(R.bool.config_backAnimationRequiresPointerPilfer); - mBgHandler = bgHandler; shellInit.addInitCallback(this::onInit, this); mAnimationBackground = backAnimationBackground; mShellBackAnimationRegistry = shellBackAnimationRegistry; @@ -305,8 +280,6 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont } private void onInit() { - setupAnimationDeveloperSettingsObserver(mContentResolver, mBgHandler); - updateEnableAnimationFromFlags(); createAdapter(); mShellController.addExternalInterface(IBackAnimation.DESCRIPTOR, this::createExternalInterface, this); @@ -314,42 +287,6 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont mShellController.addConfigurationChangeListener(this); } - private void setupAnimationDeveloperSettingsObserver( - @NonNull ContentResolver contentResolver, - @NonNull @ShellBackgroundThread final Handler backgroundHandler) { - if (predictiveBackSystemAnims()) { - ProtoLog.d(WM_SHELL_BACK_PREVIEW, "Back animation aconfig flag is enabled, therefore " - + "developer settings flag is ignored and no content observer registered"); - return; - } - ContentObserver settingsObserver = new ContentObserver(backgroundHandler) { - @Override - public void onChange(boolean selfChange, Uri uri) { - updateEnableAnimationFromFlags(); - } - }; - contentResolver.registerContentObserver( - Global.getUriFor(Global.ENABLE_BACK_ANIMATION), - false, settingsObserver, UserHandle.USER_SYSTEM - ); - } - - /** - * Updates {@link BackAnimationController#mEnableAnimations} based on the current values of the - * aconfig flag and the developer settings flag - */ - @ShellBackgroundThread - private void updateEnableAnimationFromFlags() { - boolean isEnabled = predictiveBackSystemAnims() || isDeveloperSettingEnabled(); - mEnableAnimations.set(isEnabled); - ProtoLog.d(WM_SHELL_BACK_PREVIEW, "Back animation enabled=%s", isEnabled); - } - - private boolean isDeveloperSettingEnabled() { - return Global.getInt(mContext.getContentResolver(), - Global.ENABLE_BACK_ANIMATION, SETTING_VALUE_OFF) == SETTING_VALUE_ON; - } - public BackAnimation getBackAnimationImpl() { return mBackAnimation; } @@ -617,14 +554,13 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont private void startBackNavigation(@NonNull BackTouchTracker touchTracker) { try { startLatencyTracking(); - final BackAnimationAdapter adapter = mEnableAnimations.get() - ? mBackAnimationAdapter : null; - if (adapter != null && mShellBackAnimationRegistry.hasSupportedAnimatorsChanged()) { - adapter.updateSupportedAnimators( + if (mBackAnimationAdapter != null + && mShellBackAnimationRegistry.hasSupportedAnimatorsChanged()) { + mBackAnimationAdapter.updateSupportedAnimators( mShellBackAnimationRegistry.getSupportedAnimators()); } mBackNavigationInfo = mActivityTaskManager.startBackNavigation( - mNavigationObserver, adapter); + mNavigationObserver, mBackAnimationAdapter); onBackNavigationInfoReceived(mBackNavigationInfo, touchTracker); } catch (RemoteException remoteException) { Log.e(TAG, "Failed to initAnimation", remoteException); @@ -696,9 +632,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont } private boolean shouldDispatchToAnimator() { - return mEnableAnimations.get() - && mBackNavigationInfo != null - && mBackNavigationInfo.isPrepareRemoteAnimation(); + return mBackNavigationInfo != null && mBackNavigationInfo.isPrepareRemoteAnimation(); } private void tryPilferPointers() { @@ -1194,7 +1128,6 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont */ private void dump(PrintWriter pw, String prefix) { pw.println(prefix + "BackAnimationController state:"); - pw.println(prefix + " mEnableAnimations=" + mEnableAnimations.get()); pw.println(prefix + " mBackGestureStarted=" + mBackGestureStarted); pw.println(prefix + " mPostCommitAnimationInProgress=" + mPostCommitAnimationInProgress); pw.println(prefix + " mShouldStartOnNextMoveEvent=" + mShouldStartOnNextMoveEvent); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java index 1408b6efc7f9..2600bcc18f72 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java @@ -108,7 +108,6 @@ import com.android.wm.shell.recents.TaskStackTransitionObserver; import com.android.wm.shell.shared.ShellTransitions; import com.android.wm.shell.shared.TransactionPool; import com.android.wm.shell.shared.annotations.ShellAnimationThread; -import com.android.wm.shell.shared.annotations.ShellBackgroundThread; import com.android.wm.shell.shared.annotations.ShellMainThread; import com.android.wm.shell.shared.annotations.ShellSplashscreenThread; import com.android.wm.shell.shared.desktopmode.DesktopModeStatus; @@ -438,29 +437,24 @@ public abstract class WMShellBaseModule { ShellInit shellInit, ShellController shellController, @ShellMainThread ShellExecutor shellExecutor, - @ShellBackgroundThread Handler backgroundHandler, BackAnimationBackground backAnimationBackground, Optional<ShellBackAnimationRegistry> shellBackAnimationRegistry, ShellCommandHandler shellCommandHandler, Transitions transitions, @ShellMainThread Handler handler ) { - if (BackAnimationController.IS_ENABLED) { return shellBackAnimationRegistry.map( (animations) -> new BackAnimationController( shellInit, shellController, shellExecutor, - backgroundHandler, context, backAnimationBackground, animations, shellCommandHandler, transitions, handler)); - } - return Optional.empty(); } @BindsOptionalOf diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java index 03f388c9f1c9..9e2b9b20be16 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java @@ -117,6 +117,7 @@ public abstract class Pip2Module { PipTouchHandler pipTouchHandler, PipAppOpsListener pipAppOpsListener, PhonePipMenuController pipMenuController, + PipUiEventLogger pipUiEventLogger, @ShellMainThread ShellExecutor mainExecutor) { if (!PipUtils.isPip2ExperimentEnabled()) { return Optional.empty(); @@ -126,7 +127,7 @@ public abstract class Pip2Module { displayInsetsController, pipBoundsState, pipBoundsAlgorithm, pipDisplayLayoutState, pipScheduler, taskStackListener, shellTaskOrganizer, pipTransitionState, pipTouchHandler, pipAppOpsListener, pipMenuController, - mainExecutor)); + pipUiEventLogger, mainExecutor)); } } @@ -188,11 +189,11 @@ public abstract class Pip2Module { FloatingContentCoordinator floatingContentCoordinator, PipScheduler pipScheduler, Optional<PipPerfHintController> pipPerfHintControllerOptional, - PipBoundsAlgorithm pipBoundsAlgorithm, - PipTransitionState pipTransitionState) { + PipTransitionState pipTransitionState, + PipUiEventLogger pipUiEventLogger) { return new PipMotionHelper(context, pipBoundsState, menuController, pipSnapAlgorithm, floatingContentCoordinator, pipScheduler, pipPerfHintControllerOptional, - pipBoundsAlgorithm, pipTransitionState); + pipTransitionState, pipUiEventLogger); } @WMSingleton diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java index 8c6d5f5c6660..562b26014bf3 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java @@ -59,6 +59,7 @@ import com.android.wm.shell.common.pip.PipAppOpsListener; import com.android.wm.shell.common.pip.PipBoundsAlgorithm; import com.android.wm.shell.common.pip.PipBoundsState; import com.android.wm.shell.common.pip.PipDisplayLayoutState; +import com.android.wm.shell.common.pip.PipUiEventLogger; import com.android.wm.shell.common.pip.PipUtils; import com.android.wm.shell.pip.Pip; import com.android.wm.shell.protolog.ShellProtoLogGroup; @@ -98,6 +99,7 @@ public class PipController implements ConfigurationChangeListener, private final PipTouchHandler mPipTouchHandler; private final PipAppOpsListener mPipAppOpsListener; private final PhonePipMenuController mPipMenuController; + private final PipUiEventLogger mPipUiEventLogger; private final ShellExecutor mMainExecutor; private final PipImpl mImpl; private final List<Consumer<Boolean>> mOnIsInPipStateChangedListeners = new ArrayList<>(); @@ -143,6 +145,7 @@ public class PipController implements ConfigurationChangeListener, PipTouchHandler pipTouchHandler, PipAppOpsListener pipAppOpsListener, PhonePipMenuController pipMenuController, + PipUiEventLogger pipUiEventLogger, ShellExecutor mainExecutor) { mContext = context; mShellCommandHandler = shellCommandHandler; @@ -160,6 +163,7 @@ public class PipController implements ConfigurationChangeListener, mPipTouchHandler = pipTouchHandler; mPipAppOpsListener = pipAppOpsListener; mPipMenuController = pipMenuController; + mPipUiEventLogger = pipUiEventLogger; mMainExecutor = mainExecutor; mImpl = new PipImpl(); @@ -187,6 +191,7 @@ public class PipController implements ConfigurationChangeListener, PipTouchHandler pipTouchHandler, PipAppOpsListener pipAppOpsListener, PhonePipMenuController pipMenuController, + PipUiEventLogger pipUiEventLogger, ShellExecutor mainExecutor) { if (!context.getPackageManager().hasSystemFeature(FEATURE_PICTURE_IN_PICTURE)) { ProtoLog.w(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, @@ -197,7 +202,7 @@ public class PipController implements ConfigurationChangeListener, displayController, displayInsetsController, pipBoundsState, pipBoundsAlgorithm, pipDisplayLayoutState, pipScheduler, taskStackListener, shellTaskOrganizer, pipTransitionState, pipTouchHandler, pipAppOpsListener, pipMenuController, - mainExecutor); + pipUiEventLogger, mainExecutor); } public PipImpl getPipImpl() { @@ -238,18 +243,6 @@ public class PipController implements ConfigurationChangeListener, }); mPipAppOpsListener.setCallback(mPipTouchHandler.getMotionHelper()); - mPipTransitionState.addPipTransitionStateChangedListener( - (oldState, newState, extra) -> { - if (newState == PipTransitionState.ENTERED_PIP) { - final TaskInfo taskInfo = mPipTransitionState.getPipTaskInfo(); - if (taskInfo != null && taskInfo.topActivity != null) { - mPipAppOpsListener.onActivityPinned( - taskInfo.topActivity.getPackageName()); - } - } else if (newState == PipTransitionState.EXITED_PIP) { - mPipAppOpsListener.onActivityUnpinned(); - } - }); } private ExternalInterfaceBinder createExternalInterface() { @@ -446,14 +439,25 @@ public class PipController implements ConfigurationChangeListener, mPipTransitionState.setSwipePipToHomeState(overlay, appBounds); break; case PipTransitionState.ENTERED_PIP: + final TaskInfo taskInfo = mPipTransitionState.getPipTaskInfo(); + if (taskInfo != null && taskInfo.topActivity != null) { + mPipAppOpsListener.onActivityPinned(taskInfo.topActivity.getPackageName()); + mPipUiEventLogger.setTaskInfo(taskInfo); + } if (mPipTransitionState.isInSwipePipToHomeTransition()) { + mPipUiEventLogger.log( + PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_AUTO_ENTER); mPipTransitionState.resetSwipePipToHomeState(); + } else { + mPipUiEventLogger.log(PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_ENTER); } for (Consumer<Boolean> listener : mOnIsInPipStateChangedListeners) { listener.accept(true /* inPip */); } break; case PipTransitionState.EXITED_PIP: + mPipAppOpsListener.onActivityUnpinned(); + mPipUiEventLogger.setTaskInfo(null); for (Consumer<Boolean> listener : mOnIsInPipStateChangedListeners) { listener.accept(false /* inPip */); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipMotionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipMotionHelper.java index 37296531ee34..9babe9e9e4eb 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipMotionHelper.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipMotionHelper.java @@ -43,20 +43,20 @@ import com.android.wm.shell.R; import com.android.wm.shell.animation.FloatProperties; import com.android.wm.shell.common.FloatingContentCoordinator; import com.android.wm.shell.common.pip.PipAppOpsListener; -import com.android.wm.shell.common.pip.PipBoundsAlgorithm; import com.android.wm.shell.common.pip.PipBoundsState; import com.android.wm.shell.common.pip.PipPerfHintController; import com.android.wm.shell.common.pip.PipSnapAlgorithm; +import com.android.wm.shell.common.pip.PipUiEventLogger; import com.android.wm.shell.pip2.animation.PipResizeAnimator; import com.android.wm.shell.protolog.ShellProtoLogGroup; import com.android.wm.shell.shared.animation.PhysicsAnimator; import com.android.wm.shell.shared.magnetictarget.MagnetizedObject; +import java.util.Optional; + import kotlin.Unit; import kotlin.jvm.functions.Function0; -import java.util.Optional; - /** * A helper to animate and manipulate the PiP. */ @@ -80,12 +80,12 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, private static final float DISMISS_CIRCLE_PERCENT = 0.85f; private final Context mContext; - private @NonNull PipBoundsState mPipBoundsState; - private @NonNull PipBoundsAlgorithm mPipBoundsAlgorithm; - private @NonNull PipScheduler mPipScheduler; - private @NonNull PipTransitionState mPipTransitionState; - private PhonePipMenuController mMenuController; - private PipSnapAlgorithm mSnapAlgorithm; + @NonNull private final PipBoundsState mPipBoundsState; + @NonNull private final PipScheduler mPipScheduler; + @NonNull private final PipTransitionState mPipTransitionState; + @NonNull private final PipUiEventLogger mPipUiEventLogger; + private final PhonePipMenuController mMenuController; + private final PipSnapAlgorithm mSnapAlgorithm; /** The region that all of PIP must stay within. */ private final Rect mFloatingAllowedArea = new Rect(); @@ -168,10 +168,9 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, PhonePipMenuController menuController, PipSnapAlgorithm snapAlgorithm, FloatingContentCoordinator floatingContentCoordinator, PipScheduler pipScheduler, Optional<PipPerfHintController> pipPerfHintControllerOptional, - PipBoundsAlgorithm pipBoundsAlgorithm, PipTransitionState pipTransitionState) { + PipTransitionState pipTransitionState, PipUiEventLogger pipUiEventLogger) { mContext = context; mPipBoundsState = pipBoundsState; - mPipBoundsAlgorithm = pipBoundsAlgorithm; mPipScheduler = pipScheduler; mMenuController = menuController; mSnapAlgorithm = snapAlgorithm; @@ -185,6 +184,7 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, }; mPipTransitionState = pipTransitionState; mPipTransitionState.addPipTransitionStateChangedListener(this); + mPipUiEventLogger = pipUiEventLogger; } void init() { @@ -850,9 +850,11 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, if (mPipBoundsState.getBounds().left < 0 && mPipBoundsState.getStashedState() != STASH_TYPE_LEFT) { mPipBoundsState.setStashed(STASH_TYPE_LEFT); + mPipUiEventLogger.log(PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_STASH_LEFT); } else if (mPipBoundsState.getBounds().left >= 0 && mPipBoundsState.getStashedState() != STASH_TYPE_RIGHT) { mPipBoundsState.setStashed(STASH_TYPE_RIGHT); + mPipUiEventLogger.log(PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_STASH_RIGHT); } mMenuController.hideMenu(); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentsAnimationController.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentsAnimationController.aidl index 964e5fd62a5f..af1679f2d175 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentsAnimationController.aidl +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentsAnimationController.aidl @@ -36,12 +36,6 @@ import com.android.internal.os.IResultReceiver; interface IRecentsAnimationController { /** - * Takes a screenshot of the task associated with the given {@param taskId}. Only valid for the - * current set of task ids provided to the handler. - */ - TaskSnapshot screenshotTask(int taskId); - - /** * Sets the final surface transaction on a Task. This is used by Launcher to notify the system * that animating Activity to PiP has completed and the associated task surface should be * updated accordingly. This should be called before `finish` 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 032dac9ff3a2..76496b06a4dd 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 @@ -1227,19 +1227,6 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler, } @Override - public TaskSnapshot screenshotTask(int taskId) { - try { - ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION, - "[%d] RecentsController.screenshotTask: taskId=%d", mInstanceId, taskId); - return ActivityTaskManager.getService().takeTaskSnapshot(taskId, - true /* updateCache */); - } catch (RemoteException e) { - Slog.e(TAG, "Failed to screenshot task", e); - } - return null; - } - - @Override public void setInputConsumerEnabled(boolean enabled) { mExecutor.execute(() -> { if (mFinishCB == null || !enabled) { diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java index 47ee7bb20199..bbdb90f0a37c 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java @@ -61,9 +61,7 @@ import android.os.RemoteCallback; import android.os.RemoteException; import android.platform.test.annotations.EnableFlags; import android.platform.test.flag.junit.SetFlagsRule; -import android.provider.Settings; import android.testing.AndroidTestingRunner; -import android.testing.TestableContentResolver; import android.testing.TestableLooper; import android.view.IRemoteAnimationRunner; import android.view.KeyEvent; @@ -84,7 +82,6 @@ import android.window.WindowContainerToken; import androidx.annotation.Nullable; import androidx.test.filters.SmallTest; -import com.android.internal.util.test.FakeSettingsProvider; import com.android.wm.shell.RootTaskDisplayAreaOrganizer; import com.android.wm.shell.ShellTestCase; import com.android.wm.shell.TestShellExecutor; @@ -109,7 +106,6 @@ import org.mockito.MockitoAnnotations; @RunWith(AndroidTestingRunner.class) public class BackAnimationControllerTest extends ShellTestCase { - private static final String ANIMATION_ENABLED = "1"; private final TestShellExecutor mShellExecutor = new TestShellExecutor(); private ShellInit mShellInit; @@ -148,8 +144,6 @@ public class BackAnimationControllerTest extends ShellTestCase { private Transitions.TransitionHandler mTakeoverHandler; private BackAnimationController mController; - private TestableContentResolver mContentResolver; - private TestableLooper mTestableLooper; private DefaultCrossActivityBackAnimation mDefaultCrossActivityBackAnimation; private CrossTaskBackAnimation mCrossTaskBackAnimation; @@ -166,11 +160,6 @@ public class BackAnimationControllerTest extends ShellTestCase { MockitoAnnotations.initMocks(this); mContext.addMockSystemService(InputManager.class, mInputManager); mContext.getApplicationInfo().privateFlags |= ApplicationInfo.PRIVATE_FLAG_PRIVILEGED; - mContentResolver = new TestableContentResolver(mContext); - mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider()); - Settings.Global.putString(mContentResolver, Settings.Global.ENABLE_BACK_ANIMATION, - ANIMATION_ENABLED); - mTestableLooper = TestableLooper.get(this); mShellInit = spy(new ShellInit(mShellExecutor)); mDefaultCrossActivityBackAnimation = new DefaultCrossActivityBackAnimation(mContext, mAnimationBackground, mRootTaskDisplayAreaOrganizer, mHandler); @@ -187,10 +176,8 @@ public class BackAnimationControllerTest extends ShellTestCase { mShellInit, mShellController, mShellExecutor, - new Handler(mTestableLooper.getLooper()), mActivityTaskManager, mContext, - mContentResolver, mAnimationBackground, mShellBackAnimationRegistry, mShellCommandHandler, @@ -342,47 +329,6 @@ public class BackAnimationControllerTest extends ShellTestCase { } @Test - public void animationDisabledFromSettings() throws RemoteException { - // Toggle the setting off - Settings.Global.putString(mContentResolver, Settings.Global.ENABLE_BACK_ANIMATION, "0"); - ShellInit shellInit = new ShellInit(mShellExecutor); - mController = - new BackAnimationController( - shellInit, - mShellController, - mShellExecutor, - new Handler(mTestableLooper.getLooper()), - mActivityTaskManager, - mContext, - mContentResolver, - mAnimationBackground, - mShellBackAnimationRegistry, - mShellCommandHandler, - mTransitions, - mHandler); - shellInit.init(); - registerAnimation(BackNavigationInfo.TYPE_RETURN_TO_HOME); - - ArgumentCaptor<BackMotionEvent> backEventCaptor = - ArgumentCaptor.forClass(BackMotionEvent.class); - - createNavigationInfo(BackNavigationInfo.TYPE_RETURN_TO_HOME, - /* enableAnimation = */ false, - /* isAnimationCallback = */ false); - - triggerBackGesture(); - releaseBackGesture(); - - verify(mAppCallback, times(1)).onBackInvoked(); - - verify(mAnimatorCallback, never()).onBackStarted(any()); - verify(mAnimatorCallback, never()).onBackProgressed(backEventCaptor.capture()); - verify(mAnimatorCallback, never()).onBackInvoked(); - verify(mBackAnimationRunner, never()).onAnimationStart( - anyInt(), any(), any(), any(), any()); - } - - @Test public void gestureQueued_WhenPreviousTransitionHasNotYetEnded() throws RemoteException { registerAnimation(BackNavigationInfo.TYPE_RETURN_TO_HOME); createNavigationInfo(BackNavigationInfo.TYPE_RETURN_TO_HOME, diff --git a/libs/androidfw/ApkAssets.cpp b/libs/androidfw/ApkAssets.cpp index dbb891455ddd..e693fcfd3918 100644 --- a/libs/androidfw/ApkAssets.cpp +++ b/libs/androidfw/ApkAssets.cpp @@ -162,10 +162,13 @@ const std::string& ApkAssets::GetDebugName() const { return assets_provider_->GetDebugName(); } -bool ApkAssets::IsUpToDate() const { +UpToDate ApkAssets::IsUpToDate() const { // Loaders are invalidated by the app, not the system, so assume they are up to date. - return IsLoader() || ((!loaded_idmap_ || loaded_idmap_->IsUpToDate()) - && assets_provider_->IsUpToDate()); + if (IsLoader()) { + return UpToDate::Always; + } + const auto idmap_res = loaded_idmap_ ? loaded_idmap_->IsUpToDate() : UpToDate::Always; + return combine(idmap_res, [this] { return assets_provider_->IsUpToDate(); }); } } // namespace android diff --git a/libs/androidfw/AssetsProvider.cpp b/libs/androidfw/AssetsProvider.cpp index 2d3c06506a1f..11b12eb030a6 100644 --- a/libs/androidfw/AssetsProvider.cpp +++ b/libs/androidfw/AssetsProvider.cpp @@ -24,9 +24,8 @@ #include <ziparchive/zip_archive.h> namespace android { -namespace { -constexpr const char* kEmptyDebugString = "<empty>"; -} // namespace + +static constexpr std::string_view kEmptyDebugString = "<empty>"; std::unique_ptr<Asset> AssetsProvider::Open(const std::string& path, Asset::AccessMode mode, bool* file_exists) const { @@ -86,11 +85,9 @@ void ZipAssetsProvider::ZipCloser::operator()(ZipArchive* a) const { } ZipAssetsProvider::ZipAssetsProvider(ZipArchiveHandle handle, PathOrDebugName&& path, - package_property_t flags, time_t last_mod_time) - : zip_handle_(handle), - name_(std::move(path)), - flags_(flags), - last_mod_time_(last_mod_time) {} + package_property_t flags, ModDate last_mod_time) + : zip_handle_(handle), name_(std::move(path)), flags_(flags), last_mod_time_(last_mod_time) { +} std::unique_ptr<ZipAssetsProvider> ZipAssetsProvider::Create(std::string path, package_property_t flags, @@ -104,10 +101,10 @@ std::unique_ptr<ZipAssetsProvider> ZipAssetsProvider::Create(std::string path, return {}; } - struct stat sb{.st_mtime = -1}; + ModDate mod_date = kInvalidModDate; // Skip all up-to-date checks if the file won't ever change. - if (!isReadonlyFilesystem(path.c_str())) { - if ((released_fd < 0 ? stat(path.c_str(), &sb) : fstat(released_fd, &sb)) < 0) { + if (isKnownWritablePath(path.c_str()) || !isReadonlyFilesystem(GetFileDescriptor(handle))) { + if (mod_date = getFileModDate(GetFileDescriptor(handle)); mod_date == kInvalidModDate) { // Stat requires execute permissions on all directories path to the file. If the process does // not have execute permissions on this file, allow the zip to be opened but IsUpToDate() will // always have to return true. @@ -116,7 +113,7 @@ std::unique_ptr<ZipAssetsProvider> ZipAssetsProvider::Create(std::string path, } return std::unique_ptr<ZipAssetsProvider>( - new ZipAssetsProvider(handle, PathOrDebugName::Path(std::move(path)), flags, sb.st_mtime)); + new ZipAssetsProvider(handle, PathOrDebugName::Path(std::move(path)), flags, mod_date)); } std::unique_ptr<ZipAssetsProvider> ZipAssetsProvider::Create(base::unique_fd fd, @@ -137,10 +134,10 @@ std::unique_ptr<ZipAssetsProvider> ZipAssetsProvider::Create(base::unique_fd fd, return {}; } - struct stat sb{.st_mtime = -1}; + ModDate mod_date = kInvalidModDate; // Skip all up-to-date checks if the file won't ever change. if (!isReadonlyFilesystem(released_fd)) { - if (fstat(released_fd, &sb) < 0) { + if (mod_date = getFileModDate(released_fd); mod_date == kInvalidModDate) { // Stat requires execute permissions on all directories path to the file. If the process does // not have execute permissions on this file, allow the zip to be opened but IsUpToDate() will // always have to return true. @@ -150,7 +147,7 @@ std::unique_ptr<ZipAssetsProvider> ZipAssetsProvider::Create(base::unique_fd fd, } return std::unique_ptr<ZipAssetsProvider>(new ZipAssetsProvider( - handle, PathOrDebugName::DebugName(std::move(friendly_name)), flags, sb.st_mtime)); + handle, PathOrDebugName::DebugName(std::move(friendly_name)), flags, mod_date)); } std::unique_ptr<Asset> ZipAssetsProvider::OpenInternal(const std::string& path, @@ -282,21 +279,16 @@ const std::string& ZipAssetsProvider::GetDebugName() const { return name_.GetDebugName(); } -bool ZipAssetsProvider::IsUpToDate() const { - if (last_mod_time_ == -1) { - return true; - } - struct stat sb{}; - if (fstat(GetFileDescriptor(zip_handle_.get()), &sb) < 0) { - // If fstat fails on the zip archive, return true so the zip archive the resource system does - // attempt to refresh the ApkAsset. - return true; +UpToDate ZipAssetsProvider::IsUpToDate() const { + if (last_mod_time_ == kInvalidModDate) { + return UpToDate::Always; } - return last_mod_time_ == sb.st_mtime; + return fromBool(last_mod_time_ == getFileModDate(GetFileDescriptor(zip_handle_.get()))); } -DirectoryAssetsProvider::DirectoryAssetsProvider(std::string&& path, time_t last_mod_time) - : dir_(std::move(path)), last_mod_time_(last_mod_time) {} +DirectoryAssetsProvider::DirectoryAssetsProvider(std::string&& path, ModDate last_mod_time) + : dir_(std::move(path)), last_mod_time_(last_mod_time) { +} std::unique_ptr<DirectoryAssetsProvider> DirectoryAssetsProvider::Create(std::string path) { struct stat sb; @@ -317,7 +309,7 @@ std::unique_ptr<DirectoryAssetsProvider> DirectoryAssetsProvider::Create(std::st const bool isReadonly = isReadonlyFilesystem(path.c_str()); return std::unique_ptr<DirectoryAssetsProvider>( - new DirectoryAssetsProvider(std::move(path), isReadonly ? -1 : sb.st_mtime)); + new DirectoryAssetsProvider(std::move(path), isReadonly ? kInvalidModDate : getModDate(sb))); } std::unique_ptr<Asset> DirectoryAssetsProvider::OpenInternal(const std::string& path, @@ -346,17 +338,11 @@ const std::string& DirectoryAssetsProvider::GetDebugName() const { return dir_; } -bool DirectoryAssetsProvider::IsUpToDate() const { - if (last_mod_time_ == -1) { - return true; - } - struct stat sb; - if (stat(dir_.c_str(), &sb) < 0) { - // If stat fails on the zip archive, return true so the zip archive the resource system does - // attempt to refresh the ApkAsset. - return true; +UpToDate DirectoryAssetsProvider::IsUpToDate() const { + if (last_mod_time_ == kInvalidModDate) { + return UpToDate::Always; } - return last_mod_time_ == sb.st_mtime; + return fromBool(last_mod_time_ == getFileModDate(dir_.c_str())); } MultiAssetsProvider::MultiAssetsProvider(std::unique_ptr<AssetsProvider>&& primary, @@ -369,8 +355,14 @@ MultiAssetsProvider::MultiAssetsProvider(std::unique_ptr<AssetsProvider>&& prima std::unique_ptr<AssetsProvider> MultiAssetsProvider::Create( std::unique_ptr<AssetsProvider>&& primary, std::unique_ptr<AssetsProvider>&& secondary) { - if (primary == nullptr || secondary == nullptr) { - return nullptr; + if (primary == nullptr && secondary == nullptr) { + return EmptyAssetsProvider::Create(); + } + if (!primary) { + return secondary; + } + if (!secondary) { + return primary; } return std::unique_ptr<MultiAssetsProvider>(new MultiAssetsProvider(std::move(primary), std::move(secondary))); @@ -397,8 +389,8 @@ const std::string& MultiAssetsProvider::GetDebugName() const { return debug_name_; } -bool MultiAssetsProvider::IsUpToDate() const { - return primary_->IsUpToDate() && secondary_->IsUpToDate(); +UpToDate MultiAssetsProvider::IsUpToDate() const { + return combine(primary_->IsUpToDate(), [this] { return secondary_->IsUpToDate(); }); } EmptyAssetsProvider::EmptyAssetsProvider(std::optional<std::string>&& path) : @@ -438,12 +430,12 @@ const std::string& EmptyAssetsProvider::GetDebugName() const { if (path_.has_value()) { return *path_; } - const static std::string kEmpty = kEmptyDebugString; + constexpr static std::string kEmpty{kEmptyDebugString}; return kEmpty; } -bool EmptyAssetsProvider::IsUpToDate() const { - return true; +UpToDate EmptyAssetsProvider::IsUpToDate() const { + return UpToDate::Always; } } // namespace android diff --git a/libs/androidfw/Idmap.cpp b/libs/androidfw/Idmap.cpp index 3ecd82b074a1..262e7df185b7 100644 --- a/libs/androidfw/Idmap.cpp +++ b/libs/androidfw/Idmap.cpp @@ -22,9 +22,10 @@ #include "android-base/logging.h" #include "android-base/stringprintf.h" #include "android-base/utf8.h" -#include "androidfw/misc.h" +#include "androidfw/AssetManager.h" #include "androidfw/ResourceTypes.h" #include "androidfw/Util.h" +#include "androidfw/misc.h" #include "utils/ByteOrder.h" #include "utils/Trace.h" @@ -268,11 +269,16 @@ LoadedIdmap::LoadedIdmap(const std::string& idmap_path, const Idmap_header* head configurations_(configs), overlay_entries_(overlay_entries), string_pool_(std::move(string_pool)), - idmap_fd_( - android::base::utf8::open(idmap_path.c_str(), O_RDONLY | O_CLOEXEC | O_BINARY | O_PATH)), overlay_apk_path_(overlay_apk_path), target_apk_path_(target_apk_path), - idmap_last_mod_time_(getFileModDate(idmap_fd_.get())) { + idmap_last_mod_time_(kInvalidModDate) { + if (!isReadonlyFilesystem(std::string(overlay_apk_path_).c_str()) || + !(target_apk_path_ == AssetManager::TARGET_APK_PATH || + isReadonlyFilesystem(std::string(target_apk_path_).c_str()))) { + idmap_fd_.reset( + android::base::utf8::open(idmap_path.c_str(), O_RDONLY | O_CLOEXEC | O_BINARY | O_PATH)); + idmap_last_mod_time_ = getFileModDate(idmap_fd_); + } } std::unique_ptr<LoadedIdmap> LoadedIdmap::Load(StringPiece idmap_path, StringPiece idmap_data) { @@ -381,8 +387,11 @@ std::unique_ptr<LoadedIdmap> LoadedIdmap::Load(StringPiece idmap_path, StringPie overlay_entries, std::move(idmap_string_pool), *overlay_path, *target_path)); } -bool LoadedIdmap::IsUpToDate() const { - return idmap_last_mod_time_ == getFileModDate(idmap_fd_.get()); +UpToDate LoadedIdmap::IsUpToDate() const { + if (idmap_last_mod_time_ == kInvalidModDate) { + return UpToDate::Always; + } + return fromBool(idmap_last_mod_time_ == getFileModDate(idmap_fd_.get())); } } // namespace android diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp index de9991a8be5e..a8eb062a2ece 100644 --- a/libs/androidfw/ResourceTypes.cpp +++ b/libs/androidfw/ResourceTypes.cpp @@ -152,12 +152,11 @@ static void fill9patchOffsets(Res_png_9patch* patch) { patch->colorsOffset = patch->yDivsOffset + (patch->numYDivs * sizeof(int32_t)); } -void Res_value::copyFrom_dtoh(const Res_value& src) -{ - size = dtohs(src.size); - res0 = src.res0; - dataType = src.dataType; - data = dtohl(src.data); +void Res_value::copyFrom_dtoh_slow(const Res_value& src) { + size = dtohs(src.size); + res0 = src.res0; + dataType = src.dataType; + data = dtohl(src.data); } void Res_png_9patch::deviceToFile() @@ -2031,16 +2030,6 @@ status_t ResXMLTree::validateNode(const ResXMLTree_node* node) const // -------------------------------------------------------------------- // -------------------------------------------------------------------- -void ResTable_config::copyFromDeviceNoSwap(const ResTable_config& o) { - const size_t size = dtohl(o.size); - if (size >= sizeof(ResTable_config)) { - *this = o; - } else { - memcpy(this, &o, size); - memset(((uint8_t*)this)+size, 0, sizeof(ResTable_config)-size); - } -} - /* static */ size_t unpackLanguageOrRegion(const char in[2], const char base, char out[4]) { if (in[0] & 0x80) { @@ -2105,34 +2094,33 @@ size_t ResTable_config::unpackRegion(char region[4]) const { return unpackLanguageOrRegion(this->country, '0', region); } - -void ResTable_config::copyFromDtoH(const ResTable_config& o) { - copyFromDeviceNoSwap(o); - size = sizeof(ResTable_config); - mcc = dtohs(mcc); - mnc = dtohs(mnc); - density = dtohs(density); - screenWidth = dtohs(screenWidth); - screenHeight = dtohs(screenHeight); - sdkVersion = dtohs(sdkVersion); - minorVersion = dtohs(minorVersion); - smallestScreenWidthDp = dtohs(smallestScreenWidthDp); - screenWidthDp = dtohs(screenWidthDp); - screenHeightDp = dtohs(screenHeightDp); -} - -void ResTable_config::swapHtoD() { - size = htodl(size); - mcc = htods(mcc); - mnc = htods(mnc); - density = htods(density); - screenWidth = htods(screenWidth); - screenHeight = htods(screenHeight); - sdkVersion = htods(sdkVersion); - minorVersion = htods(minorVersion); - smallestScreenWidthDp = htods(smallestScreenWidthDp); - screenWidthDp = htods(screenWidthDp); - screenHeightDp = htods(screenHeightDp); +void ResTable_config::copyFromDtoH_slow(const ResTable_config& o) { + copyFromDeviceNoSwap(o); + size = sizeof(ResTable_config); + mcc = dtohs(mcc); + mnc = dtohs(mnc); + density = dtohs(density); + screenWidth = dtohs(screenWidth); + screenHeight = dtohs(screenHeight); + sdkVersion = dtohs(sdkVersion); + minorVersion = dtohs(minorVersion); + smallestScreenWidthDp = dtohs(smallestScreenWidthDp); + screenWidthDp = dtohs(screenWidthDp); + screenHeightDp = dtohs(screenHeightDp); +} + +void ResTable_config::swapHtoD_slow() { + size = htodl(size); + mcc = htods(mcc); + mnc = htods(mnc); + density = htods(density); + screenWidth = htods(screenWidth); + screenHeight = htods(screenHeight); + sdkVersion = htods(sdkVersion); + minorVersion = htods(minorVersion); + smallestScreenWidthDp = htods(smallestScreenWidthDp); + screenWidthDp = htods(screenWidthDp); + screenHeightDp = htods(screenHeightDp); } /* static */ inline int compareLocales(const ResTable_config &l, const ResTable_config &r) { @@ -2145,7 +2133,7 @@ void ResTable_config::swapHtoD() { // systems should happen very infrequently (if at all.) // The comparison code relies on memcmp low-level optimizations that make it // more efficient than strncmp. - const char emptyScript[sizeof(l.localeScript)] = {'\0', '\0', '\0', '\0'}; + static constexpr char emptyScript[sizeof(l.localeScript)] = {'\0', '\0', '\0', '\0'}; const char *lScript = l.localeScriptWasComputed ? emptyScript : l.localeScript; const char *rScript = r.localeScriptWasComputed ? emptyScript : r.localeScript; diff --git a/libs/androidfw/Util.cpp b/libs/androidfw/Util.cpp index be55fe8b4bb6..86c459fb4647 100644 --- a/libs/androidfw/Util.cpp +++ b/libs/androidfw/Util.cpp @@ -32,13 +32,18 @@ namespace android { namespace util { void ReadUtf16StringFromDevice(const uint16_t* src, size_t len, std::string* out) { - char buf[5]; - while (*src && len != 0) { - char16_t c = static_cast<char16_t>(dtohs(*src)); - utf16_to_utf8(&c, 1, buf, sizeof(buf)); - out->append(buf, strlen(buf)); - ++src; - --len; + static constexpr bool kDeviceEndiannessSame = dtohs(0x1001) == 0x1001; + if constexpr (kDeviceEndiannessSame) { + *out = Utf16ToUtf8({(const char16_t*)src, strnlen16((const char16_t*)src, len)}); + } else { + char buf[5]; + while (*src && len != 0) { + char16_t c = static_cast<char16_t>(dtohs(*src)); + utf16_to_utf8(&c, 1, buf, sizeof(buf)); + out->append(buf, strlen(buf)); + ++src; + --len; + } } } @@ -63,8 +68,10 @@ std::string Utf16ToUtf8(StringPiece16 utf16) { } std::string utf8; - utf8.resize(utf8_length); - utf16_to_utf8(utf16.data(), utf16.length(), &*utf8.begin(), utf8_length + 1); + utf8.resize_and_overwrite(utf8_length, [&utf16](char* data, size_t size) { + utf16_to_utf8(utf16.data(), utf16.length(), data, size + 1); + return size; + }); return utf8; } diff --git a/libs/androidfw/include/androidfw/ApkAssets.h b/libs/androidfw/include/androidfw/ApkAssets.h index 231808beb718..3f6f4661f2f7 100644 --- a/libs/androidfw/include/androidfw/ApkAssets.h +++ b/libs/androidfw/include/androidfw/ApkAssets.h @@ -116,7 +116,7 @@ class ApkAssets : public RefBase { return resources_asset_ != nullptr && resources_asset_->isAllocated(); } - bool IsUpToDate() const; + UpToDate IsUpToDate() const; // DANGER! // This is a destructive method that rips the assets provider out of ApkAssets object. diff --git a/libs/androidfw/include/androidfw/AssetsProvider.h b/libs/androidfw/include/androidfw/AssetsProvider.h index d33c325ff369..e3b3ae41f7f4 100644 --- a/libs/androidfw/include/androidfw/AssetsProvider.h +++ b/libs/androidfw/include/androidfw/AssetsProvider.h @@ -14,8 +14,7 @@ * limitations under the License. */ -#ifndef ANDROIDFW_ASSETSPROVIDER_H -#define ANDROIDFW_ASSETSPROVIDER_H +#pragma once #include <memory> #include <string> @@ -58,7 +57,7 @@ struct AssetsProvider { WARN_UNUSED virtual const std::string& GetDebugName() const = 0; // Returns whether the interface provides the most recent version of its files. - WARN_UNUSED virtual bool IsUpToDate() const = 0; + WARN_UNUSED virtual UpToDate IsUpToDate() const = 0; // Creates an Asset from a file on disk. static std::unique_ptr<Asset> CreateAssetFromFile(const std::string& path); @@ -95,7 +94,7 @@ struct ZipAssetsProvider : public AssetsProvider { WARN_UNUSED std::optional<std::string_view> GetPath() const override; WARN_UNUSED const std::string& GetDebugName() const override; - WARN_UNUSED bool IsUpToDate() const override; + WARN_UNUSED UpToDate IsUpToDate() const override; WARN_UNUSED std::optional<uint32_t> GetCrc(std::string_view path) const; ~ZipAssetsProvider() override = default; @@ -106,7 +105,7 @@ struct ZipAssetsProvider : public AssetsProvider { private: struct PathOrDebugName; ZipAssetsProvider(ZipArchive* handle, PathOrDebugName&& path, package_property_t flags, - time_t last_mod_time); + ModDate last_mod_time); struct PathOrDebugName { static PathOrDebugName Path(std::string value) { @@ -135,7 +134,7 @@ struct ZipAssetsProvider : public AssetsProvider { std::unique_ptr<ZipArchive, ZipCloser> zip_handle_; PathOrDebugName name_; package_property_t flags_; - time_t last_mod_time_; + ModDate last_mod_time_; }; // Supplies assets from a root directory. @@ -147,7 +146,7 @@ struct DirectoryAssetsProvider : public AssetsProvider { WARN_UNUSED std::optional<std::string_view> GetPath() const override; WARN_UNUSED const std::string& GetDebugName() const override; - WARN_UNUSED bool IsUpToDate() const override; + WARN_UNUSED UpToDate IsUpToDate() const override; ~DirectoryAssetsProvider() override = default; protected: @@ -156,23 +155,23 @@ struct DirectoryAssetsProvider : public AssetsProvider { bool* file_exists) const override; private: - explicit DirectoryAssetsProvider(std::string&& path, time_t last_mod_time); + explicit DirectoryAssetsProvider(std::string&& path, ModDate last_mod_time); std::string dir_; - time_t last_mod_time_; + ModDate last_mod_time_; }; // Supplies assets from a `primary` asset provider and falls back to supplying assets from the // `secondary` asset provider if the asset cannot be found in the `primary`. struct MultiAssetsProvider : public AssetsProvider { static std::unique_ptr<AssetsProvider> Create(std::unique_ptr<AssetsProvider>&& primary, - std::unique_ptr<AssetsProvider>&& secondary); + std::unique_ptr<AssetsProvider>&& secondary = {}); bool ForEachFile(const std::string& root_path, base::function_ref<void(StringPiece, FileType)> f) const override; WARN_UNUSED std::optional<std::string_view> GetPath() const override; WARN_UNUSED const std::string& GetDebugName() const override; - WARN_UNUSED bool IsUpToDate() const override; + WARN_UNUSED UpToDate IsUpToDate() const override; ~MultiAssetsProvider() override = default; protected: @@ -199,7 +198,7 @@ struct EmptyAssetsProvider : public AssetsProvider { WARN_UNUSED std::optional<std::string_view> GetPath() const override; WARN_UNUSED const std::string& GetDebugName() const override; - WARN_UNUSED bool IsUpToDate() const override; + WARN_UNUSED UpToDate IsUpToDate() const override; ~EmptyAssetsProvider() override = default; protected: @@ -212,5 +211,3 @@ struct EmptyAssetsProvider : public AssetsProvider { }; } // namespace android - -#endif /* ANDROIDFW_ASSETSPROVIDER_H */ diff --git a/libs/androidfw/include/androidfw/Idmap.h b/libs/androidfw/include/androidfw/Idmap.h index ac75eb3bb98c..87f3c9df9a91 100644 --- a/libs/androidfw/include/androidfw/Idmap.h +++ b/libs/androidfw/include/androidfw/Idmap.h @@ -14,8 +14,7 @@ * limitations under the License. */ -#ifndef IDMAP_H_ -#define IDMAP_H_ +#pragma once #include <memory> #include <string> @@ -32,6 +31,31 @@ namespace android { +// An enum that tracks more states than just 'up to date' or 'not' for a resources container: +// there are several cases where we know for sure that the object can't change and won't get +// out of date. Reporting those states to the managed layer allows it to stop checking here +// completely, speeding up the cache lookups by dozens of milliseconds. +enum class UpToDate : int { False, True, Always }; + +// Combines two UpToDate values, and only accesses the second one if it matters to the result. +template <class Getter> +UpToDate combine(UpToDate first, Getter secondGetter) { + switch (first) { + case UpToDate::False: + return UpToDate::False; + case UpToDate::True: { + const auto second = secondGetter(); + return second == UpToDate::False ? UpToDate::False : UpToDate::True; + } + case UpToDate::Always: + return secondGetter(); + } +} + +inline UpToDate fromBool(bool value) { + return value ? UpToDate::True : UpToDate::False; +} + class LoadedIdmap; class IdmapResMap; struct Idmap_header; @@ -196,7 +220,7 @@ class LoadedIdmap { // Returns whether the idmap file on disk has not been modified since the construction of this // LoadedIdmap. - bool IsUpToDate() const; + UpToDate IsUpToDate() const; protected: // Exposed as protected so that tests can subclass and mock this class out. @@ -231,5 +255,3 @@ class LoadedIdmap { }; } // namespace android - -#endif // IDMAP_H_ diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h index e330410ed1a0..819fe4b38c87 100644 --- a/libs/androidfw/include/androidfw/ResourceTypes.h +++ b/libs/androidfw/include/androidfw/ResourceTypes.h @@ -47,6 +47,8 @@ namespace android { +constexpr const bool kDeviceEndiannessSame = dtohs(0x1001) == 0x1001; + constexpr const uint32_t kIdmapMagic = 0x504D4449u; constexpr const uint32_t kIdmapCurrentVersion = 0x0000000Au; @@ -408,7 +410,16 @@ struct Res_value typedef uint32_t data_type; data_type data; - void copyFrom_dtoh(const Res_value& src); + void copyFrom_dtoh(const Res_value& src) { + if constexpr (kDeviceEndiannessSame) { + *this = src; + } else { + copyFrom_dtoh_slow(src); + } + } + + private: + void copyFrom_dtoh_slow(const Res_value& src); }; /** @@ -1254,11 +1265,32 @@ struct ResTable_config // Varies in length from 3 to 8 chars. Zero-filled value. char localeNumberingSystem[8]; - void copyFromDeviceNoSwap(const ResTable_config& o); - - void copyFromDtoH(const ResTable_config& o); - - void swapHtoD(); + void copyFromDeviceNoSwap(const ResTable_config& o) { + const auto o_size = dtohl(o.size); + if (o_size >= sizeof(ResTable_config)) [[likely]] { + *this = o; + } else { + memcpy(this, &o, o_size); + memset(((uint8_t*)this) + o_size, 0, sizeof(ResTable_config) - o_size); + } + this->size = sizeof(*this); + } + + void copyFromDtoH(const ResTable_config& o) { + if constexpr (kDeviceEndiannessSame) { + copyFromDeviceNoSwap(o); + } else { + copyFromDtoH_slow(o); + } + } + + void swapHtoD() { + if constexpr (kDeviceEndiannessSame) { + ; // noop + } else { + swapHtoD_slow(); + } + } int compare(const ResTable_config& o) const; int compareLogical(const ResTable_config& o) const; @@ -1384,6 +1416,10 @@ struct ResTable_config bool isBetterThanBeforeLocale(const ResTable_config& o, const ResTable_config* requested) const; String8 toString() const; + + private: + void copyFromDtoH_slow(const ResTable_config& o); + void swapHtoD_slow(); }; /** diff --git a/libs/androidfw/include/androidfw/misc.h b/libs/androidfw/include/androidfw/misc.h index c9ba8a01a5e9..d8ca64a174a2 100644 --- a/libs/androidfw/include/androidfw/misc.h +++ b/libs/androidfw/include/androidfw/misc.h @@ -15,6 +15,7 @@ */ #pragma once +#include <sys/stat.h> #include <time.h> // @@ -64,10 +65,15 @@ ModDate getFileModDate(const char* fileName); /* same, but also returns -1 if the file has already been deleted */ ModDate getFileModDate(int fd); +// Extract the modification date from the stat structure. +ModDate getModDate(const struct ::stat& st); + // Check if |path| or |fd| resides on a readonly filesystem. bool isReadonlyFilesystem(const char* path); bool isReadonlyFilesystem(int fd); +bool isKnownWritablePath(const char* path); + } // namespace android // Whoever uses getFileModDate() will need this as well diff --git a/libs/androidfw/misc.cpp b/libs/androidfw/misc.cpp index 32f3624a3aee..26eb320805c9 100644 --- a/libs/androidfw/misc.cpp +++ b/libs/androidfw/misc.cpp @@ -16,10 +16,10 @@ #define LOG_TAG "misc" -// -// Miscellaneous utility functions. -// -#include <androidfw/misc.h> +#include "androidfw/misc.h" + +#include <errno.h> +#include <sys/stat.h> #include "android-base/logging.h" @@ -28,9 +28,7 @@ #include <sys/vfs.h> #endif // __linux__ -#include <errno.h> -#include <sys/stat.h> - +#include <array> #include <cstdio> #include <cstring> #include <tuple> @@ -40,28 +38,26 @@ namespace android { /* * Get a file's type. */ -FileType getFileType(const char* fileName) -{ - struct stat sb; - - if (stat(fileName, &sb) < 0) { - if (errno == ENOENT || errno == ENOTDIR) - return kFileTypeNonexistent; - else { - PLOG(ERROR) << "getFileType(): stat(" << fileName << ") failed"; - return kFileTypeUnknown; - } - } else { - if (S_ISREG(sb.st_mode)) - return kFileTypeRegular; - else if (S_ISDIR(sb.st_mode)) - return kFileTypeDirectory; - else if (S_ISCHR(sb.st_mode)) - return kFileTypeCharDev; - else if (S_ISBLK(sb.st_mode)) - return kFileTypeBlockDev; - else if (S_ISFIFO(sb.st_mode)) - return kFileTypeFifo; +FileType getFileType(const char* fileName) { + struct stat sb; + if (stat(fileName, &sb) < 0) { + if (errno == ENOENT || errno == ENOTDIR) + return kFileTypeNonexistent; + else { + PLOG(ERROR) << "getFileType(): stat(" << fileName << ") failed"; + return kFileTypeUnknown; + } + } else { + if (S_ISREG(sb.st_mode)) + return kFileTypeRegular; + else if (S_ISDIR(sb.st_mode)) + return kFileTypeDirectory; + else if (S_ISCHR(sb.st_mode)) + return kFileTypeCharDev; + else if (S_ISBLK(sb.st_mode)) + return kFileTypeBlockDev; + else if (S_ISFIFO(sb.st_mode)) + return kFileTypeFifo; #if defined(S_ISLNK) else if (S_ISLNK(sb.st_mode)) return kFileTypeSymlink; @@ -75,7 +71,7 @@ FileType getFileType(const char* fileName) } } -static ModDate getModDate(const struct stat& st) { +ModDate getModDate(const struct stat& st) { #ifdef _WIN32 return st.st_mtime; #elif defined(__APPLE__) @@ -113,8 +109,14 @@ bool isReadonlyFilesystem(const char*) { bool isReadonlyFilesystem(int) { return false; } +bool isKnownWritablePath(const char*) { + return false; +} #else // __linux__ bool isReadonlyFilesystem(const char* path) { + if (isKnownWritablePath(path)) { + return false; + } struct statfs sfs; if (::statfs(path, &sfs)) { PLOG(ERROR) << "isReadonlyFilesystem(): statfs(" << path << ") failed"; @@ -131,6 +133,13 @@ bool isReadonlyFilesystem(int fd) { } return (sfs.f_flags & ST_RDONLY) != 0; } + +bool isKnownWritablePath(const char* path) { + // We know that all paths in /data/ are writable. + static constexpr char kRwPrefix[] = "/data/"; + return strncmp(kRwPrefix, path, std::size(kRwPrefix) - 1) == 0; +} + #endif // __linux__ } // namespace android diff --git a/libs/androidfw/tests/Idmap_test.cpp b/libs/androidfw/tests/Idmap_test.cpp index cb2e56f5f5e4..22b9e69500d9 100644 --- a/libs/androidfw/tests/Idmap_test.cpp +++ b/libs/androidfw/tests/Idmap_test.cpp @@ -218,10 +218,11 @@ TEST_F(IdmapTest, OverlayAssetsIsUpToDate) { auto apk_assets = ApkAssets::LoadOverlay(temp_file.path); ASSERT_NE(nullptr, apk_assets); - ASSERT_TRUE(apk_assets->IsUpToDate()); + ASSERT_TRUE(apk_assets->IsOverlay()); + ASSERT_EQ(UpToDate::True, apk_assets->IsUpToDate()); unlink(temp_file.path); - ASSERT_FALSE(apk_assets->IsUpToDate()); + ASSERT_EQ(UpToDate::False, apk_assets->IsUpToDate()); const auto sleep_duration = std::chrono::nanoseconds(std::max(kModDateResolutionNs, 1'000'000ull)); @@ -230,7 +231,27 @@ TEST_F(IdmapTest, OverlayAssetsIsUpToDate) { base::WriteStringToFile("hello", temp_file.path); std::this_thread::sleep_for(sleep_duration); - ASSERT_FALSE(apk_assets->IsUpToDate()); + ASSERT_EQ(UpToDate::False, apk_assets->IsUpToDate()); +} + +TEST(IdmapTestUpToDate, Combine) { + ASSERT_EQ(UpToDate::False, combine(UpToDate::False, [] { + ADD_FAILURE(); // Shouldn't get called at all. + return UpToDate::False; + })); + + ASSERT_EQ(UpToDate::False, combine(UpToDate::True, [] { return UpToDate::False; })); + + ASSERT_EQ(UpToDate::True, combine(UpToDate::True, [] { return UpToDate::True; })); + ASSERT_EQ(UpToDate::True, combine(UpToDate::True, [] { return UpToDate::Always; })); + ASSERT_EQ(UpToDate::True, combine(UpToDate::Always, [] { return UpToDate::True; })); + + ASSERT_EQ(UpToDate::Always, combine(UpToDate::Always, [] { return UpToDate::Always; })); +} + +TEST(IdmapTestUpToDate, FromBool) { + ASSERT_EQ(UpToDate::False, fromBool(false)); + ASSERT_EQ(UpToDate::True, fromBool(true)); } } // namespace diff --git a/packages/InputDevices/res/raw/keyboard_layout_romanian.kcm b/packages/InputDevices/res/raw/keyboard_layout_romanian.kcm new file mode 100644 index 000000000000..b384a2418ff2 --- /dev/null +++ b/packages/InputDevices/res/raw/keyboard_layout_romanian.kcm @@ -0,0 +1,357 @@ +# Copyright (C) 2024 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Romanian keyboard layout. +# + +type OVERLAY + +map key 86 PLUS + +### ROW 1 + +key GRAVE { + label: '\u201e' + base: '\u201e' + shift: '\u201d' + ralt: '`' + ralt+shift: '~' +} + +key 1 { + label: '1' + base: '1' + shift: '!' + ralt: '\u0303' +} + +key 2 { + label: '2' + base: '2' + shift: '@' + ralt: '\u030C' +} + +key 3 { + label: '3' + base: '3' + shift: '#' + ralt: '\u0302' +} + +key 4 { + label: '4' + base: '4' + shift: '$' + ralt: '\u0306' +} + +key 5 { + label: '5' + base: '5' + shift: '%' + ralt: '\u030A' +} + +key 6 { + label: '6' + base: '6' + shift: '^' + ralt: '\u0328' +} + +key 7 { + label: '7' + base: '7' + shift: '&' + ralt: '\u0300' +} + +key 8 { + label: '8' + base: '8' + shift: '*' + ralt: '\u0307' +} + +key 9 { + label: '9' + base: '9' + shift: '(' + ralt: '\u0301' +} + +key 0 { + label: '0' + base: '0' + shift: ')' + ralt: '\u030B' +} + +key MINUS { + label: '-' + base: '-' + shift: '_' + ralt: '\u0308' + ralt+shift: '\u2013' +} + +key EQUALS { + label: '=' + base: '=' + shift: '+' + ralt: '\u0327' + ralt+shift: '\u00b1' +} + +### ROW 2 + +key Q { + label: 'Q' + base, capslock+shift: 'q' + shift, capslock: 'Q' +} + +key W { + label: 'W' + base, capslock+shift: 'w' + shift, capslock: 'W' +} + +key E { + label: 'E' + base, capslock+shift: 'e' + shift, capslock: 'E' + ralt: '\u20ac' +} + +key R { + label: 'R' + base, capslock+shift: 'r' + shift, capslock: 'R' +} + +key T { + label: 'T' + base, capslock+shift: 't' + shift, capslock: 'T' +} + +key Y { + label: 'Y' + base, capslock+shift: 'y' + shift, capslock: 'Y' +} + +key U { + label: 'U' + base, capslock+shift: 'u' + shift, capslock: 'U' +} + +key I { + label: 'I' + base, capslock+shift: 'i' + shift, capslock: 'I' +} + +key O { + label: 'O' + base, capslock+shift: 'o' + shift, capslock: 'O' +} + +key P { + label: 'P' + base, capslock+shift: 'p' + shift, capslock: 'P' + ralt: '\u00a7' +} + +key LEFT_BRACKET { + label: '\u0102' + base, capslock+shift: '\u0103' + shift, capslock: '\u0102' + ralt: '[' + ralt+shift: '{' +} + +key RIGHT_BRACKET { + label: '\u00ce' + base, capslock+shift: '\u00ee' + shift, capslock: '\u00ce' + ralt: ']' + ralt+shift: '}' +} + +### ROW 3 + +key A { + label: 'A' + base, capslock+shift: 'a' + shift, capslock: 'A' +} + +key S { + label: 'S' + base, capslock+shift: 's' + shift, capslock: 'S' + ralt: '\u00df' +} + +key D { + label: 'D' + base, capslock+shift: 'd' + shift, capslock: 'D' + ralt: '\u0111' + ralt+shift, ralt+capslock: '\u0110' + ralt+shift+capslock: '\u0111' +} + +key F { + label: 'F' + base, capslock+shift: 'f' + shift, capslock: 'F' +} + +key G { + label: 'G' + base, capslock+shift: 'g' + shift, capslock: 'G' +} + +key H { + label: 'H' + base, capslock+shift: 'h' + shift, capslock: 'H' +} + +key J { + label: 'J' + base, capslock+shift: 'j' + shift, capslock: 'J' +} + +key K { + label: 'K' + base, capslock+shift: 'k' + shift, capslock: 'K' +} + +key L { + label: 'L' + base, capslock+shift: 'l' + shift, capslock: 'L' + ralt: '\u0142' + ralt+shift, ralt+capslock: '\u0141' + ralt+shift+capslock: '\u0142' +} + +key SEMICOLON { + label: '\u0218' + base, capslock+shift: '\u0219' + shift, capslock: '\u0218' + ralt: ';' + ralt+shift: ':' +} + +key APOSTROPHE { + label: '\u021a' + base, capslock+shift: '\u021b' + shift, capslock: '\u021a' + ralt: '\'' + ralt+shift: '\u0022' +} + +key BACKSLASH { + label: '\u00c2' + base, capslock+shift: '\u00e2' + shift, capslock: '\u00c2' + ralt: '\\' + ralt+shift: '|' +} + +### ROW 4 + +key PLUS { + label: '\\' + base: '\\' + shift: '|' +} + +key Z { + label: 'Z' + base, capslock+shift: 'z' + shift, capslock: 'Z' +} + +key X { + label: 'X' + base, capslock+shift: 'x' + shift, capslock: 'X' +} + +key C { + label: 'C' + base, capslock+shift: 'c' + shift, capslock: 'C' + ralt: '\u00a9' +} + +key V { + label: 'V' + base, capslock+shift: 'v' + shift, capslock: 'V' +} + +key B { + label: 'B' + base, capslock+shift: 'b' + shift, capslock: 'B' +} + +key N { + label: 'N' + base, capslock+shift: 'n' + shift, capslock: 'N' +} + +key M { + label: 'M' + base, capslock+shift: 'm' + shift, capslock: 'M' +} + +key COMMA { + label: ',' + base: ',' + shift: ';' + ralt: '<' + ralt+shift: '\u00ab' +} + +key PERIOD { + label: '.' + base: '.' + shift: ':' + ralt: '>' + ralt+shift: '\u00bb' +} + +key SLASH { + label: '/' + base: '/' + shift: '?' +} diff --git a/packages/InputDevices/res/values/strings.xml b/packages/InputDevices/res/values/strings.xml index 5a911256d9be..bd7cdc481524 100644 --- a/packages/InputDevices/res/values/strings.xml +++ b/packages/InputDevices/res/values/strings.xml @@ -164,4 +164,7 @@ <!-- Montenegrin (Cyrillic) keyboard layout label. [CHAR LIMIT=35] --> <string name="keyboard_layout_montenegrin_cyrillic">Montenegrin (Cyrillic)</string> + + <!-- Romanian keyboard layout label. [CHAR LIMIT=35] --> + <string name="keyboard_layout_romanian">Romanian</string> </resources> diff --git a/packages/InputDevices/res/xml/keyboard_layouts.xml b/packages/InputDevices/res/xml/keyboard_layouts.xml index 93094890418d..9ce9a87a1f9f 100644 --- a/packages/InputDevices/res/xml/keyboard_layouts.xml +++ b/packages/InputDevices/res/xml/keyboard_layouts.xml @@ -360,4 +360,11 @@ android:keyboardLayout="@raw/keyboard_layout_serbian_and_montenegrin_cyrillic" android:keyboardLocale="cnr-Cyrl-ME" android:keyboardLayoutType="extended" /> + + <keyboard-layout + android:name="keyboard_layout_romanian" + android:label="@string/keyboard_layout_romanian" + android:keyboardLayout="@raw/keyboard_layout_romanian" + android:keyboardLocale="ro-Latn-RO" + android:keyboardLayoutType="qwerty" /> </keyboard-layouts> diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/common/BytesFormatter.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/common/BytesFormatter.kt new file mode 100644 index 000000000000..5b7e2a86135a --- /dev/null +++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/common/BytesFormatter.kt @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settingslib.spaprivileged.framework.common + +import android.content.Context +import android.content.res.Resources +import android.icu.text.DecimalFormat +import android.icu.text.MeasureFormat +import android.icu.text.NumberFormat +import android.icu.text.UnicodeSet +import android.icu.text.UnicodeSetSpanner +import android.icu.util.Measure +import android.text.format.Formatter +import android.text.format.Formatter.RoundedBytesResult +import java.math.BigDecimal + +class BytesFormatter(resources: Resources) { + + enum class UseCase(val flag: Int) { + FileSize(Formatter.FLAG_SI_UNITS), + DataUsage(Formatter.FLAG_IEC_UNITS), + } + + data class Result(val number: String, val units: String) + + constructor(context: Context) : this(context.resources) + + private val locale = resources.configuration.locales[0] + + fun format(bytes: Long, useCase: UseCase): String { + val rounded = RoundedBytesResult.roundBytes(bytes, useCase.flag) + val numberFormatter = getNumberFormatter(rounded.fractionDigits) + return numberFormatter.formatRoundedBytesResult(rounded) + } + + fun formatWithUnits(bytes: Long, useCase: UseCase): Result { + val rounded = RoundedBytesResult.roundBytes(bytes, useCase.flag) + val numberFormatter = getNumberFormatter(rounded.fractionDigits) + val formattedString = numberFormatter.formatRoundedBytesResult(rounded) + val formattedNumber = numberFormatter.format(rounded.value) + return Result( + number = formattedNumber, + units = formattedString.removeFirst(formattedNumber), + ) + } + + private fun NumberFormat.formatRoundedBytesResult(rounded: RoundedBytesResult): String { + val measureFormatter = + MeasureFormat.getInstance(locale, MeasureFormat.FormatWidth.SHORT, this) + return measureFormatter.format(Measure(rounded.value, rounded.units)) + } + + private fun getNumberFormatter(fractionDigits: Int) = + NumberFormat.getInstance(locale).apply { + minimumFractionDigits = fractionDigits + maximumFractionDigits = fractionDigits + isGroupingUsed = false + if (this is DecimalFormat) { + setRoundingMode(BigDecimal.ROUND_HALF_UP) + } + } + + private companion object { + fun String.removeFirst(removed: String): String = + SPACES_AND_CONTROLS.trim(replaceFirst(removed, "")).toString() + + val SPACES_AND_CONTROLS = UnicodeSetSpanner(UnicodeSet("[[:Zs:][:Cf:]]").freeze()) + } +} diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/framework/common/BytesFormatterTest.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/framework/common/BytesFormatterTest.kt new file mode 100644 index 000000000000..7220848eebff --- /dev/null +++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/framework/common/BytesFormatterTest.kt @@ -0,0 +1,178 @@ +/* + * Copyright (C) 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settingslib.spaprivileged.framework.common + +import android.content.Context +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.google.common.truth.Truth.assertThat +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +class BytesFormatterTest { + + private val context: Context = ApplicationProvider.getApplicationContext() + + private val formatter = BytesFormatter(context) + + @Test + fun `Zero bytes`() { + // Given a byte value of 0, the formatted output should be "0 byte" for both FileSize + // and DataUsage UseCases. This verifies special handling of zero values. + + val fileSizeResult = formatter.format(0, BytesFormatter.UseCase.FileSize) + assertThat(fileSizeResult).isEqualTo("0 byte") + + val dataUsageResult = formatter.format(0, BytesFormatter.UseCase.DataUsage) + assertThat(dataUsageResult).isEqualTo("0 byte") + } + + @Test + fun `Positive bytes`() { + // Given a positive byte value (e.g., 1000), the formatted output should be correctly + // displayed with appropriate units (e.g., '1.00 kB') for both UseCases. + + val fileSizeResult = formatter.format(1000, BytesFormatter.UseCase.FileSize) + assertThat(fileSizeResult).isEqualTo("1.00 kB") + + val dataUsageResult = formatter.format(1024, BytesFormatter.UseCase.DataUsage) + assertThat(dataUsageResult).isEqualTo("1.00 kB") + } + + @Test + fun `Large bytes`() { + // Given a very large byte value (e.g., Long.MAX_VALUE), the formatted output should be + // correctly displayed with the largest unit (e.g., 'PB') for both UseCases. + + val fileSizeResult = formatter.format(Long.MAX_VALUE, BytesFormatter.UseCase.FileSize) + assertThat(fileSizeResult).isEqualTo("9223 PB") + + val dataUsageResult = formatter.format(Long.MAX_VALUE, BytesFormatter.UseCase.DataUsage) + assertThat(dataUsageResult).isEqualTo("8192 PB") + } + + @Test + fun `Bytes requiring rounding`() { + // Given byte values that require rounding (e.g., 1512), the formatted output should be + // rounded to the appropriate number of decimal places (e.g., '1.51 kB'). + + val fileSizeResult = formatter.format(1512, BytesFormatter.UseCase.FileSize) + assertThat(fileSizeResult).isEqualTo("1.51 kB") + + val dataUsageResult = formatter.format(1512, BytesFormatter.UseCase.DataUsage) + assertThat(dataUsageResult).isEqualTo("1.48 kB") + } + + @Test + fun `FileSize UseCase`() { + // When the UseCase is FileSize, the correct units (byte, KB, kB, GB, TB, PB) should + // be used. + val values = + listOf( + 1L, + 1024L, + 1024L * 1024L, + 1024L * 1024L * 1024L, + 1024L * 1024L * 1024L * 1024L, + 1024L * 1024L * 1024L * 1024L * 1024L, + 1024L * 1024L * 1024L * 1024L * 1024L * 1024L, + ) + val expectedUnits = listOf("byte", "kB", "MB", "GB", "TB", "PB", "PB") + + values.zip(expectedUnits).forEach { (value, expectedUnit) -> + val result = formatter.format(value, BytesFormatter.UseCase.FileSize) + assertThat(result).contains(expectedUnit) + } + } + + @Test + fun `DataUsage UseCase`() { + // When the UseCase is DataUsage, the correct units (byte, kB, MB, GB, TB, PB) should + // be used. + val values = + listOf( + 1L, + 1024L, + 1024L * 1024L, + 1024L * 1024L * 1024L, + 1024L * 1024L * 1024L * 1024L, + 1024L * 1024L * 1024L * 1024L * 1024L, + 1024L * 1024L * 1024L * 1024L * 1024L * 1024L, + ) + val expectedUnits = listOf("byte", "kB", "MB", "GB", "TB", "PB", "PB") + + values.zip(expectedUnits).forEach { (value, expectedUnit) -> + val result = formatter.format(value, BytesFormatter.UseCase.DataUsage) + assertThat(result).contains(expectedUnit) + } + } + + @Test + fun `Fraction digits`() { + // The number of fraction digits in the output should be correctly determined based on + // the rounded byte value. + + assertThat(formatter.format(1500, BytesFormatter.UseCase.FileSize)).isEqualTo("1.50 kB") + assertThat(formatter.format(1050, BytesFormatter.UseCase.FileSize)).isEqualTo("1.05 kB") + assertThat(formatter.format(999, BytesFormatter.UseCase.FileSize)).isEqualTo("1.00 kB") + } + + @Test + fun `Rounding mode`() { + // The rounding mode used for formatting should be ROUND_HALF_UP. + + val result = formatter.format(1006, BytesFormatter.UseCase.FileSize) + + assertThat(result).isEqualTo("1.01 kB") // Ensure rounding mode is effective + } + + @Test + fun `Grouping separator`() { + // Grouping separators should not be used in the formatted output. + + val result = formatter.format(Long.MAX_VALUE, BytesFormatter.UseCase.FileSize) + + assertThat(result).isEqualTo("9223 PB") + } + + @Test + fun `Format with units`() { + // Verify that the `formatWithUnits` method correctly formats the given bytes with the + // specified units. + + val resultByte = formatter.formatWithUnits(0, BytesFormatter.UseCase.FileSize) + assertThat(resultByte).isEqualTo(BytesFormatter.Result("0", "byte")) + + val resultKb = formatter.formatWithUnits(1000, BytesFormatter.UseCase.FileSize) + assertThat(resultKb).isEqualTo(BytesFormatter.Result("1.00", "kB")) + + val resultMb = formatter.formatWithUnits(479_999_999, BytesFormatter.UseCase.FileSize) + assertThat(resultMb).isEqualTo(BytesFormatter.Result("480", "MB")) + + val resultGb = formatter.formatWithUnits(20_100_000_000, BytesFormatter.UseCase.FileSize) + assertThat(resultGb).isEqualTo(BytesFormatter.Result("20.10", "GB")) + + val resultTb = + formatter.formatWithUnits(300_100_000_000_000, BytesFormatter.UseCase.FileSize) + assertThat(resultTb).isEqualTo(BytesFormatter.Result("300", "TB")) + + val resultPb = + formatter.formatWithUnits(1000_000_000_000_000, BytesFormatter.UseCase.FileSize) + assertThat(resultPb).isEqualTo(BytesFormatter.Result("1.00", "PB")) + } +} diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java index 145b62cd12b5..68e9fe703090 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java @@ -73,6 +73,10 @@ public class BluetoothUtils { private static final Set<Integer> SA_PROFILES = ImmutableSet.of( BluetoothProfile.A2DP, BluetoothProfile.LE_AUDIO, BluetoothProfile.HEARING_AID); + private static final List<Integer> BLUETOOTH_DEVICE_CLASS_HEADSET = + List.of( + BluetoothClass.Device.AUDIO_VIDEO_HEADPHONES, + BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET); private static final String TEMP_BOND_TYPE = "TEMP_BOND_TYPE"; private static final String TEMP_BOND_DEVICE_METADATA_VALUE = "le_audio_sharing"; @@ -390,6 +394,19 @@ public class BluetoothUtils { return false; } + /** Checks whether the bluetooth device is a headset. */ + public static boolean isHeadset(@NonNull BluetoothDevice bluetoothDevice) { + String deviceType = + BluetoothUtils.getStringMetaData( + bluetoothDevice, BluetoothDevice.METADATA_DEVICE_TYPE); + if (!TextUtils.isEmpty(deviceType)) { + return BluetoothDevice.DEVICE_TYPE_HEADSET.equals(deviceType) + || BluetoothDevice.DEVICE_TYPE_UNTETHERED_HEADSET.equals(deviceType); + } + BluetoothClass btClass = bluetoothDevice.getBluetoothClass(); + return btClass != null && BLUETOOTH_DEVICE_CLASS_HEADSET.contains(btClass.getDeviceClass()); + } + /** Create an Icon pointing to a drawable. */ public static IconCompat createIconWithDrawable(Drawable drawable) { Bitmap bitmap; diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java index d49447f05011..cafe19ff9a9b 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java @@ -80,7 +80,9 @@ public class BluetoothUtilsTest { @Mock(answer = Answers.RETURNS_DEEP_STUBS) private CachedBluetoothDevice mCachedBluetoothDevice; - @Mock private BluetoothDevice mBluetoothDevice; + @Mock(answer = Answers.RETURNS_DEEP_STUBS) + private BluetoothDevice mBluetoothDevice; + @Mock private AudioManager mAudioManager; @Mock private PackageManager mPackageManager; @Mock private LeAudioProfile mA2dpProfile; @@ -399,6 +401,38 @@ public class BluetoothUtilsTest { } @Test + public void isHeadset_metadataMatched_returnTrue() { + when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_DEVICE_TYPE)) + .thenReturn(BluetoothDevice.DEVICE_TYPE_UNTETHERED_HEADSET.getBytes()); + + assertThat(BluetoothUtils.isHeadset(mBluetoothDevice)).isTrue(); + } + + @Test + public void isHeadset_metadataNotMatched_returnFalse() { + when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_DEVICE_TYPE)) + .thenReturn(BluetoothDevice.DEVICE_TYPE_CARKIT.getBytes()); + + assertThat(BluetoothUtils.isHeadset(mBluetoothDevice)).isFalse(); + } + + @Test + public void isHeadset_btClassMatched_returnTrue() { + when(mBluetoothDevice.getBluetoothClass().getDeviceClass()) + .thenReturn(BluetoothClass.Device.AUDIO_VIDEO_HEADPHONES); + + assertThat(BluetoothUtils.isHeadset(mBluetoothDevice)).isTrue(); + } + + @Test + public void isHeadset_btClassNotMatched_returnFalse() { + when(mBluetoothDevice.getBluetoothClass().getDeviceClass()) + .thenReturn(BluetoothClass.Device.AUDIO_VIDEO_LOUDSPEAKER); + + assertThat(BluetoothUtils.isHeadset(mBluetoothDevice)).isFalse(); + } + + @Test public void isAvailableMediaBluetoothDevice_isConnectedLeAudioDevice_returnTrue() { when(mCachedBluetoothDevice.isConnectedLeAudioDevice()).thenReturn(true); when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice); diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml index 5ddf005d9468..dafcc729b8f1 100644 --- a/packages/SettingsProvider/res/values/defaults.xml +++ b/packages/SettingsProvider/res/values/defaults.xml @@ -322,9 +322,6 @@ <!-- Whether vibrate icon is shown in the status bar by default. --> <integer name="def_statusBarVibrateIconEnabled">0</integer> - <!-- Whether predictive back animation is enabled by default. --> - <bool name="def_enable_back_animation">false</bool> - <!-- Whether wifi is always requested by default. --> <bool name="def_enable_wifi_always_requested">false</bool> diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java index a2cc008843a4..ef0bc3b100e0 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java @@ -193,6 +193,7 @@ public class SettingsBackupAgent extends BackupAgentHelper { "power_button_instantly_locks"; private static final String KEY_LOCK_SETTINGS_PIN_ENHANCED_PRIVACY = "pin_enhanced_privacy"; + private static final int NUM_LOCK_SETTINGS = 5; // Error messages for logging metrics. private static final String ERROR_COULD_NOT_READ_FROM_CURSOR = @@ -208,6 +209,13 @@ public class SettingsBackupAgent extends BackupAgentHelper { private static final String ERROR_SKIPPED_DUE_TO_LARGE_SCREEN = "skipped_due_to_large_screen"; private static final String ERROR_DID_NOT_PASS_VALIDATION = "did_not_pass_validation"; + private static final String ERROR_IO_EXCEPTION = "io_exception"; + private static final String ERROR_FAILED_TO_RESTORE_SOFTAP_CONFIG = + "failed_to_restore_softap_config"; + private static final String ERROR_FAILED_TO_CONVERT_NETWORK_POLICIES = + "failed_to_convert_network_policies"; + private static final String ERROR_UNKNOWN_BACKUP_SERIALIZATION_VERSION = + "unknown_backup_serialization_version"; // Name of the temporary file we use during full backup/restore. This is @@ -794,29 +802,44 @@ public class SettingsBackupAgent extends BackupAgentHelper { ByteArrayOutputStream baos = new ByteArrayOutputStream(); DataOutputStream out = new DataOutputStream(baos); + int backedUpSettingsCount = 0; try { out.writeUTF(KEY_LOCK_SETTINGS_OWNER_INFO_ENABLED); out.writeUTF(ownerInfoEnabled ? "1" : "0"); + backedUpSettingsCount++; if (ownerInfo != null) { out.writeUTF(KEY_LOCK_SETTINGS_OWNER_INFO); out.writeUTF(ownerInfo != null ? ownerInfo : ""); + backedUpSettingsCount++; } if (lockPatternUtils.isVisiblePatternEverChosen(userId)) { out.writeUTF(KEY_LOCK_SETTINGS_VISIBLE_PATTERN_ENABLED); out.writeUTF(visiblePatternEnabled ? "1" : "0"); + backedUpSettingsCount++; } if (lockPatternUtils.isPowerButtonInstantlyLocksEverChosen(userId)) { out.writeUTF(KEY_LOCK_SETTINGS_POWER_BUTTON_INSTANTLY_LOCKS); out.writeUTF(powerButtonInstantlyLocks ? "1" : "0"); + backedUpSettingsCount++; } if (lockPatternUtils.isPinEnhancedPrivacyEverChosen(userId)) { out.writeUTF(KEY_LOCK_SETTINGS_PIN_ENHANCED_PRIVACY); out.writeUTF(lockPatternUtils.isPinEnhancedPrivacyEnabled(userId) ? "1" : "0"); + backedUpSettingsCount++; } // End marker out.writeUTF(""); out.flush(); + if (areAgentMetricsEnabled) { + numberOfSettingsPerKey.put(KEY_LOCK_SETTINGS, backedUpSettingsCount); + } } catch (IOException ioe) { + if (areAgentMetricsEnabled) { + mBackupRestoreEventLogger.logItemsBackupFailed( + KEY_LOCK_SETTINGS, + NUM_LOCK_SETTINGS - backedUpSettingsCount, + ERROR_IO_EXCEPTION); + } } return baos.toByteArray(); } @@ -1162,6 +1185,7 @@ public class SettingsBackupAgent extends BackupAgentHelper { ByteArrayInputStream bais = new ByteArrayInputStream(buffer, 0, nBytes); DataInputStream in = new DataInputStream(bais); + int restoredLockSettingsCount = 0; try { String key; // Read until empty string marker @@ -1187,9 +1211,20 @@ public class SettingsBackupAgent extends BackupAgentHelper { lockPatternUtils.setPinEnhancedPrivacyEnabled("1".equals(value), userId); break; } + if (areAgentMetricsEnabled) { + mBackupRestoreEventLogger.logItemsRestored(KEY_LOCK_SETTINGS, /* count= */ 1); + restoredLockSettingsCount++; + } + } in.close(); } catch (IOException ioe) { + if (areAgentMetricsEnabled) { + mBackupRestoreEventLogger.logItemsRestoreFailed( + KEY_LOCK_SETTINGS, + NUM_LOCK_SETTINGS - restoredLockSettingsCount, + ERROR_IO_EXCEPTION); + } } } @@ -1309,12 +1344,31 @@ public class SettingsBackupAgent extends BackupAgentHelper { mWifiManager.restoreSupplicantBackupData(supplicant_bytes, ipconfig_bytes); } - private byte[] getSoftAPConfiguration() { - return mWifiManager.retrieveSoftApBackupData(); + @VisibleForTesting + byte[] getSoftAPConfiguration() { + byte[] data = mWifiManager.retrieveSoftApBackupData(); + if (areAgentMetricsEnabled) { + // We're unable to determine how many settings this includes, so we'll just log 1. + numberOfSettingsPerKey.put(KEY_SOFTAP_CONFIG, 1); + } + return data; } - private void restoreSoftApConfiguration(byte[] data) { - SoftApConfiguration configInCloud = mWifiManager.restoreSoftApBackupData(data); + @VisibleForTesting + void restoreSoftApConfiguration(byte[] data) { + SoftApConfiguration configInCloud; + if (areAgentMetricsEnabled) { + try { + configInCloud = mWifiManager.restoreSoftApBackupData(data); + mBackupRestoreEventLogger.logItemsRestored(KEY_SOFTAP_CONFIG, /* count= */ 1); + } catch (Exception e) { + configInCloud = null; + mBackupRestoreEventLogger.logItemsRestoreFailed( + KEY_SOFTAP_CONFIG, /* count= */ 1, ERROR_FAILED_TO_RESTORE_SOFTAP_CONFIG); + } + } else { + configInCloud = mWifiManager.restoreSoftApBackupData(data); + } if (configInCloud != null) { if (DEBUG) Log.d(TAG, "Successfully unMarshaled SoftApConfiguration "); // Depending on device hardware, we may need to notify the user of a setting change @@ -1384,6 +1438,7 @@ public class SettingsBackupAgent extends BackupAgentHelper { try { out.writeInt(NETWORK_POLICIES_BACKUP_VERSION); out.writeInt(policies.length); + int numberOfPoliciesBackedUp = 0; for (NetworkPolicy policy : policies) { // We purposefully only backup policies that the user has // defined; any inferred policies might include @@ -1393,13 +1448,23 @@ public class SettingsBackupAgent extends BackupAgentHelper { out.writeByte(BackupUtils.NOT_NULL); out.writeInt(marshaledPolicy.length); out.write(marshaledPolicy); + if (areAgentMetricsEnabled) { + numberOfPoliciesBackedUp++; + } } else { out.writeByte(BackupUtils.NULL); } } + if (areAgentMetricsEnabled) { + numberOfSettingsPerKey.put(KEY_NETWORK_POLICIES, numberOfPoliciesBackedUp); + } } catch (IOException ioe) { Log.e(TAG, "Failed to convert NetworkPolicies to byte array " + ioe.getMessage()); baos.reset(); + mBackupRestoreEventLogger.logItemsBackupFailed( + KEY_NETWORK_POLICIES, + policies.length, + ERROR_FAILED_TO_CONVERT_NETWORK_POLICIES); } } return baos.toByteArray(); @@ -1433,6 +1498,10 @@ public class SettingsBackupAgent extends BackupAgentHelper { try { int version = in.readInt(); if (version < 1 || version > NETWORK_POLICIES_BACKUP_VERSION) { + mBackupRestoreEventLogger.logItemsRestoreFailed( + KEY_NETWORK_POLICIES, + /* count= */ 1, + ERROR_UNKNOWN_BACKUP_SERIALIZATION_VERSION); throw new BackupUtils.BadVersionException( "Unknown Backup Serialization Version"); } @@ -1449,10 +1518,15 @@ public class SettingsBackupAgent extends BackupAgentHelper { } // Only set the policies if there was no error in the restore operation networkPolicyManager.setNetworkPolicies(policies); + mBackupRestoreEventLogger.logItemsRestored(KEY_NETWORK_POLICIES, policies.length); } catch (NullPointerException | IOException | BackupUtils.BadVersionException | DateTimeException e) { // NPE can be thrown when trying to instantiate a NetworkPolicy Log.e(TAG, "Failed to convert byte array to NetworkPolicies " + e.getMessage()); + mBackupRestoreEventLogger.logItemsRestoreFailed( + KEY_NETWORK_POLICIES, + /* count= */ 1, + ERROR_FAILED_TO_CONVERT_NETWORK_POLICIES); } } } diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java index ed193515b382..cb656bdd5d54 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java @@ -6122,17 +6122,7 @@ public class SettingsProvider extends ContentProvider { } if (currentVersion == 220) { - final SettingsState globalSettings = getGlobalSettingsLocked(); - final Setting enableBackAnimation = - globalSettings.getSettingLocked(Global.ENABLE_BACK_ANIMATION); - if (enableBackAnimation.isNull()) { - final boolean defEnableBackAnimation = - getContext() - .getResources() - .getBoolean(R.bool.def_enable_back_animation); - initGlobalSettingsDefaultValLocked( - Settings.Global.ENABLE_BACK_ANIMATION, defEnableBackAnimation); - } + // Version 221: Removed currentVersion = 221; } diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java index c88a7fd834d6..cbdb36fff98c 100644 --- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java +++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java @@ -564,7 +564,6 @@ public class SettingsBackupTest { Settings.Global.WATCHDOG_TIMEOUT_MILLIS, Settings.Global.MANAGED_PROVISIONING_DEFER_PROVISIONING_TO_ROLE_HOLDER, Settings.Global.REVIEW_PERMISSIONS_NOTIFICATION_STATE, - Settings.Global.ENABLE_BACK_ANIMATION, // Temporary for T, dev option only Settings.Global.HEARING_DEVICE_LOCAL_AMBIENT_VOLUME, // cache per hearing device Settings.Global.HEARING_DEVICE_LOCAL_NOTIFICATION, // cache per hearing device Settings.Global.Wearable.COMBINED_LOCATION_ENABLE, diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsBackupAgentTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsBackupAgentTest.java index 18c43a704bcc..95dd0db40c0e 100644 --- a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsBackupAgentTest.java +++ b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsBackupAgentTest.java @@ -16,6 +16,8 @@ package com.android.providers.settings; +import static com.android.providers.settings.SettingsBackupRestoreKeys.KEY_SOFTAP_CONFIG; + import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertNotNull; import static junit.framework.Assert.assertNull; @@ -28,6 +30,7 @@ import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.when; +import android.annotation.Nullable; import android.app.backup.BackupAnnotations.BackupDestination; import android.app.backup.BackupAnnotations.OperationType; import android.app.backup.BackupDataInput; @@ -42,6 +45,8 @@ import android.content.pm.PackageManager; import android.database.Cursor; import android.database.MatrixCursor; import android.net.Uri; +import android.net.wifi.SoftApConfiguration; +import android.net.wifi.WifiManager; import android.os.Build; import android.os.Bundle; import android.os.UserHandle; @@ -126,6 +131,7 @@ public class SettingsBackupAgentTest extends BaseSettingsProviderTest { @Mock private BackupDataInput mBackupDataInput; @Mock private BackupDataOutput mBackupDataOutput; + @Mock private static WifiManager mWifiManager; private TestFriendlySettingsBackupAgent mAgentUnderTest; private Context mContext; @@ -754,6 +760,80 @@ public class SettingsBackupAgentTest extends BaseSettingsProviderTest { assertNull(getLoggingResultForDatatype(TEST_KEY, mAgentUnderTest)); } + @Test + @EnableFlags(com.android.server.backup.Flags.FLAG_ENABLE_METRICS_SETTINGS_BACKUP_AGENTS) + public void getSoftAPConfiguration_flagIsEnabled_numberOfSettingsInKeyAreRecorded() { + mAgentUnderTest.onCreate( + UserHandle.SYSTEM, BackupDestination.CLOUD, OperationType.BACKUP); + when(mWifiManager.retrieveSoftApBackupData()).thenReturn(null); + + mAgentUnderTest.getSoftAPConfiguration(); + + assertEquals(mAgentUnderTest.getNumberOfSettingsPerKey(KEY_SOFTAP_CONFIG), 1); + } + + @Test + @DisableFlags(com.android.server.backup.Flags.FLAG_ENABLE_METRICS_SETTINGS_BACKUP_AGENTS) + public void getSoftAPConfiguration_flagIsNotEnabled_numberOfSettingsInKeyAreNotRecorded() { + mAgentUnderTest.onCreate( + UserHandle.SYSTEM, BackupDestination.CLOUD, OperationType.BACKUP); + when(mWifiManager.retrieveSoftApBackupData()).thenReturn(null); + + mAgentUnderTest.getSoftAPConfiguration(); + + assertEquals(mAgentUnderTest.getNumberOfSettingsPerKey(KEY_SOFTAP_CONFIG), 0); + } + + @Test + @EnableFlags(com.android.server.backup.Flags.FLAG_ENABLE_METRICS_SETTINGS_BACKUP_AGENTS) + public void + restoreSoftApConfiguration_flagIsEnabled_restoreIsSuccessful_successMetricsAreLogged() { + mAgentUnderTest.onCreate( + UserHandle.SYSTEM, BackupDestination.CLOUD, OperationType.RESTORE); + SoftApConfiguration config = new SoftApConfiguration.Builder().setSsid("test").build(); + byte[] data = config.toString().getBytes(); + when(mWifiManager.restoreSoftApBackupData(any())).thenReturn(null); + + mAgentUnderTest.restoreSoftApConfiguration(data); + + DataTypeResult loggingResult = + getLoggingResultForDatatype(KEY_SOFTAP_CONFIG, mAgentUnderTest); + assertNotNull(loggingResult); + assertEquals(loggingResult.getSuccessCount(), 1); + } + + @Test + @EnableFlags(com.android.server.backup.Flags.FLAG_ENABLE_METRICS_SETTINGS_BACKUP_AGENTS) + public void + restoreSoftApConfiguration_flagIsEnabled_restoreIsNotSuccessful_failureMetricsAreLogged() { + mAgentUnderTest.onCreate( + UserHandle.SYSTEM, BackupDestination.CLOUD, OperationType.RESTORE); + SoftApConfiguration config = new SoftApConfiguration.Builder().setSsid("test").build(); + byte[] data = config.toString().getBytes(); + when(mWifiManager.restoreSoftApBackupData(any())).thenThrow(new RuntimeException()); + + mAgentUnderTest.restoreSoftApConfiguration(data); + + DataTypeResult loggingResult = + getLoggingResultForDatatype(KEY_SOFTAP_CONFIG, mAgentUnderTest); + assertNotNull(loggingResult); + assertEquals(loggingResult.getFailCount(), 1); + } + + @Test + @DisableFlags(com.android.server.backup.Flags.FLAG_ENABLE_METRICS_SETTINGS_BACKUP_AGENTS) + public void restoreSoftApConfiguration_flagIsNotEnabled_metricsAreNotLogged() { + mAgentUnderTest.onCreate( + UserHandle.SYSTEM, BackupDestination.CLOUD, OperationType.RESTORE); + SoftApConfiguration config = new SoftApConfiguration.Builder().setSsid("test").build(); + byte[] data = config.toString().getBytes(); + when(mWifiManager.restoreSoftApBackupData(any())).thenReturn(null); + + mAgentUnderTest.restoreSoftApConfiguration(data); + + assertNull(getLoggingResultForDatatype(KEY_SOFTAP_CONFIG, mAgentUnderTest)); + } + private byte[] generateBackupData(Map<String, String> keyValueData) { int totalBytes = 0; for (String key : keyValueData.keySet()) { @@ -890,6 +970,13 @@ public class SettingsBackupAgentTest extends BaseSettingsProviderTest { this.numberOfSettingsPerKey.put(key, numberOfSettings); } } + + int getNumberOfSettingsPerKey(String key) { + if (numberOfSettingsPerKey == null || !numberOfSettingsPerKey.containsKey(key)) { + return 0; + } + return numberOfSettingsPerKey.get(key); + } } /** The TestSettingsHelper tracks which values have been backed up and/or restored. */ @@ -944,6 +1031,14 @@ public class SettingsBackupAgentTest extends BaseSettingsProviderTest { public ContentResolver getContentResolver() { return mContentResolver; } + + @Override + public Object getSystemService(String name) { + if (name.equals(Context.WIFI_SERVICE)) { + return mWifiManager; + } + return super.getSystemService(name); + } } /** ContentProvider which returns a set of known test values. */ diff --git a/packages/SystemUI/aconfig/predictive_back.aconfig b/packages/SystemUI/aconfig/predictive_back.aconfig index ad4a02764176..ee918c275b7b 100644 --- a/packages/SystemUI/aconfig/predictive_back.aconfig +++ b/packages/SystemUI/aconfig/predictive_back.aconfig @@ -7,17 +7,3 @@ flag { description: "Enable Shade Animations" bug: "327732946" } - -flag { - name: "predictive_back_animate_bouncer" - namespace: "systemui" - description: "Enable Predictive Back Animation in Bouncer" - bug: "327733487" -} - -flag { - name: "predictive_back_animate_dialogs" - namespace: "systemui" - description: "Enable Predictive Back Animation for SysUI dialogs" - bug: "327721544" -} diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig index 715d22328f2b..7d5fd903c01b 100644 --- a/packages/SystemUI/aconfig/systemui.aconfig +++ b/packages/SystemUI/aconfig/systemui.aconfig @@ -133,14 +133,6 @@ flag { } flag { - name: "notifications_footer_view_refactor" - namespace: "systemui" - description: "Enables the refactored version of the footer view in the notification shade " - "(containing the \"Clear all\" button). Should not bring any behavior changes" - bug: "293167744" -} - -flag { name: "notifications_icon_container_refactor" namespace: "systemui" description: "Enables the refactored version of the notification icon container in StatusBar, " diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/AnimationFeatureFlags.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/AnimationFeatureFlags.kt deleted file mode 100644 index 1c9dabbb0e07..000000000000 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/AnimationFeatureFlags.kt +++ /dev/null @@ -1,6 +0,0 @@ -package com.android.systemui.animation - -interface AnimationFeatureFlags { - val isPredictiveBackQsDialogAnim: Boolean - get() = false -} diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogTransitionAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogTransitionAnimator.kt index 907c39d842ce..c88c4ebb1a8d 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogTransitionAnimator.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogTransitionAnimator.kt @@ -59,13 +59,8 @@ constructor( private val mainExecutor: Executor, private val callback: Callback, private val interactionJankMonitor: InteractionJankMonitor, - private val featureFlags: AnimationFeatureFlags, private val transitionAnimator: TransitionAnimator = - TransitionAnimator( - mainExecutor, - TIMINGS, - INTERPOLATORS, - ), + TransitionAnimator(mainExecutor, TIMINGS, INTERPOLATORS), private val isForTesting: Boolean = false, ) { private companion object { @@ -219,7 +214,7 @@ constructor( dialog: Dialog, view: View, cuj: DialogCuj? = null, - animateBackgroundBoundsChange: Boolean = false + animateBackgroundBoundsChange: Boolean = false, ) { val controller = Controller.fromView(view, cuj) if (controller == null) { @@ -245,7 +240,7 @@ constructor( fun show( dialog: Dialog, controller: Controller, - animateBackgroundBoundsChange: Boolean = false + animateBackgroundBoundsChange: Boolean = false, ) { if (Looper.myLooper() != Looper.getMainLooper()) { throw IllegalStateException( @@ -263,15 +258,14 @@ constructor( val controller = animatedParent?.dialogContentWithBackground?.let { Controller.fromView(it, controller.cuj) - } - ?: controller + } ?: controller // Make sure we don't run the launch animation from the same source twice at the same time. if (openedDialogs.any { it.controller.sourceIdentity == controller.sourceIdentity }) { Log.e( TAG, "Not running dialog launch animation from source as it is already expanded into a" + - " dialog" + " dialog", ) dialog.show() return @@ -288,7 +282,6 @@ constructor( animateBackgroundBoundsChange = animateBackgroundBoundsChange, parentAnimatedDialog = animatedParent, forceDisableSynchronization = isForTesting, - featureFlags = featureFlags, ) openedDialogs.add(animatedDialog) @@ -305,7 +298,7 @@ constructor( dialog: Dialog, animateFrom: Dialog, cuj: DialogCuj? = null, - animateBackgroundBoundsChange: Boolean = false + animateBackgroundBoundsChange: Boolean = false, ) { val view = openedDialogs.firstOrNull { it.dialog == animateFrom }?.dialogContentWithBackground @@ -313,7 +306,7 @@ constructor( Log.w( TAG, "Showing dialog $dialog normally as the dialog it is shown from was not shown " + - "using DialogTransitionAnimator" + "using DialogTransitionAnimator", ) dialog.show() return @@ -323,7 +316,7 @@ constructor( dialog, view, animateBackgroundBoundsChange = animateBackgroundBoundsChange, - cuj = cuj + cuj = cuj, ) } @@ -346,8 +339,7 @@ constructor( val animatedDialog = openedDialogs.firstOrNull { it.dialog.window?.decorView?.viewRootImpl == view.viewRootImpl - } - ?: return null + } ?: return null return createActivityTransitionController(animatedDialog, cujType) } @@ -373,7 +365,7 @@ constructor( private fun createActivityTransitionController( animatedDialog: AnimatedDialog, - cujType: Int? = null + cujType: Int? = null, ): ActivityTransitionAnimator.Controller? { // At this point, we know that the intent of the caller is to dismiss the dialog to show // an app, so we disable the exit animation into the source because we will never want to @@ -440,7 +432,7 @@ constructor( } private fun disableDialogDismiss() { - dialog.setDismissOverride { /* Do nothing */} + dialog.setDismissOverride { /* Do nothing */ } } private fun enableDialogDismiss() { @@ -530,7 +522,6 @@ private class AnimatedDialog( * Whether synchronization should be disabled, which can be useful if we are running in a test. */ private val forceDisableSynchronization: Boolean, - private val featureFlags: AnimationFeatureFlags, ) { /** * The DecorView of this dialog window. @@ -643,8 +634,7 @@ private class AnimatedDialog( originalDialogBackgroundColor = GhostedViewTransitionAnimatorController.findGradientDrawable(background) ?.color - ?.defaultColor - ?: Color.BLACK + ?.defaultColor ?: Color.BLACK // Make the background view invisible until we start the animation. We use the transition // visibility like GhostView does so that we don't mess up with the accessibility tree (see @@ -700,7 +690,7 @@ private class AnimatedDialog( oldLeft: Int, oldTop: Int, oldRight: Int, - oldBottom: Int + oldBottom: Int, ) { dialogContentWithBackground.removeOnLayoutChangeListener(this) @@ -717,9 +707,7 @@ private class AnimatedDialog( // the dialog. dialog.setDismissOverride(this::onDialogDismissed) - if (featureFlags.isPredictiveBackQsDialogAnim) { - dialog.registerAnimationOnBackInvoked(targetView = dialogContentWithBackground) - } + dialog.registerAnimationOnBackInvoked(targetView = dialogContentWithBackground) // Show the dialog. dialog.show() @@ -815,7 +803,7 @@ private class AnimatedDialog( if (hasInstrumentedJank) { interactionJankMonitor.end(controller.cuj!!.cujType) } - } + }, ) } @@ -888,14 +876,14 @@ private class AnimatedDialog( onAnimationFinished(true /* instantDismiss */) onDialogDismissed(this@AnimatedDialog) } - } + }, ) } private fun startAnimation( isLaunching: Boolean, onLaunchAnimationStart: () -> Unit = {}, - onLaunchAnimationEnd: () -> Unit = {} + onLaunchAnimationEnd: () -> Unit = {}, ) { // Create 2 controllers to animate both the dialog and the source. val startController = @@ -969,7 +957,7 @@ private class AnimatedDialog( override fun onTransitionAnimationProgress( state: TransitionAnimator.State, progress: Float, - linearProgress: Float + linearProgress: Float, ) { startController.onTransitionAnimationProgress(state, progress, linearProgress) @@ -1026,7 +1014,7 @@ private class AnimatedDialog( oldLeft: Int, oldTop: Int, oldRight: Int, - oldBottom: Int + oldBottom: Int, ) { // Don't animate if bounds didn't actually change. if (left == oldLeft && top == oldTop && right == oldRight && bottom == oldBottom) { diff --git a/packages/SystemUI/compose/core/src/com/android/compose/gesture/NestedDraggable.kt b/packages/SystemUI/compose/core/src/com/android/compose/gesture/NestedDraggable.kt index e02e8b483543..5f1f588bb2b5 100644 --- a/packages/SystemUI/compose/core/src/com/android/compose/gesture/NestedDraggable.kt +++ b/packages/SystemUI/compose/core/src/com/android/compose/gesture/NestedDraggable.kt @@ -37,6 +37,7 @@ import androidx.compose.ui.input.pointer.PointerEventPass import androidx.compose.ui.input.pointer.PointerId import androidx.compose.ui.input.pointer.PointerInputChange import androidx.compose.ui.input.pointer.PointerInputScope +import androidx.compose.ui.input.pointer.PointerType import androidx.compose.ui.input.pointer.SuspendingPointerInputModifierNode import androidx.compose.ui.input.pointer.changedToDownIgnoreConsumed import androidx.compose.ui.input.pointer.changedToUpIgnoreConsumed @@ -52,7 +53,6 @@ import androidx.compose.ui.node.currentValueOf import androidx.compose.ui.platform.LocalViewConfiguration import androidx.compose.ui.unit.IntSize import androidx.compose.ui.unit.Velocity -import androidx.compose.ui.util.fastSumBy import com.android.compose.modifiers.thenIf import kotlin.math.sign import kotlinx.coroutines.CompletableDeferred @@ -81,7 +81,13 @@ interface NestedDraggable { * in the direction given by [sign], with the given number of [pointersDown] when the touch slop * was detected. */ - fun onDragStarted(position: Offset, sign: Float, pointersDown: Int): Controller + fun onDragStarted( + position: Offset, + sign: Float, + pointersDown: Int, + // TODO(b/382665591): Make this non-nullable. + pointerType: PointerType?, + ): Controller /** * Whether this draggable should consume any scroll amount with the given [sign] coming from a @@ -184,8 +190,8 @@ private class NestedDraggableNode( */ private var lastFirstDown: Offset? = null - /** The number of pointers down. */ - private var pointersDownCount = 0 + /** The pointers currently down, in order of which they were done and mapping to their type. */ + private val pointersDown = linkedMapOf<PointerId, PointerType>() init { delegate(nestedScrollModifierNode(this, nestedScrollDispatcher)) @@ -256,7 +262,9 @@ private class NestedDraggableNode( check(down.position == lastFirstDown) { "Position from detectDrags() is not the same as position in trackDownPosition()" } - check(pointersDownCount == 1) { "pointersDownCount is equal to $pointersDownCount" } + check(pointersDown.size == 1 && pointersDown.keys.first() == down.id) { + "pointersDown should only contain $down but it contains $pointersDown" + } var overSlop = 0f val onTouchSlopReached = { change: PointerInputChange, over: Float -> @@ -295,8 +303,9 @@ private class NestedDraggableNode( } } - check(pointersDownCount > 0) { "pointersDownCount is equal to $pointersDownCount" } - val controller = draggable.onDragStarted(down.position, sign, pointersDownCount) + check(pointersDown.size > 0) { "pointersDown is empty" } + val controller = + draggable.onDragStarted(down.position, sign, pointersDown.size, drag.type) if (overSlop != 0f) { onDrag(controller, drag, overSlop, velocityTracker) } @@ -450,20 +459,24 @@ private class NestedDraggableNode( private suspend fun PointerInputScope.trackDownPosition() { awaitEachGesture { - val down = awaitFirstDown(requireUnconsumed = false) - lastFirstDown = down.position - pointersDownCount = 1 + try { + val down = awaitFirstDown(requireUnconsumed = false) + lastFirstDown = down.position + pointersDown[down.id] = down.type - do { - pointersDownCount += - awaitPointerEvent().changes.fastSumBy { change -> + do { + awaitPointerEvent().changes.forEach { change -> when { - change.changedToDownIgnoreConsumed() -> 1 - change.changedToUpIgnoreConsumed() -> -1 - else -> 0 + change.changedToDownIgnoreConsumed() -> { + pointersDown[change.id] = change.type + } + change.changedToUpIgnoreConsumed() -> pointersDown.remove(change.id) } } - } while (pointersDownCount > 0) + } while (pointersDown.size > 0) + } finally { + pointersDown.clear() + } } } @@ -491,12 +504,13 @@ private class NestedDraggableNode( if (nestedScrollController == null && draggable.shouldConsumeNestedScroll(sign)) { val startedPosition = checkNotNull(lastFirstDown) { "lastFirstDown is not set" } - // TODO(b/382665591): Replace this by check(pointersDownCount > 0). - val pointersDown = pointersDownCount.coerceAtLeast(1) + // TODO(b/382665591): Ensure that there is at least one pointer down. + val pointersDownCount = pointersDown.size.coerceAtLeast(1) + val pointerType = pointersDown.entries.firstOrNull()?.value nestedScrollController = NestedScrollController( overscrollEffect, - draggable.onDragStarted(startedPosition, sign, pointersDown), + draggable.onDragStarted(startedPosition, sign, pointersDownCount, pointerType), ) } diff --git a/packages/SystemUI/compose/core/tests/src/com/android/compose/gesture/NestedDraggableTest.kt b/packages/SystemUI/compose/core/tests/src/com/android/compose/gesture/NestedDraggableTest.kt index 9c49090916e3..7f70e97411f4 100644 --- a/packages/SystemUI/compose/core/tests/src/com/android/compose/gesture/NestedDraggableTest.kt +++ b/packages/SystemUI/compose/core/tests/src/com/android/compose/gesture/NestedDraggableTest.kt @@ -33,10 +33,12 @@ import androidx.compose.ui.geometry.Offset import androidx.compose.ui.input.nestedscroll.NestedScrollConnection import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.input.pointer.PointerInputChange +import androidx.compose.ui.input.pointer.PointerType import androidx.compose.ui.platform.LocalViewConfiguration import androidx.compose.ui.test.junit4.ComposeContentTestRule import androidx.compose.ui.test.junit4.createComposeRule import androidx.compose.ui.test.onRoot +import androidx.compose.ui.test.performMouseInput import androidx.compose.ui.test.performTouchInput import androidx.compose.ui.test.swipeDown import androidx.compose.ui.test.swipeLeft @@ -653,6 +655,61 @@ class NestedDraggableTest(override val orientation: Orientation) : OrientationAw assertThat(flingIsDone).isTrue() } + @Test + fun pointerType() { + val draggable = TestDraggable() + val touchSlop = + rule.setContentWithTouchSlop { + Box(Modifier.fillMaxSize().nestedDraggable(draggable, orientation)) + } + + rule.onRoot().performTouchInput { + down(center) + moveBy(touchSlop.toOffset()) + } + + assertThat(draggable.onDragStartedPointerType).isEqualTo(PointerType.Touch) + } + + @Test + fun pointerType_mouse() { + val draggable = TestDraggable() + val touchSlop = + rule.setContentWithTouchSlop { + Box(Modifier.fillMaxSize().nestedDraggable(draggable, orientation)) + } + + rule.onRoot().performMouseInput { + moveTo(center) + press() + moveBy(touchSlop.toOffset()) + release() + } + + assertThat(draggable.onDragStartedPointerType).isEqualTo(PointerType.Mouse) + } + + @Test + fun pointersDown_clearedWhenDisabled() { + val draggable = TestDraggable() + var enabled by mutableStateOf(true) + rule.setContent { + Box(Modifier.fillMaxSize().nestedDraggable(draggable, orientation, enabled = enabled)) + } + + rule.onRoot().performTouchInput { down(center) } + + enabled = false + rule.waitForIdle() + + rule.onRoot().performTouchInput { up() } + + enabled = true + rule.waitForIdle() + + rule.onRoot().performTouchInput { down(center) } + } + private fun ComposeContentTestRule.setContentWithTouchSlop( content: @Composable () -> Unit ): Float { @@ -688,6 +745,7 @@ class NestedDraggableTest(override val orientation: Orientation) : OrientationAw var onDragStartedPosition = Offset.Zero var onDragStartedSign = 0f var onDragStartedPointersDown = 0 + var onDragStartedPointerType: PointerType? = null var onDragDelta = 0f override fun shouldStartDrag(change: PointerInputChange): Boolean = shouldStartDrag @@ -696,11 +754,13 @@ class NestedDraggableTest(override val orientation: Orientation) : OrientationAw position: Offset, sign: Float, pointersDown: Int, + pointerType: PointerType?, ): NestedDraggable.Controller { onDragStartedCalled = true onDragStartedPosition = position onDragStartedSign = sign onDragStartedPointersDown = pointersDown + onDragStartedPointerType = pointerType onDragDelta = 0f onDragStarted.invoke(position, sign) diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt index c704a3e96467..de428a7d3548 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt @@ -35,6 +35,7 @@ import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.IntSize import androidx.compose.ui.unit.LayoutDirection +import com.android.compose.gesture.NestedScrollableBound import com.android.compose.gesture.effect.ContentOverscrollEffect /** @@ -238,6 +239,18 @@ interface BaseContentScope : ElementStateScope { fun Modifier.noResizeDuringTransitions(): Modifier /** + * Temporarily disable this content swipe actions when any scrollable below this modifier has + * consumed any amount of scroll delta, until the scroll gesture is finished. + * + * This can for instance be used to ensure that a scrollable list is overscrolled once it + * reached its bounds instead of directly starting a scene transition from the same scroll + * gesture. + */ + fun Modifier.disableSwipesWhenScrolling( + bounds: NestedScrollableBound = NestedScrollableBound.Any + ): Modifier + + /** * A [NestedSceneTransitionLayout] will share its elements with its ancestor STLs therefore * enabling sharedElement transitions between them. */ diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeAnimation.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeAnimation.kt index 607e4fadc256..ba92f9bea07d 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeAnimation.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeAnimation.kt @@ -315,16 +315,10 @@ internal class SwipeAnimation<T : ContentKey>( val skipAnimation = hasReachedTargetContent && !contentTransition.isWithinProgressRange(initialProgress) - val targetOffset = - if (targetContent == fromContent) { - 0f - } else { - val distance = distance() - check(distance != DistanceUnspecified) { - "distance is equal to $DistanceUnspecified" - } - distance - } + val distance = distance() + check(distance != DistanceUnspecified) { "distance is equal to $DistanceUnspecified" } + + val targetOffset = if (targetContent == fromContent) 0f else distance // If the effective current content changed, it should be reflected right now in the // current state, even before the settle animation is ongoing. That way all the @@ -343,7 +337,16 @@ internal class SwipeAnimation<T : ContentKey>( } val animatable = - Animatable(initialOffset, OffsetVisibilityThreshold).also { offsetAnimation = it } + Animatable(initialOffset, OffsetVisibilityThreshold).also { + offsetAnimation = it + + // We should animate when the progress value is between [0, 1]. + if (distance > 0) { + it.updateBounds(0f, distance) + } else { + it.updateBounds(distance, 0f) + } + } check(isAnimatingOffset()) @@ -370,42 +373,26 @@ internal class SwipeAnimation<T : ContentKey>( val velocityConsumed = CompletableDeferred<Float>() offsetAnimationRunnable.complete { - try { + val result = animatable.animateTo( targetValue = targetOffset, animationSpec = swipeSpec, initialVelocity = initialVelocity, - ) { - // Immediately stop this transition if we are bouncing on a content that - // does not bounce. - if (!contentTransition.isWithinProgressRange(progress)) { - // We are no longer able to consume the velocity, the rest can be - // consumed by another component in the hierarchy. - velocityConsumed.complete(initialVelocity - velocity) - throw SnapException() - } - } - } catch (_: SnapException) { - /* Ignore. */ - } finally { - if (!velocityConsumed.isCompleted) { - // The animation consumed the whole available velocity - velocityConsumed.complete(initialVelocity) - } + ) - // Wait for overscroll to finish so that the transition is removed from the STLState - // only after the overscroll is done, to avoid dropping frame right when the user - // lifts their finger and overscroll is animated to 0. - overscrollCompletable?.await() - } + // We are no longer able to consume the velocity, the rest can be consumed by another + // component in the hierarchy. + velocityConsumed.complete(initialVelocity - result.endState.velocity) + + // Wait for overscroll to finish so that the transition is removed from the STLState + // only after the overscroll is done, to avoid dropping frame right when the user + // lifts their finger and overscroll is animated to 0. + overscrollCompletable?.await() } return velocityConsumed.await() } - /** An exception thrown during the animation to stop it immediately. */ - private class SnapException : Exception() - private fun canChangeContent(targetContent: ContentKey): Boolean { return when (val transition = contentTransition) { is TransitionState.Transition.ChangeScene -> diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt index c5b3df222855..3f6bce724b1b 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt @@ -54,7 +54,7 @@ private fun DraggableHandlerImpl.contentForSwipes(): Content { /** Whether swipe should be enabled in the given [orientation]. */ internal fun Content.shouldEnableSwipes(orientation: Orientation): Boolean { - if (userActions.isEmpty()) { + if (userActions.isEmpty() || !areSwipesAllowed()) { return false } @@ -69,6 +69,10 @@ internal fun Content.shouldEnableSwipes(orientation: Orientation): Boolean { * @return The best matching [UserActionResult], or `null` if no match is found. */ internal fun Content.findActionResultBestMatch(swipe: Swipe.Resolved): UserActionResult? { + if (!areSwipesAllowed()) { + return null + } + var bestPoints = Int.MIN_VALUE var bestMatch: UserActionResult? = null userActions.forEach { (actionSwipe, actionResult) -> diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/Content.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/Content.kt index 4c15f7a4534f..59b4a09385f5 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/Content.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/Content.kt @@ -56,7 +56,10 @@ import com.android.compose.animation.scene.effect.GestureEffect import com.android.compose.animation.scene.effect.VisualEffect import com.android.compose.animation.scene.element import com.android.compose.animation.scene.modifiers.noResizeDuringTransitions +import com.android.compose.gesture.NestedScrollControlState +import com.android.compose.gesture.NestedScrollableBound import com.android.compose.gesture.effect.OffsetOverscrollEffect +import com.android.compose.gesture.nestedScrollController import com.android.compose.modifiers.thenIf import com.android.compose.ui.graphics.ContainerState import com.android.compose.ui.graphics.container @@ -70,7 +73,8 @@ internal sealed class Content( actions: Map<UserAction.Resolved, UserActionResult>, zIndex: Float, ) { - internal val scope = ContentScopeImpl(layoutImpl, content = this) + private val nestedScrollControlState = NestedScrollControlState() + internal val scope = ContentScopeImpl(layoutImpl, content = this, nestedScrollControlState) val containerState = ContainerState() var content by mutableStateOf(content) @@ -101,11 +105,14 @@ internal sealed class Content( scope.content() } } + + fun areSwipesAllowed(): Boolean = nestedScrollControlState.isOuterScrollAllowed } internal class ContentScopeImpl( private val layoutImpl: SceneTransitionLayoutImpl, private val content: Content, + private val nestedScrollControlState: NestedScrollControlState, ) : ContentScope, ElementStateScope by layoutImpl.elementStateScope { override val contentKey: ContentKey get() = content.key @@ -176,6 +183,10 @@ internal class ContentScopeImpl( return noResizeDuringTransitions(layoutState = layoutImpl.state) } + override fun Modifier.disableSwipesWhenScrolling(bounds: NestedScrollableBound): Modifier { + return nestedScrollController(nestedScrollControlState, bounds) + } + @Composable override fun NestedSceneTransitionLayout( state: SceneTransitionLayoutState, diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ContentTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ContentTest.kt new file mode 100644 index 000000000000..06a9735d97e2 --- /dev/null +++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ContentTest.kt @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.compose.animation.scene + +import androidx.compose.foundation.gestures.Orientation +import androidx.compose.foundation.gestures.rememberScrollableState +import androidx.compose.foundation.gestures.scrollable +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier +import androidx.compose.ui.test.junit4.createComposeRule +import androidx.compose.ui.test.onRoot +import androidx.compose.ui.test.performTouchInput +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.android.compose.animation.scene.TestScenes.SceneA +import com.google.common.truth.Truth.assertThat +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +class ContentTest { + @get:Rule val rule = createComposeRule() + + @Test + fun disableSwipesWhenScrolling() { + lateinit var layoutImpl: SceneTransitionLayoutImpl + rule.setContent { + SceneTransitionLayoutForTesting( + remember { MutableSceneTransitionLayoutState(SceneA) }, + onLayoutImpl = { layoutImpl = it }, + ) { + scene(SceneA) { + Box( + Modifier.fillMaxSize() + .disableSwipesWhenScrolling() + .scrollable(rememberScrollableState { it }, Orientation.Vertical) + ) + } + } + } + + val content = layoutImpl.content(SceneA) + assertThat(content.areSwipesAllowed()).isTrue() + rule.onRoot().performTouchInput { + down(topLeft) + moveBy(bottomLeft) + } + + assertThat(content.areSwipesAllowed()).isFalse() + rule.onRoot().performTouchInput { up() } + assertThat(content.areSwipesAllowed()).isTrue() + } +} diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutTest.kt index 7c8c6e5f6c12..e580e3c40690 100644 --- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutTest.kt +++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutTest.kt @@ -21,6 +21,7 @@ import androidx.compose.animation.core.LinearEasing import androidx.compose.animation.core.tween import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.offset import androidx.compose.foundation.layout.size @@ -33,6 +34,7 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.LocalViewConfiguration import androidx.compose.ui.platform.testTag import androidx.compose.ui.test.SemanticsNodeInteraction import androidx.compose.ui.test.assertHeightIsEqualTo @@ -43,6 +45,9 @@ import androidx.compose.ui.test.junit4.createComposeRule import androidx.compose.ui.test.onChild import androidx.compose.ui.test.onNodeWithTag import androidx.compose.ui.test.onNodeWithText +import androidx.compose.ui.test.onRoot +import androidx.compose.ui.test.performTouchInput +import androidx.compose.ui.test.swipeDown import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.DpOffset import androidx.compose.ui.unit.IntOffset @@ -469,4 +474,41 @@ class SceneTransitionLayoutTest { assertThat(layoutImpl.overlaysOrNullForTest()).isNull() } + + @Test + fun transitionProgressBoundedBetween0And1() { + val layoutWidth = 200.dp + val layoutHeight = 400.dp + + // The draggable touch slop, i.e. the min px distance a touch pointer must move before it is + // detected as a drag event. + var touchSlop = 0f + val state = rule.runOnUiThread { MutableSceneTransitionLayoutState(initialScene = SceneA) } + rule.setContent { + touchSlop = LocalViewConfiguration.current.touchSlop + SceneTransitionLayout(state, Modifier.size(layoutWidth, layoutHeight)) { + scene(SceneA, userActions = mapOf(Swipe.Down to SceneB)) { + Spacer(Modifier.fillMaxSize()) + } + scene(SceneB) { Spacer(Modifier.fillMaxSize()) } + } + } + assertThat(state.transitionState).isIdle() + + rule.mainClock.autoAdvance = false + + // Swipe the verticalSwipeDistance. + rule.onRoot().performTouchInput { + swipeDown(endY = bottom + touchSlop, durationMillis = 50) + } + + rule.mainClock.advanceTimeBy(16) + val transition = assertThat(state.transitionState).isSceneTransition() + assertThat(transition).isNotNull() + assertThat(transition).hasProgress(1f, tolerance = 0.01f) + + rule.mainClock.advanceTimeBy(16) + // Fling animation, we are overscrolling now. Progress should always be between [0, 1]. + assertThat(transition).hasProgress(1f) + } } diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockTextView.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockTextView.kt index 0f8ca947479b..2b0825f39243 100644 --- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockTextView.kt +++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockTextView.kt @@ -30,7 +30,6 @@ import android.util.AttributeSet import android.util.Log import android.util.MathUtils import android.util.TypedValue -import android.view.View.MeasureSpec.AT_MOST import android.view.View.MeasureSpec.EXACTLY import android.view.animation.Interpolator import android.widget.TextView @@ -77,7 +76,6 @@ open class SimpleDigitalClockTextView(clockCtx: ClockContext, attrs: AttributeSe var maxSingleDigitWidth = -1 var digitTranslateAnimator: DigitTranslateAnimator? = null var aodFontSizePx: Float = -1F - var isVertical: Boolean = false // Store the font size when there's no height constraint as a reference when adjusting font size private var lastUnconstrainedTextSize: Float = Float.MAX_VALUE @@ -148,16 +146,7 @@ open class SimpleDigitalClockTextView(clockCtx: ClockContext, attrs: AttributeSe override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { logger.d("onMeasure()") - if (isVertical) { - // use at_most to avoid apply measuredWidth from last measuring to measuredHeight - // cause we use max to setMeasuredDimension - super.onMeasure( - MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(widthMeasureSpec), AT_MOST), - MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(heightMeasureSpec), AT_MOST), - ) - } else { - super.onMeasure(widthMeasureSpec, heightMeasureSpec) - } + super.onMeasure(widthMeasureSpec, heightMeasureSpec) val layout = this.layout if (layout != null) { @@ -213,18 +202,10 @@ open class SimpleDigitalClockTextView(clockCtx: ClockContext, attrs: AttributeSe ) } - if (isVertical) { - expectedWidth = expectedHeight.also { expectedHeight = expectedWidth } - } setMeasuredDimension(expectedWidth, expectedHeight) } override fun onDraw(canvas: Canvas) { - if (isVertical) { - canvas.save() - canvas.translate(0F, measuredHeight.toFloat()) - canvas.rotate(-90F) - } logger.d({ "onDraw(); ls: $str1" }) { str1 = textAnimator.textInterpolator.shapedText } val translation = getLocalTranslation() canvas.translate(translation.x.toFloat(), translation.y.toFloat()) @@ -238,9 +219,6 @@ open class SimpleDigitalClockTextView(clockCtx: ClockContext, attrs: AttributeSe canvas.translate(-it.updatedTranslate.x.toFloat(), -it.updatedTranslate.y.toFloat()) } canvas.translate(-translation.x.toFloat(), -translation.y.toFloat()) - if (isVertical) { - canvas.restore() - } } override fun invalidate() { @@ -353,18 +331,20 @@ open class SimpleDigitalClockTextView(clockCtx: ClockContext, attrs: AttributeSe } private fun updateXtranslation(inPoint: Point, interpolatedTextBounds: Rect): Point { - val viewWidth = if (isVertical) measuredHeight else measuredWidth when (horizontalAlignment) { HorizontalAlignment.LEFT -> { inPoint.x = lockScreenPaint.strokeWidth.toInt() - interpolatedTextBounds.left } HorizontalAlignment.RIGHT -> { inPoint.x = - viewWidth - interpolatedTextBounds.right - lockScreenPaint.strokeWidth.toInt() + measuredWidth - + interpolatedTextBounds.right - + lockScreenPaint.strokeWidth.toInt() } HorizontalAlignment.CENTER -> { inPoint.x = - (viewWidth - interpolatedTextBounds.width()) / 2 - interpolatedTextBounds.left + (measuredWidth - interpolatedTextBounds.width()) / 2 - + interpolatedTextBounds.left } } return inPoint @@ -373,7 +353,6 @@ open class SimpleDigitalClockTextView(clockCtx: ClockContext, attrs: AttributeSe // translation of reference point of text // used for translation when calling textInterpolator private fun getLocalTranslation(): Point { - val viewHeight = if (isVertical) measuredWidth else measuredHeight val interpolatedTextBounds = updateInterpolatedTextBounds() val localTranslation = Point(0, 0) val correctedBaseline = if (baseline != -1) baseline else baselineFromMeasure @@ -381,7 +360,7 @@ open class SimpleDigitalClockTextView(clockCtx: ClockContext, attrs: AttributeSe when (verticalAlignment) { VerticalAlignment.CENTER -> { localTranslation.y = - ((viewHeight - interpolatedTextBounds.height()) / 2 - + ((measuredHeight - interpolatedTextBounds.height()) / 2 - interpolatedTextBounds.top - correctedBaseline) } @@ -392,7 +371,7 @@ open class SimpleDigitalClockTextView(clockCtx: ClockContext, attrs: AttributeSe } VerticalAlignment.BOTTOM -> { localTranslation.y = - viewHeight - + measuredHeight - interpolatedTextBounds.bottom - lockScreenPaint.strokeWidth.toInt() - correctedBaseline diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt index 2c1dacdfae73..4d2a6d9bd57a 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt @@ -232,7 +232,6 @@ class KeyguardPinViewControllerTest : SysuiTestCase() { @Test fun testOnViewAttached_withAutoPinConfirmationFailedPasswordAttemptsLessThan5() { val pinViewController = constructPinViewController(mockKeyguardPinView) - `when`(featureFlags.isEnabled(Flags.AUTO_PIN_CONFIRMATION)).thenReturn(true) `when`(lockPatternUtils.getPinLength(anyInt())).thenReturn(6) `when`(lockPatternUtils.isAutoPinConfirmEnabled(anyInt())).thenReturn(true) `when`(lockPatternUtils.getCurrentFailedPasswordAttempts(anyInt())).thenReturn(3) @@ -249,7 +248,6 @@ class KeyguardPinViewControllerTest : SysuiTestCase() { @Test fun testOnViewAttached_withAutoPinConfirmationFailedPasswordAttemptsMoreThan5() { val pinViewController = constructPinViewController(mockKeyguardPinView) - `when`(featureFlags.isEnabled(Flags.AUTO_PIN_CONFIRMATION)).thenReturn(true) `when`(lockPatternUtils.getPinLength(anyInt())).thenReturn(6) `when`(lockPatternUtils.isAutoPinConfirmEnabled(anyInt())).thenReturn(true) `when`(lockPatternUtils.getCurrentFailedPasswordAttempts(anyInt())).thenReturn(6) @@ -275,7 +273,6 @@ class KeyguardPinViewControllerTest : SysuiTestCase() { @Test fun onUserInput_autoConfirmation_attemptsUnlock() { val pinViewController = constructPinViewController(mockKeyguardPinView) - whenever(featureFlags.isEnabled(Flags.AUTO_PIN_CONFIRMATION)).thenReturn(true) whenever(lockPatternUtils.getPinLength(anyInt())).thenReturn(6) whenever(lockPatternUtils.isAutoPinConfirmEnabled(anyInt())).thenReturn(true) whenever(passwordTextView.text).thenReturn("000000") diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/CustomInputGesturesRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/CustomInputGesturesRepositoryTest.kt index e659ef274980..698fac107a1d 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/CustomInputGesturesRepositoryTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/CustomInputGesturesRepositoryTest.kt @@ -18,7 +18,9 @@ package com.android.systemui.keyboard.shortcut.data.repository import android.content.Context import android.content.Context.INPUT_SERVICE +import android.content.Intent import android.hardware.input.InputGestureData +import android.hardware.input.InputManager import android.hardware.input.InputManager.CUSTOM_INPUT_GESTURE_RESULT_SUCCESS import android.hardware.input.fakeInputManager import android.platform.test.annotations.EnableFlags @@ -27,9 +29,12 @@ import androidx.test.filters.SmallTest import com.android.hardware.input.Flags.FLAG_ENABLE_CUSTOMIZABLE_INPUT_GESTURES import com.android.hardware.input.Flags.FLAG_USE_KEY_GESTURE_EVENT_HANDLER import com.android.systemui.SysuiTestCase +import com.android.systemui.broadcast.broadcastDispatcher import com.android.systemui.coroutines.collectLastValue import com.android.systemui.keyboard.shortcut.customInputGesturesRepository import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.allAppsInputGestureData +import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.goHomeInputGestureData +import com.android.systemui.keyboard.shortcut.shortcutHelperTestHelper import com.android.systemui.kosmos.testScope import com.android.systemui.settings.FakeUserTracker import com.android.systemui.settings.userTracker @@ -48,18 +53,41 @@ import org.mockito.kotlin.whenever @EnableFlags(FLAG_ENABLE_CUSTOMIZABLE_INPUT_GESTURES, FLAG_USE_KEY_GESTURE_EVENT_HANDLER) class CustomInputGesturesRepositoryTest : SysuiTestCase() { - private val mockUserContext: Context = mock() + private val primaryUserContext: Context = mock() + private val secondaryUserContext: Context = mock() + private var activeUserContext: Context = primaryUserContext + private val kosmos = testKosmos().also { - it.userTracker = FakeUserTracker(onCreateCurrentUserContext = { mockUserContext }) + it.userTracker = FakeUserTracker(onCreateCurrentUserContext = { activeUserContext }) } private val inputManager = kosmos.fakeInputManager.inputManager + private val broadcastDispatcher = kosmos.broadcastDispatcher + private val inputManagerForSecondaryUser: InputManager = mock() private val testScope = kosmos.testScope + private val testHelper = kosmos.shortcutHelperTestHelper private val customInputGesturesRepository = kosmos.customInputGesturesRepository @Before - fun setup(){ - whenever(mockUserContext.getSystemService(INPUT_SERVICE)).thenReturn(inputManager) + fun setup() { + activeUserContext = primaryUserContext + whenever(primaryUserContext.getSystemService(INPUT_SERVICE)).thenReturn(inputManager) + whenever(secondaryUserContext.getSystemService(INPUT_SERVICE)) + .thenReturn(inputManagerForSecondaryUser) + } + + @Test + fun customInputGestures_emitsNewUsersInputGesturesWhenUserIsSwitch() { + testScope.runTest { + setCustomInputGesturesForPrimaryUser(allAppsInputGestureData) + setCustomInputGesturesForSecondaryUser(goHomeInputGestureData) + + val inputGestures by collectLastValue(customInputGesturesRepository.customInputGestures) + assertThat(inputGestures).containsExactly(allAppsInputGestureData) + + switchToSecondaryUser() + assertThat(inputGestures).containsExactly(goHomeInputGestureData) + } } @Test @@ -115,4 +143,24 @@ class CustomInputGesturesRepositoryTest : SysuiTestCase() { } } + private fun setCustomInputGesturesForPrimaryUser(vararg inputGesture: InputGestureData) { + whenever( + inputManager.getCustomInputGestures(/* filter= */ InputGestureData.Filter.KEY) + ).thenReturn(inputGesture.toList()) + } + + private fun setCustomInputGesturesForSecondaryUser(vararg inputGesture: InputGestureData) { + whenever( + inputManagerForSecondaryUser.getCustomInputGestures(/* filter= */ InputGestureData.Filter.KEY) + ).thenReturn(inputGesture.toList()) + } + + private fun switchToSecondaryUser() { + activeUserContext = secondaryUserContext + broadcastDispatcher.sendIntentToMatchingReceiversOnly( + context, + Intent(Intent.ACTION_USER_SWITCHED) + ) + } + }
\ No newline at end of file diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/QuickSettingsControllerImplTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/QuickSettingsControllerImplTest.java index 2e9d6e85d0aa..49cbb5a924f1 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/QuickSettingsControllerImplTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/QuickSettingsControllerImplTest.java @@ -53,7 +53,6 @@ import androidx.test.filters.SmallTest; import com.android.systemui.plugins.qs.QS; import com.android.systemui.qs.flags.QSComposeFragment; import com.android.systemui.res.R; -import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor; import org.junit.Test; import org.junit.runner.RunWith; @@ -365,7 +364,6 @@ public class QuickSettingsControllerImplTest extends QuickSettingsControllerImpl } @Test - @EnableFlags(FooterViewRefactor.FLAG_NAME) public void updateExpansion_partiallyExpanded_fullscreenFalse() { // WHEN QS are only partially expanded mQsController.setExpanded(true); diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/condition/ConditionExtensionsTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/condition/ConditionExtensionsTest.kt index 83fb14aaf792..6b2c4b260806 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/condition/ConditionExtensionsTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/condition/ConditionExtensionsTest.kt @@ -9,9 +9,8 @@ import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.flowOf -import kotlinx.coroutines.test.StandardTestDispatcher import kotlinx.coroutines.test.TestScope -import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.UnconfinedTestDispatcher import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test @@ -25,7 +24,7 @@ class ConditionExtensionsTest : SysuiTestCase() { @Before fun setUp() { - testScope = TestScope(StandardTestDispatcher()) + testScope = TestScope(UnconfinedTestDispatcher()) } @Test @@ -34,11 +33,9 @@ class ConditionExtensionsTest : SysuiTestCase() { val flow = flowOf(true) val condition = flow.toCondition(scope = this, Condition.START_EAGERLY) - runCurrent() assertThat(condition.isConditionSet).isFalse() condition.start() - runCurrent() assertThat(condition.isConditionSet).isTrue() assertThat(condition.isConditionMet).isTrue() } @@ -49,11 +46,9 @@ class ConditionExtensionsTest : SysuiTestCase() { val flow = flowOf(false) val condition = flow.toCondition(scope = this, Condition.START_EAGERLY) - runCurrent() assertThat(condition.isConditionSet).isFalse() condition.start() - runCurrent() assertThat(condition.isConditionSet).isTrue() assertThat(condition.isConditionMet).isFalse() } @@ -65,7 +60,6 @@ class ConditionExtensionsTest : SysuiTestCase() { val condition = flow.toCondition(scope = this, Condition.START_EAGERLY) condition.start() - runCurrent() assertThat(condition.isConditionSet).isFalse() assertThat(condition.isConditionMet).isFalse() } @@ -78,11 +72,10 @@ class ConditionExtensionsTest : SysuiTestCase() { flow.toCondition( scope = this, strategy = Condition.START_EAGERLY, - initialValue = true + initialValue = true, ) condition.start() - runCurrent() assertThat(condition.isConditionSet).isTrue() assertThat(condition.isConditionMet).isTrue() } @@ -95,11 +88,10 @@ class ConditionExtensionsTest : SysuiTestCase() { flow.toCondition( scope = this, strategy = Condition.START_EAGERLY, - initialValue = false + initialValue = false, ) condition.start() - runCurrent() assertThat(condition.isConditionSet).isTrue() assertThat(condition.isConditionMet).isFalse() } @@ -111,16 +103,13 @@ class ConditionExtensionsTest : SysuiTestCase() { val condition = flow.toCondition(scope = this, strategy = Condition.START_EAGERLY) condition.start() - runCurrent() assertThat(condition.isConditionSet).isTrue() assertThat(condition.isConditionMet).isFalse() flow.value = true - runCurrent() assertThat(condition.isConditionMet).isTrue() flow.value = false - runCurrent() assertThat(condition.isConditionMet).isFalse() condition.stop() @@ -131,15 +120,12 @@ class ConditionExtensionsTest : SysuiTestCase() { testScope.runTest { val flow = MutableSharedFlow<Boolean>() val condition = flow.toCondition(scope = this, strategy = Condition.START_EAGERLY) - runCurrent() assertThat(flow.subscriptionCount.value).isEqualTo(0) condition.start() - runCurrent() assertThat(flow.subscriptionCount.value).isEqualTo(1) condition.stop() - runCurrent() assertThat(flow.subscriptionCount.value).isEqualTo(0) } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt index a62d9d5ce62f..0061c4142e8b 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt @@ -132,7 +132,7 @@ class CallChipViewModelTest : SysuiTestCase() { val latest by collectLastValue(underTest.chip) repo.setOngoingCallState( - inCallModel(startTimeMs = 1000, notificationIcon = mock<StatusBarIconView>()) + inCallModel(startTimeMs = 1000, notificationIcon = createStatusBarIconViewOrNull()) ) assertThat((latest as OngoingActivityChipModel.Shown).icon) @@ -147,11 +147,12 @@ class CallChipViewModelTest : SysuiTestCase() { @Test @EnableFlags(FLAG_STATUS_BAR_CALL_CHIP_NOTIFICATION_ICON) + @DisableFlags(StatusBarConnectedDisplays.FLAG_NAME) fun chip_positiveStartTime_notifIconFlagOn_iconIsNotifIcon() = testScope.runTest { val latest by collectLastValue(underTest.chip) - val notifIcon = mock<StatusBarIconView>() + val notifIcon = createStatusBarIconViewOrNull() repo.setOngoingCallState(inCallModel(startTimeMs = 1000, notificationIcon = notifIcon)) assertThat((latest as OngoingActivityChipModel.Shown).icon) @@ -165,6 +166,24 @@ class CallChipViewModelTest : SysuiTestCase() { @Test @EnableFlags(FLAG_STATUS_BAR_CALL_CHIP_NOTIFICATION_ICON, StatusBarConnectedDisplays.FLAG_NAME) + fun chip_positiveStartTime_notifIconFlagOn_cdFlagOn_iconIsNotifKeyIcon() = + testScope.runTest { + val latest by collectLastValue(underTest.chip) + + repo.setOngoingCallState( + inCallModel( + startTimeMs = 1000, + notificationIcon = createStatusBarIconViewOrNull(), + notificationKey = "notifKey", + ) + ) + + assertThat((latest as OngoingActivityChipModel.Shown).icon) + .isEqualTo(OngoingActivityChipModel.ChipIcon.StatusBarNotificationIcon("notifKey")) + } + + @Test + @EnableFlags(FLAG_STATUS_BAR_CALL_CHIP_NOTIFICATION_ICON, StatusBarConnectedDisplays.FLAG_NAME) fun chip_positiveStartTime_notifIconAndConnectedDisplaysFlagOn_iconIsNotifIcon() = testScope.runTest { val latest by collectLastValue(underTest.chip) @@ -192,7 +211,7 @@ class CallChipViewModelTest : SysuiTestCase() { val latest by collectLastValue(underTest.chip) repo.setOngoingCallState( - inCallModel(startTimeMs = 0, notificationIcon = mock<StatusBarIconView>()) + inCallModel(startTimeMs = 0, notificationIcon = createStatusBarIconViewOrNull()) ) assertThat((latest as OngoingActivityChipModel.Shown).icon) @@ -207,11 +226,12 @@ class CallChipViewModelTest : SysuiTestCase() { @Test @EnableFlags(FLAG_STATUS_BAR_CALL_CHIP_NOTIFICATION_ICON) - fun chip_zeroStartTime_notifIconFlagOn_iconIsNotifIcon() = + @DisableFlags(StatusBarConnectedDisplays.FLAG_NAME) + fun chip_zeroStartTime_notifIconFlagOn_cdFlagOff_iconIsNotifIcon() = testScope.runTest { val latest by collectLastValue(underTest.chip) - val notifIcon = mock<StatusBarIconView>() + val notifIcon = createStatusBarIconViewOrNull() repo.setOngoingCallState(inCallModel(startTimeMs = 0, notificationIcon = notifIcon)) assertThat((latest as OngoingActivityChipModel.Shown).icon) @@ -224,8 +244,27 @@ class CallChipViewModelTest : SysuiTestCase() { } @Test + @EnableFlags(FLAG_STATUS_BAR_CALL_CHIP_NOTIFICATION_ICON, StatusBarConnectedDisplays.FLAG_NAME) + fun chip_zeroStartTime_notifIconFlagOn_cdFlagOn_iconIsNotifKeyIcon() = + testScope.runTest { + val latest by collectLastValue(underTest.chip) + + repo.setOngoingCallState( + inCallModel( + startTimeMs = 0, + notificationIcon = createStatusBarIconViewOrNull(), + notificationKey = "notifKey", + ) + ) + + assertThat((latest as OngoingActivityChipModel.Shown).icon) + .isEqualTo(OngoingActivityChipModel.ChipIcon.StatusBarNotificationIcon("notifKey")) + } + + @Test @EnableFlags(FLAG_STATUS_BAR_CALL_CHIP_NOTIFICATION_ICON) - fun chip_notifIconFlagOn_butNullNotifIcon_iconIsPhone() = + @DisableFlags(StatusBarConnectedDisplays.FLAG_NAME) + fun chip_notifIconFlagOn_butNullNotifIcon_cdFlagOff_iconIsPhone() = testScope.runTest { val latest by collectLastValue(underTest.chip) @@ -242,6 +281,24 @@ class CallChipViewModelTest : SysuiTestCase() { } @Test + @EnableFlags(FLAG_STATUS_BAR_CALL_CHIP_NOTIFICATION_ICON, StatusBarConnectedDisplays.FLAG_NAME) + fun chip_notifIconFlagOn_butNullNotifIcon_iconNotifKey() = + testScope.runTest { + val latest by collectLastValue(underTest.chip) + + repo.setOngoingCallState( + inCallModel( + startTimeMs = 1000, + notificationIcon = null, + notificationKey = "notifKey", + ) + ) + + assertThat((latest as OngoingActivityChipModel.Shown).icon) + .isEqualTo(OngoingActivityChipModel.ChipIcon.StatusBarNotificationIcon("notifKey")) + } + + @Test fun chip_positiveStartTime_colorsAreThemed() = testScope.runTest { val latest by collectLastValue(underTest.chip) @@ -330,4 +387,13 @@ class CallChipViewModelTest : SysuiTestCase() { verify(kosmos.activityStarter).postStartActivityDismissingKeyguard(intent, null) } + + companion object { + fun createStatusBarIconViewOrNull(): StatusBarIconView? = + if (StatusBarConnectedDisplays.isEnabled) { + null + } else { + mock<StatusBarIconView>() + } + } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/domain/interactor/SingleNotificationChipInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/domain/interactor/SingleNotificationChipInteractorTest.kt index 0d033a4098ec..fe15eac46e2d 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/domain/interactor/SingleNotificationChipInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/domain/interactor/SingleNotificationChipInteractorTest.kt @@ -16,6 +16,7 @@ package com.android.systemui.statusbar.chips.notification.domain.interactor +import android.platform.test.annotations.DisableFlags import android.platform.test.annotations.EnableFlags import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest @@ -148,7 +149,8 @@ class SingleNotificationChipInteractorTest : SysuiTestCase() { } @Test - fun notificationChip_missingStatusBarIconChipView_inConstructor_emitsNull() = + @DisableFlags(StatusBarConnectedDisplays.FLAG_NAME) + fun notificationChip_missingStatusBarIconChipView_cdFlagDisabled_inConstructor_emitsNull() = kosmos.runTest { val underTest = factory.create( @@ -167,6 +169,25 @@ class SingleNotificationChipInteractorTest : SysuiTestCase() { @Test @EnableFlags(StatusBarConnectedDisplays.FLAG_NAME) + fun notificationChip_missingStatusBarIconChipView_cdFlagEnabled_inConstructor_emitsNotNull() = + kosmos.runTest { + val underTest = + factory.create( + activeNotificationModel( + key = "notif1", + statusBarChipIcon = null, + promotedContent = PROMOTED_CONTENT, + ), + 32L, + ) + + val latest by collectLastValue(underTest.notificationChip) + + assertThat(latest).isNotNull() + } + + @Test + @EnableFlags(StatusBarConnectedDisplays.FLAG_NAME) fun notificationChip_cdEnabled_missingStatusBarIconChipView_inConstructor_emitsNotNull() = kosmos.runTest { val underTest = @@ -186,7 +207,8 @@ class SingleNotificationChipInteractorTest : SysuiTestCase() { } @Test - fun notificationChip_missingStatusBarIconChipView_inSet_emitsNull() = + @DisableFlags(StatusBarConnectedDisplays.FLAG_NAME) + fun notificationChip_cdFlagDisabled_missingStatusBarIconChipView_inSet_emitsNull() = kosmos.runTest { val startingNotif = activeNotificationModel( @@ -211,6 +233,31 @@ class SingleNotificationChipInteractorTest : SysuiTestCase() { @Test @EnableFlags(StatusBarConnectedDisplays.FLAG_NAME) + fun notificationChip_cdFlagEnabled_missingStatusBarIconChipView_inSet_emitsNotNull() = + kosmos.runTest { + val startingNotif = + activeNotificationModel( + key = "notif1", + statusBarChipIcon = mock(), + promotedContent = PROMOTED_CONTENT, + ) + val underTest = factory.create(startingNotif, 123L) + val latest by collectLastValue(underTest.notificationChip) + assertThat(latest).isNotNull() + + underTest.setNotification( + activeNotificationModel( + key = "notif1", + statusBarChipIcon = null, + promotedContent = PROMOTED_CONTENT, + ) + ) + + assertThat(latest).isNotNull() + } + + @Test + @EnableFlags(StatusBarConnectedDisplays.FLAG_NAME) fun notificationChip_missingStatusBarIconChipView_inSet_cdEnabled_emitsNotNull() = kosmos.runTest { val startingNotif = diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/domain/interactor/StatusBarNotificationChipsInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/domain/interactor/StatusBarNotificationChipsInteractorTest.kt index f703d785ceac..ee4a52d35d68 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/domain/interactor/StatusBarNotificationChipsInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/domain/interactor/StatusBarNotificationChipsInteractorTest.kt @@ -30,6 +30,7 @@ import com.android.systemui.kosmos.runTest import com.android.systemui.kosmos.useUnconfinedTestDispatcher import com.android.systemui.statusbar.StatusBarIconView import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips +import com.android.systemui.statusbar.core.StatusBarConnectedDisplays import com.android.systemui.statusbar.notification.data.model.activeNotificationModel import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationsStore import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository @@ -83,7 +84,8 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() { @Test @EnableFlags(StatusBarNotifChips.FLAG_NAME) - fun notificationChips_notifMissingStatusBarChipIconView_empty() = + @DisableFlags(StatusBarConnectedDisplays.FLAG_NAME) + fun notificationChips_notifMissingStatusBarChipIconView_cdFlagOff_empty() = kosmos.runTest { val latest by collectLastValue(underTest.notificationChips) @@ -101,6 +103,25 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() { } @Test + @EnableFlags(StatusBarNotifChips.FLAG_NAME, StatusBarConnectedDisplays.FLAG_NAME) + fun notificationChips_notifMissingStatusBarChipIconView_cdFlagOn_notEmpty() = + kosmos.runTest { + val latest by collectLastValue(underTest.notificationChips) + + setNotifs( + listOf( + activeNotificationModel( + key = "notif", + statusBarChipIcon = null, + promotedContent = PromotedNotificationContentModel.Builder("notif").build(), + ) + ) + ) + + assertThat(latest).isNotEmpty() + } + + @Test @EnableFlags(StatusBarNotifChips.FLAG_NAME) fun notificationChips_onePromotedNotif_statusBarIconViewMatches() = kosmos.runTest { diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt index 17076b4d7505..e561e3ea27d7 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt @@ -23,7 +23,6 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.Flags.FLAG_PROMOTE_NOTIFICATIONS_AUTOMATICALLY import com.android.systemui.SysuiTestCase -import com.android.systemui.coroutines.collectLastValue import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository import com.android.systemui.kosmos.collectLastValue @@ -31,6 +30,7 @@ import com.android.systemui.kosmos.runTest import com.android.systemui.kosmos.testScope import com.android.systemui.kosmos.useUnconfinedTestDispatcher import com.android.systemui.statusbar.StatusBarIconView +import com.android.systemui.statusbar.chips.call.ui.viewmodel.CallChipViewModelTest.Companion.createStatusBarIconViewOrNull import com.android.systemui.statusbar.chips.notification.domain.interactor.statusBarNotificationChipsInteractor import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips import com.android.systemui.statusbar.chips.ui.model.ColorsModel @@ -48,7 +48,6 @@ import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat import kotlin.test.Test import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.runner.RunWith import org.mockito.kotlin.mock @@ -84,8 +83,8 @@ class NotifChipsViewModelTest : SysuiTestCase() { } @Test - @DisableFlags(FLAG_PROMOTE_NOTIFICATIONS_AUTOMATICALLY) - fun chips_notifMissingStatusBarChipIconView_empty() = + @DisableFlags(FLAG_PROMOTE_NOTIFICATIONS_AUTOMATICALLY, StatusBarConnectedDisplays.FLAG_NAME) + fun chips_notifMissingStatusBarChipIconView_cdFlagDisabled_empty() = kosmos.runTest { val latest by collectLastValue(underTest.chips) @@ -104,11 +103,31 @@ class NotifChipsViewModelTest : SysuiTestCase() { @Test @DisableFlags(FLAG_PROMOTE_NOTIFICATIONS_AUTOMATICALLY) + @EnableFlags(StatusBarConnectedDisplays.FLAG_NAME) + fun chips_notifMissingStatusBarChipIconView_cdFlagEnabled_notEmpty() = + kosmos.runTest { + val latest by collectLastValue(underTest.chips) + + setNotifs( + listOf( + activeNotificationModel( + key = "notif", + statusBarChipIcon = null, + promotedContent = PromotedNotificationContentModel.Builder("notif").build(), + ) + ) + ) + + assertThat(latest).isNotEmpty() + } + + @Test + @DisableFlags(FLAG_PROMOTE_NOTIFICATIONS_AUTOMATICALLY) fun chips_onePromotedNotif_statusBarIconViewMatches() = kosmos.runTest { val latest by collectLastValue(underTest.chips) - val icon = mock<StatusBarIconView>() + val icon = createStatusBarIconViewOrNull() setNotifs( listOf( activeNotificationModel( @@ -121,8 +140,7 @@ class NotifChipsViewModelTest : SysuiTestCase() { assertThat(latest).hasSize(1) val chip = latest!![0] - assertThat(chip).isInstanceOf(OngoingActivityChipModel.Shown::class.java) - assertThat(chip.icon).isEqualTo(OngoingActivityChipModel.ChipIcon.StatusBarView(icon)) + assertIsNotifChip(chip, icon, "notif") } @Test @@ -168,7 +186,7 @@ class NotifChipsViewModelTest : SysuiTestCase() { listOf( activeNotificationModel( key = "notif", - statusBarChipIcon = mock<StatusBarIconView>(), + statusBarChipIcon = createStatusBarIconViewOrNull(), promotedContent = promotedContentBuilder.build(), ) ) @@ -187,8 +205,8 @@ class NotifChipsViewModelTest : SysuiTestCase() { kosmos.runTest { val latest by collectLastValue(underTest.chips) - val firstIcon = mock<StatusBarIconView>() - val secondIcon = mock<StatusBarIconView>() + val firstIcon = createStatusBarIconViewOrNull() + val secondIcon = createStatusBarIconViewOrNull() setNotifs( listOf( activeNotificationModel( @@ -203,15 +221,15 @@ class NotifChipsViewModelTest : SysuiTestCase() { ), activeNotificationModel( key = "notif3", - statusBarChipIcon = mock<StatusBarIconView>(), + statusBarChipIcon = createStatusBarIconViewOrNull(), promotedContent = null, ), ) ) assertThat(latest).hasSize(2) - assertIsNotifChip(latest!![0], firstIcon) - assertIsNotifChip(latest!![1], secondIcon) + assertIsNotifChip(latest!![0], firstIcon, "notif1") + assertIsNotifChip(latest!![1], secondIcon, "notif2") } @Test @@ -269,7 +287,7 @@ class NotifChipsViewModelTest : SysuiTestCase() { listOf( activeNotificationModel( key = "notif", - statusBarChipIcon = mock<StatusBarIconView>(), + statusBarChipIcon = createStatusBarIconViewOrNull(), promotedContent = promotedContentBuilder.build(), ) ) @@ -293,7 +311,7 @@ class NotifChipsViewModelTest : SysuiTestCase() { listOf( activeNotificationModel( key = "notif", - statusBarChipIcon = mock<StatusBarIconView>(), + statusBarChipIcon = createStatusBarIconViewOrNull(), promotedContent = promotedContentBuilder.build(), ) ) @@ -323,7 +341,7 @@ class NotifChipsViewModelTest : SysuiTestCase() { listOf( activeNotificationModel( key = "notif", - statusBarChipIcon = mock<StatusBarIconView>(), + statusBarChipIcon = createStatusBarIconViewOrNull(), promotedContent = promotedContentBuilder.build(), ) ) @@ -353,7 +371,7 @@ class NotifChipsViewModelTest : SysuiTestCase() { listOf( activeNotificationModel( key = "notif", - statusBarChipIcon = mock<StatusBarIconView>(), + statusBarChipIcon = createStatusBarIconViewOrNull(), promotedContent = promotedContentBuilder.build(), ) ) @@ -382,7 +400,7 @@ class NotifChipsViewModelTest : SysuiTestCase() { listOf( activeNotificationModel( key = "notif", - statusBarChipIcon = mock<StatusBarIconView>(), + statusBarChipIcon = createStatusBarIconViewOrNull(), promotedContent = promotedContentBuilder.build(), ) ) @@ -411,7 +429,7 @@ class NotifChipsViewModelTest : SysuiTestCase() { listOf( activeNotificationModel( key = "notif", - statusBarChipIcon = mock<StatusBarIconView>(), + statusBarChipIcon = createStatusBarIconViewOrNull(), promotedContent = promotedContentBuilder.build(), ) ) @@ -439,7 +457,7 @@ class NotifChipsViewModelTest : SysuiTestCase() { listOf( activeNotificationModel( key = "notif", - statusBarChipIcon = mock<StatusBarIconView>(), + statusBarChipIcon = createStatusBarIconViewOrNull(), promotedContent = promotedContentBuilder.build(), ) ) @@ -467,7 +485,7 @@ class NotifChipsViewModelTest : SysuiTestCase() { listOf( activeNotificationModel( key = "notif", - statusBarChipIcon = mock<StatusBarIconView>(), + statusBarChipIcon = createStatusBarIconViewOrNull(), promotedContent = promotedContentBuilder.build(), ) ) @@ -499,7 +517,7 @@ class NotifChipsViewModelTest : SysuiTestCase() { listOf( activeNotificationModel( key = "notif", - statusBarChipIcon = mock<StatusBarIconView>(), + statusBarChipIcon = createStatusBarIconViewOrNull(), promotedContent = promotedContentBuilder.build(), ) ) @@ -531,7 +549,7 @@ class NotifChipsViewModelTest : SysuiTestCase() { listOf( activeNotificationModel( key = "clickTest", - statusBarChipIcon = mock<StatusBarIconView>(), + statusBarChipIcon = createStatusBarIconViewOrNull(), promotedContent = PromotedNotificationContentModel.Builder("clickTest").build(), ) @@ -552,9 +570,21 @@ class NotifChipsViewModelTest : SysuiTestCase() { } companion object { - fun assertIsNotifChip(latest: OngoingActivityChipModel?, expectedIcon: StatusBarIconView) { - assertThat((latest as OngoingActivityChipModel.Shown).icon) - .isEqualTo(OngoingActivityChipModel.ChipIcon.StatusBarView(expectedIcon)) + fun assertIsNotifChip( + latest: OngoingActivityChipModel?, + expectedIcon: StatusBarIconView?, + notificationKey: String, + ) { + val shown = latest as OngoingActivityChipModel.Shown + if (StatusBarConnectedDisplays.isEnabled) { + assertThat(shown.icon) + .isEqualTo( + OngoingActivityChipModel.ChipIcon.StatusBarNotificationIcon(notificationKey) + ) + } else { + assertThat(latest.icon) + .isEqualTo(OngoingActivityChipModel.ChipIcon.StatusBarView(expectedIcon!!)) + } } fun assertIsNotifKey(latest: OngoingActivityChipModel?, expectedKey: String) { diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt index 4fb42e94adb2..42358cce59a2 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt @@ -41,6 +41,7 @@ import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.Me import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel import com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer +import com.android.systemui.statusbar.core.StatusBarConnectedDisplays import com.android.systemui.statusbar.phone.SystemUIDialog import com.android.systemui.statusbar.phone.mockSystemUIDialogFactory import com.android.systemui.statusbar.phone.ongoingcall.data.repository.ongoingCallRepository @@ -169,29 +170,35 @@ class OngoingActivityChipsViewModelTest : SysuiTestCase() { @Test fun primaryChip_screenRecordAndShareToAppAndCastToOtherHideAndCallShown_callShown() = testScope.runTest { + val notificationKey = "call" screenRecordState.value = ScreenRecordModel.DoingNothing // MediaProjection covers both share-to-app and cast-to-other-device mediaProjectionState.value = MediaProjectionState.NotProjecting - callRepo.setOngoingCallState(inCallModel(startTimeMs = 34)) + callRepo.setOngoingCallState( + inCallModel(startTimeMs = 34, notificationKey = notificationKey) + ) val latest by collectLastValue(underTest.primaryChip) - assertIsCallChip(latest) + assertIsCallChip(latest, notificationKey) } @Test fun primaryChip_higherPriorityChipAdded_lowerPriorityChipReplaced() = testScope.runTest { // Start with just the lowest priority chip shown - callRepo.setOngoingCallState(inCallModel(startTimeMs = 34)) + val callNotificationKey = "call" + callRepo.setOngoingCallState( + inCallModel(startTimeMs = 34, notificationKey = callNotificationKey) + ) // And everything else hidden mediaProjectionState.value = MediaProjectionState.NotProjecting screenRecordState.value = ScreenRecordModel.DoingNothing val latest by collectLastValue(underTest.primaryChip) - assertIsCallChip(latest) + assertIsCallChip(latest, callNotificationKey) // WHEN the higher priority media projection chip is added mediaProjectionState.value = @@ -218,7 +225,10 @@ class OngoingActivityChipsViewModelTest : SysuiTestCase() { screenRecordState.value = ScreenRecordModel.Recording mediaProjectionState.value = MediaProjectionState.Projecting.EntireScreen(NORMAL_PACKAGE) - callRepo.setOngoingCallState(inCallModel(startTimeMs = 34)) + val callNotificationKey = "call" + callRepo.setOngoingCallState( + inCallModel(startTimeMs = 34, notificationKey = callNotificationKey) + ) val latest by collectLastValue(underTest.primaryChip) @@ -235,7 +245,7 @@ class OngoingActivityChipsViewModelTest : SysuiTestCase() { mediaProjectionState.value = MediaProjectionState.NotProjecting // THEN the lower priority call is used - assertIsCallChip(latest) + assertIsCallChip(latest, callNotificationKey) } /** Regression test for b/347726238. */ @@ -364,13 +374,27 @@ class OngoingActivityChipsViewModelTest : SysuiTestCase() { assertThat(icon.res).isEqualTo(R.drawable.ic_present_to_all) } - fun assertIsCallChip(latest: OngoingActivityChipModel?) { - assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java) + fun assertIsCallChip(latest: OngoingActivityChipModel?, notificationKey: String) { + assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown.Timer::class.java) + if (StatusBarConnectedDisplays.isEnabled) { + assertNotificationIcon(latest, notificationKey) + return + } val icon = (((latest as OngoingActivityChipModel.Shown).icon) as OngoingActivityChipModel.ChipIcon.SingleColorIcon) .impl as Icon.Resource assertThat(icon.res).isEqualTo(com.android.internal.R.drawable.ic_phone) } + + private fun assertNotificationIcon( + latest: OngoingActivityChipModel?, + notificationKey: String, + ) { + val shown = latest as OngoingActivityChipModel.Shown + val notificationIcon = + shown.icon as OngoingActivityChipModel.ChipIcon.StatusBarNotificationIcon + assertThat(notificationIcon.notificationKey).isEqualTo(notificationKey) + } } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsWithNotifsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsWithNotifsViewModelTest.kt index 0050ebee64d6..0f42f29e76ee 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsWithNotifsViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsWithNotifsViewModelTest.kt @@ -34,7 +34,7 @@ import com.android.systemui.mediaprojection.taskswitcher.FakeActivityTaskManager import com.android.systemui.res.R import com.android.systemui.screenrecord.data.model.ScreenRecordModel import com.android.systemui.screenrecord.data.repository.screenRecordRepository -import com.android.systemui.statusbar.StatusBarIconView +import com.android.systemui.statusbar.chips.call.ui.viewmodel.CallChipViewModelTest.Companion.createStatusBarIconViewOrNull import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractorTest.Companion.NORMAL_PACKAGE import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractorTest.Companion.setUpPackageManagerForMediaProjection import com.android.systemui.statusbar.chips.notification.domain.interactor.statusBarNotificationChipsInteractor @@ -186,13 +186,16 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() { @Test fun chips_screenRecordShowAndCallShow_primaryIsScreenRecordSecondaryIsCall() = testScope.runTest { + val callNotificationKey = "call" screenRecordState.value = ScreenRecordModel.Recording - callRepo.setOngoingCallState(inCallModel(startTimeMs = 34)) + callRepo.setOngoingCallState( + inCallModel(startTimeMs = 34, notificationKey = callNotificationKey) + ) val latest by collectLastValue(underTest.chips) assertIsScreenRecordChip(latest!!.primary) - assertIsCallChip(latest!!.secondary) + assertIsCallChip(latest!!.secondary, callNotificationKey) } @Test @@ -240,15 +243,18 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() { @Test fun chips_shareToAppShowAndCallShow_primaryIsShareToAppSecondaryIsCall() = testScope.runTest { + val callNotificationKey = "call" screenRecordState.value = ScreenRecordModel.DoingNothing mediaProjectionState.value = MediaProjectionState.Projecting.EntireScreen(NORMAL_PACKAGE) - callRepo.setOngoingCallState(inCallModel(startTimeMs = 34)) + callRepo.setOngoingCallState( + inCallModel(startTimeMs = 34, notificationKey = callNotificationKey) + ) val latest by collectLastValue(underTest.chips) assertIsShareToAppChip(latest!!.primary) - assertIsCallChip(latest!!.secondary) + assertIsCallChip(latest!!.secondary, callNotificationKey) } @Test @@ -258,25 +264,31 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() { // MediaProjection covers both share-to-app and cast-to-other-device mediaProjectionState.value = MediaProjectionState.NotProjecting - callRepo.setOngoingCallState(inCallModel(startTimeMs = 34)) + val callNotificationKey = "call" + callRepo.setOngoingCallState( + inCallModel(startTimeMs = 34, notificationKey = callNotificationKey) + ) val latest by collectLastValue(underTest.primaryChip) - assertIsCallChip(latest) + assertIsCallChip(latest, callNotificationKey) } @Test fun chips_onlyCallShown_primaryIsCallSecondaryIsHidden() = testScope.runTest { + val callNotificationKey = "call" screenRecordState.value = ScreenRecordModel.DoingNothing // MediaProjection covers both share-to-app and cast-to-other-device mediaProjectionState.value = MediaProjectionState.NotProjecting - callRepo.setOngoingCallState(inCallModel(startTimeMs = 34)) + callRepo.setOngoingCallState( + inCallModel(startTimeMs = 34, notificationKey = callNotificationKey) + ) val latest by collectLastValue(underTest.chips) - assertIsCallChip(latest!!.primary) + assertIsCallChip(latest!!.primary, callNotificationKey) assertThat(latest!!.secondary).isInstanceOf(OngoingActivityChipModel.Hidden::class.java) } @@ -285,7 +297,7 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() { testScope.runTest { val latest by collectLastValue(underTest.chips) - val icon = mock<StatusBarIconView>() + val icon = createStatusBarIconViewOrNull() setNotifs( listOf( activeNotificationModel( @@ -296,7 +308,7 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() { ) ) - assertIsNotifChip(latest!!.primary, icon) + assertIsNotifChip(latest!!.primary, icon, "notif") assertThat(latest!!.secondary).isInstanceOf(OngoingActivityChipModel.Hidden::class.java) } @@ -305,8 +317,8 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() { testScope.runTest { val latest by collectLastValue(underTest.chips) - val firstIcon = mock<StatusBarIconView>() - val secondIcon = mock<StatusBarIconView>() + val firstIcon = createStatusBarIconViewOrNull() + val secondIcon = createStatusBarIconViewOrNull() setNotifs( listOf( activeNotificationModel( @@ -324,8 +336,8 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() { ) ) - assertIsNotifChip(latest!!.primary, firstIcon) - assertIsNotifChip(latest!!.secondary, secondIcon) + assertIsNotifChip(latest!!.primary, firstIcon, "firstNotif") + assertIsNotifChip(latest!!.secondary, secondIcon, "secondNotif") } @Test @@ -333,9 +345,9 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() { testScope.runTest { val latest by collectLastValue(underTest.chips) - val firstIcon = mock<StatusBarIconView>() - val secondIcon = mock<StatusBarIconView>() - val thirdIcon = mock<StatusBarIconView>() + val firstIcon = createStatusBarIconViewOrNull() + val secondIcon = createStatusBarIconViewOrNull() + val thirdIcon = createStatusBarIconViewOrNull() setNotifs( listOf( activeNotificationModel( @@ -359,8 +371,8 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() { ) ) - assertIsNotifChip(latest!!.primary, firstIcon) - assertIsNotifChip(latest!!.secondary, secondIcon) + assertIsNotifChip(latest!!.primary, firstIcon, "firstNotif") + assertIsNotifChip(latest!!.secondary, secondIcon, "secondNotif") } @Test @@ -368,8 +380,12 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() { testScope.runTest { val latest by collectLastValue(underTest.chips) - callRepo.setOngoingCallState(inCallModel(startTimeMs = 34)) - val firstIcon = mock<StatusBarIconView>() + val callNotificationKey = "call" + callRepo.setOngoingCallState( + inCallModel(startTimeMs = 34, notificationKey = callNotificationKey) + ) + + val firstIcon = createStatusBarIconViewOrNull() setNotifs( listOf( activeNotificationModel( @@ -380,43 +396,47 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() { ), activeNotificationModel( key = "secondNotif", - statusBarChipIcon = mock<StatusBarIconView>(), + statusBarChipIcon = createStatusBarIconViewOrNull(), promotedContent = PromotedNotificationContentModel.Builder("secondNotif").build(), ), ) ) - assertIsCallChip(latest!!.primary) - assertIsNotifChip(latest!!.secondary, firstIcon) + assertIsCallChip(latest!!.primary, callNotificationKey) + assertIsNotifChip(latest!!.secondary, firstIcon, "firstNotif") } @Test fun chips_screenRecordAndCallAndPromotedNotifs_notifsNotShown() = testScope.runTest { + val callNotificationKey = "call" val latest by collectLastValue(underTest.chips) - callRepo.setOngoingCallState(inCallModel(startTimeMs = 34)) + callRepo.setOngoingCallState( + inCallModel(startTimeMs = 34, notificationKey = callNotificationKey) + ) screenRecordState.value = ScreenRecordModel.Recording setNotifs( listOf( activeNotificationModel( key = "notif", - statusBarChipIcon = mock<StatusBarIconView>(), + statusBarChipIcon = createStatusBarIconViewOrNull(), promotedContent = PromotedNotificationContentModel.Builder("notif").build(), ) ) ) assertIsScreenRecordChip(latest!!.primary) - assertIsCallChip(latest!!.secondary) + assertIsCallChip(latest!!.secondary, callNotificationKey) } @Test fun primaryChip_higherPriorityChipAdded_lowerPriorityChipReplaced() = testScope.runTest { + val callNotificationKey = "call" // Start with just the lowest priority chip shown - val notifIcon = mock<StatusBarIconView>() + val notifIcon = createStatusBarIconViewOrNull() setNotifs( listOf( activeNotificationModel( @@ -433,13 +453,15 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() { val latest by collectLastValue(underTest.primaryChip) - assertIsNotifChip(latest, notifIcon) + assertIsNotifChip(latest, notifIcon, "notif") // WHEN the higher priority call chip is added - callRepo.setOngoingCallState(inCallModel(startTimeMs = 34)) + callRepo.setOngoingCallState( + inCallModel(startTimeMs = 34, notificationKey = callNotificationKey) + ) // THEN the higher priority call chip is used - assertIsCallChip(latest) + assertIsCallChip(latest, callNotificationKey) // WHEN the higher priority media projection chip is added mediaProjectionState.value = @@ -462,12 +484,15 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() { @Test fun primaryChip_highestPriorityChipRemoved_showsNextPriorityChip() = testScope.runTest { + val callNotificationKey = "call" // WHEN all chips are active screenRecordState.value = ScreenRecordModel.Recording mediaProjectionState.value = MediaProjectionState.Projecting.EntireScreen(NORMAL_PACKAGE) - callRepo.setOngoingCallState(inCallModel(startTimeMs = 34)) - val notifIcon = mock<StatusBarIconView>() + callRepo.setOngoingCallState( + inCallModel(startTimeMs = 34, notificationKey = callNotificationKey) + ) + val notifIcon = createStatusBarIconViewOrNull() setNotifs( listOf( activeNotificationModel( @@ -493,20 +518,21 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() { mediaProjectionState.value = MediaProjectionState.NotProjecting // THEN the lower priority call is used - assertIsCallChip(latest) + assertIsCallChip(latest, callNotificationKey) // WHEN the higher priority call is removed callRepo.setOngoingCallState(OngoingCallModel.NoCall) // THEN the lower priority notif is used - assertIsNotifChip(latest, notifIcon) + assertIsNotifChip(latest, notifIcon, "notif") } @Test fun chips_movesChipsAroundAccordingToPriority() = testScope.runTest { + val callNotificationKey = "call" // Start with just the lowest priority chip shown - val notifIcon = mock<StatusBarIconView>() + val notifIcon = createStatusBarIconViewOrNull() setNotifs( listOf( activeNotificationModel( @@ -523,16 +549,18 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() { val latest by collectLastValue(underTest.chips) - assertIsNotifChip(latest!!.primary, notifIcon) + assertIsNotifChip(latest!!.primary, notifIcon, "notif") assertThat(latest!!.secondary).isInstanceOf(OngoingActivityChipModel.Hidden::class.java) // WHEN the higher priority call chip is added - callRepo.setOngoingCallState(inCallModel(startTimeMs = 34)) + callRepo.setOngoingCallState( + inCallModel(startTimeMs = 34, notificationKey = callNotificationKey) + ) // THEN the higher priority call chip is used as primary and notif is demoted to // secondary - assertIsCallChip(latest!!.primary) - assertIsNotifChip(latest!!.secondary, notifIcon) + assertIsCallChip(latest!!.primary, callNotificationKey) + assertIsNotifChip(latest!!.secondary, notifIcon, "notif") // WHEN the higher priority media projection chip is added mediaProjectionState.value = @@ -545,7 +573,7 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() { // THEN the higher priority media projection chip is used as primary and call is demoted // to secondary (and notif is dropped altogether) assertIsShareToAppChip(latest!!.primary) - assertIsCallChip(latest!!.secondary) + assertIsCallChip(latest!!.secondary, callNotificationKey) // WHEN the higher priority screen record chip is added screenRecordState.value = ScreenRecordModel.Recording @@ -559,13 +587,13 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() { // THEN media projection and notif remain assertIsShareToAppChip(latest!!.primary) - assertIsNotifChip(latest!!.secondary, notifIcon) + assertIsNotifChip(latest!!.secondary, notifIcon, "notif") // WHEN media projection is dropped mediaProjectionState.value = MediaProjectionState.NotProjecting // THEN notif is promoted to primary - assertIsNotifChip(latest!!.primary, notifIcon) + assertIsNotifChip(latest!!.primary, notifIcon, "notif") assertThat(latest!!.secondary).isInstanceOf(OngoingActivityChipModel.Hidden::class.java) } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/render/RenderStageManagerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/render/RenderStageManagerTest.kt index a70d24efada7..912633c874ed 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/render/RenderStageManagerTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/render/RenderStageManagerTest.kt @@ -28,11 +28,11 @@ import com.android.systemui.statusbar.notification.collection.ShadeListBuilder import com.android.systemui.statusbar.notification.collection.listbuilder.OnAfterRenderEntryListener import com.android.systemui.statusbar.notification.collection.listbuilder.OnAfterRenderGroupListener import com.android.systemui.statusbar.notification.collection.listbuilder.OnAfterRenderListListener +import com.android.systemui.util.mockito.withArgCaptor import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.kotlin.any -import org.mockito.kotlin.argumentCaptor import org.mockito.kotlin.inOrder import org.mockito.kotlin.mock import org.mockito.kotlin.never @@ -59,10 +59,9 @@ class RenderStageManagerTest : SysuiTestCase() { fun setUp() { renderStageManager = RenderStageManager() renderStageManager.attach(shadeListBuilder) - - val captor = argumentCaptor<ShadeListBuilder.OnRenderListListener>() - verify(shadeListBuilder).setOnRenderListListener(captor.capture()) - onRenderListListener = captor.lastValue + onRenderListListener = withArgCaptor { + verify(shadeListBuilder).setOnRenderListListener(capture()) + } } private fun setUpRenderer() { diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/emptyshade/ui/viewmodel/EmptyShadeViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/emptyshade/ui/viewmodel/EmptyShadeViewModelTest.kt index 34f46088ad79..3d5d1eddf581 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/emptyshade/ui/viewmodel/EmptyShadeViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/emptyshade/ui/viewmodel/EmptyShadeViewModelTest.kt @@ -33,7 +33,6 @@ import com.android.systemui.kosmos.testScope import com.android.systemui.shared.settings.data.repository.fakeSecureSettingsRepository import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository import com.android.systemui.statusbar.notification.emptyshade.shared.ModesEmptyShadeFix -import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor import com.android.systemui.statusbar.policy.data.repository.zenModeRepository import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat @@ -48,7 +47,6 @@ import platform.test.runner.parameterized.Parameters @OptIn(ExperimentalCoroutinesApi::class) @RunWith(ParameterizedAndroidJunit4::class) @SmallTest -@EnableFlags(FooterViewRefactor.FLAG_NAME) class EmptyShadeViewModelTest(flags: FlagsParameterization) : SysuiTestCase() { private val kosmos = testKosmos() private val testScope = kosmos.testScope diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterViewTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterViewTest.java index 615f4b01df9b..daa1db2d49fa 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterViewTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterViewTest.java @@ -16,8 +16,6 @@ package com.android.systemui.statusbar.notification.footer.ui.view; -import static com.android.systemui.log.LogAssertKt.assertLogsWtf; - import static com.google.common.truth.Truth.assertThat; import static junit.framework.Assert.assertFalse; @@ -34,7 +32,6 @@ import static org.mockito.Mockito.verify; import android.content.Context; import android.platform.test.annotations.DisableFlags; -import android.platform.test.annotations.EnableFlags; import android.platform.test.flag.junit.FlagsParameterization; import android.view.LayoutInflater; import android.view.View; @@ -44,7 +41,6 @@ import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; import com.android.systemui.res.R; -import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor; import com.android.systemui.statusbar.notification.footer.shared.NotifRedesignFooter; import org.junit.Before; @@ -62,8 +58,7 @@ public class FooterViewTest extends SysuiTestCase { @Parameters(name = "{0}") public static List<FlagsParameterization> getFlags() { - return FlagsParameterization.progressionOf(FooterViewRefactor.FLAG_NAME, - NotifRedesignFooter.FLAG_NAME); + return FlagsParameterization.allCombinationsOf(NotifRedesignFooter.FLAG_NAME); } public FooterViewTest(FlagsParameterization flags) { @@ -106,24 +101,6 @@ public class FooterViewTest extends SysuiTestCase { } @Test - @DisableFlags({FooterViewRefactor.FLAG_NAME, NotifRedesignFooter.FLAG_NAME}) - public void setHistoryShown() { - mView.showHistory(true); - assertTrue(mView.isHistoryShown()); - assertTrue(((TextView) mView.findViewById(R.id.manage_text)) - .getText().toString().contains("History")); - } - - @Test - @DisableFlags({FooterViewRefactor.FLAG_NAME, NotifRedesignFooter.FLAG_NAME}) - public void setHistoryNotShown() { - mView.showHistory(false); - assertFalse(mView.isHistoryShown()); - assertTrue(((TextView) mView.findViewById(R.id.manage_text)) - .getText().toString().contains("Manage")); - } - - @Test public void testPerformVisibilityAnimation() { mView.setVisible(false /* visible */, false /* animate */); assertFalse(mView.isVisible()); @@ -140,7 +117,6 @@ public class FooterViewTest extends SysuiTestCase { } @Test - @EnableFlags(FooterViewRefactor.FLAG_NAME) @DisableFlags(NotifRedesignFooter.FLAG_NAME) public void testSetManageOrHistoryButtonText_resourceOnlyFetchedOnce() { int resId = R.string.manage_notifications_history_text; @@ -160,16 +136,6 @@ public class FooterViewTest extends SysuiTestCase { } @Test - @DisableFlags({FooterViewRefactor.FLAG_NAME, NotifRedesignFooter.FLAG_NAME}) - public void testSetManageOrHistoryButtonText_expectsFlagEnabled() { - clearInvocations(mSpyContext); - int resId = R.string.manage_notifications_history_text; - assertLogsWtf(() -> mView.setManageOrHistoryButtonText(resId)); - verify(mSpyContext, never()).getString(anyInt()); - } - - @Test - @EnableFlags(FooterViewRefactor.FLAG_NAME) @DisableFlags(NotifRedesignFooter.FLAG_NAME) public void testSetManageOrHistoryButtonDescription_resourceOnlyFetchedOnce() { int resId = R.string.manage_notifications_history_text; @@ -189,16 +155,6 @@ public class FooterViewTest extends SysuiTestCase { } @Test - @DisableFlags({FooterViewRefactor.FLAG_NAME, NotifRedesignFooter.FLAG_NAME}) - public void testSetManageOrHistoryButtonDescription_expectsFlagEnabled() { - clearInvocations(mSpyContext); - int resId = R.string.accessibility_clear_all; - assertLogsWtf(() -> mView.setManageOrHistoryButtonDescription(resId)); - verify(mSpyContext, never()).getString(anyInt()); - } - - @Test - @EnableFlags(FooterViewRefactor.FLAG_NAME) public void testSetClearAllButtonText_resourceOnlyFetchedOnce() { int resId = R.string.clear_all_notifications_text; mView.setClearAllButtonText(resId); @@ -217,16 +173,6 @@ public class FooterViewTest extends SysuiTestCase { } @Test - @DisableFlags({FooterViewRefactor.FLAG_NAME, NotifRedesignFooter.FLAG_NAME}) - public void testSetClearAllButtonText_expectsFlagEnabled() { - clearInvocations(mSpyContext); - int resId = R.string.clear_all_notifications_text; - assertLogsWtf(() -> mView.setClearAllButtonText(resId)); - verify(mSpyContext, never()).getString(anyInt()); - } - - @Test - @EnableFlags(FooterViewRefactor.FLAG_NAME) public void testSetClearAllButtonDescription_resourceOnlyFetchedOnce() { int resId = R.string.accessibility_clear_all; mView.setClearAllButtonDescription(resId); @@ -245,16 +191,6 @@ public class FooterViewTest extends SysuiTestCase { } @Test - @DisableFlags({FooterViewRefactor.FLAG_NAME, NotifRedesignFooter.FLAG_NAME}) - public void testSetClearAllButtonDescription_expectsFlagEnabled() { - clearInvocations(mSpyContext); - int resId = R.string.accessibility_clear_all; - assertLogsWtf(() -> mView.setClearAllButtonDescription(resId)); - verify(mSpyContext, never()).getString(anyInt()); - } - - @Test - @EnableFlags(FooterViewRefactor.FLAG_NAME) public void testSetMessageString_resourceOnlyFetchedOnce() { int resId = R.string.unlock_to_see_notif_text; mView.setMessageString(resId); @@ -273,16 +209,6 @@ public class FooterViewTest extends SysuiTestCase { } @Test - @DisableFlags({FooterViewRefactor.FLAG_NAME, NotifRedesignFooter.FLAG_NAME}) - public void testSetMessageString_expectsFlagEnabled() { - clearInvocations(mSpyContext); - int resId = R.string.unlock_to_see_notif_text; - assertLogsWtf(() -> mView.setMessageString(resId)); - verify(mSpyContext, never()).getString(anyInt()); - } - - @Test - @EnableFlags(FooterViewRefactor.FLAG_NAME) public void testSetMessageIcon_resourceOnlyFetchedOnce() { int resId = R.drawable.ic_friction_lock_closed; mView.setMessageIcon(resId); @@ -298,15 +224,6 @@ public class FooterViewTest extends SysuiTestCase { } @Test - @DisableFlags({FooterViewRefactor.FLAG_NAME, NotifRedesignFooter.FLAG_NAME}) - public void testSetMessageIcon_expectsFlagEnabled() { - clearInvocations(mSpyContext); - int resId = R.drawable.ic_friction_lock_closed; - assertLogsWtf(() -> mView.setMessageIcon(resId)); - verify(mSpyContext, never()).getDrawable(anyInt()); - } - - @Test public void testSetFooterLabelVisible() { mView.setFooterLabelVisible(true); assertThat(mView.findViewById(R.id.unlock_prompt_footer).getVisibility()) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModelTest.kt index 1adfc2b72214..06b1c432955a 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModelTest.kt @@ -40,7 +40,6 @@ import com.android.systemui.shared.settings.data.repository.fakeSecureSettingsRe import com.android.systemui.statusbar.notification.collection.render.NotifStats import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository import com.android.systemui.statusbar.notification.emptyshade.shared.ModesEmptyShadeFix -import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor import com.android.systemui.statusbar.notification.footer.shared.NotifRedesignFooter import com.android.systemui.testKosmos import com.android.systemui.util.ui.isAnimating @@ -57,7 +56,6 @@ import platform.test.runner.parameterized.Parameters @RunWith(ParameterizedAndroidJunit4::class) @SmallTest -@EnableFlags(FooterViewRefactor.FLAG_NAME) class FooterViewModelTest(flags: FlagsParameterization) : SysuiTestCase() { private val kosmos = testKosmos().apply { diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java index c6cffa9da13b..20cd6c7517e2 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java @@ -25,14 +25,10 @@ import static com.android.systemui.statusbar.notification.stack.NotificationStac import static kotlinx.coroutines.flow.FlowKt.emptyFlow; -import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.argThat; -import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.RETURNS_DEEP_STUBS; -import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; @@ -45,7 +41,6 @@ import android.platform.test.annotations.DisableFlags; import android.platform.test.annotations.EnableFlags; import android.testing.TestableLooper; import android.view.MotionEvent; -import android.view.View; import android.view.ViewTreeObserver; import androidx.test.ext.junit.runners.AndroidJUnit4; @@ -57,15 +52,12 @@ import com.android.internal.logging.nano.MetricsProto; import com.android.internal.statusbar.IStatusBarService; import com.android.systemui.ExpandHelper; import com.android.systemui.SysuiTestCase; -import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor; import com.android.systemui.classifier.FalsingCollectorFake; import com.android.systemui.classifier.FalsingManagerFake; import com.android.systemui.dump.DumpManager; import com.android.systemui.flags.DisableSceneContainer; import com.android.systemui.flags.EnableSceneContainer; import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository; -import com.android.systemui.keyguard.shared.model.KeyguardState; -import com.android.systemui.keyguard.shared.model.TransitionStep; import com.android.systemui.kosmos.KosmosJavaAdapter; import com.android.systemui.media.controls.ui.controller.KeyguardMediaController; import com.android.systemui.plugins.ActivityStarter; @@ -78,23 +70,18 @@ import com.android.systemui.shade.ShadeController; import com.android.systemui.statusbar.LockscreenShadeTransitionController; import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.NotificationLockscreenUserManager.UserChangedListener; -import com.android.systemui.statusbar.NotificationRemoteInputManager; -import com.android.systemui.statusbar.RemoteInputController; import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.notification.ColorUpdateLogger; import com.android.systemui.statusbar.notification.DynamicPrivacyController; -import com.android.systemui.statusbar.notification.headsup.HeadsUpTouchHelper; import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator; import com.android.systemui.statusbar.notification.collection.NotifCollection; import com.android.systemui.statusbar.notification.collection.NotifPipeline; import com.android.systemui.statusbar.notification.collection.provider.NotificationDismissibilityProvider; import com.android.systemui.statusbar.notification.collection.provider.VisibilityLocationProviderDelegator; import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager; -import com.android.systemui.statusbar.notification.collection.render.NotifStats; import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider; -import com.android.systemui.statusbar.notification.collection.render.SectionHeaderController; -import com.android.systemui.statusbar.notification.domain.interactor.SeenNotificationsInteractor; -import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor; +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager; +import com.android.systemui.statusbar.notification.headsup.HeadsUpTouchHelper; import com.android.systemui.statusbar.notification.init.NotificationsController; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.NotificationGutsManager; @@ -106,11 +93,8 @@ import com.android.systemui.statusbar.notification.stack.ui.viewbinder.Notificat import com.android.systemui.statusbar.phone.HeadsUpAppearanceController; import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.policy.ConfigurationController; -import com.android.systemui.statusbar.policy.DeviceProvisionedController; -import com.android.systemui.statusbar.notification.headsup.HeadsUpManager; import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController; import com.android.systemui.statusbar.policy.SensitiveNotificationProtectionController; -import com.android.systemui.statusbar.policy.ZenModeController; import com.android.systemui.tuner.TunerService; import com.android.systemui.util.settings.SecureSettings; import com.android.systemui.wallpapers.domain.interactor.WallpaperInteractor; @@ -145,16 +129,13 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase { @Mock private Provider<IStatusBarService> mStatusBarService; @Mock private NotificationRoundnessManager mNotificationRoundnessManager; @Mock private TunerService mTunerService; - @Mock private DeviceProvisionedController mDeviceProvisionedController; @Mock private DynamicPrivacyController mDynamicPrivacyController; @Mock private ConfigurationController mConfigurationController; @Mock private NotificationStackScrollLayout mNotificationStackScrollLayout; - @Mock private ZenModeController mZenModeController; @Mock private KeyguardMediaController mKeyguardMediaController; @Mock private SysuiStatusBarStateController mSysuiStatusBarStateController; @Mock private KeyguardBypassController mKeyguardBypassController; @Mock private PowerInteractor mPowerInteractor; - @Mock private PrimaryBouncerInteractor mPrimaryBouncerInteractor; @Mock private WallpaperInteractor mWallpaperInteractor; @Mock private NotificationLockscreenUserManager mNotificationLockscreenUserManager; @Mock private MetricsLogger mMetricsLogger; @@ -164,12 +145,10 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase { private NotificationSwipeHelper.Builder mNotificationSwipeHelperBuilder; @Mock private NotificationSwipeHelper mNotificationSwipeHelper; @Mock private GroupExpansionManager mGroupExpansionManager; - @Mock private SectionHeaderController mSilentHeaderController; @Mock private NotifPipeline mNotifPipeline; @Mock private NotifCollection mNotifCollection; @Mock private UiEventLogger mUiEventLogger; @Mock private LockscreenShadeTransitionController mLockscreenShadeTransitionController; - @Mock private NotificationRemoteInputManager mRemoteInputManager; @Mock private VisibilityLocationProviderDelegator mVisibilityLocationProviderDelegator; @Mock private ShadeController mShadeController; @Mock private Provider<WindowRootView> mWindowRootView; @@ -193,9 +172,6 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase { @Captor private ArgumentCaptor<StatusBarStateController.StateListener> mStateListenerArgumentCaptor; - private final SeenNotificationsInteractor mSeenNotificationsInteractor = - mKosmos.getSeenNotificationsInteractor(); - private NotificationStackScrollLayoutController mController; private NotificationTestHelper mNotificationTestHelper; @@ -279,114 +255,6 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase { } @Test - @DisableFlags(FooterViewRefactor.FLAG_NAME) - public void testUpdateEmptyShadeView_notificationsVisible_zenHiding() { - when(mZenModeController.areNotificationsHiddenInShade()).thenReturn(true); - initController(/* viewIsAttached= */ true); - - setupShowEmptyShadeViewState(true); - reset(mNotificationStackScrollLayout); - mController.updateShowEmptyShadeView(); - verify(mNotificationStackScrollLayout).updateEmptyShadeView( - /* visible= */ true, - /* notifVisibleInShade= */ true); - - setupShowEmptyShadeViewState(false); - reset(mNotificationStackScrollLayout); - mController.updateShowEmptyShadeView(); - verify(mNotificationStackScrollLayout).updateEmptyShadeView( - /* visible= */ false, - /* notifVisibleInShade= */ true); - } - - @Test - @DisableFlags(FooterViewRefactor.FLAG_NAME) - public void testUpdateEmptyShadeView_notificationsHidden_zenNotHiding() { - when(mZenModeController.areNotificationsHiddenInShade()).thenReturn(false); - initController(/* viewIsAttached= */ true); - - setupShowEmptyShadeViewState(true); - reset(mNotificationStackScrollLayout); - mController.updateShowEmptyShadeView(); - verify(mNotificationStackScrollLayout).updateEmptyShadeView( - /* visible= */ true, - /* notifVisibleInShade= */ false); - - setupShowEmptyShadeViewState(false); - reset(mNotificationStackScrollLayout); - mController.updateShowEmptyShadeView(); - verify(mNotificationStackScrollLayout).updateEmptyShadeView( - /* visible= */ false, - /* notifVisibleInShade= */ false); - } - - @Test - @DisableFlags(FooterViewRefactor.FLAG_NAME) - public void testUpdateEmptyShadeView_splitShadeMode_alwaysShowEmptyView() { - when(mZenModeController.areNotificationsHiddenInShade()).thenReturn(false); - initController(/* viewIsAttached= */ true); - - verify(mSysuiStatusBarStateController).addCallback( - mStateListenerArgumentCaptor.capture(), anyInt()); - StatusBarStateController.StateListener stateListener = - mStateListenerArgumentCaptor.getValue(); - stateListener.onStateChanged(SHADE); - mController.getView().removeAllViews(); - - mController.setQsFullScreen(false); - reset(mNotificationStackScrollLayout); - mController.updateShowEmptyShadeView(); - verify(mNotificationStackScrollLayout).updateEmptyShadeView( - /* visible= */ true, - /* notifVisibleInShade= */ false); - - mController.setQsFullScreen(true); - reset(mNotificationStackScrollLayout); - mController.updateShowEmptyShadeView(); - verify(mNotificationStackScrollLayout).updateEmptyShadeView( - /* visible= */ true, - /* notifVisibleInShade= */ false); - } - - @Test - @DisableFlags(FooterViewRefactor.FLAG_NAME) - public void testUpdateEmptyShadeView_bouncerShowing_hideEmptyView() { - when(mZenModeController.areNotificationsHiddenInShade()).thenReturn(false); - initController(/* viewIsAttached= */ true); - - when(mPrimaryBouncerInteractor.isBouncerShowing()).thenReturn(true); - - setupShowEmptyShadeViewState(true); - reset(mNotificationStackScrollLayout); - mController.updateShowEmptyShadeView(); - - // THEN the PrimaryBouncerInteractor value is used. Since the bouncer is showing, we - // hide the empty view. - verify(mNotificationStackScrollLayout).updateEmptyShadeView( - /* visible= */ false, - /* areNotificationsHiddenInShade= */ false); - } - - @Test - @DisableFlags(FooterViewRefactor.FLAG_NAME) - public void testUpdateEmptyShadeView_bouncerNotShowing_showEmptyView() { - when(mZenModeController.areNotificationsHiddenInShade()).thenReturn(false); - initController(/* viewIsAttached= */ true); - - when(mPrimaryBouncerInteractor.isBouncerShowing()).thenReturn(false); - - setupShowEmptyShadeViewState(true); - reset(mNotificationStackScrollLayout); - mController.updateShowEmptyShadeView(); - - // THEN the PrimaryBouncerInteractor value is used. Since the bouncer isn't showing, we - // can show the empty view. - verify(mNotificationStackScrollLayout).updateEmptyShadeView( - /* visible= */ true, - /* areNotificationsHiddenInShade= */ false); - } - - @Test public void testOnUserChange_verifyNotSensitive() { when(mNotificationLockscreenUserManager.isAnyProfilePublicMode()).thenReturn(false); initController(/* viewIsAttached= */ true); @@ -788,31 +656,6 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase { } @Test - @DisableFlags(FooterViewRefactor.FLAG_NAME) - public void testUpdateFooter_remoteInput() { - ArgumentCaptor<RemoteInputController.Callback> callbackCaptor = - ArgumentCaptor.forClass(RemoteInputController.Callback.class); - doNothing().when(mRemoteInputManager).addControllerCallback(callbackCaptor.capture()); - when(mRemoteInputManager.isRemoteInputActive()).thenReturn(false); - initController(/* viewIsAttached= */ true); - verify(mNotificationStackScrollLayout).setIsRemoteInputActive(false); - RemoteInputController.Callback callback = callbackCaptor.getValue(); - callback.onRemoteInputActive(true); - verify(mNotificationStackScrollLayout).setIsRemoteInputActive(true); - } - - @Test - @DisableFlags(FooterViewRefactor.FLAG_NAME) - public void testSetNotifStats_updatesHasFilteredOutSeenNotifications() { - initController(/* viewIsAttached= */ true); - mSeenNotificationsInteractor.setHasFilteredOutSeenNotifications(true); - mController.getNotifStackController().setNotifStats(NotifStats.getEmpty()); - verify(mNotificationStackScrollLayout).setHasFilteredOutSeenNotifications(true); - verify(mNotificationStackScrollLayout).updateFooter(); - verify(mNotificationStackScrollLayout).updateEmptyShadeView(anyBoolean(), anyBoolean()); - } - - @Test public void testAttach_updatesViewStatusBarState() { // GIVEN: Controller is attached initController(/* viewIsAttached= */ true); @@ -844,98 +687,6 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase { } @Test - @DisableFlags(FooterViewRefactor.FLAG_NAME) - public void updateImportantForAccessibility_noChild_onKeyGuard_notImportantForA11y() { - // GIVEN: Controller is attached, active notifications is empty, - // and mNotificationStackScrollLayout.onKeyguard() is true - initController(/* viewIsAttached= */ true); - when(mNotificationStackScrollLayout.onKeyguard()).thenReturn(true); - mController.getNotifStackController().setNotifStats(NotifStats.getEmpty()); - - // THEN: mNotificationStackScrollLayout should not be important for A11y - verify(mNotificationStackScrollLayout) - .setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO); - } - - @Test - @DisableFlags(FooterViewRefactor.FLAG_NAME) - public void updateImportantForAccessibility_hasChild_onKeyGuard_importantForA11y() { - // GIVEN: Controller is attached, active notifications is not empty, - // and mNotificationStackScrollLayout.onKeyguard() is true - initController(/* viewIsAttached= */ true); - when(mNotificationStackScrollLayout.onKeyguard()).thenReturn(true); - mController.getNotifStackController().setNotifStats( - new NotifStats( - /* numActiveNotifs = */ 1, - /* hasNonClearableAlertingNotifs = */ false, - /* hasClearableAlertingNotifs = */ false, - /* hasNonClearableSilentNotifs = */ false, - /* hasClearableSilentNotifs = */ false) - ); - - // THEN: mNotificationStackScrollLayout should be important for A11y - verify(mNotificationStackScrollLayout) - .setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES); - } - - @Test - @DisableFlags(FooterViewRefactor.FLAG_NAME) - public void updateImportantForAccessibility_hasChild_notOnKeyGuard_importantForA11y() { - // GIVEN: Controller is attached, active notifications is not empty, - // and mNotificationStackScrollLayout.onKeyguard() is false - initController(/* viewIsAttached= */ true); - when(mNotificationStackScrollLayout.onKeyguard()).thenReturn(false); - mController.getNotifStackController().setNotifStats( - new NotifStats( - /* numActiveNotifs = */ 1, - /* hasNonClearableAlertingNotifs = */ false, - /* hasClearableAlertingNotifs = */ false, - /* hasNonClearableSilentNotifs = */ false, - /* hasClearableSilentNotifs = */ false) - ); - - // THEN: mNotificationStackScrollLayout should be important for A11y - verify(mNotificationStackScrollLayout) - .setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES); - } - - @Test - @DisableFlags(FooterViewRefactor.FLAG_NAME) - public void updateImportantForAccessibility_noChild_notOnKeyGuard_importantForA11y() { - // GIVEN: Controller is attached, active notifications is empty, - // and mNotificationStackScrollLayout.onKeyguard() is false - initController(/* viewIsAttached= */ true); - when(mNotificationStackScrollLayout.onKeyguard()).thenReturn(false); - mController.getNotifStackController().setNotifStats(NotifStats.getEmpty()); - - // THEN: mNotificationStackScrollLayout should be important for A11y - verify(mNotificationStackScrollLayout) - .setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES); - } - - @Test - @DisableFlags(FooterViewRefactor.FLAG_NAME) - public void updateEmptyShadeView_onKeyguardTransitionToAod_hidesView() { - initController(/* viewIsAttached= */ true); - mController.onKeyguardTransitionChanged( - new TransitionStep( - /* from= */ KeyguardState.GONE, - /* to= */ KeyguardState.AOD)); - verify(mNotificationStackScrollLayout).updateEmptyShadeView(eq(false), anyBoolean()); - } - - @Test - @DisableFlags(FooterViewRefactor.FLAG_NAME) - public void updateEmptyShadeView_onKeyguardOccludedTransitionToAod_hidesView() { - initController(/* viewIsAttached= */ true); - mController.onKeyguardTransitionChanged( - new TransitionStep( - /* from= */ KeyguardState.OCCLUDED, - /* to= */ KeyguardState.AOD)); - verify(mNotificationStackScrollLayout).updateEmptyShadeView(eq(false), anyBoolean()); - } - - @Test @DisableFlags(FLAG_SCREENSHARE_NOTIFICATION_HIDING) public void sensitiveNotificationProtectionControllerListenerNotRegistered() { initController(/* viewIsAttached= */ true); @@ -996,24 +747,6 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase { return argThat(new LogMatcher(category, type)); } - private void setupShowEmptyShadeViewState(boolean toShow) { - if (toShow) { - mController.onKeyguardTransitionChanged( - new TransitionStep( - /* from= */ KeyguardState.LOCKSCREEN, - /* to= */ KeyguardState.GONE)); - mController.setQsFullScreen(false); - mController.getView().removeAllViews(); - } else { - mController.onKeyguardTransitionChanged( - new TransitionStep( - /* from= */ KeyguardState.GONE, - /* to= */ KeyguardState.AOD)); - mController.setQsFullScreen(true); - mController.getView().addContainerView(mock(ExpandableNotificationRow.class)); - } - } - private void initController(boolean viewIsAttached) { when(mNotificationStackScrollLayout.isAttachedToWindow()).thenReturn(viewIsAttached); ViewTreeObserver viewTreeObserver = mock(ViewTreeObserver.class); @@ -1033,16 +766,12 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase { mStatusBarService, mNotificationRoundnessManager, mTunerService, - mDeviceProvisionedController, mDynamicPrivacyController, mConfigurationController, mSysuiStatusBarStateController, mKeyguardMediaController, mKeyguardBypassController, mPowerInteractor, - mPrimaryBouncerInteractor, - mKeyguardTransitionRepo, - mZenModeController, mNotificationLockscreenUserManager, mMetricsLogger, mColorUpdateLogger, @@ -1051,14 +780,11 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase { new FalsingManagerFake(), mNotificationSwipeHelperBuilder, mGroupExpansionManager, - mSilentHeaderController, mNotifPipeline, mNotifCollection, mLockscreenShadeTransitionController, mUiEventLogger, - mRemoteInputManager, mVisibilityLocationProviderDelegator, - mSeenNotificationsInteractor, mViewBinder, mShadeController, mWindowRootView, @@ -1076,7 +802,7 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase { } static class LogMatcher implements ArgumentMatcher<LogMaker> { - private int mCategory, mType; + private final int mCategory, mType; LogMatcher(int category, int type) { mCategory = category; diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt index dcac2941b48b..39cff63f363e 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt @@ -2,12 +2,10 @@ package com.android.systemui.statusbar.notification.stack import android.annotation.DimenRes import android.content.pm.PackageManager -import android.platform.test.annotations.DisableFlags import android.platform.test.flag.junit.FlagsParameterization import android.widget.FrameLayout import androidx.test.filters.SmallTest import com.android.keyguard.BouncerPanelExpansionCalculator.aboutToShowBouncerProgress -import com.android.systemui.Flags import com.android.systemui.SysuiTestCase import com.android.systemui.animation.ShadeInterpolation.getContentAlpha import com.android.systemui.dump.DumpManager @@ -740,20 +738,6 @@ class StackScrollAlgorithmTest(flags: FlagsParameterization) : SysuiTestCase() { assertThat((footerView.viewState as FooterViewState).hideContent).isTrue() } - @DisableFlags(Flags.FLAG_NOTIFICATIONS_FOOTER_VIEW_REFACTOR) - @Test - fun resetViewStates_clearAllInProgress_allRowsRemoved_emptyShade_footerHidden() { - ambientState.isClearAllInProgress = true - ambientState.isShadeExpanded = true - ambientState.stackEndHeight = maxPanelHeight // plenty space for the footer in the stack - hostView.removeAllViews() // remove all rows - hostView.addView(footerView) - - stackScrollAlgorithm.resetViewStates(ambientState, 0) - - assertThat((footerView.viewState as FooterViewState).hideContent).isTrue() - } - @Test fun getGapForLocation_onLockscreen_returnsSmallGap() { val gap = diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelTest.kt index e592e4b319e3..1b4f9a79557d 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelTest.kt @@ -18,7 +18,6 @@ package com.android.systemui.statusbar.notification.stack.ui.viewmodel -import android.platform.test.annotations.EnableFlags import android.platform.test.flag.junit.FlagsParameterization import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase @@ -41,7 +40,6 @@ import com.android.systemui.statusbar.notification.data.repository.FakeHeadsUpRo import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository import com.android.systemui.statusbar.notification.data.repository.setActiveNotifs import com.android.systemui.statusbar.notification.emptyshade.shared.ModesEmptyShadeFix -import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor import com.android.systemui.statusbar.notification.headsup.PinnedStatus import com.android.systemui.statusbar.notification.stack.data.repository.headsUpNotificationRepository import com.android.systemui.statusbar.policy.data.repository.fakeUserSetupRepository @@ -63,7 +61,6 @@ import platform.test.runner.parameterized.Parameters @SmallTest @RunWith(ParameterizedAndroidJunit4::class) -@EnableFlags(FooterViewRefactor.FLAG_NAME) class NotificationListViewModelTest(flags: FlagsParameterization) : SysuiTestCase() { private val kosmos = testKosmos().apply { diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java index d174484219ff..2e12336f6e93 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java @@ -40,7 +40,6 @@ import static org.mockito.Mockito.when; import android.platform.test.annotations.DisableFlags; import android.platform.test.annotations.EnableFlags; -import android.platform.test.annotations.RequiresFlagsEnabled; import android.platform.test.flag.junit.CheckFlagsRule; import android.platform.test.flag.junit.DeviceFlagsValueProvider; import android.testing.TestableLooper; @@ -610,7 +609,6 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { } @Test - @RequiresFlagsEnabled(com.android.systemui.Flags.FLAG_PREDICTIVE_BACK_ANIMATE_BOUNCER) public void testPredictiveBackCallback_registration() { /* verify that a predictive back callback is registered when the bouncer becomes visible */ mBouncerExpansionCallback.onVisibilityChanged(true); @@ -625,7 +623,6 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { } @Test - @RequiresFlagsEnabled(com.android.systemui.Flags.FLAG_PREDICTIVE_BACK_ANIMATE_BOUNCER) public void testPredictiveBackCallback_invocationHidesBouncer() { mBouncerExpansionCallback.onVisibilityChanged(true); /* capture the predictive back callback during registration */ @@ -643,7 +640,6 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { } @Test - @RequiresFlagsEnabled(com.android.systemui.Flags.FLAG_PREDICTIVE_BACK_ANIMATE_BOUNCER) public void testPredictiveBackCallback_noBackAnimationForFullScreenBouncer() { when(mKeyguardSecurityModel.getSecurityMode(anyInt())) .thenReturn(KeyguardSecurityModel.SecurityMode.SimPin); @@ -663,7 +659,6 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { } @Test - @RequiresFlagsEnabled(com.android.systemui.Flags.FLAG_PREDICTIVE_BACK_ANIMATE_BOUNCER) public void testPredictiveBackCallback_forwardsBackDispatches() { mBouncerExpansionCallback.onVisibilityChanged(true); /* capture the predictive back callback during registration */ diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/SystemUIDialogTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/SystemUIDialogTest.java index 0652a835cb7c..650fa7ce46de 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/SystemUIDialogTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/SystemUIDialogTest.java @@ -31,7 +31,6 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.res.Configuration; -import android.platform.test.annotations.RequiresFlagsEnabled; import android.platform.test.flag.junit.CheckFlagsRule; import android.platform.test.flag.junit.DeviceFlagsValueProvider; import android.platform.test.ravenwood.RavenwoodRule; @@ -41,7 +40,6 @@ import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.systemui.Dependency; -import com.android.systemui.Flags; import com.android.systemui.SysuiTestCase; import com.android.systemui.animation.DialogTransitionAnimator; import com.android.systemui.animation.back.BackAnimationSpec; @@ -137,7 +135,6 @@ public class SystemUIDialogTest extends SysuiTestCase { } @Test - @RequiresFlagsEnabled(Flags.FLAG_PREDICTIVE_BACK_ANIMATE_DIALOGS) public void usePredictiveBackAnimFlag() { final SystemUIDialog dialog = new SystemUIDialog(mContext); diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractorTest.kt index 3d6882c3fdaf..c6bae197ad76 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractorTest.kt @@ -18,9 +18,8 @@ package com.android.systemui.statusbar.policy.domain.interactor import android.app.AutomaticZenRule import android.app.Flags -import android.app.NotificationManager.INTERRUPTION_FILTER_NONE -import android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY import android.app.NotificationManager.Policy +import android.media.AudioManager import android.platform.test.annotations.EnableFlags import android.provider.Settings import android.provider.Settings.Secure.ZEN_DURATION @@ -34,6 +33,7 @@ import androidx.test.filters.SmallTest import com.android.internal.R import com.android.settingslib.notification.data.repository.updateNotificationPolicy import com.android.settingslib.notification.modes.TestModeBuilder +import com.android.settingslib.volume.shared.model.AudioStream import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue import com.android.systemui.kosmos.testScope @@ -402,115 +402,124 @@ class ZenModeInteractorTest : SysuiTestCase() { @Test @EnableFlags(Flags.FLAG_MODES_UI) - fun activeModesBlockingEverything_hasModesWithFilterNone() = + fun activeModesBlockingMedia_hasModesWithPolicyBlockingMedia() = testScope.runTest { - val blockingEverything by collectLastValue(underTest.activeModesBlockingEverything) + val blockingMedia by + collectLastValue( + underTest.activeModesBlockingStream(AudioStream(AudioManager.STREAM_MUSIC)) + ) zenModeRepository.addModes( listOf( TestModeBuilder() - .setName("Filter=None, Not active") - .setInterruptionFilter(INTERRUPTION_FILTER_NONE) + .setName("Blocks media, Not active") + .setZenPolicy(ZenPolicy.Builder().allowMedia(false).build()) .setActive(false) .build(), TestModeBuilder() - .setName("Filter=Priority, Active") - .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) + .setName("Allows media, Active") + .setZenPolicy(ZenPolicy.Builder().allowMedia(true).build()) .setActive(true) .build(), TestModeBuilder() - .setName("Filter=None, Active") - .setInterruptionFilter(INTERRUPTION_FILTER_NONE) + .setName("Blocks media, Active") + .setZenPolicy(ZenPolicy.Builder().allowMedia(false).build()) .setActive(true) .build(), TestModeBuilder() - .setName("Filter=None, Active Too") - .setInterruptionFilter(INTERRUPTION_FILTER_NONE) + .setName("Blocks media, Active Too") + .setZenPolicy(ZenPolicy.Builder().allowMedia(false).build()) .setActive(true) .build(), ) ) runCurrent() - assertThat(blockingEverything!!.mainMode!!.name).isEqualTo("Filter=None, Active") - assertThat(blockingEverything!!.modeNames) - .containsExactly("Filter=None, Active", "Filter=None, Active Too") + assertThat(blockingMedia!!.mainMode!!.name).isEqualTo("Blocks media, Active") + assertThat(blockingMedia!!.modeNames) + .containsExactly("Blocks media, Active", "Blocks media, Active Too") .inOrder() } @Test @EnableFlags(Flags.FLAG_MODES_UI) - fun activeModesBlockingMedia_hasModesWithPolicyBlockingMedia() = + fun activeModesBlockingAlarms_hasModesWithPolicyBlockingAlarms() = testScope.runTest { - val blockingMedia by collectLastValue(underTest.activeModesBlockingMedia) + val blockingAlarms by + collectLastValue( + underTest.activeModesBlockingStream(AudioStream(AudioManager.STREAM_ALARM)) + ) zenModeRepository.addModes( listOf( TestModeBuilder() - .setName("Blocks media, Not active") - .setZenPolicy(ZenPolicy.Builder().allowMedia(false).build()) + .setName("Blocks alarms, Not active") + .setZenPolicy(ZenPolicy.Builder().allowAlarms(false).build()) .setActive(false) .build(), TestModeBuilder() - .setName("Allows media, Active") - .setZenPolicy(ZenPolicy.Builder().allowMedia(true).build()) + .setName("Allows alarms, Active") + .setZenPolicy(ZenPolicy.Builder().allowAlarms(true).build()) .setActive(true) .build(), TestModeBuilder() - .setName("Blocks media, Active") - .setZenPolicy(ZenPolicy.Builder().allowMedia(false).build()) + .setName("Blocks alarms, Active") + .setZenPolicy(ZenPolicy.Builder().allowAlarms(false).build()) .setActive(true) .build(), TestModeBuilder() - .setName("Blocks media, Active Too") - .setZenPolicy(ZenPolicy.Builder().allowMedia(false).build()) + .setName("Blocks alarms, Active Too") + .setZenPolicy(ZenPolicy.Builder().allowAlarms(false).build()) .setActive(true) .build(), ) ) runCurrent() - assertThat(blockingMedia!!.mainMode!!.name).isEqualTo("Blocks media, Active") - assertThat(blockingMedia!!.modeNames) - .containsExactly("Blocks media, Active", "Blocks media, Active Too") + assertThat(blockingAlarms!!.mainMode!!.name).isEqualTo("Blocks alarms, Active") + assertThat(blockingAlarms!!.modeNames) + .containsExactly("Blocks alarms, Active", "Blocks alarms, Active Too") .inOrder() } @Test @EnableFlags(Flags.FLAG_MODES_UI) - fun activeModesBlockingAlarms_hasModesWithPolicyBlockingAlarms() = + fun activeModesBlockingAlarms_hasModesWithPolicyBlockingSystem() = testScope.runTest { - val blockingAlarms by collectLastValue(underTest.activeModesBlockingAlarms) + val blockingSystem by + collectLastValue( + underTest.activeModesBlockingStream(AudioStream(AudioManager.STREAM_SYSTEM)) + ) zenModeRepository.addModes( listOf( TestModeBuilder() - .setName("Blocks alarms, Not active") - .setZenPolicy(ZenPolicy.Builder().allowAlarms(false).build()) + .setName("Blocks system, Not active") + .setZenPolicy(ZenPolicy.Builder().allowSystem(false).build()) .setActive(false) .build(), TestModeBuilder() - .setName("Allows alarms, Active") - .setZenPolicy(ZenPolicy.Builder().allowAlarms(true).build()) + .setName("Allows system, Active") + .setZenPolicy(ZenPolicy.Builder().allowSystem(true).build()) .setActive(true) .build(), TestModeBuilder() - .setName("Blocks alarms, Active") - .setZenPolicy(ZenPolicy.Builder().allowAlarms(false).build()) + .setName("Blocks system, Active") + .setZenPolicy(ZenPolicy.Builder().allowSystem(false).build()) .setActive(true) .build(), TestModeBuilder() - .setName("Blocks alarms, Active Too") - .setZenPolicy(ZenPolicy.Builder().allowAlarms(false).build()) + .setName("Blocks system, Active Too") + .setZenPolicy(ZenPolicy.Builder().allowSystem(false).build()) .setActive(true) .build(), ) ) runCurrent() - assertThat(blockingAlarms!!.mainMode!!.name).isEqualTo("Blocks alarms, Active") - assertThat(blockingAlarms!!.modeNames) - .containsExactly("Blocks alarms, Active", "Blocks alarms, Active Too") + assertThat(blockingSystem!!.mainMode!!.name).isEqualTo("Blocks system, Active") + assertThat(blockingSystem!!.modeNames) + .containsExactly("Blocks system, Active", "Blocks system, Active Too") .inOrder() } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/StateTransitionsTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/StateTransitionsTest.kt deleted file mode 100644 index 2ad1124d72d4..000000000000 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/StateTransitionsTest.kt +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright (C) 2024 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.touchpad.tutorial.ui - -import androidx.test.ext.junit.runners.AndroidJUnit4 -import androidx.test.filters.SmallTest -import com.android.systemui.SysuiTestCase -import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState -import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.Error -import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.Finished -import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.InProgress -import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.InProgressAfterError -import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.NotStarted -import com.android.systemui.touchpad.tutorial.ui.composable.toGestureUiState -import com.android.systemui.touchpad.tutorial.ui.composable.toTutorialActionState -import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState -import com.google.common.truth.Truth.assertThat -import org.junit.Test -import org.junit.runner.RunWith - -@SmallTest -@RunWith(AndroidJUnit4::class) -class StateTransitionsTest : SysuiTestCase() { - - companion object { - private const val START_MARKER = "startMarker" - private const val END_MARKER = "endMarker" - private const val SUCCESS_ANIMATION = 0 - } - - // needed to simulate caching last state as it's used to create new state - private var lastState: TutorialActionState = NotStarted - - private fun GestureState.toTutorialActionState(): TutorialActionState { - val newState = - this.toGestureUiState( - progressStartMarker = START_MARKER, - progressEndMarker = END_MARKER, - successAnimation = SUCCESS_ANIMATION, - ) - .toTutorialActionState(lastState) - lastState = newState - return lastState - } - - @Test - fun gestureStateProducesEquivalentTutorialActionStateInHappyPath() { - val happyPath = - listOf( - GestureState.NotStarted, - GestureState.InProgress(0f), - GestureState.InProgress(0.5f), - GestureState.InProgress(1f), - GestureState.Finished, - ) - - val resultingStates = mutableListOf<TutorialActionState>() - happyPath.forEach { resultingStates.add(it.toTutorialActionState()) } - - assertThat(resultingStates) - .containsExactly( - NotStarted, - InProgress(0f, START_MARKER, END_MARKER), - InProgress(0.5f, START_MARKER, END_MARKER), - InProgress(1f, START_MARKER, END_MARKER), - Finished(SUCCESS_ANIMATION), - ) - .inOrder() - } - - @Test - fun gestureStateProducesEquivalentTutorialActionStateInErrorPath() { - val errorPath = - listOf( - GestureState.NotStarted, - GestureState.InProgress(0f), - GestureState.Error, - GestureState.InProgress(0.5f), - GestureState.InProgress(1f), - GestureState.Finished, - ) - - val resultingStates = mutableListOf<TutorialActionState>() - errorPath.forEach { resultingStates.add(it.toTutorialActionState()) } - - assertThat(resultingStates) - .containsExactly( - NotStarted, - InProgress(0f, START_MARKER, END_MARKER), - Error, - InProgressAfterError(InProgress(0.5f, START_MARKER, END_MARKER)), - InProgressAfterError(InProgress(1f, START_MARKER, END_MARKER)), - Finished(SUCCESS_ANIMATION), - ) - .inOrder() - } -} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/BackGestureScreenViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/BackGestureScreenViewModelTest.kt index 4aec88e8497b..d752046f4791 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/BackGestureScreenViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/BackGestureScreenViewModelTest.kt @@ -23,16 +23,16 @@ import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository import com.android.systemui.inputdevice.tutorial.inputDeviceTutorialLogger +import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState +import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.Error +import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.Finished +import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.InProgress import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.collectLastValue import com.android.systemui.kosmos.runTest import com.android.systemui.kosmos.useUnconfinedTestDispatcher import com.android.systemui.res.R import com.android.systemui.testKosmos -import com.android.systemui.touchpad.tutorial.ui.composable.GestureUiState -import com.android.systemui.touchpad.tutorial.ui.composable.GestureUiState.Error -import com.android.systemui.touchpad.tutorial.ui.composable.GestureUiState.Finished -import com.android.systemui.touchpad.tutorial.ui.composable.GestureUiState.InProgress import com.android.systemui.touchpad.tutorial.ui.gesture.MultiFingerGesture.Companion.SWIPE_DISTANCE import com.android.systemui.touchpad.tutorial.ui.gesture.ThreeFingerGesture import com.android.systemui.touchpad.ui.gesture.touchpadGestureResources @@ -71,8 +71,8 @@ class BackGestureScreenViewModelTest : SysuiTestCase() { expected = InProgress( progress = 1f, - progressStartMarker = "gesture to L", - progressEndMarker = "end progress L", + startMarker = "gesture to L", + endMarker = "end progress L", ), ) } @@ -85,8 +85,8 @@ class BackGestureScreenViewModelTest : SysuiTestCase() { expected = InProgress( progress = 1f, - progressStartMarker = "gesture to R", - progressEndMarker = "end progress R", + startMarker = "gesture to R", + endMarker = "end progress R", ), ) } @@ -114,7 +114,7 @@ class BackGestureScreenViewModelTest : SysuiTestCase() { kosmos.runTest { fun performBackGesture() = ThreeFingerGesture.swipeLeft().forEach { viewModel.handleEvent(it) } - val state by collectLastValue(viewModel.gestureUiState) + val state by collectLastValue(viewModel.tutorialState) performBackGesture() assertThat(state).isInstanceOf(Finished::class.java) @@ -134,15 +134,21 @@ class BackGestureScreenViewModelTest : SysuiTestCase() { fakeConfigRepository.onAnyConfigurationChange() } - private fun Kosmos.assertProgressWhileMovingFingers(deltaX: Float, expected: GestureUiState) { + private fun Kosmos.assertProgressWhileMovingFingers( + deltaX: Float, + expected: TutorialActionState, + ) { assertStateAfterEvents( events = ThreeFingerGesture.eventsForGestureInProgress { move(deltaX = deltaX) }, expected = expected, ) } - private fun Kosmos.assertStateAfterEvents(events: List<MotionEvent>, expected: GestureUiState) { - val state by collectLastValue(viewModel.gestureUiState) + private fun Kosmos.assertStateAfterEvents( + events: List<MotionEvent>, + expected: TutorialActionState, + ) { + val state by collectLastValue(viewModel.tutorialState) events.forEach { viewModel.handleEvent(it) } assertThat(state).isEqualTo(expected) } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/HomeGestureScreenViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/HomeGestureScreenViewModelTest.kt index 65a995dcd043..7862fd32ca04 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/HomeGestureScreenViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/HomeGestureScreenViewModelTest.kt @@ -23,16 +23,16 @@ import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository import com.android.systemui.inputdevice.tutorial.inputDeviceTutorialLogger +import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState +import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.Error +import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.Finished +import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.InProgress import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.collectLastValue import com.android.systemui.kosmos.runTest import com.android.systemui.kosmos.useUnconfinedTestDispatcher import com.android.systemui.res.R import com.android.systemui.testKosmos -import com.android.systemui.touchpad.tutorial.ui.composable.GestureUiState -import com.android.systemui.touchpad.tutorial.ui.composable.GestureUiState.Error -import com.android.systemui.touchpad.tutorial.ui.composable.GestureUiState.Finished -import com.android.systemui.touchpad.tutorial.ui.composable.GestureUiState.InProgress import com.android.systemui.touchpad.tutorial.ui.gesture.MultiFingerGesture.Companion.SWIPE_DISTANCE import com.android.systemui.touchpad.tutorial.ui.gesture.ThreeFingerGesture import com.android.systemui.touchpad.tutorial.ui.gesture.Velocity @@ -86,8 +86,8 @@ class HomeGestureScreenViewModelTest : SysuiTestCase() { expected = InProgress( progress = 1f, - progressStartMarker = "drag with gesture", - progressEndMarker = "release playback realtime", + startMarker = "drag with gesture", + endMarker = "release playback realtime", ), ) } @@ -108,7 +108,7 @@ class HomeGestureScreenViewModelTest : SysuiTestCase() { @Test fun gestureRecognitionTakesLatestDistanceThresholdIntoAccount() = kosmos.runTest { - val state by collectLastValue(viewModel.gestureUiState) + val state by collectLastValue(viewModel.tutorialState) performHomeGesture() assertThat(state).isInstanceOf(Finished::class.java) @@ -121,7 +121,7 @@ class HomeGestureScreenViewModelTest : SysuiTestCase() { @Test fun gestureRecognitionTakesLatestVelocityThresholdIntoAccount() = kosmos.runTest { - val state by collectLastValue(viewModel.gestureUiState) + val state by collectLastValue(viewModel.tutorialState) performHomeGesture() assertThat(state).isInstanceOf(Finished::class.java) @@ -147,8 +147,11 @@ class HomeGestureScreenViewModelTest : SysuiTestCase() { fakeConfigRepository.onAnyConfigurationChange() } - private fun Kosmos.assertStateAfterEvents(events: List<MotionEvent>, expected: GestureUiState) { - val state by collectLastValue(viewModel.gestureUiState) + private fun Kosmos.assertStateAfterEvents( + events: List<MotionEvent>, + expected: TutorialActionState, + ) { + val state by collectLastValue(viewModel.tutorialState) events.forEach { viewModel.handleEvent(it) } assertThat(state).isEqualTo(expected) } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/RecentAppsGestureScreenViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/RecentAppsGestureScreenViewModelTest.kt index 1bc60b67095e..6180fa98b1cd 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/RecentAppsGestureScreenViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/RecentAppsGestureScreenViewModelTest.kt @@ -23,16 +23,16 @@ import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository import com.android.systemui.inputdevice.tutorial.inputDeviceTutorialLogger +import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState +import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.Error +import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.Finished +import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.InProgress import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.collectLastValue import com.android.systemui.kosmos.runTest import com.android.systemui.kosmos.useUnconfinedTestDispatcher import com.android.systemui.res.R import com.android.systemui.testKosmos -import com.android.systemui.touchpad.tutorial.ui.composable.GestureUiState -import com.android.systemui.touchpad.tutorial.ui.composable.GestureUiState.Error -import com.android.systemui.touchpad.tutorial.ui.composable.GestureUiState.Finished -import com.android.systemui.touchpad.tutorial.ui.composable.GestureUiState.InProgress import com.android.systemui.touchpad.tutorial.ui.gesture.MultiFingerGesture.Companion.SWIPE_DISTANCE import com.android.systemui.touchpad.tutorial.ui.gesture.ThreeFingerGesture import com.android.systemui.touchpad.tutorial.ui.gesture.Velocity @@ -89,8 +89,8 @@ class RecentAppsGestureScreenViewModelTest : SysuiTestCase() { expected = InProgress( progress = 1f, - progressStartMarker = "drag with gesture", - progressEndMarker = "onPause", + startMarker = "drag with gesture", + endMarker = "onPause", ), ) } @@ -111,7 +111,7 @@ class RecentAppsGestureScreenViewModelTest : SysuiTestCase() { @Test fun gestureRecognitionTakesLatestDistanceThresholdIntoAccount() = kosmos.runTest { - val state by collectLastValue(viewModel.gestureUiState) + val state by collectLastValue(viewModel.tutorialState) performRecentAppsGesture() assertThat(state).isInstanceOf(Finished::class.java) @@ -124,7 +124,7 @@ class RecentAppsGestureScreenViewModelTest : SysuiTestCase() { @Test fun gestureRecognitionTakesLatestVelocityThresholdIntoAccount() = kosmos.runTest { - val state by collectLastValue(viewModel.gestureUiState) + val state by collectLastValue(viewModel.tutorialState) performRecentAppsGesture() assertThat(state).isInstanceOf(Finished::class.java) @@ -150,8 +150,11 @@ class RecentAppsGestureScreenViewModelTest : SysuiTestCase() { fakeConfigRepository.onAnyConfigurationChange() } - private fun Kosmos.assertStateAfterEvents(events: List<MotionEvent>, expected: GestureUiState) { - val state by collectLastValue(viewModel.gestureUiState) + private fun Kosmos.assertStateAfterEvents( + events: List<MotionEvent>, + expected: TutorialActionState, + ) { + val state by collectLastValue(viewModel.tutorialState) events.forEach { viewModel.handleEvent(it) } assertThat(state).isEqualTo(expected) } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/TouchpadTutorialScreenViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/TouchpadTutorialScreenViewModelTest.kt new file mode 100644 index 000000000000..c113dd9e1eff --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/TouchpadTutorialScreenViewModelTest.kt @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.touchpad.tutorial.ui.viewmodel + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.Error +import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.Finished +import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.InProgress +import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.InProgressAfterError +import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.NotStarted +import com.android.systemui.kosmos.collectValues +import com.android.systemui.kosmos.runTest +import com.android.systemui.kosmos.useUnconfinedTestDispatcher +import com.android.systemui.testKosmos +import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.asFlow +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith + +@SmallTest +@RunWith(AndroidJUnit4::class) +class TouchpadTutorialScreenViewModelTest : SysuiTestCase() { + + companion object { + private const val START_MARKER = "startMarker" + private const val END_MARKER = "endMarker" + private const val SUCCESS_ANIMATION = 0 + } + + private val kosmos = testKosmos() + private val animationProperties = + TutorialAnimationProperties( + progressStartMarker = START_MARKER, + progressEndMarker = END_MARKER, + successAnimation = SUCCESS_ANIMATION, + ) + + @Before + fun before() { + kosmos.useUnconfinedTestDispatcher() + } + + @Test + fun gestureStateProducesEquivalentTutorialActionStateInHappyPath() = + kosmos.runTest { + val happyPath: Flow<Pair<GestureState, TutorialAnimationProperties>> = + listOf( + GestureState.NotStarted, + GestureState.InProgress(0f), + GestureState.InProgress(0.5f), + GestureState.InProgress(1f), + GestureState.Finished, + ) + .map { it to animationProperties } + .asFlow() + + val resultingStates by collectValues(happyPath.mapToTutorialState()) + + assertThat(resultingStates) + .containsExactly( + NotStarted, + InProgress(0f, START_MARKER, END_MARKER), + InProgress(0.5f, START_MARKER, END_MARKER), + InProgress(1f, START_MARKER, END_MARKER), + Finished(SUCCESS_ANIMATION), + ) + .inOrder() + } + + @Test + fun gestureStateProducesEquivalentTutorialActionStateInErrorPath() = + kosmos.runTest { + val errorPath: Flow<Pair<GestureState, TutorialAnimationProperties>> = + listOf( + GestureState.NotStarted, + GestureState.InProgress(0f), + GestureState.Error, + GestureState.InProgress(0.5f), + GestureState.InProgress(1f), + GestureState.Finished, + ) + .map { it to animationProperties } + .asFlow() + + val resultingStates by collectValues(errorPath.mapToTutorialState()) + + assertThat(resultingStates) + .containsExactly( + NotStarted, + InProgress(0f, START_MARKER, END_MARKER), + Error, + InProgressAfterError(InProgress(0.5f, START_MARKER, END_MARKER)), + InProgressAfterError(InProgress(1f, START_MARKER, END_MARKER)), + Finished(SUCCESS_ANIMATION), + ) + .inOrder() + } +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModelTest.kt index d3071f87f744..51cac6976362 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModelTest.kt @@ -23,66 +23,40 @@ import android.platform.test.annotations.EnableFlags import android.service.notification.ZenPolicy import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest -import com.android.internal.logging.uiEventLogger import com.android.settingslib.notification.modes.TestModeBuilder import com.android.settingslib.volume.shared.model.AudioStream import com.android.systemui.SysuiTestCase -import com.android.systemui.coroutines.collectLastValue -import com.android.systemui.haptics.slider.sliderHapticsViewModelFactory -import com.android.systemui.kosmos.testScope +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.applicationCoroutineScope +import com.android.systemui.kosmos.collectLastValue +import com.android.systemui.kosmos.runCurrent +import com.android.systemui.kosmos.runTest import com.android.systemui.statusbar.policy.data.repository.fakeZenModeRepository -import com.android.systemui.statusbar.policy.domain.interactor.zenModeInteractor import com.android.systemui.testKosmos -import com.android.systemui.volume.domain.interactor.audioVolumeInteractor -import com.android.systemui.volume.shared.volumePanelLogger import com.google.common.truth.Truth.assertThat -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.test.runCurrent -import kotlinx.coroutines.test.runTest -import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -@OptIn(ExperimentalCoroutinesApi::class) @SmallTest @RunWith(AndroidJUnit4::class) class AudioStreamSliderViewModelTest : SysuiTestCase() { private val kosmos = testKosmos() - private val testScope = kosmos.testScope private val zenModeRepository = kosmos.fakeZenModeRepository - private lateinit var mediaStream: AudioStreamSliderViewModel - private lateinit var alarmsStream: AudioStreamSliderViewModel - private lateinit var notificationStream: AudioStreamSliderViewModel - private lateinit var otherStream: AudioStreamSliderViewModel - - @Before - fun setUp() { - mediaStream = audioStreamSliderViewModel(AudioManager.STREAM_MUSIC) - alarmsStream = audioStreamSliderViewModel(AudioManager.STREAM_ALARM) - notificationStream = audioStreamSliderViewModel(AudioManager.STREAM_NOTIFICATION) - otherStream = audioStreamSliderViewModel(AudioManager.STREAM_VOICE_CALL) - } - - private fun audioStreamSliderViewModel(stream: Int): AudioStreamSliderViewModel { - return AudioStreamSliderViewModel( + private fun Kosmos.audioStreamSliderViewModel(stream: Int): AudioStreamSliderViewModel { + return audioStreamSliderViewModelFactory.create( AudioStreamSliderViewModel.FactoryAudioStreamWrapper(AudioStream(stream)), - testScope.backgroundScope, - context, - kosmos.audioVolumeInteractor, - kosmos.zenModeInteractor, - kosmos.uiEventLogger, - kosmos.volumePanelLogger, - kosmos.sliderHapticsViewModelFactory, + applicationCoroutineScope, ) } @Test @EnableFlags(Flags.FLAG_MODES_UI, Flags.FLAG_MODES_UI_ICONS) fun slider_media_hasDisabledByModesText() = - testScope.runTest { - val mediaSlider by collectLastValue(mediaStream.slider) + kosmos.runTest { + val mediaSlider by + collectLastValue(audioStreamSliderViewModel(AudioManager.STREAM_MUSIC).slider) zenModeRepository.addMode( TestModeBuilder() @@ -112,8 +86,9 @@ class AudioStreamSliderViewModelTest : SysuiTestCase() { @Test @EnableFlags(Flags.FLAG_MODES_UI, Flags.FLAG_MODES_UI_ICONS) fun slider_alarms_hasDisabledByModesText() = - testScope.runTest { - val alarmsSlider by collectLastValue(alarmsStream.slider) + kosmos.runTest { + val alarmsSlider by + collectLastValue(audioStreamSliderViewModel(AudioManager.STREAM_ALARM).slider) zenModeRepository.addMode( TestModeBuilder() @@ -141,9 +116,10 @@ class AudioStreamSliderViewModelTest : SysuiTestCase() { @Test @EnableFlags(Flags.FLAG_MODES_UI, Flags.FLAG_MODES_UI_ICONS) - fun slider_other_hasDisabledByModesText() = - testScope.runTest { - val otherSlider by collectLastValue(otherStream.slider) + fun slider_other_hasDisabledText() = + kosmos.runTest { + val otherSlider by + collectLastValue(audioStreamSliderViewModel(AudioManager.STREAM_VOICE_CALL).slider) zenModeRepository.addMode( TestModeBuilder() @@ -154,20 +130,17 @@ class AudioStreamSliderViewModelTest : SysuiTestCase() { ) runCurrent() - assertThat(otherSlider!!.disabledMessage) - .isEqualTo("Unavailable because Everything blocked is on") - - zenModeRepository.clearModes() - runCurrent() - assertThat(otherSlider!!.disabledMessage).isEqualTo("Unavailable") } @Test @EnableFlags(Flags.FLAG_MODES_UI, Flags.FLAG_MODES_UI_ICONS) fun slider_notification_hasSpecialDisabledText() = - testScope.runTest { - val notificationSlider by collectLastValue(notificationStream.slider) + kosmos.runTest { + val notificationSlider by + collectLastValue( + audioStreamSliderViewModel(AudioManager.STREAM_NOTIFICATION).slider + ) runCurrent() assertThat(notificationSlider!!.disabledMessage) diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index cd37c22c8bc3..a01ff3d5258f 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -3798,7 +3798,7 @@ <!-- Title at the top of the keyboard shortcut helper UI when in customize mode. The helper is a component that shows the user which keyboard shortcuts they can use. [CHAR LIMIT=NONE] --> - <string name="shortcut_helper_customize_mode_title">Customize keyboard shortcuts</string> + <string name="shortcut_helper_customize_mode_title">Customize shortcuts</string> <!-- Title at the top of the keyboard shortcut helper remove shortcut dialog. The helper is a component that shows the user which keyboard shortcuts they can use. Also allows the user to add/remove custom shortcuts.[CHAR LIMIT=NONE] --> diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java index 7d220b505aa0..6e23a0783c9d 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java @@ -21,11 +21,9 @@ import android.util.Log; import android.view.RemoteAnimationTarget; import android.view.SurfaceControl; import android.window.PictureInPictureSurfaceTransaction; -import android.window.TaskSnapshot; import android.window.WindowAnimationState; import com.android.internal.os.IResultReceiver; -import com.android.systemui.shared.recents.model.ThumbnailData; import com.android.wm.shell.recents.IRecentsAnimationController; public class RecentsAnimationControllerCompat { @@ -40,18 +38,6 @@ public class RecentsAnimationControllerCompat { mAnimationController = animationController; } - public ThumbnailData screenshotTask(int taskId) { - try { - final TaskSnapshot snapshot = mAnimationController.screenshotTask(taskId); - if (snapshot != null) { - return ThumbnailData.fromSnapshot(snapshot); - } - } catch (RemoteException e) { - Log.e(TAG, "Failed to screenshot task", e); - } - return new ThumbnailData(); - } - public void setInputConsumerEnabled(boolean enabled) { try { mAnimationController.setInputConsumerEnabled(enabled); diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java index d3c02e6f6449..b159a70066ce 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java @@ -29,7 +29,6 @@ import com.android.keyguard.domain.interactor.KeyguardKeyboardInteractor; import com.android.systemui.bouncer.ui.helper.BouncerHapticPlayer; import com.android.systemui.classifier.FalsingCollector; import com.android.systemui.flags.FeatureFlags; -import com.android.systemui.flags.Flags; import com.android.systemui.res.R; import com.android.systemui.statusbar.policy.DevicePostureController; import com.android.systemui.user.domain.interactor.SelectedUserInteractor; @@ -93,10 +92,8 @@ public class KeyguardPinViewController mPasswordEntry.setUserActivityListener(this::onUserInput); mView.onDevicePostureChanged(mPostureController.getDevicePosture()); mPostureController.addCallback(mPostureCallback); - if (mFeatureFlags.isEnabled(Flags.AUTO_PIN_CONFIRMATION)) { - mPasswordEntry.setUsePinShapes(true); - updateAutoConfirmationState(); - } + mPasswordEntry.setUsePinShapes(true); + updateAutoConfirmationState(); } protected void onUserInput() { diff --git a/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt b/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt index 63ac783ad42b..129a6bb72996 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt +++ b/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt @@ -35,7 +35,6 @@ import com.android.systemui.scene.shared.flag.SceneContainerFlag import com.android.systemui.shade.shared.flag.DualShade import com.android.systemui.statusbar.notification.collection.SortBySectionTimeFlag import com.android.systemui.statusbar.notification.emptyshade.shared.ModesEmptyShadeFix -import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor import com.android.systemui.statusbar.notification.interruption.VisualInterruptionRefactor import com.android.systemui.statusbar.notification.shared.NotificationAvalancheSuppression import com.android.systemui.statusbar.notification.shared.NotificationMinimalism @@ -57,7 +56,6 @@ class FlagDependencies @Inject constructor(featureFlags: FeatureFlagsClassic, ha NotificationAvalancheSuppression.token dependsOn VisualInterruptionRefactor.token PriorityPeopleSection.token dependsOn SortBySectionTimeFlag.token NotificationMinimalism.token dependsOn NotificationThrottleHun.token - ModesEmptyShadeFix.token dependsOn FooterViewRefactor.token ModesEmptyShadeFix.token dependsOn modesUi // SceneContainer dependencies diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt index c039e0188064..2c33c0b4403b 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt +++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt @@ -76,21 +76,10 @@ object Flags { val LOCKSCREEN_CUSTOM_CLOCKS = resourceBooleanFlag(R.bool.config_enableLockScreenCustomClocks, "lockscreen_custom_clocks") - /** - * Migration from the legacy isDozing/dozeAmount paths to the new KeyguardTransitionRepository - * will occur in stages. This is one stage of many to come. - */ - // TODO(b/255607168): Tracking Bug - @JvmField val DOZING_MIGRATION_1 = unreleasedFlag("dozing_migration_1") - /** Flag to control the revamp of keyguard biometrics progress animation */ // TODO(b/244313043): Tracking bug @JvmField val BIOMETRICS_ANIMATION_REVAMP = unreleasedFlag("biometrics_animation_revamp") - // flag for controlling auto pin confirmation and material u shapes in bouncer - @JvmField - val AUTO_PIN_CONFIRMATION = releasedFlag("auto_pin_confirmation", "auto_pin_confirmation") - /** Enables code to show contextual loyalty cards in wallet entrypoints */ // TODO(b/294110497): Tracking Bug @JvmField @@ -100,10 +89,6 @@ object Flags { // TODO(b/242908637): Tracking Bug @JvmField val WALLPAPER_FULLSCREEN_PREVIEW = releasedFlag("wallpaper_fullscreen_preview") - /** Inflate and bind views upon emitting a blueprint value . */ - // TODO(b/297365780): Tracking Bug - @JvmField val LAZY_INFLATE_KEYGUARD = releasedFlag("lazy_inflate_keyguard") - /** Enables UI updates for AI wallpapers in the wallpaper picker. */ // TODO(b/267722622): Tracking Bug @JvmField val WALLPAPER_PICKER_UI_FOR_AIWP = releasedFlag("wallpaper_picker_ui_for_aiwp") diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ShortcutHelperCoreStartable.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ShortcutHelperCoreStartable.kt index 19a19d551613..c702ba9f401e 100644 --- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ShortcutHelperCoreStartable.kt +++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ShortcutHelperCoreStartable.kt @@ -25,6 +25,7 @@ import com.android.systemui.CoreStartable import com.android.systemui.broadcast.BroadcastDispatcher import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background +import com.android.systemui.keyboard.shortcut.data.repository.CustomInputGesturesRepository import com.android.systemui.keyboard.shortcut.data.repository.ShortcutHelperStateRepository import com.android.systemui.plugins.ActivityStarter import com.android.systemui.statusbar.CommandQueue @@ -41,6 +42,7 @@ constructor( private val stateRepository: ShortcutHelperStateRepository, private val activityStarter: ActivityStarter, @Background private val backgroundScope: CoroutineScope, + private val customInputGesturesRepository: CustomInputGesturesRepository ) : CoreStartable { override fun start() { registerBroadcastReceiver( @@ -55,6 +57,10 @@ constructor( action = Intent.ACTION_CLOSE_SYSTEM_DIALOGS, onReceive = { stateRepository.hide() }, ) + registerBroadcastReceiver( + action = Intent.ACTION_USER_SWITCHED, + onReceive = { customInputGesturesRepository.refreshCustomInputGestures() }, + ) commandQueue.addCallback( object : CommandQueue.Callbacks { override fun dismissKeyboardShortcutsMenu() { diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/CustomInputGesturesRepository.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/CustomInputGesturesRepository.kt index 36cd40052041..e5c638cbdfba 100644 --- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/CustomInputGesturesRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/CustomInputGesturesRepository.kt @@ -25,6 +25,7 @@ import android.hardware.input.InputManager.CUSTOM_INPUT_GESTURE_RESULT_ERROR_RES import android.hardware.input.InputManager.CUSTOM_INPUT_GESTURE_RESULT_SUCCESS import android.hardware.input.InputSettings import android.util.Log +import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.keyboard.shared.model.ShortcutCustomizationRequestResult import com.android.systemui.keyboard.shared.model.ShortcutCustomizationRequestResult.ERROR_OTHER @@ -37,6 +38,7 @@ import kotlinx.coroutines.withContext import javax.inject.Inject import kotlin.coroutines.CoroutineContext +@SysUISingleton class CustomInputGesturesRepository @Inject constructor(private val userTracker: UserTracker, @@ -56,7 +58,7 @@ constructor(private val userTracker: UserTracker, val customInputGestures = _customInputGesture.onStart { refreshCustomInputGestures() } - private fun refreshCustomInputGestures() { + fun refreshCustomInputGestures() { setCustomInputGestures(inputGestures = retrieveCustomInputGestures()) } diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/InputGestureMaps.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/InputGestureMaps.kt index d7be5e622276..e255bdea6100 100644 --- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/InputGestureMaps.kt +++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/InputGestureMaps.kt @@ -27,14 +27,19 @@ import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_ASSISTANT import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_SYSTEM_SETTINGS import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_VOICE_ASSISTANT import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_LOCK_SCREEN +import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_MINIMIZE_FREEFORM_WINDOW +import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_MOVE_TO_NEXT_DISPLAY import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_OPEN_NOTES import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_OPEN_SHORTCUT_HELPER import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS_SWITCHER +import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_SNAP_LEFT_FREEFORM_WINDOW +import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_SNAP_RIGHT_FREEFORM_WINDOW import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_LEFT import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_RIGHT import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_TAKE_SCREENSHOT +import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_MAXIMIZE_FREEFORM_WINDOW import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.AppCategories import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.MultiTasking @@ -66,6 +71,11 @@ class InputGestureMaps @Inject constructor(private val context: Context) { KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION to MultiTasking, KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_LEFT to MultiTasking, KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_RIGHT to MultiTasking, + KEY_GESTURE_TYPE_SNAP_LEFT_FREEFORM_WINDOW to MultiTasking, + KEY_GESTURE_TYPE_SNAP_RIGHT_FREEFORM_WINDOW to MultiTasking, + KEY_GESTURE_TYPE_MINIMIZE_FREEFORM_WINDOW to MultiTasking, + KEY_GESTURE_TYPE_TOGGLE_MAXIMIZE_FREEFORM_WINDOW to MultiTasking, + KEY_GESTURE_TYPE_MOVE_TO_NEXT_DISPLAY to MultiTasking, // App Category KEY_GESTURE_TYPE_LAUNCH_APPLICATION to AppCategories, @@ -102,15 +112,23 @@ class InputGestureMaps @Inject constructor(private val context: Context) { R.string.shortcutHelper_category_split_screen, KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_RIGHT to R.string.shortcutHelper_category_split_screen, + KEY_GESTURE_TYPE_SNAP_LEFT_FREEFORM_WINDOW to + R.string.shortcutHelper_category_split_screen, + KEY_GESTURE_TYPE_SNAP_RIGHT_FREEFORM_WINDOW to + R.string.shortcutHelper_category_split_screen, + KEY_GESTURE_TYPE_MINIMIZE_FREEFORM_WINDOW to + R.string.shortcutHelper_category_split_screen, + KEY_GESTURE_TYPE_TOGGLE_MAXIMIZE_FREEFORM_WINDOW to + R.string.shortcutHelper_category_split_screen, + KEY_GESTURE_TYPE_MOVE_TO_NEXT_DISPLAY to R.string.shortcutHelper_category_split_screen, // App Category - KEY_GESTURE_TYPE_LAUNCH_APPLICATION to - R.string.keyboard_shortcut_group_applications, + KEY_GESTURE_TYPE_LAUNCH_APPLICATION to R.string.keyboard_shortcut_group_applications, ) /** - * App Category shortcut labels are mapped dynamically based on intent - * see [InputGestureDataAdapter.fetchShortcutLabelByAppLaunchData] + * App Category shortcut labels are mapped dynamically based on intent see + * [InputGestureDataAdapter.fetchShortcutLabelByAppLaunchData] */ val gestureToInternalKeyboardShortcutInfoLabelResIdMap = mapOf( @@ -136,6 +154,16 @@ class InputGestureMaps @Inject constructor(private val context: Context) { KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_LEFT to R.string.system_multitasking_lhs, KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_RIGHT to R.string.system_multitasking_rhs, KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION to R.string.system_multitasking_full_screen, + KEY_GESTURE_TYPE_SNAP_LEFT_FREEFORM_WINDOW to + R.string.system_desktop_mode_snap_left_window, + KEY_GESTURE_TYPE_SNAP_RIGHT_FREEFORM_WINDOW to + R.string.system_desktop_mode_snap_right_window, + KEY_GESTURE_TYPE_MINIMIZE_FREEFORM_WINDOW to + R.string.system_desktop_mode_minimize_window, + KEY_GESTURE_TYPE_TOGGLE_MAXIMIZE_FREEFORM_WINDOW to + R.string.system_desktop_mode_toggle_maximize_window, + KEY_GESTURE_TYPE_MOVE_TO_NEXT_DISPLAY to + R.string.system_multitasking_move_to_next_display, ) val shortcutLabelToKeyGestureTypeMap: Map<String, Int> diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java index d40fe468b0a5..591383999182 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java @@ -538,27 +538,30 @@ public class KeyguardService extends Service { @Override // Binder interface public void onFinishedGoingToSleep( - @PowerManager.GoToSleepReason int pmSleepReason, boolean cameraGestureTriggered) { + @PowerManager.GoToSleepReason int pmSleepReason, boolean + powerButtonLaunchGestureTriggered) { trace("onFinishedGoingToSleep pmSleepReason=" + pmSleepReason - + " cameraGestureTriggered=" + cameraGestureTriggered); + + " powerButtonLaunchTriggered=" + powerButtonLaunchGestureTriggered); checkPermission(); mKeyguardViewMediator.onFinishedGoingToSleep( WindowManagerPolicyConstants.translateSleepReasonToOffReason(pmSleepReason), - cameraGestureTriggered); - mPowerInteractor.onFinishedGoingToSleep(cameraGestureTriggered); + powerButtonLaunchGestureTriggered); + mPowerInteractor.onFinishedGoingToSleep(powerButtonLaunchGestureTriggered); mKeyguardLifecyclesDispatcher.dispatch( KeyguardLifecyclesDispatcher.FINISHED_GOING_TO_SLEEP); } @Override // Binder interface public void onStartedWakingUp( - @PowerManager.WakeReason int pmWakeReason, boolean cameraGestureTriggered) { + @PowerManager.WakeReason int pmWakeReason, + boolean powerButtonLaunchGestureTriggered) { trace("onStartedWakingUp pmWakeReason=" + pmWakeReason - + " cameraGestureTriggered=" + cameraGestureTriggered); + + " powerButtonLaunchGestureTriggered=" + powerButtonLaunchGestureTriggered); Trace.beginSection("KeyguardService.mBinder#onStartedWakingUp"); checkPermission(); - mKeyguardViewMediator.onStartedWakingUp(pmWakeReason, cameraGestureTriggered); - mPowerInteractor.onStartedWakingUp(pmWakeReason, cameraGestureTriggered); + mKeyguardViewMediator.onStartedWakingUp(pmWakeReason, + powerButtonLaunchGestureTriggered); + mPowerInteractor.onStartedWakingUp(pmWakeReason, powerButtonLaunchGestureTriggered); mKeyguardLifecyclesDispatcher.dispatch( KeyguardLifecyclesDispatcher.STARTED_WAKING_UP, pmWakeReason); Trace.endSection(); diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogDelegate.kt index e5ff2529e335..f295c0ccb3de 100644 --- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogDelegate.kt +++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogDelegate.kt @@ -19,6 +19,7 @@ import android.content.Context import android.hardware.display.DisplayManager import android.os.Bundle import android.os.UserHandle +import android.view.View import androidx.annotation.StyleRes import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.mediaprojection.MediaProjectionMetricsLogger @@ -119,6 +120,12 @@ class ScreenRecordPermissionDialogDelegate( super<BaseMediaProjectionPermissionDialogDelegate>.onCreate(dialog, savedInstanceState) setDialogTitle(R.string.screenrecord_permission_dialog_title) dialog.setTitle(R.string.screenrecord_title) + setStartButtonOnClickListener { v: View? -> + val screenRecordViewBinder: ScreenRecordPermissionViewBinder? = + viewBinder as ScreenRecordPermissionViewBinder? + screenRecordViewBinder?.startButtonOnClicked() + dialog.dismiss() + } setCancelButtonOnClickListener { dialog.dismiss() } } } diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionViewBinder.kt b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionViewBinder.kt index 9f7e1ade964a..691bdd4a1b27 100644 --- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionViewBinder.kt @@ -79,37 +79,38 @@ class ScreenRecordPermissionViewBinder( override fun bind() { super.bind() initRecordOptionsView() - setStartButtonOnClickListener { _: View? -> - onStartRecordingClicked?.run() - if (selectedScreenShareOption.mode == ENTIRE_SCREEN) { - requestScreenCapture( - captureTarget = null, - displayId = selectedScreenShareOption.displayId, - ) - } - if (selectedScreenShareOption.mode == SINGLE_APP) { - val intent = Intent(dialog.context, MediaProjectionAppSelectorActivity::class.java) - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + setStartButtonOnClickListener { startButtonOnClicked() } + } - // We can't start activity for result here so we use result receiver to get - // the selected target to capture - intent.putExtra( - MediaProjectionAppSelectorActivity.EXTRA_CAPTURE_REGION_RESULT_RECEIVER, - CaptureTargetResultReceiver(), - ) + fun startButtonOnClicked() { + onStartRecordingClicked?.run() + if (selectedScreenShareOption.mode == ENTIRE_SCREEN) { + requestScreenCapture( + captureTarget = null, + displayId = selectedScreenShareOption.displayId, + ) + } + if (selectedScreenShareOption.mode == SINGLE_APP) { + val intent = Intent(dialog.context, MediaProjectionAppSelectorActivity::class.java) + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) - intent.putExtra( - MediaProjectionAppSelectorActivity.EXTRA_HOST_APP_USER_HANDLE, - hostUserHandle, - ) - intent.putExtra(MediaProjectionAppSelectorActivity.EXTRA_HOST_APP_UID, hostUid) - intent.putExtra( - MediaProjectionAppSelectorActivity.EXTRA_SCREEN_SHARE_TYPE, - MediaProjectionAppSelectorActivity.ScreenShareType.ScreenRecord.name, - ) - activityStarter.startActivity(intent, /* dismissShade= */ true) - } - dialog.dismiss() + // We can't start activity for result here so we use result receiver to get + // the selected target to capture + intent.putExtra( + MediaProjectionAppSelectorActivity.EXTRA_CAPTURE_REGION_RESULT_RECEIVER, + CaptureTargetResultReceiver(), + ) + + intent.putExtra( + MediaProjectionAppSelectorActivity.EXTRA_HOST_APP_USER_HANDLE, + hostUserHandle, + ) + intent.putExtra(MediaProjectionAppSelectorActivity.EXTRA_HOST_APP_UID, hostUid) + intent.putExtra( + MediaProjectionAppSelectorActivity.EXTRA_SCREEN_SHARE_TYPE, + MediaProjectionAppSelectorActivity.ScreenShareType.ScreenRecord.name, + ) + activityStarter.startActivity(intent, /* dismissShade= */ true) } } diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java index e168025b2bf8..c9eb4962ab00 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java @@ -162,7 +162,6 @@ import com.android.systemui.statusbar.notification.PropertyAnimator; import com.android.systemui.statusbar.notification.ViewGroupFadeHelper; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor; -import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor; import com.android.systemui.statusbar.notification.headsup.HeadsUpManager; import com.android.systemui.statusbar.notification.headsup.HeadsUpTouchHelper; import com.android.systemui.statusbar.notification.headsup.OnHeadsUpChangedListener; @@ -1214,14 +1213,8 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump } private boolean hasVisibleNotifications() { - if (FooterViewRefactor.isEnabled()) { - return mActiveNotificationsInteractor.getAreAnyNotificationsPresentValue() - || mMediaDataManager.hasActiveMediaOrRecommendation(); - } else { - return mNotificationStackScrollLayoutController - .getVisibleNotificationCount() != 0 - || mMediaDataManager.hasActiveMediaOrRecommendation(); - } + return mActiveNotificationsInteractor.getAreAnyNotificationsPresentValue() + || mMediaDataManager.hasActiveMediaOrRecommendation(); } @Override @@ -2218,9 +2211,6 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump @Override public void setBouncerShowing(boolean bouncerShowing) { mBouncerShowing = bouncerShowing; - if (!FooterViewRefactor.isEnabled()) { - mNotificationStackScrollLayoutController.updateShowEmptyShadeView(); - } updateVisibility(); } diff --git a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java index c88e7b827881..14087a0efcfc 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java @@ -86,7 +86,6 @@ import com.android.systemui.statusbar.PulseExpansionHandler; import com.android.systemui.statusbar.QsFrameTranslateController; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor; -import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor; import com.android.systemui.statusbar.notification.stack.AmbientState; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController; @@ -96,8 +95,8 @@ 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.ShadeTouchableRegionManager; +import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; import com.android.systemui.statusbar.policy.CastController; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.SplitShadeStateController; @@ -1022,12 +1021,6 @@ public class QuickSettingsControllerImpl implements QuickSettingsController, Dum } void updateQsState() { - if (!FooterViewRefactor.isEnabled()) { - // Update full screen state; note that this will be true if the QS panel is only - // partially expanded, and that is fixed with the footer view refactor. - setQsFullScreen(/* qsFullScreen = */ getExpanded() && !mSplitShadeEnabled); - } - if (mQsStateUpdateListener != null) { mQsStateUpdateListener.onQsStateUpdated(getExpanded(), mStackScrollerOverscrolling); } @@ -1094,10 +1087,8 @@ public class QuickSettingsControllerImpl implements QuickSettingsController, Dum // Update the light bar mLightBarController.setQsExpanded(mFullyExpanded); - if (FooterViewRefactor.isEnabled()) { - // Update full screen state - setQsFullScreen(/* qsFullScreen = */ mFullyExpanded && !mSplitShadeEnabled); - } + // Update full screen state + setQsFullScreen(/* qsFullScreen = */ mFullyExpanded && !mSplitShadeEnabled); } float getLockscreenShadeDragProgress() { @@ -2268,10 +2259,8 @@ public class QuickSettingsControllerImpl implements QuickSettingsController, Dum setExpansionHeight(qsHeight); } - boolean hasNotifications = FooterViewRefactor.isEnabled() - ? mActiveNotificationsInteractor.getAreAnyNotificationsPresentValue() - : mNotificationStackScrollLayoutController.getVisibleNotificationCount() - != 0; + boolean hasNotifications = + mActiveNotificationsInteractor.getAreAnyNotificationsPresentValue(); if (!hasNotifications && !mMediaDataManager.hasActiveMediaOrRecommendation()) { // No notifications are visible, let's animate to the height of qs instead if (isQsFragmentCreated()) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/ChronometerText.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/ChronometerText.kt index a747abbc6a6e..1c14d3349027 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/ChronometerText.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/ChronometerText.kt @@ -28,17 +28,11 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color -import androidx.compose.ui.layout.Measurable -import androidx.compose.ui.layout.MeasureResult -import androidx.compose.ui.layout.MeasureScope -import androidx.compose.ui.node.LayoutModifierNode -import androidx.compose.ui.node.ModifierNodeElement import androidx.compose.ui.text.TextStyle -import androidx.compose.ui.unit.Constraints -import androidx.compose.ui.unit.constrain import androidx.lifecycle.Lifecycle import androidx.lifecycle.compose.LocalLifecycleOwner import androidx.lifecycle.repeatOnLifecycle +import com.android.systemui.statusbar.chips.ui.compose.modifiers.neverDecreaseWidth import kotlinx.coroutines.delay /** Platform-optimized interface for getting current time */ @@ -97,35 +91,3 @@ fun ChronometerText( modifier = modifier.neverDecreaseWidth(), ) } - -/** A modifier that ensures the width of the content only increases and never decreases. */ -private fun Modifier.neverDecreaseWidth(): Modifier { - return this.then(neverDecreaseWidthElement) -} - -private data object neverDecreaseWidthElement : ModifierNodeElement<NeverDecreaseWidthNode>() { - override fun create(): NeverDecreaseWidthNode { - return NeverDecreaseWidthNode() - } - - override fun update(node: NeverDecreaseWidthNode) { - error("This should never be called") - } -} - -private class NeverDecreaseWidthNode : Modifier.Node(), LayoutModifierNode { - private var minWidth = 0 - - override fun MeasureScope.measure( - measurable: Measurable, - constraints: Constraints, - ): MeasureResult { - val placeable = measurable.measure(Constraints(minWidth = minWidth).constrain(constraints)) - val width = placeable.width - val height = placeable.height - - minWidth = maxOf(minWidth, width) - - return layout(width, height) { placeable.place(0, 0) } - } -} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/modifiers/NeverDecreaseWidth.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/modifiers/NeverDecreaseWidth.kt new file mode 100644 index 000000000000..505a5fcb18b4 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/modifiers/NeverDecreaseWidth.kt @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.chips.ui.compose.modifiers + +import androidx.compose.ui.Modifier +import androidx.compose.ui.layout.Measurable +import androidx.compose.ui.layout.MeasureResult +import androidx.compose.ui.layout.MeasureScope +import androidx.compose.ui.node.LayoutModifierNode +import androidx.compose.ui.node.ModifierNodeElement +import androidx.compose.ui.unit.Constraints +import androidx.compose.ui.unit.constrain + +/** A modifier that ensures the width of the content only increases and never decreases. */ +fun Modifier.neverDecreaseWidth(): Modifier { + return this.then(neverDecreaseWidthElement) +} + +private data object neverDecreaseWidthElement : ModifierNodeElement<NeverDecreaseWidthNode>() { + override fun create(): NeverDecreaseWidthNode { + return NeverDecreaseWidthNode() + } + + override fun update(node: NeverDecreaseWidthNode) { + error("This should never be called") + } +} + +private class NeverDecreaseWidthNode : Modifier.Node(), LayoutModifierNode { + private var minWidth = 0 + + override fun MeasureScope.measure( + measurable: Measurable, + constraints: Constraints, + ): MeasureResult { + val placeable = measurable.measure(Constraints(minWidth = minWidth).constrain(constraints)) + val width = placeable.width + val height = placeable.height + + minWidth = maxOf(minWidth, width) + + return layout(width, height) { placeable.place(0, 0) } + } +} 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 254b792f8152..d327fc23fd06 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java @@ -16,8 +16,6 @@ package com.android.systemui.statusbar.dagger; -import static com.android.systemui.Flags.predictiveBackAnimateDialogs; - import android.content.Context; import android.os.Handler; import android.os.RemoteException; @@ -28,7 +26,6 @@ import com.android.internal.jank.InteractionJankMonitor; import com.android.internal.statusbar.IStatusBarService; import com.android.systemui.CoreStartable; import com.android.systemui.animation.ActivityTransitionAnimator; -import com.android.systemui.animation.AnimationFeatureFlags; import com.android.systemui.animation.DialogTransitionAnimator; import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor; import com.android.systemui.dagger.SysUISingleton; @@ -226,8 +223,7 @@ public interface CentralSurfacesDependenciesModule { IDreamManager dreamManager, KeyguardStateController keyguardStateController, Lazy<AlternateBouncerInteractor> alternateBouncerInteractor, - InteractionJankMonitor interactionJankMonitor, - AnimationFeatureFlags animationFeatureFlags) { + InteractionJankMonitor interactionJankMonitor) { DialogTransitionAnimator.Callback callback = new DialogTransitionAnimator.Callback() { @Override public boolean isDreaming() { @@ -249,19 +245,6 @@ public interface CentralSurfacesDependenciesModule { return alternateBouncerInteractor.get().canShowAlternateBouncerForFingerprint(); } }; - return new DialogTransitionAnimator( - mainExecutor, callback, interactionJankMonitor, animationFeatureFlags); - } - - /** */ - @Provides - @SysUISingleton - static AnimationFeatureFlags provideAnimationFeatureFlags() { - return new AnimationFeatureFlags() { - @Override - public boolean isPredictiveBackQsDialogAnim() { - return predictiveBackAnimateDialogs(); - } - }; + return new DialogTransitionAnimator(mainExecutor, callback, interactionJankMonitor); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinator.kt index 32de65be5b5b..d4d3cdf42fb1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinator.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinator.kt @@ -27,7 +27,6 @@ import com.android.systemui.statusbar.notification.collection.render.NotifStackC import com.android.systemui.statusbar.notification.collection.render.NotifStats import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor import com.android.systemui.statusbar.notification.domain.interactor.RenderNotificationListInteractor -import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor import com.android.systemui.statusbar.notification.stack.BUCKET_SILENT import com.android.systemui.statusbar.policy.SensitiveNotificationProtectionController import javax.inject.Inject @@ -43,7 +42,8 @@ internal constructor( private val groupExpansionManagerImpl: GroupExpansionManagerImpl, private val renderListInteractor: RenderNotificationListInteractor, private val activeNotificationsInteractor: ActiveNotificationsInteractor, - private val sensitiveNotificationProtectionController: SensitiveNotificationProtectionController, + private val sensitiveNotificationProtectionController: + SensitiveNotificationProtectionController, ) : Coordinator { override fun attach(pipeline: NotifPipeline) { @@ -51,14 +51,11 @@ internal constructor( groupExpansionManagerImpl.attach(pipeline) } + // TODO: b/293167744 - Remove controller param. private fun onAfterRenderList(entries: List<ListEntry>, controller: NotifStackController) = traceSection("StackCoordinator.onAfterRenderList") { val notifStats = calculateNotifStats(entries) - if (FooterViewRefactor.isEnabled) { - activeNotificationsInteractor.setNotifStats(notifStats) - } else { - controller.setNotifStats(notifStats) - } + activeNotificationsInteractor.setNotifStats(notifStats) renderListInteractor.setRenderedList(entries) } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/emptyshade/ui/viewmodel/EmptyShadeViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/emptyshade/ui/viewmodel/EmptyShadeViewModel.kt index fbec6406e9d4..7e2361f24da9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/emptyshade/ui/viewmodel/EmptyShadeViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/emptyshade/ui/viewmodel/EmptyShadeViewModel.kt @@ -26,7 +26,6 @@ import com.android.systemui.shared.notifications.domain.interactor.NotificationS import com.android.systemui.statusbar.notification.NotificationActivityStarter.SettingsIntent import com.android.systemui.statusbar.notification.domain.interactor.SeenNotificationsInteractor import com.android.systemui.statusbar.notification.emptyshade.shared.ModesEmptyShadeFix -import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor import com.android.systemui.statusbar.notification.footer.ui.viewmodel.FooterMessageViewModel import com.android.systemui.statusbar.policy.domain.interactor.ZenModeInteractor import com.android.systemui.util.kotlin.FlowDumperImpl @@ -35,7 +34,6 @@ import dagger.assisted.AssistedInject import java.util.Locale import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.flowOf @@ -57,9 +55,7 @@ constructor( dumpManager: DumpManager, ) : FlowDumperImpl(dumpManager) { val areNotificationsHiddenInShade: Flow<Boolean> by lazy { - if (FooterViewRefactor.isUnexpectedlyInLegacyMode()) { - flowOf(false) - } else if (ModesEmptyShadeFix.isEnabled) { + if (ModesEmptyShadeFix.isEnabled) { zenModeInteractor.areNotificationsHiddenInShade .dumpWhileCollecting("areNotificationsHiddenInShade") .flowOn(bgDispatcher) @@ -70,15 +66,10 @@ constructor( } } - val hasFilteredOutSeenNotifications: StateFlow<Boolean> by lazy { - if (FooterViewRefactor.isUnexpectedlyInLegacyMode()) { - MutableStateFlow(false) - } else { - seenNotificationsInteractor.hasFilteredOutSeenNotifications.dumpValue( - "hasFilteredOutSeenNotifications" - ) - } - } + val hasFilteredOutSeenNotifications: StateFlow<Boolean> = + seenNotificationsInteractor.hasFilteredOutSeenNotifications.dumpValue( + "hasFilteredOutSeenNotifications" + ) val text: Flow<String> by lazy { if (ModesEmptyShadeFix.isUnexpectedlyInLegacyMode()) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/shared/FooterViewRefactor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/shared/FooterViewRefactor.kt deleted file mode 100644 index 7e6044eb6869..000000000000 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/shared/FooterViewRefactor.kt +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.statusbar.notification.footer.shared - -import com.android.systemui.Flags -import com.android.systemui.flags.FlagToken -import com.android.systemui.flags.RefactorFlagUtils - -/** Helper for reading or using the FooterView refactor flag state. */ -@Suppress("NOTHING_TO_INLINE") -object FooterViewRefactor { - /** The aconfig flag name */ - const val FLAG_NAME = Flags.FLAG_NOTIFICATIONS_FOOTER_VIEW_REFACTOR - - /** A token used for dependency declaration */ - val token: FlagToken - get() = FlagToken(FLAG_NAME, isEnabled) - - /** Is the refactor enabled */ - @JvmStatic - inline val isEnabled - get() = Flags.notificationsFooterViewRefactor() - - /** - * Called to ensure code is only run when the flag is enabled. This protects users from the - * unintended behaviors caused by accidentally running new logic, while also crashing on an eng - * build to ensure that the refactor author catches issues in testing. - */ - @JvmStatic - inline fun isUnexpectedlyInLegacyMode() = - RefactorFlagUtils.isUnexpectedlyInLegacyMode(isEnabled, FLAG_NAME) - - /** - * Called to ensure code is only run when the flag is disabled. This will throw an exception if - * the flag is enabled to ensure that the refactor author catches issues in testing. - */ - @JvmStatic - inline fun assertInLegacyMode() = RefactorFlagUtils.assertInLegacyMode(isEnabled, FLAG_NAME) -} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java index d25889820629..a670f69df601 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java @@ -41,7 +41,6 @@ import androidx.annotation.NonNull; import com.android.systemui.res.R; import com.android.systemui.statusbar.notification.ColorUpdateLogger; -import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor; import com.android.systemui.statusbar.notification.footer.shared.NotifRedesignFooter; import com.android.systemui.statusbar.notification.row.FooterViewButton; import com.android.systemui.statusbar.notification.row.StackScrollerDecorView; @@ -63,16 +62,9 @@ public class FooterView extends StackScrollerDecorView { private FooterViewButton mSettingsButton; private FooterViewButton mHistoryButton; private boolean mShouldBeHidden; - private boolean mShowHistory; - // String cache, for performance reasons. - // Reading them from a Resources object can be quite slow sometimes. - private String mManageNotificationText; - private String mManageNotificationHistoryText; // Footer label private TextView mSeenNotifsFooterTextView; - private String mSeenNotifsFilteredText; - private Drawable mSeenNotifsFilteredIcon; private @StringRes int mClearAllButtonTextId; private @StringRes int mClearAllButtonDescriptionId; @@ -159,8 +151,8 @@ public class FooterView extends StackScrollerDecorView { IndentingPrintWriter pw = DumpUtilsKt.asIndenting(pwOriginal); super.dump(pw, args); DumpUtilsKt.withIncreasedIndent(pw, () -> { + // TODO: b/375010573 - update dumps for redesign pw.println("visibility: " + DumpUtilsKt.visibilityString(getVisibility())); - pw.println("manageButton showHistory: " + mShowHistory); pw.println("manageButton visibility: " + DumpUtilsKt.visibilityString(mClearAllButton.getVisibility())); pw.println("dismissButton visibility: " @@ -170,7 +162,6 @@ public class FooterView extends StackScrollerDecorView { /** Set the text label for the "Clear all" button. */ public void setClearAllButtonText(@StringRes int textId) { - if (FooterViewRefactor.isUnexpectedlyInLegacyMode()) return; if (mClearAllButtonTextId == textId) { return; // nothing changed } @@ -187,9 +178,6 @@ public class FooterView extends StackScrollerDecorView { /** Set the accessibility content description for the "Clear all" button. */ public void setClearAllButtonDescription(@StringRes int contentDescriptionId) { - if (FooterViewRefactor.isUnexpectedlyInLegacyMode()) { - return; - } if (mClearAllButtonDescriptionId == contentDescriptionId) { return; // nothing changed } @@ -207,7 +195,6 @@ public class FooterView extends StackScrollerDecorView { /** Set the text label for the "Manage"/"History" button. */ public void setManageOrHistoryButtonText(@StringRes int textId) { NotifRedesignFooter.assertInLegacyMode(); - if (FooterViewRefactor.isUnexpectedlyInLegacyMode()) return; if (mManageOrHistoryButtonTextId == textId) { return; // nothing changed } @@ -226,9 +213,6 @@ public class FooterView extends StackScrollerDecorView { /** Set the accessibility content description for the "Clear all" button. */ public void setManageOrHistoryButtonDescription(@StringRes int contentDescriptionId) { NotifRedesignFooter.assertInLegacyMode(); - if (FooterViewRefactor.isUnexpectedlyInLegacyMode()) { - return; - } if (mManageOrHistoryButtonDescriptionId == contentDescriptionId) { return; // nothing changed } @@ -247,7 +231,6 @@ public class FooterView extends StackScrollerDecorView { /** Set the string for a message to be shown instead of the buttons. */ public void setMessageString(@StringRes int messageId) { - if (FooterViewRefactor.isUnexpectedlyInLegacyMode()) return; if (mMessageStringId == messageId) { return; // nothing changed } @@ -265,7 +248,6 @@ public class FooterView extends StackScrollerDecorView { /** Set the icon to be shown before the message (see {@link #setMessageString(int)}). */ public void setMessageIcon(@DrawableRes int iconId) { - if (FooterViewRefactor.isUnexpectedlyInLegacyMode()) return; if (mMessageIconId == iconId) { return; // nothing changed } @@ -303,32 +285,17 @@ public class FooterView extends StackScrollerDecorView { mManageOrHistoryButton = findViewById(R.id.manage_text); } mSeenNotifsFooterTextView = findViewById(R.id.unlock_prompt_footer); - if (!FooterViewRefactor.isEnabled()) { - updateResources(); - } updateContent(); updateColors(); } /** Show a message instead of the footer buttons. */ public void setFooterLabelVisible(boolean isVisible) { - // In the refactored code, hiding the buttons is handled in the FooterViewModel - if (FooterViewRefactor.isEnabled()) { - if (isVisible) { - mSeenNotifsFooterTextView.setVisibility(View.VISIBLE); - } else { - mSeenNotifsFooterTextView.setVisibility(View.GONE); - } + // Note: hiding the buttons is handled in the FooterViewModel + if (isVisible) { + mSeenNotifsFooterTextView.setVisibility(View.VISIBLE); } else { - if (isVisible) { - mManageOrHistoryButton.setVisibility(View.GONE); - mClearAllButton.setVisibility(View.GONE); - mSeenNotifsFooterTextView.setVisibility(View.VISIBLE); - } else { - mManageOrHistoryButton.setVisibility(View.VISIBLE); - mClearAllButton.setVisibility(View.VISIBLE); - mSeenNotifsFooterTextView.setVisibility(View.GONE); - } + mSeenNotifsFooterTextView.setVisibility(View.GONE); } } @@ -359,10 +326,8 @@ public class FooterView extends StackScrollerDecorView { /** Set onClickListener for the clear all (end) button. */ public void setClearAllButtonClickListener(OnClickListener listener) { - if (FooterViewRefactor.isEnabled()) { - if (mClearAllButtonClickListener == listener) return; - mClearAllButtonClickListener = listener; - } + if (mClearAllButtonClickListener == listener) return; + mClearAllButtonClickListener = listener; mClearAllButton.setOnClickListener(listener); } @@ -379,62 +344,17 @@ public class FooterView extends StackScrollerDecorView { || touchY > mContent.getY() + mContent.getHeight(); } - /** Show "History" instead of "Manage" on the start button. */ - public void showHistory(boolean showHistory) { - FooterViewRefactor.assertInLegacyMode(); - if (mShowHistory == showHistory) { - return; - } - mShowHistory = showHistory; - updateContent(); - } - private void updateContent() { - if (FooterViewRefactor.isEnabled()) { - updateClearAllButtonText(); - updateClearAllButtonDescription(); - - if (!NotifRedesignFooter.isEnabled()) { - updateManageOrHistoryButtonText(); - updateManageOrHistoryButtonDescription(); - } - - updateMessageString(); - updateMessageIcon(); - } else { - // NOTE: Prior to the refactor, `updateResources` set the class properties to the right - // string values. It was always being called together with `updateContent`, which - // deals with actually associating those string values with the correct views - // (buttons or text). - // In the new code, the resource IDs are being set in the view binder (through - // setMessageString and similar setters). The setters themselves now deal with - // updating both the resource IDs and the views where appropriate (as in, calling - // `updateMessageString` when the resource ID changes). This eliminates the need for - // `updateResources`, which will eventually be removed. There are, however, still - // situations in which we want to update the views even if the resource IDs didn't - // change, such as configuration changes. - if (mShowHistory) { - mManageOrHistoryButton.setText(mManageNotificationHistoryText); - mManageOrHistoryButton.setContentDescription(mManageNotificationHistoryText); - } else { - mManageOrHistoryButton.setText(mManageNotificationText); - mManageOrHistoryButton.setContentDescription(mManageNotificationText); - } - - mClearAllButton.setText(R.string.clear_all_notifications_text); - mClearAllButton.setContentDescription( - mContext.getString(R.string.accessibility_clear_all)); + updateClearAllButtonText(); + updateClearAllButtonDescription(); - mSeenNotifsFooterTextView.setText(mSeenNotifsFilteredText); - mSeenNotifsFooterTextView - .setCompoundDrawablesRelative(mSeenNotifsFilteredIcon, null, null, null); + if (!NotifRedesignFooter.isEnabled()) { + updateManageOrHistoryButtonText(); + updateManageOrHistoryButtonDescription(); } - } - /** Whether the start button shows "History" (true) or "Manage" (false). */ - public boolean isHistoryShown() { - FooterViewRefactor.assertInLegacyMode(); - return mShowHistory; + updateMessageString(); + updateMessageIcon(); } @Override @@ -445,9 +365,6 @@ public class FooterView extends StackScrollerDecorView { } super.onConfigurationChanged(newConfig); updateColors(); - if (!FooterViewRefactor.isEnabled()) { - updateResources(); - } updateContent(); } @@ -502,18 +419,6 @@ public class FooterView extends StackScrollerDecorView { } } - private void updateResources() { - FooterViewRefactor.assertInLegacyMode(); - mManageNotificationText = getContext().getString(R.string.manage_notifications_text); - mManageNotificationHistoryText = getContext() - .getString(R.string.manage_notifications_history_text); - int unlockIconSize = getResources() - .getDimensionPixelSize(R.dimen.notifications_unseen_footer_icon_size); - mSeenNotifsFilteredText = getContext().getString(R.string.unlock_to_see_notif_text); - mSeenNotifsFilteredIcon = getContext().getDrawable(R.drawable.ic_friction_lock_closed); - mSeenNotifsFilteredIcon.setBounds(0, 0, unlockIconSize, unlockIconSize); - } - @Override @NonNull public ExpandableViewState createExpandableViewState() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModel.kt index e724935e3ef4..5696e9f0c5a2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModel.kt @@ -27,7 +27,6 @@ import com.android.systemui.statusbar.notification.NotificationActivityStarter.S import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor import com.android.systemui.statusbar.notification.domain.interactor.SeenNotificationsInteractor import com.android.systemui.statusbar.notification.emptyshade.shared.ModesEmptyShadeFix -import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor import com.android.systemui.statusbar.notification.footer.ui.view.FooterView import com.android.systemui.util.kotlin.sample import com.android.systemui.util.ui.AnimatableEvent @@ -144,6 +143,7 @@ class FooterViewModel( ) } +// TODO: b/293167744 - remove this, use new viewmodel style @Module object FooterViewModelModule { @Provides @@ -153,18 +153,13 @@ object FooterViewModelModule { notificationSettingsInteractor: Provider<NotificationSettingsInteractor>, seenNotificationsInteractor: Provider<SeenNotificationsInteractor>, shadeInteractor: Provider<ShadeInteractor>, - ): Optional<FooterViewModel> { - return if (FooterViewRefactor.isEnabled) { - Optional.of( - FooterViewModel( - activeNotificationsInteractor.get(), - notificationSettingsInteractor.get(), - seenNotificationsInteractor.get(), - shadeInteractor.get(), - ) + ): Optional<FooterViewModel> = + Optional.of( + FooterViewModel( + activeNotificationsInteractor.get(), + notificationSettingsInteractor.get(), + seenNotificationsInteractor.get(), + shadeInteractor.get(), ) - } else { - Optional.empty() - } - } + ) } 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 071d23283c43..76591ac4e453 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 @@ -108,7 +108,6 @@ import com.android.systemui.statusbar.notification.collection.render.GroupExpans import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager; import com.android.systemui.statusbar.notification.emptyshade.shared.ModesEmptyShadeFix; import com.android.systemui.statusbar.notification.emptyshade.ui.view.EmptyShadeView; -import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor; import com.android.systemui.statusbar.notification.footer.ui.view.FooterView; import com.android.systemui.statusbar.notification.headsup.HeadsUpTouchHelper; import com.android.systemui.statusbar.notification.headsup.HeadsUpUtil; @@ -703,9 +702,6 @@ public class NotificationStackScrollLayout if (!ModesEmptyShadeFix.isEnabled()) { inflateEmptyShadeView(); } - if (!FooterViewRefactor.isEnabled()) { - inflateFooterView(); - } } /** @@ -741,22 +737,12 @@ public class NotificationStackScrollLayout } void reinflateViews() { - if (!FooterViewRefactor.isEnabled()) { - inflateFooterView(); - updateFooter(); - } if (!ModesEmptyShadeFix.isEnabled()) { inflateEmptyShadeView(); } mSectionsManager.reinflateViews(); } - public void setIsRemoteInputActive(boolean isActive) { - FooterViewRefactor.assertInLegacyMode(); - mIsRemoteInputActive = isActive; - updateFooter(); - } - void sendRemoteInputRowBottomBound(Float bottom) { if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return; if (bottom != null) { @@ -766,43 +752,6 @@ public class NotificationStackScrollLayout mScrollViewFields.sendRemoteInputRowBottomBound(bottom); } - /** Setter for filtered notifs, to be removed with the FooterViewRefactor flag. */ - public void setHasFilteredOutSeenNotifications(boolean hasFilteredOutSeenNotifications) { - FooterViewRefactor.assertInLegacyMode(); - mHasFilteredOutSeenNotifications = hasFilteredOutSeenNotifications; - } - - @VisibleForTesting - public void updateFooter() { - FooterViewRefactor.assertInLegacyMode(); - if (mFooterView == null || mController == null) { - return; - } - final boolean showHistory = mController.isHistoryEnabled(); - final boolean showDismissView = shouldShowDismissView(); - - updateFooterView(shouldShowFooterView(showDismissView)/* visible */, - showDismissView /* showDismissView */, - showHistory/* showHistory */); - } - - private boolean shouldShowDismissView() { - FooterViewRefactor.assertInLegacyMode(); - return mController.hasActiveClearableNotifications(ROWS_ALL); - } - - private boolean shouldShowFooterView(boolean showDismissView) { - FooterViewRefactor.assertInLegacyMode(); - return (showDismissView || mController.getVisibleNotificationCount() > 0) - && mIsCurrentUserSetup // see: b/193149550 - && !onKeyguard() - && mUpcomingStatusBarState != StatusBarState.KEYGUARD - // quick settings don't affect notifications when not in full screen - && (getQsExpansionFraction() != 1 || !mQsFullScreen) - && !mScreenOffAnimationController.shouldHideNotificationsFooter() - && !mIsRemoteInputActive; - } - void updateBgColor() { for (int i = 0; i < getChildCount(); i++) { View child = getChildAt(i); @@ -1861,9 +1810,6 @@ public class NotificationStackScrollLayout */ private float getAppearEndPosition() { SceneContainerFlag.assertInLegacyMode(); - if (FooterViewRefactor.isUnexpectedlyInLegacyMode()) { - return getAppearEndPositionLegacy(); - } int appearPosition = mAmbientState.getStackTopMargin(); if (mEmptyShadeView.getVisibility() == GONE) { @@ -1883,32 +1829,6 @@ public class NotificationStackScrollLayout return appearPosition + (onKeyguard() ? getTopPadding() : getIntrinsicPadding()); } - /** - * The version of {@code getAppearEndPosition} that uses the notif count. The view shouldn't - * need to know about that, so we want to phase this out with the footer view refactor. - */ - private float getAppearEndPositionLegacy() { - FooterViewRefactor.assertInLegacyMode(); - - int appearPosition = mAmbientState.getStackTopMargin(); - int visibleNotifCount = mController.getVisibleNotificationCount(); - if (mEmptyShadeView.getVisibility() == GONE && visibleNotifCount > 0) { - if (isHeadsUpTransition() - || (mInHeadsUpPinnedMode && !mAmbientState.isDozing())) { - if (mShelf.getVisibility() != GONE && visibleNotifCount > 1) { - appearPosition += mShelf.getIntrinsicHeight() + mPaddingBetweenElements; - } - appearPosition += getTopHeadsUpPinnedHeight() - + getPositionInLinearLayout(mAmbientState.getTrackedHeadsUpRow()); - } else if (mShelf.getVisibility() != GONE) { - appearPosition += mShelf.getIntrinsicHeight(); - } - } else { - appearPosition = mEmptyShadeView.getHeight(); - } - return appearPosition + (onKeyguard() ? getTopPadding() : getIntrinsicPadding()); - } - private boolean isHeadsUpTransition() { return mAmbientState.getTrackedHeadsUpRow() != null; } @@ -1928,8 +1848,7 @@ public class NotificationStackScrollLayout // This can't use expansion fraction as that goes only from 0 to 1. Also when // appear fraction for HUN is 0, expansion fraction will be already around 0.2-0.3 // and that makes translation jump immediately. - float appearEndPosition = FooterViewRefactor.isEnabled() ? getAppearEndPosition() - : getAppearEndPositionLegacy(); + float appearEndPosition = getAppearEndPosition(); float appearStartPosition = getAppearStartPosition(); float hunAppearFraction = (height - appearStartPosition) / (appearEndPosition - appearStartPosition); @@ -4848,15 +4767,6 @@ public class NotificationStackScrollLayout } } - /** - * Returns whether or not a History button is shown in the footer. If there is no footer, then - * this will return false. - **/ - public boolean isHistoryShown() { - FooterViewRefactor.assertInLegacyMode(); - return mFooterView != null && mFooterView.isHistoryShown(); - } - /** Bind the {@link FooterView} to the NSSL. */ public void setFooterView(@NonNull FooterView footerView) { int index = -1; @@ -4866,18 +4776,6 @@ public class NotificationStackScrollLayout } mFooterView = footerView; addView(mFooterView, index); - if (!FooterViewRefactor.isEnabled()) { - if (mManageButtonClickListener != null) { - mFooterView.setManageButtonClickListener(mManageButtonClickListener); - } - mFooterView.setClearAllButtonClickListener(v -> { - if (mFooterClearAllListener != null) { - mFooterClearAllListener.onClearAll(); - } - clearNotifications(ROWS_ALL, true /* closeShade */); - footerView.setClearAllButtonVisible(false /* visible */, true /* animate */); - }); - } } public void setEmptyShadeView(EmptyShadeView emptyShadeView) { @@ -4890,13 +4788,6 @@ public class NotificationStackScrollLayout addView(mEmptyShadeView, index); } - /** Legacy version, should be removed with the footer refactor flag. */ - public void updateEmptyShadeView(boolean visible, boolean areNotificationsHiddenInShade) { - FooterViewRefactor.assertInLegacyMode(); - updateEmptyShadeView(visible, areNotificationsHiddenInShade, - mHasFilteredOutSeenNotifications); - } - /** Trigger an update for the empty shade resources and visibility. */ public void updateEmptyShadeView(boolean visible, boolean areNotificationsHiddenInShade, boolean hasFilteredOutSeenNotifications) { @@ -4949,18 +4840,6 @@ public class NotificationStackScrollLayout return mEmptyShadeView.isVisible(); } - public void updateFooterView(boolean visible, boolean showDismissView, boolean showHistory) { - FooterViewRefactor.assertInLegacyMode(); - if (mFooterView == null || mNotificationStackSizeCalculator == null) { - return; - } - boolean animate = mIsExpanded && mAnimationsEnabled; - mFooterView.setVisible(visible, animate); - mFooterView.showHistory(showHistory); - mFooterView.setClearAllButtonVisible(showDismissView, animate); - mFooterView.setFooterLabelVisible(mHasFilteredOutSeenNotifications); - } - @VisibleForTesting public void setClearAllInProgress(boolean clearAllInProgress) { mClearAllInProgress = clearAllInProgress; @@ -5244,10 +5123,8 @@ public class NotificationStackScrollLayout public void setQsFullScreen(boolean qsFullScreen) { SceneContainerFlag.assertInLegacyMode(); - if (FooterViewRefactor.isEnabled()) { - if (qsFullScreen == mQsFullScreen) { - return; // no change - } + if (qsFullScreen == mQsFullScreen) { + return; // no change } mQsFullScreen = qsFullScreen; updateAlgorithmLayoutMinHeight(); @@ -5266,8 +5143,6 @@ public class NotificationStackScrollLayout public void setQsExpansionFraction(float qsExpansionFraction) { SceneContainerFlag.assertInLegacyMode(); - boolean footerAffected = getQsExpansionFraction() != qsExpansionFraction - && (getQsExpansionFraction() == 1 || qsExpansionFraction == 1); mQsExpansionFraction = qsExpansionFraction; updateUseRoundedRectClipping(); @@ -5276,9 +5151,6 @@ public class NotificationStackScrollLayout if (getOwnScrollY() > 0) { setOwnScrollY((int) MathUtils.lerp(getOwnScrollY(), 0, getQsExpansionFraction())); } - if (!FooterViewRefactor.isEnabled() && footerAffected) { - updateFooter(); - } } @VisibleForTesting @@ -5456,14 +5328,6 @@ public class NotificationStackScrollLayout requestChildrenUpdate(); } - void setUpcomingStatusBarState(int upcomingStatusBarState) { - FooterViewRefactor.assertInLegacyMode(); - mUpcomingStatusBarState = upcomingStatusBarState; - if (mUpcomingStatusBarState != mStatusBarState) { - updateFooter(); - } - } - void onStatePostChange(boolean fromShadeLocked) { boolean onKeyguard = onKeyguard(); @@ -5472,9 +5336,6 @@ public class NotificationStackScrollLayout } setExpandingEnabled(!onKeyguard); - if (!FooterViewRefactor.isEnabled()) { - updateFooter(); - } requestChildrenUpdate(); onUpdateRowStates(); updateVisibility(); @@ -5490,8 +5351,7 @@ public class NotificationStackScrollLayout if (mEmptyShadeView == null || mEmptyShadeView.getVisibility() == GONE) { return getMinExpansionHeight(); } else { - return FooterViewRefactor.isEnabled() ? getAppearEndPosition() - : getAppearEndPositionLegacy(); + return getAppearEndPosition(); } } @@ -5583,12 +5443,6 @@ public class NotificationStackScrollLayout for (int i = 0; i < childCount; i++) { ExpandableView child = getChildAtIndex(i); child.dump(pw, args); - if (!FooterViewRefactor.isEnabled()) { - if (child instanceof FooterView) { - DumpUtilsKt.withIncreasedIndent(pw, - () -> dumpFooterViewVisibility(pw)); - } - } pw.println(); } int transientViewCount = getTransientViewCount(); @@ -5615,45 +5469,6 @@ public class NotificationStackScrollLayout pw.append(" bottomRadius=").println(mBgCornerRadii[4]); } - private void dumpFooterViewVisibility(IndentingPrintWriter pw) { - FooterViewRefactor.assertInLegacyMode(); - final boolean showDismissView = shouldShowDismissView(); - - pw.println("showFooterView: " + shouldShowFooterView(showDismissView)); - DumpUtilsKt.withIncreasedIndent( - pw, - () -> { - pw.println("showDismissView: " + showDismissView); - DumpUtilsKt.withIncreasedIndent( - pw, - () -> { - pw.println( - "hasActiveClearableNotifications: " - + mController.hasActiveClearableNotifications( - ROWS_ALL)); - }); - pw.println(); - pw.println("showHistory: " + mController.isHistoryEnabled()); - pw.println(); - pw.println( - "visibleNotificationCount: " - + mController.getVisibleNotificationCount()); - pw.println("mIsCurrentUserSetup: " + mIsCurrentUserSetup); - pw.println("onKeyguard: " + onKeyguard()); - pw.println("mUpcomingStatusBarState: " + mUpcomingStatusBarState); - if (!SceneContainerFlag.isEnabled()) { - pw.println("QsExpansionFraction: " + getQsExpansionFraction()); - } - pw.println("mQsFullScreen: " + mQsFullScreen); - pw.println( - "mScreenOffAnimationController" - + ".shouldHideNotificationsFooter: " - + mScreenOffAnimationController - .shouldHideNotificationsFooter()); - pw.println("mIsRemoteInputActive: " + mIsRemoteInputActive); - }); - } - public boolean isFullyHidden() { return mAmbientState.isFullyHidden(); } @@ -5764,14 +5579,6 @@ public class NotificationStackScrollLayout clearNotifications(ROWS_GENTLE, closeShade, hideSilentSection); } - /** Legacy version of clearNotifications below. Uses the old data source for notif stats. */ - void clearNotifications(@SelectedRows int selection, boolean closeShade) { - FooterViewRefactor.assertInLegacyMode(); - final boolean hideSilentSection = !mController.hasNotifications( - ROWS_GENTLE, false /* clearable */); - clearNotifications(selection, closeShade, hideSilentSection); - } - /** * Collects a list of visible rows, and animates them away in a staggered fashion as if they * were dismissed. Notifications are dismissed in the backend via onClearAllAnimationsEnd. @@ -5826,25 +5633,6 @@ public class NotificationStackScrollLayout return canChildBeCleared(row) && matchesSelection(row, selection); } - /** - * Register a {@link View.OnClickListener} to be invoked when the Manage button is clicked. - */ - public void setManageButtonClickListener(@Nullable OnClickListener listener) { - FooterViewRefactor.assertInLegacyMode(); - mManageButtonClickListener = listener; - if (mFooterView != null) { - mFooterView.setManageButtonClickListener(mManageButtonClickListener); - } - } - - @VisibleForTesting - protected void inflateFooterView() { - FooterViewRefactor.assertInLegacyMode(); - FooterView footerView = (FooterView) LayoutInflater.from(mContext).inflate( - R.layout.status_bar_notification_footer, this, false); - setFooterView(footerView); - } - private void inflateEmptyShadeView() { ModesEmptyShadeFix.assertInLegacyMode(); @@ -6091,11 +5879,6 @@ public class NotificationStackScrollLayout mHighPriorityBeforeSpeedBump = highPriorityBeforeSpeedBump; } - void setFooterClearAllListener(FooterClearAllListener listener) { - FooterViewRefactor.assertInLegacyMode(); - mFooterClearAllListener = listener; - } - void setClearAllFinishedWhilePanelExpandedRunnable(Runnable runnable) { mClearAllFinishedWhilePanelExpandedRunnable = runnable; } @@ -6394,17 +6177,6 @@ public class NotificationStackScrollLayout } /** - * Sets whether the current user is set up, which is required to show the footer (b/193149550) - */ - public void setCurrentUserSetup(boolean isCurrentUserSetup) { - FooterViewRefactor.assertInLegacyMode(); - if (mIsCurrentUserSetup != isCurrentUserSetup) { - mIsCurrentUserSetup = isCurrentUserSetup; - updateFooter(); - } - } - - /** * Sets a {@link StackStateLogger} which is notified as the {@link StackStateAnimator} updates * the views. */ 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 a33a9ed2df75..b892bebb3120 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 @@ -29,11 +29,8 @@ import static com.android.systemui.statusbar.StatusBarState.KEYGUARD; import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.OnEmptySpaceClickListener; import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.OnOverscrollTopChangedListener; import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_ALL; -import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_GENTLE; -import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_HIGH_PRIORITY; import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.SelectedRows; import static com.android.systemui.statusbar.notification.stack.StackStateAnimator.ANIMATION_DURATION_STANDARD; -import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow; import android.animation.ObjectAnimator; import android.content.res.Configuration; @@ -64,14 +61,10 @@ import com.android.internal.view.OneShotPreDrawListener; import com.android.systemui.Dumpable; import com.android.systemui.ExpandHelper; 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.dump.DumpManager; -import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository; -import com.android.systemui.keyguard.shared.model.KeyguardState; -import com.android.systemui.keyguard.shared.model.TransitionStep; import com.android.systemui.media.controls.ui.controller.KeyguardMediaController; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.FalsingManager; @@ -92,18 +85,13 @@ import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.LockscreenShadeTransitionController; import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.NotificationLockscreenUserManager.UserChangedListener; -import com.android.systemui.statusbar.NotificationRemoteInputManager; import com.android.systemui.statusbar.NotificationShelf; import com.android.systemui.statusbar.RemoteInputController; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.notification.ColorUpdateLogger; import com.android.systemui.statusbar.notification.DynamicPrivacyController; -import com.android.systemui.statusbar.notification.headsup.HeadsUpNotificationViewControllerEmptyImpl; -import com.android.systemui.statusbar.notification.headsup.HeadsUpTouchHelper; -import com.android.systemui.statusbar.notification.headsup.HeadsUpTouchHelper.HeadsUpNotificationViewController; import com.android.systemui.statusbar.notification.LaunchAnimationParameters; -import com.android.systemui.statusbar.notification.NotificationActivityStarter; import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator; import com.android.systemui.statusbar.notification.collection.EntryWithDismissStats; import com.android.systemui.statusbar.notification.collection.NotifCollection; @@ -115,14 +103,15 @@ import com.android.systemui.statusbar.notification.collection.notifcollection.Di import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener; import com.android.systemui.statusbar.notification.collection.provider.NotificationDismissibilityProvider; import com.android.systemui.statusbar.notification.collection.provider.VisibilityLocationProviderDelegator; +import com.android.systemui.statusbar.notification.collection.render.DefaultNotifStackController; import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager; import com.android.systemui.statusbar.notification.collection.render.NotifStackController; -import com.android.systemui.statusbar.notification.collection.render.NotifStats; import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider; -import com.android.systemui.statusbar.notification.collection.render.SectionHeaderController; -import com.android.systemui.statusbar.notification.dagger.SilentHeader; -import com.android.systemui.statusbar.notification.domain.interactor.SeenNotificationsInteractor; -import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor; +import com.android.systemui.statusbar.notification.headsup.HeadsUpManager; +import com.android.systemui.statusbar.notification.headsup.HeadsUpNotificationViewControllerEmptyImpl; +import com.android.systemui.statusbar.notification.headsup.HeadsUpTouchHelper; +import com.android.systemui.statusbar.notification.headsup.HeadsUpTouchHelper.HeadsUpNotificationViewController; +import com.android.systemui.statusbar.notification.headsup.OnHeadsUpChangedListener; import com.android.systemui.statusbar.notification.init.NotificationsController; import com.android.systemui.statusbar.notification.logging.NotificationLogger; import com.android.systemui.statusbar.notification.row.ActivatableNotificationView; @@ -137,13 +126,8 @@ import com.android.systemui.statusbar.phone.HeadsUpAppearanceController; import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener; -import com.android.systemui.statusbar.policy.DeviceProvisionedController; -import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener; -import com.android.systemui.statusbar.notification.headsup.HeadsUpManager; -import com.android.systemui.statusbar.notification.headsup.OnHeadsUpChangedListener; import com.android.systemui.statusbar.policy.SensitiveNotificationProtectionController; import com.android.systemui.statusbar.policy.SplitShadeStateController; -import com.android.systemui.statusbar.policy.ZenModeController; import com.android.systemui.tuner.TunerService; import com.android.systemui.util.Compile; import com.android.systemui.util.settings.SecureSettings; @@ -179,10 +163,8 @@ public class NotificationStackScrollLayoutController implements Dumpable { private HeadsUpTouchHelper mHeadsUpTouchHelper; private final NotificationRoundnessManager mNotificationRoundnessManager; private final TunerService mTunerService; - private final DeviceProvisionedController mDeviceProvisionedController; private final DynamicPrivacyController mDynamicPrivacyController; private final ConfigurationController mConfigurationController; - private final ZenModeController mZenModeController; private final MetricsLogger mMetricsLogger; private final ColorUpdateLogger mColorUpdateLogger; @@ -193,7 +175,6 @@ public class NotificationStackScrollLayoutController implements Dumpable { private final NotifPipeline mNotifPipeline; private final NotifCollection mNotifCollection; private final UiEventLogger mUiEventLogger; - private final NotificationRemoteInputManager mRemoteInputManager; private final VisibilityLocationProviderDelegator mVisibilityLocationProviderDelegator; private final ShadeController mShadeController; private final Provider<WindowRootView> mWindowRootView; @@ -201,9 +182,7 @@ public class NotificationStackScrollLayoutController implements Dumpable { private final SysuiStatusBarStateController mStatusBarStateController; private final KeyguardBypassController mKeyguardBypassController; private final PowerInteractor mPowerInteractor; - private final PrimaryBouncerInteractor mPrimaryBouncerInteractor; private final NotificationLockscreenUserManager mLockscreenUserManager; - private final SectionHeaderController mSilentHeaderController; private final LockscreenShadeTransitionController mLockscreenShadeTransitionController; private final InteractionJankMonitor mJankMonitor; private final NotificationStackSizeCalculator mNotificationStackSizeCalculator; @@ -211,8 +190,6 @@ public class NotificationStackScrollLayoutController implements Dumpable { private final NotificationStackScrollLogger mLogger; private final GroupExpansionManager mGroupExpansionManager; - private final SeenNotificationsInteractor mSeenNotificationsInteractor; - private final KeyguardTransitionRepository mKeyguardTransitionRepo; private NotificationStackScrollLayout mView; private TouchHandler mTouchHandler; private NotificationSwipeHelper mSwipeHelper; @@ -220,7 +197,6 @@ public class NotificationStackScrollLayoutController implements Dumpable { private Boolean mHistoryEnabled; private int mBarState; private HeadsUpAppearanceController mHeadsUpAppearanceController; - private boolean mIsInTransitionToAod = false; private final NotificationTargetsHelper mNotificationTargetsHelper; private final SecureSettings mSecureSettings; @@ -235,11 +211,9 @@ public class NotificationStackScrollLayoutController implements Dumpable { private final NotificationListContainerImpl mNotificationListContainer = new NotificationListContainerImpl(); + // TODO: b/293167744 - Remove this. private final NotifStackController mNotifStackController = - new NotifStackControllerImpl(); - - @Nullable - private NotificationActivityStarter mNotificationActivityStarter; + new DefaultNotifStackController(); @VisibleForTesting final View.OnAttachStateChangeListener mOnAttachStateChangeListener = @@ -248,9 +222,6 @@ public class NotificationStackScrollLayoutController implements Dumpable { public void onViewAttachedToWindow(View v) { mColorUpdateLogger.logTriggerEvent("NSSLC.onViewAttachedToWindow()"); mConfigurationController.addCallback(mConfigurationListener); - if (!FooterViewRefactor.isEnabled()) { - mZenModeController.addCallback(mZenModeControllerCallback); - } final int newBarState = mStatusBarStateController.getState(); if (newBarState != mBarState) { mStateListener.onStateChanged(newBarState); @@ -264,9 +235,6 @@ public class NotificationStackScrollLayoutController implements Dumpable { public void onViewDetachedFromWindow(View v) { mColorUpdateLogger.logTriggerEvent("NSSLC.onViewDetachedFromWindow()"); mConfigurationController.removeCallback(mConfigurationListener); - if (!FooterViewRefactor.isEnabled()) { - mZenModeController.removeCallback(mZenModeControllerCallback); - } mStatusBarStateController.removeCallback(mStateListener); } }; @@ -287,28 +255,6 @@ public class NotificationStackScrollLayoutController implements Dumpable { @Nullable private ObjectAnimator mHideAlphaAnimator = null; - private final DeviceProvisionedListener mDeviceProvisionedListener = - new DeviceProvisionedListener() { - @Override - public void onDeviceProvisionedChanged() { - updateCurrentUserIsSetup(); - } - - @Override - public void onUserSwitched() { - updateCurrentUserIsSetup(); - } - - @Override - public void onUserSetupChanged() { - updateCurrentUserIsSetup(); - } - - private void updateCurrentUserIsSetup() { - mView.setCurrentUserSetup(mDeviceProvisionedController.isCurrentUserSetup()); - } - }; - private final Runnable mSensitiveStateChangedListener = new Runnable() { @Override public void run() { @@ -318,20 +264,10 @@ public class NotificationStackScrollLayoutController implements Dumpable { } }; - private final DynamicPrivacyController.Listener mDynamicPrivacyControllerListener = () -> { - if (!FooterViewRefactor.isEnabled()) { - // Let's update the footer once the notifications have been updated (in the next frame) - mView.post(this::updateFooter); - } - }; - @VisibleForTesting final ConfigurationListener mConfigurationListener = new ConfigurationListener() { @Override public void onDensityOrFontScaleChanged() { - if (!FooterViewRefactor.isEnabled()) { - updateShowEmptyShadeView(); - } mView.reinflateViews(); } @@ -351,10 +287,6 @@ public class NotificationStackScrollLayoutController implements Dumpable { mView.updateBgColor(); mView.updateDecorViews(); mView.reinflateViews(); - if (!FooterViewRefactor.isEnabled()) { - updateShowEmptyShadeView(); - updateFooter(); - } } @Override @@ -363,7 +295,6 @@ public class NotificationStackScrollLayoutController implements Dumpable { } }; - private NotifStats mNotifStats = NotifStats.getEmpty(); private float mMaxAlphaForKeyguard = 1.0f; private String mMaxAlphaForKeyguardSource = "constructor"; private float mMaxAlphaForUnhide = 1.0f; @@ -401,19 +332,9 @@ public class NotificationStackScrollLayoutController implements Dumpable { } @Override - public void onUpcomingStateChanged(int newState) { - if (!FooterViewRefactor.isEnabled()) { - mView.setUpcomingStatusBarState(newState); - } - } - - @Override public void onStatePostChange() { updateSensitivenessWithAnimation(mStatusBarStateController.goingToFullShade()); mView.onStatePostChange(mStatusBarStateController.fromShadeLocked()); - if (!FooterViewRefactor.isEnabled()) { - updateImportantForAccessibility(); - } } }; @@ -422,9 +343,6 @@ public class NotificationStackScrollLayoutController implements Dumpable { public void onUserChanged(int userId) { updateSensitivenessWithAnimation(false); mHistoryEnabled = null; - if (!FooterViewRefactor.isEnabled()) { - updateFooter(); - } } }; @@ -656,7 +574,7 @@ public class NotificationStackScrollLayoutController implements Dumpable { == null) { mHeadsUpManager.removeNotification( row.getEntry().getSbn().getKey(), - /* removeImmediately= */ true , + /* removeImmediately= */ true, /* reason= */ "onChildSnappedBack" ); } @@ -714,14 +632,6 @@ public class NotificationStackScrollLayoutController implements Dumpable { } }; - private final ZenModeController.Callback mZenModeControllerCallback = - new ZenModeController.Callback() { - @Override - public void onZenChanged(int zen) { - updateShowEmptyShadeView(); - } - }; - @Inject public NotificationStackScrollLayoutController( NotificationStackScrollLayout view, @@ -734,16 +644,12 @@ public class NotificationStackScrollLayoutController implements Dumpable { Provider<IStatusBarService> statusBarService, NotificationRoundnessManager notificationRoundnessManager, TunerService tunerService, - DeviceProvisionedController deviceProvisionedController, DynamicPrivacyController dynamicPrivacyController, @ShadeDisplayAware ConfigurationController configurationController, SysuiStatusBarStateController statusBarStateController, KeyguardMediaController keyguardMediaController, KeyguardBypassController keyguardBypassController, PowerInteractor powerInteractor, - PrimaryBouncerInteractor primaryBouncerInteractor, - KeyguardTransitionRepository keyguardTransitionRepo, - ZenModeController zenModeController, NotificationLockscreenUserManager lockscreenUserManager, MetricsLogger metricsLogger, ColorUpdateLogger colorUpdateLogger, @@ -752,14 +658,11 @@ public class NotificationStackScrollLayoutController implements Dumpable { FalsingManager falsingManager, NotificationSwipeHelper.Builder notificationSwipeHelperBuilder, GroupExpansionManager groupManager, - @SilentHeader SectionHeaderController silentHeaderController, NotifPipeline notifPipeline, NotifCollection notifCollection, LockscreenShadeTransitionController lockscreenShadeTransitionController, UiEventLogger uiEventLogger, - NotificationRemoteInputManager remoteInputManager, VisibilityLocationProviderDelegator visibilityLocationProviderDelegator, - SeenNotificationsInteractor seenNotificationsInteractor, NotificationListViewBinder viewBinder, ShadeController shadeController, Provider<WindowRootView> windowRootView, @@ -775,7 +678,6 @@ public class NotificationStackScrollLayoutController implements Dumpable { SensitiveNotificationProtectionController sensitiveNotificationProtectionController, WallpaperInteractor wallpaperInteractor) { mView = view; - mKeyguardTransitionRepo = keyguardTransitionRepo; mViewBinder = viewBinder; mStackStateLogger = stackLogger; mLogger = logger; @@ -795,15 +697,12 @@ public class NotificationStackScrollLayoutController implements Dumpable { } mNotificationRoundnessManager = notificationRoundnessManager; mTunerService = tunerService; - mDeviceProvisionedController = deviceProvisionedController; mDynamicPrivacyController = dynamicPrivacyController; mConfigurationController = configurationController; mStatusBarStateController = statusBarStateController; mKeyguardMediaController = keyguardMediaController; mKeyguardBypassController = keyguardBypassController; mPowerInteractor = powerInteractor; - mPrimaryBouncerInteractor = primaryBouncerInteractor; - mZenModeController = zenModeController; mLockscreenUserManager = lockscreenUserManager; mMetricsLogger = metricsLogger; mColorUpdateLogger = colorUpdateLogger; @@ -815,13 +714,10 @@ public class NotificationStackScrollLayoutController implements Dumpable { mJankMonitor = jankMonitor; mNotificationStackSizeCalculator = notificationStackSizeCalculator; mGroupExpansionManager = groupManager; - mSilentHeaderController = silentHeaderController; mNotifPipeline = notifPipeline; mNotifCollection = notifCollection; mUiEventLogger = uiEventLogger; - mRemoteInputManager = remoteInputManager; mVisibilityLocationProviderDelegator = visibilityLocationProviderDelegator; - mSeenNotificationsInteractor = seenNotificationsInteractor; mShadeController = shadeController; mWindowRootView = windowRootView; mNotificationTargetsHelper = notificationTargetsHelper; @@ -850,18 +746,7 @@ public class NotificationStackScrollLayoutController implements Dumpable { mView.setClearAllAnimationListener(this::onAnimationEnd); mView.setClearAllListener((selection) -> mUiEventLogger.log( NotificationPanelEvent.fromSelection(selection))); - if (!FooterViewRefactor.isEnabled()) { - mView.setFooterClearAllListener(() -> - mMetricsLogger.action(MetricsEvent.ACTION_DISMISS_ALL_NOTES)); - mView.setIsRemoteInputActive(mRemoteInputManager.isRemoteInputActive()); - mRemoteInputManager.addControllerCallback(new RemoteInputController.Callback() { - @Override - public void onRemoteInputActive(boolean active) { - mView.setIsRemoteInputActive(active); - } - }); - } - mView.setClearAllFinishedWhilePanelExpandedRunnable(()-> { + mView.setClearAllFinishedWhilePanelExpandedRunnable(() -> { final Runnable doCollapseRunnable = () -> mShadeController.animateCollapseShade(CommandQueue.FLAG_EXCLUDE_NONE); mView.postDelayed(doCollapseRunnable, /* delayMillis = */ DELAY_BEFORE_SHADE_CLOSE); @@ -889,19 +774,11 @@ public class NotificationStackScrollLayoutController implements Dumpable { mView.setKeyguardBypassEnabled(mKeyguardBypassController.getBypassEnabled()); mKeyguardBypassController .registerOnBypassStateChangedListener(mView::setKeyguardBypassEnabled); - if (!FooterViewRefactor.isEnabled()) { - mView.setManageButtonClickListener(v -> { - if (mNotificationActivityStarter != null) { - mNotificationActivityStarter.startHistoryIntent(v, mView.isHistoryShown()); - } - }); - } if (!SceneContainerFlag.isEnabled()) { mHeadsUpManager.addListener(mOnHeadsUpChangedListener); } mHeadsUpManager.setAnimationStateHandler(mView::setHeadsUpGoingAwayAnimationsAllowed); - mDynamicPrivacyController.addListener(mDynamicPrivacyControllerListener); mLockscreenShadeTransitionController.setStackScroller(this); @@ -914,9 +791,6 @@ public class NotificationStackScrollLayoutController implements Dumpable { switch (key) { case Settings.Secure.NOTIFICATION_HISTORY_ENABLED: mHistoryEnabled = null; // invalidate - if (!FooterViewRefactor.isEnabled()) { - updateFooter(); - } break; case HIGH_PRIORITY: mView.setHighPriorityBeforeSpeedBump("1".equals(newValue)); @@ -938,12 +812,6 @@ public class NotificationStackScrollLayoutController implements Dumpable { return kotlin.Unit.INSTANCE; }); - if (!FooterViewRefactor.isEnabled()) { - // attach callback, and then call it to update mView immediately - mDeviceProvisionedController.addCallback(mDeviceProvisionedListener); - mDeviceProvisionedListener.onDeviceProvisionedChanged(); - } - if (screenshareNotificationHiding()) { mSensitiveNotificationProtectionController .registerSensitiveStateListener(mSensitiveStateChangedListener); @@ -953,20 +821,12 @@ public class NotificationStackScrollLayoutController implements Dumpable { mOnAttachStateChangeListener.onViewAttachedToWindow(mView); } mView.addOnAttachStateChangeListener(mOnAttachStateChangeListener); - if (!FooterViewRefactor.isEnabled()) { - mSilentHeaderController.setOnClearSectionClickListener(v -> clearSilentNotifications()); - } mGroupExpansionManager.registerGroupExpansionChangeListener( (changedRow, expanded) -> mView.onGroupExpandChanged(changedRow, expanded)); mViewBinder.bindWhileAttached(mView, this); - if (!FooterViewRefactor.isEnabled()) { - collectFlow(mView, mKeyguardTransitionRepo.getTransitions(), - this::onKeyguardTransitionChanged); - } - mView.setWallpaperInteractor(mWallpaperInteractor); } @@ -1168,11 +1028,6 @@ public class NotificationStackScrollLayoutController implements Dumpable { return mView != null && mView.isAddOrRemoveAnimationPending(); } - public int getVisibleNotificationCount() { - FooterViewRefactor.assertInLegacyMode(); - return mNotifStats.getNumActiveNotifs(); - } - public boolean isHistoryEnabled() { Boolean historyEnabled = mHistoryEnabled; if (historyEnabled == null) { @@ -1284,9 +1139,6 @@ public class NotificationStackScrollLayoutController implements Dumpable { public void setQsFullScreen(boolean fullScreen) { mView.setQsFullScreen(fullScreen); - if (!FooterViewRefactor.isEnabled()) { - updateShowEmptyShadeView(); - } } public void setScrollingEnabled(boolean enabled) { @@ -1464,64 +1316,12 @@ public class NotificationStackScrollLayoutController implements Dumpable { } /** - * Set the visibility of the view, and propagate it to specific children. + * Set the visibility of the view. * * @param visible either the view is visible or not. */ public void updateVisibility(boolean visible) { mView.setVisibility(visible ? View.VISIBLE : View.INVISIBLE); - - // Refactor note: the empty shade's visibility doesn't seem to actually depend on the - // parent visibility (so this update seemingly doesn't do anything). Therefore, this is not - // modeled in the refactored code. - if (!FooterViewRefactor.isEnabled() && mView.getVisibility() == View.VISIBLE) { - // Synchronize EmptyShadeView visibility with the parent container. - updateShowEmptyShadeView(); - updateImportantForAccessibility(); - } - } - - /** - * Update whether we should show the empty shade view ("no notifications" in the shade). - * <p> - * When in split mode, notifications are always visible regardless of the state of the - * QuickSettings panel. That being the case, empty view is always shown if the other conditions - * are true. - */ - public void updateShowEmptyShadeView() { - FooterViewRefactor.assertInLegacyMode(); - - Trace.beginSection("NSSLC.updateShowEmptyShadeView"); - - final boolean shouldShow = getVisibleNotificationCount() == 0 - && !mView.isQsFullScreen() - // Hide empty shade view when in transition to AOD. - // That avoids "No Notifications" to blink when transitioning to AOD. - // For more details, see: b/228790482 - && !mIsInTransitionToAod - // Don't show any notification content if the bouncer is showing. See b/267060171. - && !mPrimaryBouncerInteractor.isBouncerShowing(); - - mView.updateEmptyShadeView(shouldShow, mZenModeController.areNotificationsHiddenInShade()); - - Trace.endSection(); - } - - /** - * Update the importantForAccessibility of NotificationStackScrollLayout. - * <p> - * We want the NSSL to be unimportant for accessibility when there's no - * notifications in it while the device is on lock screen, to avoid unlablel NSSL view. - * Otherwise, we want it to be important for accessibility to enable accessibility - * auto-scrolling in NSSL. - */ - public void updateImportantForAccessibility() { - FooterViewRefactor.assertInLegacyMode(); - if (getVisibleNotificationCount() == 0 && mView.onKeyguard()) { - mView.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO); - } else { - mView.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES); - } } public boolean isShowingEmptyShadeView() { @@ -1577,34 +1377,6 @@ public class NotificationStackScrollLayoutController implements Dumpable { mView.setPulsing(pulsing, animatePulse); } - /** - * Return whether there are any clearable notifications - */ - public boolean hasActiveClearableNotifications(@SelectedRows int selection) { - FooterViewRefactor.assertInLegacyMode(); - return hasNotifications(selection, true /* clearable */); - } - - public boolean hasNotifications(@SelectedRows int selection, boolean isClearable) { - FooterViewRefactor.assertInLegacyMode(); - boolean hasAlertingMatchingClearable = isClearable - ? mNotifStats.getHasClearableAlertingNotifs() - : mNotifStats.getHasNonClearableAlertingNotifs(); - boolean hasSilentMatchingClearable = isClearable - ? mNotifStats.getHasClearableSilentNotifs() - : mNotifStats.getHasNonClearableSilentNotifs(); - switch (selection) { - case ROWS_GENTLE: - return hasSilentMatchingClearable; - case ROWS_HIGH_PRIORITY: - return hasAlertingMatchingClearable; - case ROWS_ALL: - return hasSilentMatchingClearable || hasAlertingMatchingClearable; - default: - throw new IllegalStateException("Bad selection: " + selection); - } - } - /** Sets whether the NSSL is displayed over the unoccluded Lockscreen. */ public void setOnLockscreen(boolean isOnLockscreen) { if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return; @@ -1637,9 +1409,6 @@ public class NotificationStackScrollLayoutController implements Dumpable { } mHeadsUpManager.setRemoteInputActive(entry, remoteInputActive); entry.notifyHeightChanged(true /* needsAnimation */); - if (!FooterViewRefactor.isEnabled()) { - updateFooter(); - } } public void lockScrollTo(NotificationEntry entry) { @@ -1662,13 +1431,6 @@ public class NotificationStackScrollLayoutController implements Dumpable { }; } - public void updateFooter() { - FooterViewRefactor.assertInLegacyMode(); - Trace.beginSection("NSSLC.updateFooter"); - mView.updateFooter(); - Trace.endSection(); - } - public void onUpdateRowStates() { mView.onUpdateRowStates(); } @@ -1695,18 +1457,10 @@ public class NotificationStackScrollLayoutController implements Dumpable { return mView.getTransientViewCount(); } - public View getTransientView(int i) { - return mView.getTransientView(i); - } - public NotificationStackScrollLayout getView() { return mView; } - public float calculateGapHeight(ExpandableView previousView, ExpandableView child, int count) { - return mView.calculateGapHeight(previousView, child, count); - } - NotificationRoundnessManager getNotificationRoundnessManager() { return mNotificationRoundnessManager; } @@ -1772,13 +1526,6 @@ public class NotificationStackScrollLayoutController implements Dumpable { return NotificationSwipeHelper.isTouchInView(event, view); } - public void clearSilentNotifications() { - FooterViewRefactor.assertInLegacyMode(); - // Leave the shade open if there will be other notifs left over to clear - final boolean closeShade = !hasActiveClearableNotifications(ROWS_HIGH_PRIORITY); - mView.clearNotifications(ROWS_GENTLE, closeShade); - } - private void onAnimationEnd(List<ExpandableNotificationRow> viewsToRemove, @SelectedRows int selectedRows) { if (selectedRows == ROWS_ALL) { @@ -1880,10 +1627,6 @@ public class NotificationStackScrollLayoutController implements Dumpable { mView.animateNextTopPaddingChange(); } - public void setNotificationActivityStarter(NotificationActivityStarter activityStarter) { - mNotificationActivityStarter = activityStarter; - } - public NotificationTargetsHelper getNotificationTargetsHelper() { return mNotificationTargetsHelper; } @@ -1898,18 +1641,6 @@ public class NotificationStackScrollLayoutController implements Dumpable { } @VisibleForTesting - void onKeyguardTransitionChanged(TransitionStep transitionStep) { - FooterViewRefactor.assertInLegacyMode(); - boolean isTransitionToAod = transitionStep.getTo().equals(KeyguardState.AOD) - && (transitionStep.getFrom().equals(KeyguardState.GONE) - || transitionStep.getFrom().equals(KeyguardState.OCCLUDED)); - if (mIsInTransitionToAod != isTransitionToAod) { - mIsInTransitionToAod = isTransitionToAod; - updateShowEmptyShadeView(); - } - } - - @VisibleForTesting TouchHandler getTouchHandler() { return mTouchHandler; } @@ -2288,22 +2019,4 @@ public class NotificationStackScrollLayoutController implements Dumpable { && !mSwipeHelper.isSwiping(); } } - - private class NotifStackControllerImpl implements NotifStackController { - @Override - public void setNotifStats(@NonNull NotifStats notifStats) { - FooterViewRefactor.assertInLegacyMode(); - mNotifStats = notifStats; - - if (!FooterViewRefactor.isEnabled()) { - mView.setHasFilteredOutSeenNotifications( - mSeenNotificationsInteractor - .getHasFilteredOutSeenNotifications().getValue()); - - updateFooter(); - updateShowEmptyShadeView(); - updateImportantForAccessibility(); - } - } - } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java index 1653029dc994..06b989aaab57 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java @@ -35,7 +35,6 @@ import com.android.systemui.shade.transition.LargeScreenShadeInterpolator; import com.android.systemui.statusbar.NotificationShelf; import com.android.systemui.statusbar.notification.SourceType; import com.android.systemui.statusbar.notification.emptyshade.ui.view.EmptyShadeView; -import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor; import com.android.systemui.statusbar.notification.footer.ui.view.FooterView; import com.android.systemui.statusbar.notification.row.ActivatableNotificationView; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; @@ -463,26 +462,23 @@ public class StackScrollAlgorithm { if (v == ambientState.getShelf()) { continue; } - if (FooterViewRefactor.isEnabled()) { - if (v instanceof EmptyShadeView) { - emptyShadeVisible = true; - } - if (v instanceof FooterView footerView) { - if (emptyShadeVisible || notGoneIndex == 0) { - // if the empty shade is visible or the footer is the first visible - // view, we're in a transitory state so let's leave the footer alone. - if (Flags.notificationsFooterVisibilityFix() - && !SceneContainerFlag.isEnabled()) { - // ...except for the hidden state, to prevent it from flashing on - // the screen (this piece is copied from updateChild, and is not - // necessary in flexiglass). - if (footerView.shouldBeHidden() - || !ambientState.isShadeExpanded()) { - footerView.getViewState().hidden = true; - } + if (v instanceof EmptyShadeView) { + emptyShadeVisible = true; + } + if (v instanceof FooterView footerView) { + if (emptyShadeVisible || notGoneIndex == 0) { + // if the empty shade is visible or the footer is the first visible + // view, we're in a transitory state so let's leave the footer alone. + if (Flags.notificationsFooterVisibilityFix() + && !SceneContainerFlag.isEnabled()) { + // ...except for the hidden state, to prevent it from flashing on + // the screen (this piece is copied from updateChild, and is not + // necessary in flexiglass). + if (footerView.shouldBeHidden() || !ambientState.isShadeExpanded()) { + footerView.getViewState().hidden = true; } - continue; } + continue; } } @@ -699,44 +695,28 @@ public class StackScrollAlgorithm { viewEnd, /* hunMax */ ambientState.getMaxHeadsUpTranslation() ); if (view instanceof FooterView) { - if (FooterViewRefactor.isEnabled()) { - if (SceneContainerFlag.isEnabled()) { - final float footerEnd = - stackTop + viewState.getYTranslation() + view.getIntrinsicHeight(); - final boolean noSpaceForFooter = footerEnd > ambientState.getStackCutoff(); - ((FooterView.FooterViewState) viewState).hideContent = - noSpaceForFooter || (ambientState.isClearAllInProgress() - && !hasNonClearableNotifs(algorithmState)); - } else { - // TODO(b/333445519): shouldBeHidden should reflect whether the shade is closed - // already, so we shouldn't need to use ambientState here. However, - // currently it doesn't get updated quickly enough and can cause the footer to - // flash when closing the shade. As such, we temporarily also check the - // ambientState directly. - if (((FooterView) view).shouldBeHidden() || !ambientState.isShadeExpanded()) { - viewState.hidden = true; - } else { - final float footerEnd = algorithmState.mCurrentExpandedYPosition - + view.getIntrinsicHeight(); - final boolean noSpaceForFooter = - footerEnd > ambientState.getStackEndHeight(); - ((FooterView.FooterViewState) viewState).hideContent = - noSpaceForFooter || (ambientState.isClearAllInProgress() - && !hasNonClearableNotifs(algorithmState)); - } - } + if (SceneContainerFlag.isEnabled()) { + final float footerEnd = + stackTop + viewState.getYTranslation() + view.getIntrinsicHeight(); + final boolean noSpaceForFooter = footerEnd > ambientState.getStackCutoff(); + ((FooterView.FooterViewState) viewState).hideContent = + noSpaceForFooter || (ambientState.isClearAllInProgress() + && !hasNonClearableNotifs(algorithmState)); } else { - final boolean shadeClosed = !ambientState.isShadeExpanded(); - final boolean isShelfShowing = algorithmState.firstViewInShelf != null; - if (shadeClosed) { + // TODO(b/333445519): shouldBeHidden should reflect whether the shade is closed + // already, so we shouldn't need to use ambientState here. However, + // currently it doesn't get updated quickly enough and can cause the footer to + // flash when closing the shade. As such, we temporarily also check the + // ambientState directly. + if (((FooterView) view).shouldBeHidden() || !ambientState.isShadeExpanded()) { viewState.hidden = true; } else { final float footerEnd = algorithmState.mCurrentExpandedYPosition + view.getIntrinsicHeight(); - final boolean noSpaceForFooter = footerEnd > ambientState.getStackEndHeight(); + final boolean noSpaceForFooter = + footerEnd > ambientState.getStackEndHeight(); ((FooterView.FooterViewState) viewState).hideContent = - isShelfShowing || noSpaceForFooter - || (ambientState.isClearAllInProgress() + noSpaceForFooter || (ambientState.isClearAllInProgress() && !hasNonClearableNotifs(algorithmState)); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt index b4561686b7b2..1d7e658932ac 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt @@ -40,7 +40,6 @@ import com.android.systemui.statusbar.notification.emptyshade.shared.ModesEmptyS import com.android.systemui.statusbar.notification.emptyshade.ui.view.EmptyShadeView import com.android.systemui.statusbar.notification.emptyshade.ui.viewbinder.EmptyShadeViewBinder import com.android.systemui.statusbar.notification.emptyshade.ui.viewmodel.EmptyShadeViewModel -import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor import com.android.systemui.statusbar.notification.footer.shared.NotifRedesignFooter import com.android.systemui.statusbar.notification.footer.ui.view.FooterView import com.android.systemui.statusbar.notification.footer.ui.viewbinder.FooterViewBinder @@ -108,25 +107,20 @@ constructor( launch { bindShelf(shelf) } bindHideList(viewController, viewModel, hiderTracker) - if (FooterViewRefactor.isEnabled) { - val hasNonClearableSilentNotifications: StateFlow<Boolean> = - viewModel.hasNonClearableSilentNotifications.stateIn(this) - launch { reinflateAndBindFooter(view, hasNonClearableSilentNotifications) } - launch { - if (ModesEmptyShadeFix.isEnabled) { - reinflateAndBindEmptyShade(view) - } else { - bindEmptyShadeLegacy(viewModel.emptyShadeViewFactory.create(), view) - } + val hasNonClearableSilentNotifications: StateFlow<Boolean> = + viewModel.hasNonClearableSilentNotifications.stateIn(this) + launch { reinflateAndBindFooter(view, hasNonClearableSilentNotifications) } + launch { + if (ModesEmptyShadeFix.isEnabled) { + reinflateAndBindEmptyShade(view) + } else { + bindEmptyShadeLegacy(viewModel.emptyShadeViewFactory.create(), view) } - launch { - bindSilentHeaderClickListener(view, hasNonClearableSilentNotifications) - } - launch { - viewModel.isImportantForAccessibility.collect { isImportantForAccessibility - -> - view.setImportantForAccessibilityYesNo(isImportantForAccessibility) - } + } + launch { bindSilentHeaderClickListener(view, hasNonClearableSilentNotifications) } + launch { + viewModel.isImportantForAccessibility.collect { isImportantForAccessibility -> + view.setImportantForAccessibilityYesNo(isImportantForAccessibility) } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt index ea714608ea66..0b2b84e60f4b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt @@ -28,7 +28,6 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor import com.android.systemui.keyguard.ui.viewmodel.ViewStateAccessor import com.android.systemui.lifecycle.repeatWhenAttached import com.android.systemui.scene.shared.flag.SceneContainerFlag -import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController import com.android.systemui.statusbar.notification.stack.NotificationStackSizeCalculator import com.android.systemui.statusbar.notification.stack.ui.view.SharedNotificationContainer @@ -81,9 +80,6 @@ constructor( controller.setOverExpansion(0f) controller.setOverScrollAmount(0) - if (!FooterViewRefactor.isEnabled) { - controller.updateFooter() - } } } } 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 38390e7bdb39..fcc671a5bae6 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 @@ -25,7 +25,6 @@ import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotif import com.android.systemui.statusbar.notification.domain.interactor.HeadsUpNotificationInteractor import com.android.systemui.statusbar.notification.emptyshade.shared.ModesEmptyShadeFix import com.android.systemui.statusbar.notification.emptyshade.ui.viewmodel.EmptyShadeViewModel -import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor import com.android.systemui.statusbar.notification.footer.ui.viewmodel.FooterViewModel import com.android.systemui.statusbar.notification.shared.HeadsUpRowKey import com.android.systemui.statusbar.notification.shelf.ui.viewmodel.NotificationShelfViewModel @@ -75,46 +74,37 @@ constructor( * we want it to be important for accessibility to enable accessibility auto-scrolling in NSSL. * See b/242235264 for more details. */ - val isImportantForAccessibility: Flow<Boolean> by lazy { - if (FooterViewRefactor.isUnexpectedlyInLegacyMode()) { - flowOf(true) - } else { - combine( - activeNotificationsInteractor.areAnyNotificationsPresent, - notificationStackInteractor.isShowingOnLockscreen, - ) { hasNotifications, isShowingOnLockscreen -> - hasNotifications || !isShowingOnLockscreen - } - .distinctUntilChanged() - .dumpWhileCollecting("isImportantForAccessibility") - .flowOn(bgDispatcher) - } - } + val isImportantForAccessibility: Flow<Boolean> = + combine( + activeNotificationsInteractor.areAnyNotificationsPresent, + notificationStackInteractor.isShowingOnLockscreen, + ) { hasNotifications, isShowingOnLockscreen -> + hasNotifications || !isShowingOnLockscreen + } + .distinctUntilChanged() + .dumpWhileCollecting("isImportantForAccessibility") + .flowOn(bgDispatcher) val shouldShowEmptyShadeView: Flow<Boolean> by lazy { ModesEmptyShadeFix.assertInLegacyMode() - if (FooterViewRefactor.isUnexpectedlyInLegacyMode()) { - flowOf(false) - } else { - combine( - activeNotificationsInteractor.areAnyNotificationsPresent, - shadeInteractor.isQsFullscreen, - notificationStackInteractor.isShowingOnLockscreen, - ) { hasNotifications, isQsFullScreen, isShowingOnLockscreen -> - when { - hasNotifications -> false - isQsFullScreen -> false - // Do not show the empty shade if the lockscreen is visible (including AOD - // b/228790482 and bouncer b/267060171), except if the shade is opened on - // top. - isShowingOnLockscreen -> false - else -> true - } + combine( + activeNotificationsInteractor.areAnyNotificationsPresent, + shadeInteractor.isQsFullscreen, + notificationStackInteractor.isShowingOnLockscreen, + ) { hasNotifications, isQsFullScreen, isShowingOnLockscreen -> + when { + hasNotifications -> false + isQsFullScreen -> false + // Do not show the empty shade if the lockscreen is visible (including AOD + // b/228790482 and bouncer b/267060171), except if the shade is opened on + // top. + isShowingOnLockscreen -> false + else -> true } - .distinctUntilChanged() - .dumpWhileCollecting("shouldShowEmptyShadeView") - .flowOn(bgDispatcher) - } + } + .distinctUntilChanged() + .dumpWhileCollecting("shouldShowEmptyShadeView") + .flowOn(bgDispatcher) } val shouldShowEmptyShadeViewAnimated: Flow<AnimatedValue<Boolean>> by lazy { @@ -164,18 +154,14 @@ constructor( */ val shouldHideFooterView: Flow<Boolean> by lazy { SceneContainerFlag.assertInLegacyMode() - if (FooterViewRefactor.isUnexpectedlyInLegacyMode()) { - flowOf(false) - } else { - // When the shade is closed, the footer is still present in the list, but not visible. - // This prevents the footer from being shown when a HUN is present, while still allowing - // the footer to be counted as part of the shade for measurements. - shadeInteractor.shadeExpansion - .map { it == 0f } - .distinctUntilChanged() - .dumpWhileCollecting("shouldHideFooterView") - .flowOn(bgDispatcher) - } + // When the shade is closed, the footer is still present in the list, but not visible. + // This prevents the footer from being shown when a HUN is present, while still allowing + // the footer to be counted as part of the shade for measurements. + shadeInteractor.shadeExpansion + .map { it == 0f } + .distinctUntilChanged() + .dumpWhileCollecting("shouldHideFooterView") + .flowOn(bgDispatcher) } /** @@ -188,68 +174,64 @@ constructor( */ val shouldIncludeFooterView: Flow<AnimatedValue<Boolean>> by lazy { SceneContainerFlag.assertInLegacyMode() - if (FooterViewRefactor.isUnexpectedlyInLegacyMode()) { - flowOf(AnimatedValue.NotAnimating(false)) - } else { - combine( - activeNotificationsInteractor.areAnyNotificationsPresent, - userSetupInteractor.isUserSetUp, - notificationStackInteractor.isShowingOnLockscreen, - shadeInteractor.isQsFullscreen, - remoteInputInteractor.isRemoteInputActive, - ) { - hasNotifications, - isUserSetUp, - isShowingOnLockscreen, - qsFullScreen, - isRemoteInputActive -> - when { - !hasNotifications -> VisibilityChange.DISAPPEAR_WITH_ANIMATION - // Hide the footer until the user setup is complete, to prevent access - // to settings (b/193149550). - !isUserSetUp -> VisibilityChange.DISAPPEAR_WITH_ANIMATION - // Do not show the footer if the lockscreen is visible (incl. AOD), - // except if the shade is opened on top. See also b/219680200. - // Do not animate, as that makes the footer appear briefly when - // transitioning between the shade and keyguard. - isShowingOnLockscreen -> VisibilityChange.DISAPPEAR_WITHOUT_ANIMATION - // Do not show the footer if quick settings are fully expanded (except - // for the foldable split shade view). See b/201427195 && b/222699879. - qsFullScreen -> VisibilityChange.DISAPPEAR_WITH_ANIMATION - // Hide the footer if remote input is active (i.e. user is replying to a - // notification). See b/75984847. - isRemoteInputActive -> VisibilityChange.DISAPPEAR_WITH_ANIMATION - else -> VisibilityChange.APPEAR_WITH_ANIMATION - } + combine( + activeNotificationsInteractor.areAnyNotificationsPresent, + userSetupInteractor.isUserSetUp, + notificationStackInteractor.isShowingOnLockscreen, + shadeInteractor.isQsFullscreen, + remoteInputInteractor.isRemoteInputActive, + ) { + hasNotifications, + isUserSetUp, + isShowingOnLockscreen, + qsFullScreen, + isRemoteInputActive -> + when { + !hasNotifications -> VisibilityChange.DISAPPEAR_WITH_ANIMATION + // Hide the footer until the user setup is complete, to prevent access + // to settings (b/193149550). + !isUserSetUp -> VisibilityChange.DISAPPEAR_WITH_ANIMATION + // Do not show the footer if the lockscreen is visible (incl. AOD), + // except if the shade is opened on top. See also b/219680200. + // Do not animate, as that makes the footer appear briefly when + // transitioning between the shade and keyguard. + isShowingOnLockscreen -> VisibilityChange.DISAPPEAR_WITHOUT_ANIMATION + // Do not show the footer if quick settings are fully expanded (except + // for the foldable split shade view). See b/201427195 && b/222699879. + qsFullScreen -> VisibilityChange.DISAPPEAR_WITH_ANIMATION + // Hide the footer if remote input is active (i.e. user is replying to a + // notification). See b/75984847. + isRemoteInputActive -> VisibilityChange.DISAPPEAR_WITH_ANIMATION + else -> VisibilityChange.APPEAR_WITH_ANIMATION } - .distinctUntilChanged( - // Equivalent unless visibility changes - areEquivalent = { a: VisibilityChange, b: VisibilityChange -> - a.visible == b.visible - } - ) - // Should we animate the visibility change? - .sample( - // TODO(b/322167853): This check is currently duplicated in FooterViewModel, - // but instead it should be a field in ShadeAnimationInteractor. - combine( - shadeInteractor.isShadeFullyExpanded, - shadeInteractor.isShadeTouchable, - ::Pair, - ) - .onStart { emit(Pair(false, false)) } - ) { visibilityChange, (isShadeFullyExpanded, animationsEnabled) -> - // Animate if the shade is interactive, but NOT on the lockscreen. Having - // animations enabled while on the lockscreen makes the footer appear briefly - // when transitioning between the shade and keyguard. - val shouldAnimate = - isShadeFullyExpanded && animationsEnabled && visibilityChange.canAnimate - AnimatableEvent(visibilityChange.visible, shouldAnimate) + } + .distinctUntilChanged( + // Equivalent unless visibility changes + areEquivalent = { a: VisibilityChange, b: VisibilityChange -> + a.visible == b.visible } - .toAnimatedValueFlow() - .dumpWhileCollecting("shouldIncludeFooterView") - .flowOn(bgDispatcher) - } + ) + // Should we animate the visibility change? + .sample( + // TODO(b/322167853): This check is currently duplicated in FooterViewModel, + // but instead it should be a field in ShadeAnimationInteractor. + combine( + shadeInteractor.isShadeFullyExpanded, + shadeInteractor.isShadeTouchable, + ::Pair, + ) + .onStart { emit(Pair(false, false)) } + ) { visibilityChange, (isShadeFullyExpanded, animationsEnabled) -> + // Animate if the shade is interactive, but NOT on the lockscreen. Having + // animations enabled while on the lockscreen makes the footer appear briefly + // when transitioning between the shade and keyguard. + val shouldAnimate = + isShadeFullyExpanded && animationsEnabled && visibilityChange.canAnimate + AnimatableEvent(visibilityChange.visible, shouldAnimate) + } + .toAnimatedValueFlow() + .dumpWhileCollecting("shouldIncludeFooterView") + .flowOn(bgDispatcher) } // This flow replaces shouldHideFooterView+shouldIncludeFooterView in flexiglass. @@ -328,25 +310,15 @@ constructor( APPEAR_WITH_ANIMATION(visible = true, canAnimate = true), } - val hasClearableAlertingNotifications: Flow<Boolean> by lazy { - if (FooterViewRefactor.isUnexpectedlyInLegacyMode()) { - flowOf(false) - } else { - activeNotificationsInteractor.hasClearableAlertingNotifications.dumpWhileCollecting( - "hasClearableAlertingNotifications" - ) - } - } + val hasClearableAlertingNotifications: Flow<Boolean> = + activeNotificationsInteractor.hasClearableAlertingNotifications.dumpWhileCollecting( + "hasClearableAlertingNotifications" + ) - val hasNonClearableSilentNotifications: Flow<Boolean> by lazy { - if (FooterViewRefactor.isUnexpectedlyInLegacyMode()) { - flowOf(false) - } else { - activeNotificationsInteractor.hasNonClearableSilentNotifications.dumpWhileCollecting( - "hasNonClearableSilentNotifications" - ) - } - } + val hasNonClearableSilentNotifications: Flow<Boolean> = + activeNotificationsInteractor.hasNonClearableSilentNotifications.dumpWhileCollecting( + "hasNonClearableSilentNotifications" + ) val topHeadsUpRow: Flow<HeadsUpRowKey?> by lazy { if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) { 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 1474789ea0e3..3d6cd7e49dfe 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java @@ -1487,8 +1487,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { mActivityTransitionAnimator.setCallback(mActivityTransitionAnimatorCallback); mActivityTransitionAnimator.addListener(mActivityTransitionAnimatorListener); mRemoteInputManager.addControllerCallback(mNotificationShadeWindowController); - mStackScrollerController.setNotificationActivityStarter( - mNotificationActivityStarterLazy.get()); mGutsManager.setNotificationActivityStarter(mNotificationActivityStarterLazy.get()); mShadeController.setNotificationPresenter(mPresenterLazy.get()); mNotificationsController.initialize( diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java index 3749b96199f6..8443edd6aa87 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -18,7 +18,6 @@ package com.android.systemui.statusbar.phone; import static android.view.WindowInsets.Type.navigationBars; -import static com.android.systemui.Flags.predictiveBackAnimateBouncer; import static com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants.EXPANSION_HIDDEN; import static com.android.systemui.plugins.ActivityStarter.OnDismissAction; import static com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK; @@ -328,7 +327,6 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb private float mQsExpansion; final Set<KeyguardViewManagerCallback> mCallbacks = new HashSet<>(); - private boolean mIsBackAnimationEnabled; private final UdfpsOverlayInteractor mUdfpsOverlayInteractor; private final ActivityStarter mActivityStarter; @@ -434,7 +432,6 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb .map(SysUIUnfoldComponent::getFoldAodAnimationController).orElse(null); mAlternateBouncerInteractor = alternateBouncerInteractor; mBouncerInteractor = bouncerInteractor; - mIsBackAnimationEnabled = predictiveBackAnimateBouncer(); mUdfpsOverlayInteractor = udfpsOverlayInteractor; mActivityStarter = activityStarter; mKeyguardTransitionInteractor = keyguardTransitionInteractor; @@ -630,7 +627,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb private boolean shouldPlayBackAnimation() { // Suppress back animation when bouncer shouldn't be dismissed on back invocation. - return !needsFullscreenBouncer() && mIsBackAnimationEnabled; + return !needsFullscreenBouncer(); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java index 03324d2a3e6a..c47ed1722bb4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java @@ -16,8 +16,6 @@ package com.android.systemui.statusbar.phone; -import static com.android.systemui.Flags.predictiveBackAnimateDialogs; - import android.app.AlertDialog; import android.app.Dialog; import android.content.BroadcastReceiver; @@ -285,15 +283,13 @@ public class SystemUIDialog extends AlertDialog implements ViewRootImpl.ConfigCh for (int i = 0; i < mOnCreateRunnables.size(); i++) { mOnCreateRunnables.get(i).run(); } - if (predictiveBackAnimateDialogs()) { - View targetView = getWindow().getDecorView(); - DialogKt.registerAnimationOnBackInvoked( - /* dialog = */ this, - /* targetView = */ targetView, - /* backAnimationSpec= */mDelegate.getBackAnimationSpec( - () -> targetView.getResources().getDisplayMetrics()) - ); - } + View targetView = getWindow().getDecorView(); + DialogKt.registerAnimationOnBackInvoked( + /* dialog = */ this, + /* targetView = */ targetView, + /* backAnimationSpec= */mDelegate.getBackAnimationSpec( + () -> targetView.getResources().getDisplayMetrics()) + ); } private void updateWindowSize() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractor.kt index 12ed647fdee7..fdc2d8d96f9b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractor.kt @@ -16,8 +16,8 @@ package com.android.systemui.statusbar.policy.domain.interactor -import android.app.NotificationManager.INTERRUPTION_FILTER_NONE import android.content.Context +import android.media.AudioManager import android.provider.Settings import android.provider.Settings.Secure.ZEN_DURATION_FOREVER import android.provider.Settings.Secure.ZEN_DURATION_PROMPT @@ -29,6 +29,7 @@ import com.android.settingslib.notification.data.repository.ZenModeRepository import com.android.settingslib.notification.modes.ZenIcon import com.android.settingslib.notification.modes.ZenIconLoader import com.android.settingslib.notification.modes.ZenMode +import com.android.settingslib.volume.shared.model.AudioStream import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.modes.shared.ModesUi import com.android.systemui.shared.notifications.data.repository.NotificationSettingsRepository @@ -67,6 +68,17 @@ constructor( deviceProvisioningRepository: DeviceProvisioningRepository, userSetupRepository: UserSetupRepository, ) { + /** + * List of predicates to determine if the [ZenMode] blocks an audio stream. Typical use case + * would be: `zenModeByStreamPredicates[stream](zenMode)` + */ + private val zenModeByStreamPredicates = + mapOf<Int, (ZenMode) -> Boolean>( + AudioManager.STREAM_MUSIC to { it.policy.priorityCategoryMedia == STATE_DISALLOW }, + AudioManager.STREAM_ALARM to { it.policy.priorityCategoryAlarms == STATE_DISALLOW }, + AudioManager.STREAM_SYSTEM to { it.policy.priorityCategorySystem == STATE_DISALLOW }, + ) + val isZenAvailable: Flow<Boolean> = combine( deviceProvisioningRepository.isDeviceProvisioned, @@ -125,21 +137,16 @@ constructor( .flowOn(bgDispatcher) .distinctUntilChanged() - val activeModesBlockingEverything: Flow<ActiveZenModes> = getFilteredActiveModesFlow { mode -> - mode.interruptionFilter == INTERRUPTION_FILTER_NONE - } - - val activeModesBlockingMedia: Flow<ActiveZenModes> = getFilteredActiveModesFlow { mode -> - mode.policy.priorityCategoryMedia == STATE_DISALLOW - } - - val activeModesBlockingAlarms: Flow<ActiveZenModes> = getFilteredActiveModesFlow { mode -> - mode.policy.priorityCategoryAlarms == STATE_DISALLOW - } + fun canBeBlockedByZenMode(stream: AudioStream): Boolean = + zenModeByStreamPredicates.containsKey(stream.value) - private fun getFilteredActiveModesFlow(predicate: (ZenMode) -> Boolean): Flow<ActiveZenModes> { + fun activeModesBlockingStream(stream: AudioStream): Flow<ActiveZenModes> { + val isBlockingStream = zenModeByStreamPredicates[stream.value] + require(isBlockingStream != null) { + "$stream is unsupported. Use canBeBlockedByZenMode to check if the stream can be affected by the Zen Mode." + } return modes - .map { modes -> modes.filter { mode -> predicate(mode) } } + .map { modes -> modes.filter { isBlockingStream(it) } } .map { modes -> buildActiveZenModes(modes) } .flowOn(bgDispatcher) .distinctUntilChanged() @@ -194,7 +201,6 @@ constructor( ) null } - ZEN_DURATION_FOREVER -> null else -> Duration.ofMinutes(zenDuration.toLong()) } diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/BackGestureTutorialScreen.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/BackGestureTutorialScreen.kt index ae32b7a6175c..bce55cbdcc4a 100644 --- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/BackGestureTutorialScreen.kt +++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/BackGestureTutorialScreen.kt @@ -50,7 +50,7 @@ fun BackGestureTutorialScreen( ) GestureTutorialScreen( screenConfig = screenConfig, - gestureUiStateFlow = viewModel.gestureUiState, + tutorialStateFlow = viewModel.tutorialState, motionEventConsumer = { easterEggGestureViewModel.accept(it) viewModel.handleEvent(it) diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/GestureTutorialScreen.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/GestureTutorialScreen.kt index 73c54af595d9..284e23e5a288 100644 --- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/GestureTutorialScreen.kt +++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/GestureTutorialScreen.kt @@ -18,7 +18,6 @@ package com.android.systemui.touchpad.tutorial.ui.composable import android.view.MotionEvent import androidx.activity.compose.BackHandler -import androidx.annotation.RawRes import androidx.compose.animation.core.Animatable import androidx.compose.animation.core.tween import androidx.compose.foundation.layout.Box @@ -27,77 +26,21 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.input.pointer.pointerInteropFilter import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.android.systemui.inputdevice.tutorial.ui.composable.ActionTutorialContent import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState +import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.NotStarted import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialScreenConfig -import com.android.systemui.touchpad.tutorial.ui.composable.GestureUiState.Finished -import com.android.systemui.touchpad.tutorial.ui.composable.GestureUiState.NotStarted -import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState import kotlinx.coroutines.flow.Flow -sealed interface GestureUiState { - data object NotStarted : GestureUiState - - data class Finished(@RawRes val successAnimation: Int) : GestureUiState - - data class InProgress( - val progress: Float = 0f, - val progressStartMarker: String, - val progressEndMarker: String, - ) : GestureUiState - - data object Error : GestureUiState -} - -fun GestureState.toGestureUiState( - progressStartMarker: String, - progressEndMarker: String, - successAnimation: Int, -): GestureUiState { - return when (this) { - GestureState.NotStarted -> NotStarted - is GestureState.InProgress -> - GestureUiState.InProgress(this.progress, progressStartMarker, progressEndMarker) - is GestureState.Finished -> GestureUiState.Finished(successAnimation) - GestureState.Error -> GestureUiState.Error - } -} - -fun GestureUiState.toTutorialActionState(previousState: TutorialActionState): TutorialActionState { - return when (this) { - NotStarted -> TutorialActionState.NotStarted - is GestureUiState.InProgress -> { - val inProgress = - TutorialActionState.InProgress( - progress = progress, - startMarker = progressStartMarker, - endMarker = progressEndMarker, - ) - if ( - previousState is TutorialActionState.InProgressAfterError || - previousState is TutorialActionState.Error - ) { - return TutorialActionState.InProgressAfterError(inProgress) - } else { - return inProgress - } - } - is Finished -> TutorialActionState.Finished(successAnimation) - GestureUiState.Error -> TutorialActionState.Error - } -} - @Composable fun GestureTutorialScreen( screenConfig: TutorialScreenConfig, - gestureUiStateFlow: Flow<GestureUiState>, + tutorialStateFlow: Flow<TutorialActionState>, motionEventConsumer: (MotionEvent) -> Boolean, easterEggTriggeredFlow: Flow<Boolean>, onEasterEggFinished: () -> Unit, @@ -106,25 +49,21 @@ fun GestureTutorialScreen( ) { BackHandler(onBack = onBack) val easterEggTriggered by easterEggTriggeredFlow.collectAsStateWithLifecycle(false) - val gestureState by gestureUiStateFlow.collectAsStateWithLifecycle(NotStarted) + val tutorialState by tutorialStateFlow.collectAsStateWithLifecycle(NotStarted) TouchpadGesturesHandlingBox( motionEventConsumer, - gestureState, + tutorialState, easterEggTriggered, onEasterEggFinished, ) { - var lastState: TutorialActionState by remember { - mutableStateOf(TutorialActionState.NotStarted) - } - lastState = gestureState.toTutorialActionState(lastState) - ActionTutorialContent(lastState, onDoneButtonClicked, screenConfig) + ActionTutorialContent(tutorialState, onDoneButtonClicked, screenConfig) } } @Composable private fun TouchpadGesturesHandlingBox( motionEventConsumer: (MotionEvent) -> Boolean, - gestureState: GestureUiState, + tutorialState: TutorialActionState, easterEggTriggered: Boolean, onEasterEggFinished: () -> Unit, modifier: Modifier = Modifier, @@ -150,7 +89,7 @@ private fun TouchpadGesturesHandlingBox( .pointerInteropFilter( onTouchEvent = { event -> // FINISHED is the final state so we don't need to process touches anymore - if (gestureState is Finished) { + if (tutorialState is TutorialActionState.Finished) { false } else { motionEventConsumer(event) diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/HomeGestureTutorialScreen.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/HomeGestureTutorialScreen.kt index 4f1f40dc4c05..4acdb6070200 100644 --- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/HomeGestureTutorialScreen.kt +++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/HomeGestureTutorialScreen.kt @@ -49,7 +49,7 @@ fun HomeGestureTutorialScreen( ) GestureTutorialScreen( screenConfig = screenConfig, - gestureUiStateFlow = viewModel.gestureUiState, + tutorialStateFlow = viewModel.tutorialState, motionEventConsumer = { easterEggGestureViewModel.accept(it) viewModel.handleEvent(it) diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/RecentAppsGestureTutorialScreen.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/RecentAppsGestureTutorialScreen.kt index 6c9e26c4b7ea..8dd53a7fb815 100644 --- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/RecentAppsGestureTutorialScreen.kt +++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/RecentAppsGestureTutorialScreen.kt @@ -50,7 +50,7 @@ fun RecentAppsGestureTutorialScreen( ) GestureTutorialScreen( screenConfig = screenConfig, - gestureUiStateFlow = viewModel.gestureUiState, + tutorialStateFlow = viewModel.tutorialState, motionEventConsumer = { easterEggGestureViewModel.accept(it) viewModel.handleEvent(it) diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/BackGestureScreenViewModel.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/BackGestureScreenViewModel.kt index 8e53669a7841..7a3d4d1ba88a 100644 --- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/BackGestureScreenViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/BackGestureScreenViewModel.kt @@ -17,12 +17,12 @@ package com.android.systemui.touchpad.tutorial.ui.viewmodel import android.view.MotionEvent +import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState import com.android.systemui.res.R -import com.android.systemui.touchpad.tutorial.ui.composable.GestureUiState -import com.android.systemui.touchpad.tutorial.ui.composable.toGestureUiState import com.android.systemui.touchpad.tutorial.ui.gesture.GestureDirection import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.InProgress +import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.NotStarted import com.android.systemui.touchpad.tutorial.ui.gesture.handleTouchpadMotionEvent import com.android.systemui.util.kotlin.pairwiseBy import kotlinx.coroutines.flow.Flow @@ -30,21 +30,26 @@ import kotlinx.coroutines.flow.Flow class BackGestureScreenViewModel(val gestureRecognizer: GestureRecognizerAdapter) : TouchpadTutorialScreenViewModel { - override val gestureUiState: Flow<GestureUiState> = - gestureRecognizer.gestureState.pairwiseBy(GestureState.NotStarted) { previous, current -> - toGestureUiState(current, previous) - } + override val tutorialState: Flow<TutorialActionState> = + gestureRecognizer.gestureState + .pairwiseBy(NotStarted) { previous, current -> + current to toAnimationProperties(current, previous) + } + .mapToTutorialState() override fun handleEvent(event: MotionEvent): Boolean { return gestureRecognizer.handleTouchpadMotionEvent(event) } - private fun toGestureUiState(current: GestureState, previous: GestureState): GestureUiState { + private fun toAnimationProperties( + current: GestureState, + previous: GestureState, + ): TutorialAnimationProperties { val (startMarker, endMarker) = if (current is InProgress && current.direction == GestureDirection.LEFT) { "gesture to L" to "end progress L" } else "gesture to R" to "end progress R" - return current.toGestureUiState( + return TutorialAnimationProperties( progressStartMarker = startMarker, progressEndMarker = endMarker, successAnimation = successAnimation(previous), diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/HomeGestureScreenViewModel.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/HomeGestureScreenViewModel.kt index 9d6f568fa1b1..c75d44f01e8c 100644 --- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/HomeGestureScreenViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/HomeGestureScreenViewModel.kt @@ -17,9 +17,8 @@ package com.android.systemui.touchpad.tutorial.ui.viewmodel import android.view.MotionEvent +import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState import com.android.systemui.res.R -import com.android.systemui.touchpad.tutorial.ui.composable.GestureUiState -import com.android.systemui.touchpad.tutorial.ui.composable.toGestureUiState import com.android.systemui.touchpad.tutorial.ui.gesture.handleTouchpadMotionEvent import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map @@ -27,14 +26,17 @@ import kotlinx.coroutines.flow.map class HomeGestureScreenViewModel(private val gestureRecognizer: GestureRecognizerAdapter) : TouchpadTutorialScreenViewModel { - override val gestureUiState: Flow<GestureUiState> = - gestureRecognizer.gestureState.map { - it.toGestureUiState( - progressStartMarker = "drag with gesture", - progressEndMarker = "release playback realtime", - successAnimation = R.raw.trackpad_home_success, - ) - } + override val tutorialState: Flow<TutorialActionState> = + gestureRecognizer.gestureState + .map { + it to + TutorialAnimationProperties( + progressStartMarker = "drag with gesture", + progressEndMarker = "release playback realtime", + successAnimation = R.raw.trackpad_home_success, + ) + } + .mapToTutorialState() override fun handleEvent(event: MotionEvent): Boolean { return gestureRecognizer.handleTouchpadMotionEvent(event) diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/RecentAppsGestureScreenViewModel.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/RecentAppsGestureScreenViewModel.kt index 97528583277f..9fab5f3641a4 100644 --- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/RecentAppsGestureScreenViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/RecentAppsGestureScreenViewModel.kt @@ -17,9 +17,8 @@ package com.android.systemui.touchpad.tutorial.ui.viewmodel import android.view.MotionEvent +import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState import com.android.systemui.res.R -import com.android.systemui.touchpad.tutorial.ui.composable.GestureUiState -import com.android.systemui.touchpad.tutorial.ui.composable.toGestureUiState import com.android.systemui.touchpad.tutorial.ui.gesture.handleTouchpadMotionEvent import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map @@ -27,14 +26,17 @@ import kotlinx.coroutines.flow.map class RecentAppsGestureScreenViewModel(private val gestureRecognizer: GestureRecognizerAdapter) : TouchpadTutorialScreenViewModel { - override val gestureUiState: Flow<GestureUiState> = - gestureRecognizer.gestureState.map { - it.toGestureUiState( - progressStartMarker = "drag with gesture", - progressEndMarker = "onPause", - successAnimation = R.raw.trackpad_recent_apps_success, - ) - } + override val tutorialState: Flow<TutorialActionState> = + gestureRecognizer.gestureState + .map { + it to + TutorialAnimationProperties( + progressStartMarker = "drag with gesture", + progressEndMarker = "onPause", + successAnimation = R.raw.trackpad_recent_apps_success, + ) + } + .mapToTutorialState() override fun handleEvent(event: MotionEvent): Boolean { return gestureRecognizer.handleTouchpadMotionEvent(event) diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/TouchpadTutorialScreenViewModel.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/TouchpadTutorialScreenViewModel.kt index 31e953d6643c..3b6e3c76cdeb 100644 --- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/TouchpadTutorialScreenViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/TouchpadTutorialScreenViewModel.kt @@ -17,11 +17,62 @@ package com.android.systemui.touchpad.tutorial.ui.viewmodel import android.view.MotionEvent -import com.android.systemui.touchpad.tutorial.ui.composable.GestureUiState +import androidx.annotation.RawRes +import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState +import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState +import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.Finished +import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.InProgress +import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.NotStarted import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flow interface TouchpadTutorialScreenViewModel { - val gestureUiState: Flow<GestureUiState> + val tutorialState: Flow<TutorialActionState> fun handleEvent(event: MotionEvent): Boolean } + +data class TutorialAnimationProperties( + val progressStartMarker: String, + val progressEndMarker: String, + @RawRes val successAnimation: Int, +) + +fun Flow<Pair<GestureState, TutorialAnimationProperties>>.mapToTutorialState(): + Flow<TutorialActionState> { + return flow<TutorialActionState> { + var lastState: TutorialActionState = TutorialActionState.NotStarted + collect { (gestureState, animationProperties) -> + val newState = gestureState.toTutorialActionState(animationProperties, lastState) + lastState = newState + emit(newState) + } + } +} + +fun GestureState.toTutorialActionState( + properties: TutorialAnimationProperties, + previousState: TutorialActionState, +): TutorialActionState { + return when (this) { + NotStarted -> TutorialActionState.NotStarted + is InProgress -> { + val inProgress = + TutorialActionState.InProgress( + progress = progress, + startMarker = properties.progressStartMarker, + endMarker = properties.progressEndMarker, + ) + if ( + previousState is TutorialActionState.InProgressAfterError || + previousState is TutorialActionState.Error + ) { + TutorialActionState.InProgressAfterError(inProgress) + } else { + inProgress + } + } + is Finished -> TutorialActionState.Finished(properties.successAnimation) + GestureState.Error -> TutorialActionState.Error + } +} diff --git a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTraceLogger.kt b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTraceLogger.kt index 65970978b4ec..7d3966b98782 100644 --- a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTraceLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTraceLogger.kt @@ -17,8 +17,9 @@ package com.android.systemui.unfold import android.content.Context import android.hardware.devicestate.DeviceStateManager -import android.os.Trace import com.android.app.tracing.TraceStateLogger +import com.android.app.tracing.coroutines.TrackTracer +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.systemui.CoreStartable import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application @@ -29,7 +30,6 @@ import com.android.systemui.util.Utils.isDeviceFoldable import javax.inject.Inject import kotlin.coroutines.CoroutineContext import kotlinx.coroutines.CoroutineScope -import com.android.app.tracing.coroutines.launchTraced as launch import kotlinx.coroutines.plus /** @@ -45,7 +45,7 @@ constructor( @Application applicationScope: CoroutineScope, @Background private val coroutineContext: CoroutineContext, private val deviceStateRepository: DeviceStateRepository, - private val deviceStateManager: DeviceStateManager + private val deviceStateManager: DeviceStateManager, ) : CoreStartable { private val isFoldable: Boolean = isDeviceFoldable(context.resources, deviceStateManager) @@ -61,7 +61,7 @@ constructor( bgScope.launch { foldStateRepository.hingeAngle.collect { - Trace.traceCounter(Trace.TRACE_TAG_APP, "hingeAngle", it.toInt()) + TrackTracer.instantForGroup("unfold", "hingeAngle", it.toInt()) } } bgScope.launch { diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogCallbacksInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogCallbacksInteractor.kt index fa1088426351..3b0c8a6b46f8 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogCallbacksInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogCallbacksInteractor.kt @@ -46,7 +46,7 @@ class VolumeDialogCallbacksInteractor constructor( private val volumeDialogController: VolumeDialogController, @VolumeDialogPlugin private val coroutineScope: CoroutineScope, - @Background private val bgHandler: Handler, + @Background private val bgHandler: Handler?, ) { @SuppressLint("SharedFlowCreation") // event-bus needed diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/dagger/VolumeDialogSliderComponent.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/dagger/VolumeDialogSliderComponent.kt index 88af210b6a36..940c79c78d76 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/dagger/VolumeDialogSliderComponent.kt +++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/dagger/VolumeDialogSliderComponent.kt @@ -19,7 +19,6 @@ package com.android.systemui.volume.dialog.sliders.dagger import com.android.systemui.volume.dialog.sliders.domain.model.VolumeDialogSliderType import com.android.systemui.volume.dialog.sliders.ui.VolumeDialogOverscrollViewBinder import com.android.systemui.volume.dialog.sliders.ui.VolumeDialogSliderHapticsViewBinder -import com.android.systemui.volume.dialog.sliders.ui.VolumeDialogSliderTouchesViewBinder import com.android.systemui.volume.dialog.sliders.ui.VolumeDialogSliderViewBinder import dagger.BindsInstance import dagger.Subcomponent @@ -34,8 +33,6 @@ interface VolumeDialogSliderComponent { fun sliderViewBinder(): VolumeDialogSliderViewBinder - fun sliderTouchesViewBinder(): VolumeDialogSliderTouchesViewBinder - fun sliderHapticsViewBinder(): VolumeDialogSliderHapticsViewBinder fun overscrollViewBinder(): VolumeDialogOverscrollViewBinder diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderTouchesViewBinder.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderTouchesViewBinder.kt deleted file mode 100644 index 4ecac7a81893..000000000000 --- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderTouchesViewBinder.kt +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2024 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.volume.dialog.sliders.ui - -import android.annotation.SuppressLint -import android.view.View -import com.android.systemui.res.R -import com.android.systemui.volume.dialog.sliders.dagger.VolumeDialogSliderScope -import com.android.systemui.volume.dialog.sliders.ui.viewmodel.VolumeDialogSliderInputEventsViewModel -import com.google.android.material.slider.Slider -import javax.inject.Inject - -@VolumeDialogSliderScope -class VolumeDialogSliderTouchesViewBinder -@Inject -constructor(private val viewModel: VolumeDialogSliderInputEventsViewModel) { - - @SuppressLint("ClickableViewAccessibility") - fun bind(view: View) { - with(view.requireViewById<Slider>(R.id.volume_dialog_slider)) { - setOnTouchListener { _, event -> - viewModel.onTouchEvent(event) - false - } - } - } -} diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt index 67ffb0602860..ccd16ac5a331 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt @@ -23,6 +23,7 @@ import androidx.dynamicanimation.animation.SpringAnimation import androidx.dynamicanimation.animation.SpringForce import com.android.systemui.res.R import com.android.systemui.volume.dialog.sliders.dagger.VolumeDialogSliderScope +import com.android.systemui.volume.dialog.sliders.ui.viewmodel.VolumeDialogSliderInputEventsViewModel import com.android.systemui.volume.dialog.sliders.ui.viewmodel.VolumeDialogSliderStateModel import com.android.systemui.volume.dialog.sliders.ui.viewmodel.VolumeDialogSliderViewModel import com.google.android.material.slider.Slider @@ -35,7 +36,10 @@ import kotlinx.coroutines.flow.onEach @VolumeDialogSliderScope class VolumeDialogSliderViewBinder @Inject -constructor(private val viewModel: VolumeDialogSliderViewModel) { +constructor( + private val viewModel: VolumeDialogSliderViewModel, + private val inputViewModel: VolumeDialogSliderInputEventsViewModel, +) { private val sliderValueProperty = object : FloatPropertyCompat<Slider>("value") { @@ -51,12 +55,16 @@ constructor(private val viewModel: VolumeDialogSliderViewModel) { dampingRatio = SpringForce.DAMPING_RATIO_NO_BOUNCY } + @SuppressLint("ClickableViewAccessibility") fun CoroutineScope.bind(view: View) { var isInitialUpdate = true val sliderView: Slider = view.requireViewById(R.id.volume_dialog_slider) val animation = SpringAnimation(sliderView, sliderValueProperty) animation.spring = springForce - + sliderView.setOnTouchListener { _, event -> + inputViewModel.onTouchEvent(event) + false + } sliderView.addOnChangeListener { _, value, fromUser -> viewModel.setStreamVolume(value.roundToInt(), fromUser) } @@ -82,7 +90,7 @@ constructor(private val viewModel: VolumeDialogSliderViewModel) { // coerce the current value to the new value range before animating it. This prevents // animating from the value that is outside of current [valueFrom, valueTo]. value = value.coerceIn(valueFrom, valueTo) - setTrackIconActiveStart(model.iconRes) + trackIconActiveStart = model.icon if (isInitialUpdate) { value = model.value } else { diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSlidersViewBinder.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSlidersViewBinder.kt index f066b56e7de0..75d427acc05b 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSlidersViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSlidersViewBinder.kt @@ -71,7 +71,6 @@ constructor(private val viewModel: VolumeDialogSlidersViewModel) { viewsToAnimate: Array<View>, ) { with(component.sliderViewBinder()) { bind(sliderContainer) } - with(component.sliderTouchesViewBinder()) { bind(sliderContainer) } with(component.sliderHapticsViewBinder()) { bind(sliderContainer) } with(component.overscrollViewBinder()) { bind(sliderContainer, viewsToAnimate) } } diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderIconProvider.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderIconProvider.kt index 5c39b6f9359c..daf4c8275d20 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderIconProvider.kt +++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderIconProvider.kt @@ -16,13 +16,16 @@ package com.android.systemui.volume.dialog.sliders.ui.viewmodel +import android.annotation.SuppressLint +import android.content.Context +import android.graphics.drawable.Drawable import android.media.AudioManager import androidx.annotation.DrawableRes -import com.android.settingslib.notification.domain.interactor.NotificationsSoundPolicyInteractor import com.android.settingslib.volume.domain.interactor.AudioVolumeInteractor import com.android.settingslib.volume.shared.model.AudioStream import com.android.settingslib.volume.shared.model.RingerMode import com.android.systemui.res.R +import com.android.systemui.statusbar.policy.domain.interactor.ZenModeInteractor import javax.inject.Inject import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine @@ -31,11 +34,12 @@ import kotlinx.coroutines.flow.flowOf class VolumeDialogSliderIconProvider @Inject constructor( - private val notificationsSoundPolicyInteractor: NotificationsSoundPolicyInteractor, + private val context: Context, + private val zenModeInteractor: ZenModeInteractor, private val audioVolumeInteractor: AudioVolumeInteractor, ) { - @DrawableRes + @SuppressLint("UseCompatLoadingForDrawables") fun getStreamIcon( stream: Int, level: Int, @@ -43,54 +47,71 @@ constructor( levelMax: Int, isMuted: Boolean, isRoutedToBluetooth: Boolean, - ): Flow<Int> { + ): Flow<Drawable> { return combine( - notificationsSoundPolicyInteractor.isZenMuted(AudioStream(stream)), + zenModeInteractor.activeModesBlockingStream(AudioStream(stream)), ringerModeForStream(stream), - ) { isZenMuted, ringerMode -> - val isStreamOffline = level == 0 || isMuted - if (isZenMuted) { - // TODO(b/372466264) use icon for the corresponding zenmode - return@combine com.android.internal.R.drawable.ic_qs_dnd - } - when (ringerMode?.value) { - AudioManager.RINGER_MODE_VIBRATE -> - return@combine R.drawable.ic_volume_ringer_vibrate - AudioManager.RINGER_MODE_SILENT -> return@combine R.drawable.ic_ring_volume_off - } - if (isRoutedToBluetooth) { - return@combine if (stream == AudioManager.STREAM_VOICE_CALL) { - R.drawable.ic_volume_bt_sco - } else { - if (isStreamOffline) { - R.drawable.ic_volume_media_bt_mute - } else { - R.drawable.ic_volume_media_bt - } - } + ) { activeModesBlockingStream, ringerMode -> + if (activeModesBlockingStream.mainMode?.icon != null) { + return@combine activeModesBlockingStream.mainMode.icon.drawable + } else { + context.getDrawable( + getIconRes( + stream, + level, + levelMin, + levelMax, + isMuted, + isRoutedToBluetooth, + ringerMode, + ) + )!! } + } + } - return@combine if (isStreamOffline) { - getMutedIconForStream(stream) ?: getIconForStream(stream) + @DrawableRes + private fun getIconRes( + stream: Int, + level: Int, + levelMin: Int, + levelMax: Int, + isMuted: Boolean, + isRoutedToBluetooth: Boolean, + ringerMode: RingerMode?, + ): Int { + val isStreamOffline = level == 0 || isMuted + when (ringerMode?.value) { + AudioManager.RINGER_MODE_VIBRATE -> return R.drawable.ic_volume_ringer_vibrate + AudioManager.RINGER_MODE_SILENT -> return R.drawable.ic_ring_volume_off + } + if (isRoutedToBluetooth) { + return if (stream == AudioManager.STREAM_VOICE_CALL) { + R.drawable.ic_volume_bt_sco } else { - if (level < (levelMax + levelMin) / 2) { - // This icon is different on TV - R.drawable.ic_volume_media_low + if (isStreamOffline) { + R.drawable.ic_volume_media_bt_mute } else { - getIconForStream(stream) + R.drawable.ic_volume_media_bt } } } - } - @DrawableRes - private fun getMutedIconForStream(stream: Int): Int? { - return when (stream) { - AudioManager.STREAM_MUSIC -> R.drawable.ic_volume_media_mute - AudioManager.STREAM_NOTIFICATION -> R.drawable.ic_volume_ringer_mute - AudioManager.STREAM_ALARM -> R.drawable.ic_volume_alarm_mute - AudioManager.STREAM_SYSTEM -> R.drawable.ic_volume_system_mute - else -> null + return if (isStreamOffline) { + when (stream) { + AudioManager.STREAM_MUSIC -> R.drawable.ic_volume_media_mute + AudioManager.STREAM_NOTIFICATION -> R.drawable.ic_volume_ringer_mute + AudioManager.STREAM_ALARM -> R.drawable.ic_volume_alarm_mute + AudioManager.STREAM_SYSTEM -> R.drawable.ic_volume_system_mute + else -> null + } ?: getIconForStream(stream) + } else { + if (level < (levelMax + levelMin) / 2) { + // This icon is different on TV + R.drawable.ic_volume_media_low + } else { + getIconForStream(stream) + } } } diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderStateModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderStateModel.kt index 5750c049082f..8df9e788905c 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderStateModel.kt +++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderStateModel.kt @@ -16,21 +16,21 @@ package com.android.systemui.volume.dialog.sliders.ui.viewmodel -import androidx.annotation.DrawableRes +import android.graphics.drawable.Drawable import com.android.systemui.volume.dialog.shared.model.VolumeDialogStreamModel data class VolumeDialogSliderStateModel( val minValue: Float, val maxValue: Float, val value: Float, - @DrawableRes val iconRes: Int, + val icon: Drawable, ) -fun VolumeDialogStreamModel.toStateModel(@DrawableRes iconRes: Int): VolumeDialogSliderStateModel { +fun VolumeDialogStreamModel.toStateModel(icon: Drawable): VolumeDialogSliderStateModel { return VolumeDialogSliderStateModel( minValue = levelMin.toFloat(), value = level.toFloat(), maxValue = levelMax.toFloat(), - iconRes = iconRes, + icon = icon, ) } diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderViewModel.kt index 6d8457be1014..06d9426fbe04 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderViewModel.kt @@ -66,7 +66,8 @@ constructor( private val model: Flow<VolumeDialogStreamModel> = interactor.slider .filter { - val lastVolumeUpdateTime = userVolumeUpdates.value?.timestampMillis ?: 0 + val currentVolumeUpdate = userVolumeUpdates.value ?: return@filter true + val lastVolumeUpdateTime = currentVolumeUpdate.timestampMillis getTimestampMillis() - lastVolumeUpdateTime > VOLUME_UPDATE_GRACE_PERIOD } .stateIn(coroutineScope, SharingStarted.Eagerly, null) diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/VolumeDialogResources.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/VolumeDialogResources.kt deleted file mode 100644 index e5cf62b91677..000000000000 --- a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/VolumeDialogResources.kt +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (C) 2024 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.volume.dialog.ui - -import android.content.Context -import android.content.res.Resources -import com.android.systemui.dagger.qualifiers.UiBackground -import com.android.systemui.res.R -import com.android.systemui.statusbar.policy.ConfigurationController -import com.android.systemui.statusbar.policy.onConfigChanged -import com.android.systemui.volume.dialog.dagger.scope.VolumeDialog -import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogScope -import javax.inject.Inject -import kotlin.coroutines.CoroutineContext -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.SharingStarted -import kotlinx.coroutines.flow.filterNotNull -import kotlinx.coroutines.flow.flowOn -import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.onStart -import kotlinx.coroutines.flow.stateIn - -/** - * Provides cached resources [Flow]s that update when the configuration changes. - * - * Consume or use [kotlinx.coroutines.flow.first] to get the value. - */ -@VolumeDialogScope -class VolumeDialogResources -@Inject -constructor( - @VolumeDialog private val coroutineScope: CoroutineScope, - @UiBackground private val uiBackgroundContext: CoroutineContext, - private val context: Context, - private val configurationController: ConfigurationController, -) { - - val dialogShowDurationMillis: Flow<Long> = configurationResource { - getInteger(R.integer.config_dialogShowAnimationDurationMs).toLong() - } - - val dialogHideDurationMillis: Flow<Long> = configurationResource { - getInteger(R.integer.config_dialogHideAnimationDurationMs).toLong() - } - - private fun <T> configurationResource(get: Resources.() -> T): Flow<T> = - configurationController.onConfigChanged - .map { context.resources.get() } - .onStart { emit(context.resources.get()) } - .flowOn(uiBackgroundContext) - .stateIn(coroutineScope, SharingStarted.Eagerly, null) - .filterNotNull() -} diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/binder/VolumeDialogViewBinder.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/binder/VolumeDialogViewBinder.kt index a3166a9978f4..1da2491c68b6 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/binder/VolumeDialogViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/binder/VolumeDialogViewBinder.kt @@ -17,6 +17,7 @@ package com.android.systemui.volume.dialog.ui.binder import android.app.Dialog +import android.content.res.Resources import android.graphics.Rect import android.graphics.Region import android.view.View @@ -25,6 +26,7 @@ import android.view.ViewTreeObserver import android.view.ViewTreeObserver.InternalInsetsInfo import androidx.constraintlayout.motion.widget.MotionLayout import com.android.internal.view.RotationPolicy +import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.res.R import com.android.systemui.util.children import com.android.systemui.volume.SystemUIInterpolators @@ -33,7 +35,6 @@ import com.android.systemui.volume.dialog.ringer.ui.binder.VolumeDialogRingerVie import com.android.systemui.volume.dialog.settings.ui.binder.VolumeDialogSettingsButtonViewBinder import com.android.systemui.volume.dialog.shared.model.VolumeDialogVisibilityModel import com.android.systemui.volume.dialog.sliders.ui.VolumeDialogSlidersViewBinder -import com.android.systemui.volume.dialog.ui.VolumeDialogResources import com.android.systemui.volume.dialog.ui.utils.JankListenerFactory import com.android.systemui.volume.dialog.ui.utils.suspendAnimate import com.android.systemui.volume.dialog.ui.viewmodel.VolumeDialogViewModel @@ -42,7 +43,6 @@ import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.mapLatest import kotlinx.coroutines.flow.onEach @@ -56,7 +56,7 @@ import kotlinx.coroutines.suspendCancellableCoroutine class VolumeDialogViewBinder @Inject constructor( - private val volumeResources: VolumeDialogResources, + @Main resources: Resources, private val viewModel: VolumeDialogViewModel, private val jankListenerFactory: JankListenerFactory, private val tracer: VolumeTracer, @@ -65,6 +65,11 @@ constructor( private val settingsButtonViewBinder: VolumeDialogSettingsButtonViewBinder, ) { + private val dialogShowAnimationDurationMs = + resources.getInteger(R.integer.config_dialogShowAnimationDurationMs).toLong() + private val dialogHideAnimationDurationMs = + resources.getInteger(R.integer.config_dialogHideAnimationDurationMs).toLong() + fun CoroutineScope.bind(dialog: Dialog) { // Root view of the Volume Dialog. val root: MotionLayout = dialog.requireViewById(R.id.volume_dialog_root) @@ -99,12 +104,12 @@ constructor( is VolumeDialogVisibilityModel.Visible -> { tracer.traceVisibilityEnd(it) calculateTranslationX(view)?.let(view::setTranslationX) - view.animateShow(volumeResources.dialogShowDurationMillis.first()) + view.animateShow(dialogShowAnimationDurationMs) } is VolumeDialogVisibilityModel.Dismissed -> { tracer.traceVisibilityEnd(it) view.animateHide( - duration = volumeResources.dialogHideDurationMillis.first(), + duration = dialogHideAnimationDurationMs, translationX = calculateTranslationX(view), ) dialog.dismiss() diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogViewModel.kt index b20dffb8ac33..7a6ede4c8b9c 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogViewModel.kt @@ -44,9 +44,9 @@ class VolumeDialogViewModel @Inject constructor( private val context: Context, - private val dialogVisibilityInteractor: VolumeDialogVisibilityInteractor, + dialogVisibilityInteractor: VolumeDialogVisibilityInteractor, volumeDialogSlidersInteractor: VolumeDialogSlidersInteractor, - private val volumeDialogStateInteractor: VolumeDialogStateInteractor, + volumeDialogStateInteractor: VolumeDialogStateInteractor, devicePostureController: DevicePostureController, configurationController: ConfigurationController, ) { diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModel.kt index cec3d1eb86f0..5b8d9b045475 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModel.kt @@ -18,9 +18,6 @@ package com.android.systemui.volume.panel.component.volume.slider.ui.viewmodel import android.content.Context import android.media.AudioManager -import android.media.AudioManager.STREAM_ALARM -import android.media.AudioManager.STREAM_MUSIC -import android.media.AudioManager.STREAM_NOTIFICATION import android.util.Log import com.android.app.tracing.coroutines.launchTraced as launch import com.android.internal.logging.UiEventLogger @@ -34,8 +31,6 @@ import com.android.systemui.haptics.slider.compose.ui.SliderHapticsViewModel import com.android.systemui.modes.shared.ModesUiIcons import com.android.systemui.res.R import com.android.systemui.statusbar.policy.domain.interactor.ZenModeInteractor -import com.android.systemui.statusbar.policy.domain.model.ActiveZenModes -import com.android.systemui.util.kotlin.combine import com.android.systemui.volume.panel.shared.VolumePanelLogger import com.android.systemui.volume.panel.ui.VolumePanelUiEvent import dagger.assisted.Assisted @@ -43,12 +38,15 @@ import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import kotlin.math.roundToInt import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.filterNotNull +import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.stateIn @@ -101,48 +99,16 @@ constructor( ) override val slider: StateFlow<SliderState> = - if (ModesUiIcons.isEnabled) { - combine( - audioVolumeInteractor.getAudioStream(audioStream), - audioVolumeInteractor.canChangeVolume(audioStream), - audioVolumeInteractor.ringerMode, - zenModeInteractor.activeModesBlockingEverything, - zenModeInteractor.activeModesBlockingAlarms, - zenModeInteractor.activeModesBlockingMedia, - ) { - model, - isEnabled, - ringerMode, - modesBlockingEverything, - modesBlockingAlarms, - modesBlockingMedia -> - volumePanelLogger.onVolumeUpdateReceived(audioStream, model.volume) - model.toState( - isEnabled, - ringerMode, - getStreamDisabledMessage( - modesBlockingEverything, - modesBlockingAlarms, - modesBlockingMedia, - ), - ) - } - .stateIn(coroutineScope, SharingStarted.Eagerly, SliderState.Empty) - } else { - combine( - audioVolumeInteractor.getAudioStream(audioStream), - audioVolumeInteractor.canChangeVolume(audioStream), - audioVolumeInteractor.ringerMode, - ) { model, isEnabled, ringerMode -> - volumePanelLogger.onVolumeUpdateReceived(audioStream, model.volume) - model.toState( - isEnabled, - ringerMode, - getStreamDisabledMessageWithoutModes(audioStream), - ) - } - .stateIn(coroutineScope, SharingStarted.Eagerly, SliderState.Empty) - } + combine( + audioVolumeInteractor.getAudioStream(audioStream), + audioVolumeInteractor.canChangeVolume(audioStream), + audioVolumeInteractor.ringerMode, + streamDisabledMessage(), + ) { model, isEnabled, ringerMode, streamDisabledMessage -> + volumePanelLogger.onVolumeUpdateReceived(audioStream, model.volume) + model.toState(isEnabled, ringerMode, streamDisabledMessage) + } + .stateIn(coroutineScope, SharingStarted.Eagerly, SliderState.Empty) init { volumeChanges @@ -229,40 +195,32 @@ constructor( ) } - private fun getStreamDisabledMessage( - blockingEverything: ActiveZenModes, - blockingAlarms: ActiveZenModes, - blockingMedia: ActiveZenModes, - ): String { - // TODO: b/372213356 - Figure out the correct messages for VOICE_CALL and RING. - // In fact, VOICE_CALL should not be affected by interruption filtering at all. - return if (audioStream.value == STREAM_NOTIFICATION) { - context.getString(R.string.stream_notification_unavailable) - } else { - val blockingModeName = - when { - blockingEverything.mainMode != null -> blockingEverything.mainMode.name - audioStream.value == STREAM_ALARM -> blockingAlarms.mainMode?.name - audioStream.value == STREAM_MUSIC -> blockingMedia.mainMode?.name - else -> null - } - - if (blockingModeName != null) { - context.getString(R.string.stream_unavailable_by_modes, blockingModeName) + // TODO: b/372213356 - Figure out the correct messages for VOICE_CALL and RING. + // In fact, VOICE_CALL should not be affected by interruption filtering at all. + private fun streamDisabledMessage(): Flow<String> { + return if (ModesUiIcons.isEnabled) { + if (audioStream.value == AudioManager.STREAM_NOTIFICATION) { + flowOf(context.getString(R.string.stream_notification_unavailable)) } else { - // Should not actually be visible, but as a catch-all. - context.getString(R.string.stream_unavailable_by_unknown) + if (zenModeInteractor.canBeBlockedByZenMode(audioStream)) { + zenModeInteractor.activeModesBlockingStream(audioStream).map { blockingZenModes + -> + blockingZenModes.mainMode?.name?.let { + context.getString(R.string.stream_unavailable_by_modes, it) + } ?: context.getString(R.string.stream_unavailable_by_unknown) + } + } else { + flowOf(context.getString(R.string.stream_unavailable_by_unknown)) + } } - } - } - - private fun getStreamDisabledMessageWithoutModes(audioStream: AudioStream): String { - // TODO: b/372213356 - Figure out the correct messages for VOICE_CALL and RING. - // In fact, VOICE_CALL should not be affected by interruption filtering at all. - return if (audioStream.value == STREAM_NOTIFICATION) { - context.getString(R.string.stream_notification_unavailable) } else { - context.getString(R.string.stream_alarm_unavailable) + flowOf( + if (audioStream.value == AudioManager.STREAM_NOTIFICATION) { + context.getString(R.string.stream_notification_unavailable) + } else { + context.getString(R.string.stream_alarm_unavailable) + } + ) } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinatorTest.kt index 2c37f510a45c..77bac59b9dcd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinatorTest.kt @@ -15,7 +15,6 @@ */ package com.android.systemui.statusbar.notification.collection.coordinator -import android.platform.test.annotations.DisableFlags import android.platform.test.annotations.EnableFlags import android.testing.TestableLooper.RunWithLooper import androidx.test.ext.junit.runners.AndroidJUnit4 @@ -33,15 +32,14 @@ import com.android.systemui.statusbar.notification.collection.render.NotifStackC import com.android.systemui.statusbar.notification.collection.render.NotifStats import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor import com.android.systemui.statusbar.notification.domain.interactor.RenderNotificationListInteractor -import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow import com.android.systemui.statusbar.notification.stack.BUCKET_ALERTING import com.android.systemui.statusbar.notification.stack.BUCKET_SILENT import com.android.systemui.statusbar.policy.SensitiveNotificationProtectionController +import com.android.systemui.util.mockito.withArgCaptor import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -import org.mockito.kotlin.argumentCaptor import org.mockito.kotlin.eq import org.mockito.kotlin.mock import org.mockito.kotlin.verify @@ -82,9 +80,9 @@ class StackCoordinatorTest : SysuiTestCase() { sensitiveNotificationProtectionController, ) coordinator.attach(pipeline) - val captor = argumentCaptor<OnAfterRenderListListener>() - verify(pipeline).addOnAfterRenderListListener(captor.capture()) - afterRenderListListener = captor.lastValue + afterRenderListListener = withArgCaptor { + verify(pipeline).addOnAfterRenderListListener(capture()) + } } @Test @@ -94,93 +92,9 @@ class StackCoordinatorTest : SysuiTestCase() { } @Test - @EnableFlags(FooterViewRefactor.FLAG_NAME) - fun testSetRenderedListOnInteractor_footerFlagOn() { - afterRenderListListener.onAfterRenderList(listOf(entry), stackController) - verify(renderListInteractor).setRenderedList(eq(listOf(entry))) - } - - @Test - @DisableFlags(FooterViewRefactor.FLAG_NAME) fun testSetNotificationStats_clearableAlerting() { whenever(section.bucket).thenReturn(BUCKET_ALERTING) afterRenderListListener.onAfterRenderList(listOf(entry), stackController) - verify(stackController) - .setNotifStats( - NotifStats( - 1, - hasNonClearableAlertingNotifs = false, - hasClearableAlertingNotifs = true, - hasNonClearableSilentNotifs = false, - hasClearableSilentNotifs = false, - ) - ) - verifyNoMoreInteractions(activeNotificationsInteractor) - } - - @Test - @DisableFlags(FooterViewRefactor.FLAG_NAME) - @EnableFlags(FLAG_SCREENSHARE_NOTIFICATION_HIDING, FLAG_SCREENSHARE_NOTIFICATION_HIDING_BUG_FIX) - fun testSetNotificationStats_isSensitiveStateActive_nonClearableAlerting() { - whenever(sensitiveNotificationProtectionController.isSensitiveStateActive).thenReturn(true) - whenever(section.bucket).thenReturn(BUCKET_ALERTING) - afterRenderListListener.onAfterRenderList(listOf(entry), stackController) - verify(stackController) - .setNotifStats( - NotifStats( - 1, - hasNonClearableAlertingNotifs = true, - hasClearableAlertingNotifs = false, - hasNonClearableSilentNotifs = false, - hasClearableSilentNotifs = false, - ) - ) - verifyNoMoreInteractions(activeNotificationsInteractor) - } - - @Test - @DisableFlags(FooterViewRefactor.FLAG_NAME) - fun testSetNotificationStats_clearableSilent() { - whenever(section.bucket).thenReturn(BUCKET_SILENT) - afterRenderListListener.onAfterRenderList(listOf(entry), stackController) - verify(stackController) - .setNotifStats( - NotifStats( - 1, - hasNonClearableAlertingNotifs = false, - hasClearableAlertingNotifs = false, - hasNonClearableSilentNotifs = false, - hasClearableSilentNotifs = true, - ) - ) - verifyNoMoreInteractions(activeNotificationsInteractor) - } - - @Test - @DisableFlags(FooterViewRefactor.FLAG_NAME) - @EnableFlags(FLAG_SCREENSHARE_NOTIFICATION_HIDING, FLAG_SCREENSHARE_NOTIFICATION_HIDING_BUG_FIX) - fun testSetNotificationStats_isSensitiveStateActive_nonClearableSilent() { - whenever(sensitiveNotificationProtectionController.isSensitiveStateActive).thenReturn(true) - whenever(section.bucket).thenReturn(BUCKET_SILENT) - afterRenderListListener.onAfterRenderList(listOf(entry), stackController) - verify(stackController) - .setNotifStats( - NotifStats( - 1, - hasNonClearableAlertingNotifs = false, - hasClearableAlertingNotifs = false, - hasNonClearableSilentNotifs = true, - hasClearableSilentNotifs = false, - ) - ) - verifyNoMoreInteractions(activeNotificationsInteractor) - } - - @Test - @EnableFlags(FooterViewRefactor.FLAG_NAME) - fun testSetNotificationStats_footerFlagOn_clearableAlerting() { - whenever(section.bucket).thenReturn(BUCKET_ALERTING) - afterRenderListListener.onAfterRenderList(listOf(entry), stackController) verify(activeNotificationsInteractor) .setNotifStats( NotifStats( @@ -195,12 +109,8 @@ class StackCoordinatorTest : SysuiTestCase() { } @Test - @EnableFlags( - FooterViewRefactor.FLAG_NAME, - FLAG_SCREENSHARE_NOTIFICATION_HIDING, - FLAG_SCREENSHARE_NOTIFICATION_HIDING_BUG_FIX, - ) - fun testSetNotificationStats_footerFlagOn_isSensitiveStateActive_nonClearableAlerting() { + @EnableFlags(FLAG_SCREENSHARE_NOTIFICATION_HIDING, FLAG_SCREENSHARE_NOTIFICATION_HIDING_BUG_FIX) + fun testSetNotificationStats_isSensitiveStateActive_nonClearableAlerting() { whenever(sensitiveNotificationProtectionController.isSensitiveStateActive).thenReturn(true) whenever(section.bucket).thenReturn(BUCKET_ALERTING) afterRenderListListener.onAfterRenderList(listOf(entry), stackController) @@ -218,8 +128,7 @@ class StackCoordinatorTest : SysuiTestCase() { } @Test - @EnableFlags(FooterViewRefactor.FLAG_NAME) - fun testSetNotificationStats_footerFlagOn_clearableSilent() { + fun testSetNotificationStats_clearableSilent() { whenever(section.bucket).thenReturn(BUCKET_SILENT) afterRenderListListener.onAfterRenderList(listOf(entry), stackController) verify(activeNotificationsInteractor) @@ -236,12 +145,8 @@ class StackCoordinatorTest : SysuiTestCase() { } @Test - @EnableFlags( - FooterViewRefactor.FLAG_NAME, - FLAG_SCREENSHARE_NOTIFICATION_HIDING, - FLAG_SCREENSHARE_NOTIFICATION_HIDING_BUG_FIX, - ) - fun testSetNotificationStats_footerFlagOn_isSensitiveStateActive_nonClearableSilent() { + @EnableFlags(FLAG_SCREENSHARE_NOTIFICATION_HIDING, FLAG_SCREENSHARE_NOTIFICATION_HIDING_BUG_FIX) + fun testSetNotificationStats_isSensitiveStateActive_nonClearableSilent() { whenever(sensitiveNotificationProtectionController.isSensitiveStateActive).thenReturn(true) whenever(section.bucket).thenReturn(BUCKET_SILENT) afterRenderListListener.onAfterRenderList(listOf(entry), stackController) @@ -259,8 +164,7 @@ class StackCoordinatorTest : SysuiTestCase() { } @Test - @EnableFlags(FooterViewRefactor.FLAG_NAME) - fun testSetNotificationStats_footerFlagOn_nonClearableRedacted() { + fun testSetNotificationStats_nonClearableRedacted() { entry.setSensitive(true, true) whenever(section.bucket).thenReturn(BUCKET_ALERTING) afterRenderListListener.onAfterRenderList(listOf(entry), stackController) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/IconManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/IconManagerTest.kt index 25138fd0ff83..57a12df0cfee 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/IconManagerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/IconManagerTest.kt @@ -38,6 +38,7 @@ import androidx.test.filters.SmallTest import com.android.systemui.Flags.FLAG_STATUS_BAR_CALL_CHIP_NOTIFICATION_ICON import com.android.systemui.SysuiTestCase import com.android.systemui.controls.controller.AuxiliaryPersistenceWrapperTest.Companion.any +import com.android.systemui.statusbar.core.StatusBarConnectedDisplays import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection @@ -123,7 +124,8 @@ class IconManagerTest : SysuiTestCase() { @Test @EnableFlags(FLAG_STATUS_BAR_CALL_CHIP_NOTIFICATION_ICON) - fun testCreateIcons_chipNotifIconFlagEnabled_statusBarChipIconIsNull() { + @DisableFlags(StatusBarConnectedDisplays.FLAG_NAME) + fun testCreateIcons_chipNotifIconFlagEnabled_cdFlagDisabled_statusBarChipIconIsNotNull() { val entry = notificationEntry(hasShortcut = true, hasMessageSenderIcon = true, hasLargeIcon = true) entry?.let { iconManager.createIcons(it) } @@ -133,6 +135,17 @@ class IconManagerTest : SysuiTestCase() { } @Test + @EnableFlags(FLAG_STATUS_BAR_CALL_CHIP_NOTIFICATION_ICON, StatusBarConnectedDisplays.FLAG_NAME) + fun testCreateIcons_chipNotifIconFlagEnabled_cdFlagEnabled_statusBarChipIconIsNull() { + val entry = + notificationEntry(hasShortcut = true, hasMessageSenderIcon = true, hasLargeIcon = true) + entry?.let { iconManager.createIcons(it) } + testScope.runCurrent() + + assertThat(entry?.icons?.statusBarChipIcon).isNull() + } + + @Test fun testCreateIcons_importantConversation_shortcutIcon() { val entry = notificationEntry(hasShortcut = true, hasMessageSenderIcon = true, hasLargeIcon = true) @@ -158,7 +171,7 @@ class IconManagerTest : SysuiTestCase() { notificationEntry( hasShortcut = false, hasMessageSenderIcon = false, - hasLargeIcon = true + hasLargeIcon = true, ) entry?.channel?.isImportantConversation = true entry?.let { iconManager.createIcons(it) } @@ -172,7 +185,7 @@ class IconManagerTest : SysuiTestCase() { notificationEntry( hasShortcut = false, hasMessageSenderIcon = false, - hasLargeIcon = false + hasLargeIcon = false, ) entry?.channel?.isImportantConversation = true entry?.let { iconManager.createIcons(it) } @@ -187,7 +200,7 @@ class IconManagerTest : SysuiTestCase() { hasShortcut = true, hasMessageSenderIcon = true, useMessagingStyle = false, - hasLargeIcon = true + hasLargeIcon = true, ) entry?.channel?.isImportantConversation = true entry?.let { iconManager.createIcons(it) } @@ -205,7 +218,8 @@ class IconManagerTest : SysuiTestCase() { @Test @EnableFlags(FLAG_STATUS_BAR_CALL_CHIP_NOTIFICATION_ICON) - fun testCreateIcons_sensitiveImportantConversation() { + @DisableFlags(StatusBarConnectedDisplays.FLAG_NAME) + fun testCreateIcons_cdFlagDisabled_sensitiveImportantConversation() { val entry = notificationEntry(hasShortcut = true, hasMessageSenderIcon = true, hasLargeIcon = false) entry?.setSensitive(true, true) @@ -219,8 +233,24 @@ class IconManagerTest : SysuiTestCase() { } @Test + @EnableFlags(FLAG_STATUS_BAR_CALL_CHIP_NOTIFICATION_ICON, StatusBarConnectedDisplays.FLAG_NAME) + fun testCreateIcons_cdFlagEnabled_sensitiveImportantConversation() { + val entry = + notificationEntry(hasShortcut = true, hasMessageSenderIcon = true, hasLargeIcon = false) + entry?.setSensitive(true, true) + entry?.channel?.isImportantConversation = true + entry?.let { iconManager.createIcons(it) } + testScope.runCurrent() + assertThat(entry?.icons?.statusBarIcon?.sourceIcon).isEqualTo(shortcutIc) + assertThat(entry?.icons?.statusBarChipIcon).isNull() + assertThat(entry?.icons?.shelfIcon?.sourceIcon).isEqualTo(smallIc) + assertThat(entry?.icons?.aodIcon?.sourceIcon).isEqualTo(smallIc) + } + + @Test @EnableFlags(FLAG_STATUS_BAR_CALL_CHIP_NOTIFICATION_ICON) - fun testUpdateIcons_sensitiveImportantConversation() { + @DisableFlags(StatusBarConnectedDisplays.FLAG_NAME) + fun testUpdateIcons_cdFlagDisabled_sensitiveImportantConversation() { val entry = notificationEntry(hasShortcut = true, hasMessageSenderIcon = true, hasLargeIcon = false) entry?.setSensitive(true, true) @@ -236,6 +266,23 @@ class IconManagerTest : SysuiTestCase() { } @Test + @EnableFlags(FLAG_STATUS_BAR_CALL_CHIP_NOTIFICATION_ICON, StatusBarConnectedDisplays.FLAG_NAME) + fun testUpdateIcons_cdFlagEnabled_sensitiveImportantConversation() { + val entry = + notificationEntry(hasShortcut = true, hasMessageSenderIcon = true, hasLargeIcon = false) + entry?.setSensitive(true, true) + entry?.channel?.isImportantConversation = true + entry?.let { iconManager.createIcons(it) } + // Updating the icons after creation shouldn't break anything + entry?.let { iconManager.updateIcons(it) } + testScope.runCurrent() + assertThat(entry?.icons?.statusBarIcon?.sourceIcon).isEqualTo(shortcutIc) + assertThat(entry?.icons?.statusBarChipIcon).isNull() + assertThat(entry?.icons?.shelfIcon?.sourceIcon).isEqualTo(smallIc) + assertThat(entry?.icons?.aodIcon?.sourceIcon).isEqualTo(smallIc) + } + + @Test fun testUpdateIcons_sensitivityChange() { val entry = notificationEntry(hasShortcut = true, hasMessageSenderIcon = true, hasLargeIcon = false) @@ -254,7 +301,7 @@ class IconManagerTest : SysuiTestCase() { hasShortcut: Boolean, hasMessageSenderIcon: Boolean, useMessagingStyle: Boolean = true, - hasLargeIcon: Boolean + hasLargeIcon: Boolean, ): NotificationEntry? { val n = Notification.Builder(mContext, "id") @@ -270,7 +317,7 @@ class IconManagerTest : SysuiTestCase() { SystemClock.currentThreadTimeMillis(), Person.Builder() .setIcon(if (hasMessageSenderIcon) messageIc else null) - .build() + .build(), ) ) if (useMessagingStyle) { 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 e1a891662889..3763282cdebc 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 @@ -16,7 +16,6 @@ package com.android.systemui.statusbar.notification.stack; -import static android.view.View.GONE; import static android.view.WindowInsets.Type.ime; import static com.android.systemui.flags.SceneContainerFlagParameterizationKt.parameterizeSceneContainerFlag; @@ -28,17 +27,14 @@ import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertWithMessage; import static junit.framework.Assert.assertEquals; -import static junit.framework.Assert.assertNotNull; import static junit.framework.Assert.assertTrue; import static org.junit.Assert.assertFalse; -import static org.mockito.AdditionalMatchers.not; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyFloat; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doNothing; @@ -64,7 +60,6 @@ import android.view.View; import android.view.ViewGroup; import android.view.WindowInsets; import android.view.WindowInsetsAnimation; -import android.widget.TextView; import androidx.test.filters.SmallTest; @@ -92,8 +87,6 @@ import com.android.systemui.statusbar.notification.collection.render.GroupExpans import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager; import com.android.systemui.statusbar.notification.emptyshade.shared.ModesEmptyShadeFix; import com.android.systemui.statusbar.notification.emptyshade.ui.view.EmptyShadeView; -import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor; -import com.android.systemui.statusbar.notification.footer.shared.NotifRedesignFooter; import com.android.systemui.statusbar.notification.footer.ui.view.FooterView; import com.android.systemui.statusbar.notification.headsup.AvalancheController; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; @@ -603,158 +596,6 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { } @Test - @DisableFlags({FooterViewRefactor.FLAG_NAME, NotifRedesignFooter.FLAG_NAME}) - public void manageNotifications_visible() { - FooterView view = mock(FooterView.class); - mStackScroller.setFooterView(view); - when(view.willBeGone()).thenReturn(true); - - mStackScroller.updateFooterView(true, false, true); - - verify(view).setVisible(eq(true), anyBoolean()); - verify(view).setClearAllButtonVisible(eq(false), anyBoolean()); - } - - @Test - @DisableFlags({FooterViewRefactor.FLAG_NAME, NotifRedesignFooter.FLAG_NAME}) - public void clearAll_visible() { - FooterView view = mock(FooterView.class); - mStackScroller.setFooterView(view); - when(view.willBeGone()).thenReturn(true); - - mStackScroller.updateFooterView(true, true, true); - - verify(view).setVisible(eq(true), anyBoolean()); - verify(view).setClearAllButtonVisible(eq(true), anyBoolean()); - } - - @Test - @DisableFlags({FooterViewRefactor.FLAG_NAME, NotifRedesignFooter.FLAG_NAME}) - public void testInflateFooterView() { - mStackScroller.inflateFooterView(); - ArgumentCaptor<FooterView> captor = ArgumentCaptor.forClass(FooterView.class); - verify(mStackScroller).setFooterView(captor.capture()); - - assertNotNull(captor.getValue().findViewById(R.id.manage_text)); - assertNotNull(captor.getValue().findViewById(R.id.dismiss_text)); - } - - @Test - @DisableFlags({FooterViewRefactor.FLAG_NAME, NotifRedesignFooter.FLAG_NAME}) - public void testUpdateFooter_noNotifications() { - setBarStateForTest(StatusBarState.SHADE); - mStackScroller.setCurrentUserSetup(true); - - FooterView view = mock(FooterView.class); - mStackScroller.setFooterView(view); - mStackScroller.updateFooter(); - verify(mStackScroller, atLeastOnce()).updateFooterView(false, false, true); - } - - @Test - @DisableFlags({FooterViewRefactor.FLAG_NAME, NotifRedesignFooter.FLAG_NAME}) - @DisableSceneContainer - public void testUpdateFooter_remoteInput() { - setBarStateForTest(StatusBarState.SHADE); - mStackScroller.setCurrentUserSetup(true); - - mStackScroller.setIsRemoteInputActive(true); - when(mStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(1); - when(mStackScrollLayoutController.hasActiveClearableNotifications(eq(ROWS_ALL))) - .thenReturn(true); - - FooterView view = mock(FooterView.class); - mStackScroller.setFooterView(view); - mStackScroller.updateFooter(); - verify(mStackScroller, atLeastOnce()).updateFooterView(false, true, true); - } - - @Test - @DisableFlags({FooterViewRefactor.FLAG_NAME, NotifRedesignFooter.FLAG_NAME}) - public void testUpdateFooter_withoutNotifications() { - setBarStateForTest(StatusBarState.SHADE); - mStackScroller.setCurrentUserSetup(true); - - when(mStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(0); - when(mStackScrollLayoutController.hasActiveClearableNotifications(eq(ROWS_ALL))) - .thenReturn(false); - - FooterView view = mock(FooterView.class); - mStackScroller.setFooterView(view); - mStackScroller.updateFooter(); - verify(mStackScroller, atLeastOnce()).updateFooterView(false, false, true); - } - - @Test - @DisableFlags({FooterViewRefactor.FLAG_NAME, NotifRedesignFooter.FLAG_NAME}) - @DisableSceneContainer - public void testUpdateFooter_oneClearableNotification() { - setBarStateForTest(StatusBarState.SHADE); - mStackScroller.setCurrentUserSetup(true); - - when(mStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(1); - when(mStackScrollLayoutController.hasActiveClearableNotifications(eq(ROWS_ALL))) - .thenReturn(true); - - FooterView view = mock(FooterView.class); - mStackScroller.setFooterView(view); - mStackScroller.updateFooter(); - verify(mStackScroller, atLeastOnce()).updateFooterView(true, true, true); - } - - @Test - @DisableFlags({FooterViewRefactor.FLAG_NAME, NotifRedesignFooter.FLAG_NAME}) - @DisableSceneContainer - public void testUpdateFooter_withoutHistory() { - setBarStateForTest(StatusBarState.SHADE); - mStackScroller.setCurrentUserSetup(true); - - when(mStackScrollLayoutController.isHistoryEnabled()).thenReturn(false); - when(mStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(1); - when(mStackScrollLayoutController.hasActiveClearableNotifications(eq(ROWS_ALL))) - .thenReturn(true); - - FooterView view = mock(FooterView.class); - mStackScroller.setFooterView(view); - mStackScroller.updateFooter(); - verify(mStackScroller, atLeastOnce()).updateFooterView(true, true, false); - } - - @Test - @DisableFlags({FooterViewRefactor.FLAG_NAME, NotifRedesignFooter.FLAG_NAME}) - public void testUpdateFooter_oneClearableNotification_beforeUserSetup() { - setBarStateForTest(StatusBarState.SHADE); - mStackScroller.setCurrentUserSetup(false); - - when(mStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(1); - when(mStackScrollLayoutController.hasActiveClearableNotifications(eq(ROWS_ALL))) - .thenReturn(true); - - FooterView view = mock(FooterView.class); - mStackScroller.setFooterView(view); - mStackScroller.updateFooter(); - verify(mStackScroller, atLeastOnce()).updateFooterView(false, true, true); - } - - @Test - @DisableFlags({FooterViewRefactor.FLAG_NAME, NotifRedesignFooter.FLAG_NAME}) - @DisableSceneContainer - public void testUpdateFooter_oneNonClearableNotification() { - setBarStateForTest(StatusBarState.SHADE); - mStackScroller.setCurrentUserSetup(true); - - when(mStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(1); - when(mStackScrollLayoutController.hasActiveClearableNotifications(eq(ROWS_ALL))) - .thenReturn(false); - when(mEmptyShadeView.getVisibility()).thenReturn(GONE); - - FooterView view = mock(FooterView.class); - mStackScroller.setFooterView(view); - mStackScroller.updateFooter(); - verify(mStackScroller, atLeastOnce()).updateFooterView(true, false, true); - } - - @Test public void testFooterPosition_atEnd() { // add footer FooterView view = mock(FooterView.class); @@ -772,19 +613,6 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { } @Test - @DisableFlags({FooterViewRefactor.FLAG_NAME, - ModesEmptyShadeFix.FLAG_NAME, - NotifRedesignFooter.FLAG_NAME}) - public void testReInflatesFooterViews() { - when(mEmptyShadeView.getTextResource()).thenReturn(R.string.empty_shade_text); - clearInvocations(mStackScroller); - mStackScroller.reinflateViews(); - verify(mStackScroller).setFooterView(any()); - verify(mStackScroller).setEmptyShadeView(any()); - } - - @Test - @EnableFlags(FooterViewRefactor.FLAG_NAME) @DisableFlags(ModesEmptyShadeFix.FLAG_NAME) public void testReInflatesEmptyShadeView() { when(mEmptyShadeView.getTextResource()).thenReturn(R.string.empty_shade_text); @@ -1231,31 +1059,6 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { } @Test - @DisableFlags({FooterViewRefactor.FLAG_NAME, NotifRedesignFooter.FLAG_NAME}) - public void hasFilteredOutSeenNotifs_updateFooter() { - mStackScroller.setCurrentUserSetup(true); - - // add footer - mStackScroller.inflateFooterView(); - TextView footerLabel = - mStackScroller.mFooterView.requireViewById(R.id.unlock_prompt_footer); - - mStackScroller.setHasFilteredOutSeenNotifications(true); - mStackScroller.updateFooter(); - - assertThat(footerLabel.getVisibility()).isEqualTo(View.VISIBLE); - } - - @Test - @DisableFlags({FooterViewRefactor.FLAG_NAME, ModesEmptyShadeFix.FLAG_NAME}) - public void hasFilteredOutSeenNotifs_updateEmptyShadeView() { - mStackScroller.setHasFilteredOutSeenNotifications(true); - mStackScroller.updateEmptyShadeView(true, false); - - verify(mEmptyShadeView).setFooterText(not(eq(0))); - } - - @Test @DisableSceneContainer public void testWindowInsetAnimationProgress_updatesBottomInset() { int imeInset = 100; diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/animation/FakeDialogTransitionAnimator.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/animation/FakeDialogTransitionAnimator.kt index 17093291e8b0..2a1877adc172 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/animation/FakeDialogTransitionAnimator.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/animation/FakeDialogTransitionAnimator.kt @@ -24,7 +24,6 @@ fun fakeDialogTransitionAnimator( @Main mainExecutor: Executor, isUnlocked: Boolean = true, isShowingAlternateAuthOnUnlock: Boolean = false, - isPredictiveBackQsDialogAnim: Boolean = false, interactionJankMonitor: InteractionJankMonitor, ): DialogTransitionAnimator { return DialogTransitionAnimator( @@ -35,10 +34,6 @@ fun fakeDialogTransitionAnimator( isShowingAlternateAuthOnUnlock = isShowingAlternateAuthOnUnlock, ), interactionJankMonitor = interactionJankMonitor, - featureFlags = - object : AnimationFeatureFlags { - override val isPredictiveBackQsDialogAnim = isPredictiveBackQsDialogAnim - }, transitionAnimator = fakeTransitionAnimator(mainExecutor), isForTesting = true, ) @@ -50,6 +45,8 @@ private class FakeCallback( private val isShowingAlternateAuthOnUnlock: Boolean = false, ) : DialogTransitionAnimator.Callback { override fun isDreaming(): Boolean = isDreaming + override fun isUnlocked(): Boolean = isUnlocked + override fun isShowingAlternateAuthOnUnlock() = isShowingAlternateAuthOnUnlock } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt index 47991b3b9689..3df3ee983ecf 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt @@ -154,6 +154,7 @@ val Kosmos.shortcutHelperCoreStartable by shortcutHelperStateRepository, activityStarter, testScope, + customInputGesturesRepository ) } diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/TouchState.java b/services/accessibility/java/com/android/server/accessibility/gestures/TouchState.java index f15b8eec3f6b..cd46b38272c2 100644 --- a/services/accessibility/java/com/android/server/accessibility/gestures/TouchState.java +++ b/services/accessibility/java/com/android/server/accessibility/gestures/TouchState.java @@ -38,7 +38,7 @@ public class TouchState { // Pointer-related constants // This constant captures the current implementation detail that // pointer IDs are between 0 and 31 inclusive (subject to change). - // (See MAX_POINTER_ID in frameworks/base/include/ui/Input.h) + // (See MAX_POINTER_ID in frameworks/native/include/input/Input.h) public static final int MAX_POINTER_COUNT = 32; // Constant referring to the ids bits of all pointers. public static final int ALL_POINTER_ID_BITS = 0xFFFFFFFF; diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java index 1f3b31692289..aeb2f5e9be84 100644 --- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java +++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java @@ -176,7 +176,7 @@ public class VirtualDeviceManagerService extends SystemService { public VirtualDeviceManagerService(Context context) { super(context); mImpl = new VirtualDeviceManagerImpl(); - mNativeImpl = Flags.enableNativeVdm() ? new VirtualDeviceManagerNativeImpl() : null; + mNativeImpl = new VirtualDeviceManagerNativeImpl(); mLocalService = new LocalService(); } @@ -208,9 +208,7 @@ public class VirtualDeviceManagerService extends SystemService { @RequiresPermission(android.Manifest.permission.MANAGE_COMPANION_DEVICES) public void onStart() { publishBinderService(Context.VIRTUAL_DEVICE_SERVICE, mImpl); - if (Flags.enableNativeVdm()) { - publishBinderService(VIRTUAL_DEVICE_NATIVE_SERVICE, mNativeImpl); - } + publishBinderService(VIRTUAL_DEVICE_NATIVE_SERVICE, mNativeImpl); publishLocalService(VirtualDeviceManagerInternal.class, mLocalService); ActivityTaskManagerInternal activityTaskManagerInternal = getLocalService( ActivityTaskManagerInternal.class); diff --git a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java index 5d9db65fe2b2..d89db8d5581b 100644 --- a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java +++ b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java @@ -312,6 +312,13 @@ public final class DeviceStateManagerService extends SystemService { mProcessObserver); } + @Override + public void onBootPhase(int phase) { + if (phase == PHASE_SYSTEM_SERVICES_READY) { + mDeviceStatePolicy.getDeviceStateProvider().onSystemReady(); + } + } + @VisibleForTesting Handler getHandler() { return mHandler; diff --git a/services/core/java/com/android/server/devicestate/DeviceStateProvider.java b/services/core/java/com/android/server/devicestate/DeviceStateProvider.java index 8d07609cef30..8a8ebc2ffc21 100644 --- a/services/core/java/com/android/server/devicestate/DeviceStateProvider.java +++ b/services/core/java/com/android/server/devicestate/DeviceStateProvider.java @@ -91,6 +91,11 @@ public interface DeviceStateProvider extends Dumpable { @interface SupportedStatesUpdatedReason {} /** + * Called when the system boot phase advances to PHASE_SYSTEM_SERVICES_READY. + */ + default void onSystemReady() {}; + + /** * Registers a listener for changes in provider state. * <p> * It is <b>required</b> that diff --git a/services/core/java/com/android/server/media/quality/MediaQualityService.java b/services/core/java/com/android/server/media/quality/MediaQualityService.java index 34bb4155c943..3babc0ae065c 100644 --- a/services/core/java/com/android/server/media/quality/MediaQualityService.java +++ b/services/core/java/com/android/server/media/quality/MediaQualityService.java @@ -18,6 +18,7 @@ package com.android.server.media.quality; import android.content.ContentValues; import android.content.Context; +import android.content.pm.PackageManager; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.media.quality.AmbientBacklightSettings; @@ -35,8 +36,13 @@ import android.media.quality.SoundProfileHandle; import android.os.Binder; import android.os.Bundle; import android.os.PersistableBundle; +import android.os.RemoteCallbackList; +import android.os.RemoteException; import android.os.UserHandle; import android.util.Log; +import android.util.Pair; +import android.util.Slog; +import android.util.SparseArray; import com.android.server.SystemService; @@ -45,9 +51,11 @@ import org.json.JSONObject; import java.util.ArrayList; import java.util.Arrays; +import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Locale; +import java.util.Map; import java.util.UUID; import java.util.stream.Collectors; @@ -64,10 +72,13 @@ public class MediaQualityService extends SystemService { private final MediaQualityDbHelper mMediaQualityDbHelper; private final BiMap<Long, String> mPictureProfileTempIdMap; private final BiMap<Long, String> mSoundProfileTempIdMap; + private final PackageManager mPackageManager; + private final SparseArray<UserState> mUserStates = new SparseArray<>(); public MediaQualityService(Context context) { super(context); mContext = context; + mPackageManager = mContext.getPackageManager(); mPictureProfileTempIdMap = new BiMap<>(); mSoundProfileTempIdMap = new BiMap<>(); mMediaQualityDbHelper = new MediaQualityDbHelper(mContext); @@ -85,12 +96,20 @@ public class MediaQualityService extends SystemService { @Override public PictureProfile createPictureProfile(PictureProfile pp, UserHandle user) { + if ((pp.getPackageName() != null && !pp.getPackageName().isEmpty() + && !incomingPackageEqualsCallingUidPackage(pp.getPackageName())) + && !hasGlobalPictureQualityServicePermission()) { + notifyError(null, PictureProfile.ERROR_NO_PERMISSION, + Binder.getCallingUid(), Binder.getCallingPid()); + } + SQLiteDatabase db = mMediaQualityDbHelper.getWritableDatabase(); ContentValues values = getContentValues(null, pp.getProfileType(), pp.getName(), - pp.getPackageName(), + pp.getPackageName() == null || pp.getPackageName().isEmpty() + ? getPackageOfCallingUid() : pp.getPackageName(), pp.getInputId(), pp.getParameters()); @@ -104,9 +123,13 @@ public class MediaQualityService extends SystemService { @Override public void updatePictureProfile(String id, PictureProfile pp, UserHandle user) { - Long intId = mPictureProfileTempIdMap.getKey(id); + Long dbId = mPictureProfileTempIdMap.getKey(id); + if (!hasPermissionToUpdatePictureProfile(dbId, pp)) { + notifyError(id, PictureProfile.ERROR_NO_PERMISSION, + Binder.getCallingUid(), Binder.getCallingPid()); + } - ContentValues values = getContentValues(intId, + ContentValues values = getContentValues(dbId, pp.getProfileType(), pp.getName(), pp.getPackageName(), @@ -118,27 +141,51 @@ public class MediaQualityService extends SystemService { null, values); } + private boolean hasPermissionToUpdatePictureProfile(Long dbId, PictureProfile toUpdate) { + PictureProfile fromDb = getPictureProfile(dbId); + return fromDb.getProfileType() == toUpdate.getProfileType() + && fromDb.getPackageName().equals(toUpdate.getPackageName()) + && fromDb.getName().equals(toUpdate.getName()) + && fromDb.getName().equals(getPackageOfCallingUid()); + } + @Override public void removePictureProfile(String id, UserHandle user) { - Long intId = mPictureProfileTempIdMap.getKey(id); - if (intId != null) { + Long dbId = mPictureProfileTempIdMap.getKey(id); + + if (!hasPermissionToRemovePictureProfile(dbId)) { + notifyError(id, PictureProfile.ERROR_NO_PERMISSION, + Binder.getCallingUid(), Binder.getCallingPid()); + } + + if (dbId != null) { SQLiteDatabase db = mMediaQualityDbHelper.getWritableDatabase(); String selection = BaseParameters.PARAMETER_ID + " = ?"; - String[] selectionArgs = {Long.toString(intId)}; - db.delete(mMediaQualityDbHelper.PICTURE_QUALITY_TABLE_NAME, selection, + String[] selectionArgs = {Long.toString(dbId)}; + int result = db.delete(mMediaQualityDbHelper.PICTURE_QUALITY_TABLE_NAME, selection, selectionArgs); - mPictureProfileTempIdMap.remove(intId); + if (result == 0) { + notifyError(id, PictureProfile.ERROR_INVALID_ARGUMENT, + Binder.getCallingUid(), Binder.getCallingPid()); + } + mPictureProfileTempIdMap.remove(dbId); } } + private boolean hasPermissionToRemovePictureProfile(Long dbId) { + PictureProfile fromDb = getPictureProfile(dbId); + return fromDb.getName().equalsIgnoreCase(getPackageOfCallingUid()); + } + @Override public PictureProfile getPictureProfile(int type, String name, Bundle options, UserHandle user) { boolean includeParams = options.getBoolean(MediaQualityManager.OPTION_INCLUDE_PARAMETERS, false); String selection = BaseParameters.PARAMETER_TYPE + " = ? AND " - + BaseParameters.PARAMETER_NAME + " = ?"; - String[] selectionArguments = {Integer.toString(type), name}; + + BaseParameters.PARAMETER_NAME + " = ? AND " + + BaseParameters.PARAMETER_PACKAGE + " = ?"; + String[] selectionArguments = {Integer.toString(type), name, getPackageOfCallingUid()}; try ( Cursor cursor = getCursorAfterQuerying( @@ -156,13 +203,42 @@ public class MediaQualityService extends SystemService { return null; } cursor.moveToFirst(); - return getPictureProfileWithTempIdFromCursor(cursor); + return convertCursorToPictureProfileWithTempId(cursor); + } + } + + private PictureProfile getPictureProfile(Long dbId) { + String selection = BaseParameters.PARAMETER_ID + " = ?"; + String[] selectionArguments = {Long.toString(dbId)}; + + try ( + Cursor cursor = getCursorAfterQuerying( + mMediaQualityDbHelper.PICTURE_QUALITY_TABLE_NAME, + getMediaProfileColumns(false), selection, selectionArguments) + ) { + int count = cursor.getCount(); + if (count == 0) { + return null; + } + if (count > 1) { + Log.wtf(TAG, String.format(Locale.US, "%d entries found for id=%d" + + " in %s. Should only ever be 0 or 1.", count, dbId, + mMediaQualityDbHelper.PICTURE_QUALITY_TABLE_NAME)); + return null; + } + cursor.moveToFirst(); + return convertCursorToPictureProfileWithTempId(cursor); } } @Override public List<PictureProfile> getPictureProfilesByPackage( String packageName, Bundle options, UserHandle user) { + if (!hasGlobalPictureQualityServicePermission()) { + notifyError(null, PictureProfile.ERROR_NO_PERMISSION, + Binder.getCallingUid(), Binder.getCallingPid()); + } + boolean includeParams = options.getBoolean(MediaQualityManager.OPTION_INCLUDE_PARAMETERS, false); String selection = BaseParameters.PARAMETER_PACKAGE + " = ?"; @@ -172,23 +248,31 @@ public class MediaQualityService extends SystemService { } @Override - public List<PictureProfile> getAvailablePictureProfiles(Bundle options, UserHandle user) { - String[] packageNames = mContext.getPackageManager().getPackagesForUid( - Binder.getCallingUid()); - if (packageNames != null && packageNames.length == 1 && !packageNames[0].isEmpty()) { - return getPictureProfilesByPackage(packageNames[0], options, user); + public List<PictureProfile> getAvailablePictureProfiles( + Bundle options, UserHandle user) { + String packageName = getPackageOfCallingUid(); + if (packageName != null) { + return getPictureProfilesByPackage(packageName, options, user); } return new ArrayList<>(); } @Override public boolean setDefaultPictureProfile(String profileId, UserHandle user) { + if (!hasGlobalPictureQualityServicePermission()) { + notifyError(profileId, PictureProfile.ERROR_NO_PERMISSION, + Binder.getCallingUid(), Binder.getCallingPid()); + } // TODO: pass the profile ID to MediaQuality HAL when ready. return false; } @Override public List<String> getPictureProfilePackageNames(UserHandle user) { + if (!hasGlobalPictureQualityServicePermission()) { + notifyError(null, PictureProfile.ERROR_NO_PERMISSION, + Binder.getCallingUid(), Binder.getCallingPid()); + } String [] column = {BaseParameters.PARAMETER_PACKAGE}; List<PictureProfile> pictureProfiles = getPictureProfilesBasedOnConditions(column, null, null); @@ -210,12 +294,19 @@ public class MediaQualityService extends SystemService { @Override public SoundProfile createSoundProfile(SoundProfile sp, UserHandle user) { + if ((sp.getPackageName() != null && !sp.getPackageName().isEmpty() + && !incomingPackageEqualsCallingUidPackage(sp.getPackageName())) + && !hasGlobalPictureQualityServicePermission()) { + //TODO: error handling + return null; + } SQLiteDatabase db = mMediaQualityDbHelper.getWritableDatabase(); ContentValues values = getContentValues(null, sp.getProfileType(), sp.getName(), - sp.getPackageName(), + sp.getPackageName() == null || sp.getPackageName().isEmpty() + ? getPackageOfCallingUid() : sp.getPackageName(), sp.getInputId(), sp.getParameters()); @@ -229,9 +320,14 @@ public class MediaQualityService extends SystemService { @Override public void updateSoundProfile(String id, SoundProfile sp, UserHandle user) { - Long intId = mSoundProfileTempIdMap.getKey(id); + Long dbId = mSoundProfileTempIdMap.getKey(id); + + if (!hasPermissionToUpdateSoundProfile(dbId, sp)) { + //TODO: error handling + return; + } - ContentValues values = getContentValues(intId, + ContentValues values = getContentValues(dbId, sp.getProfileType(), sp.getName(), sp.getPackageName(), @@ -242,27 +338,49 @@ public class MediaQualityService extends SystemService { db.replace(mMediaQualityDbHelper.SOUND_QUALITY_TABLE_NAME, null, values); } + private boolean hasPermissionToUpdateSoundProfile(Long dbId, SoundProfile sp) { + SoundProfile fromDb = getSoundProfile(dbId); + return fromDb.getProfileType() == sp.getProfileType() + && fromDb.getPackageName().equals(sp.getPackageName()) + && fromDb.getName().equals(sp.getName()) + && fromDb.getName().equals(getPackageOfCallingUid()); + } + @Override public void removeSoundProfile(String id, UserHandle user) { Long intId = mSoundProfileTempIdMap.getKey(id); + if (!hasPermissionToRemoveSoundProfile(intId)) { + //TODO: error handling + return; + } + if (intId != null) { SQLiteDatabase db = mMediaQualityDbHelper.getWritableDatabase(); String selection = BaseParameters.PARAMETER_ID + " = ?"; String[] selectionArgs = {Long.toString(intId)}; - db.delete(mMediaQualityDbHelper.SOUND_QUALITY_TABLE_NAME, selection, + int result = db.delete(mMediaQualityDbHelper.SOUND_QUALITY_TABLE_NAME, selection, selectionArgs); + if (result == 0) { + //TODO: error handling + } mSoundProfileTempIdMap.remove(intId); } } + private boolean hasPermissionToRemoveSoundProfile(Long dbId) { + SoundProfile fromDb = getSoundProfile(dbId); + return fromDb.getName().equalsIgnoreCase(getPackageOfCallingUid()); + } + @Override public SoundProfile getSoundProfile(int type, String id, Bundle options, UserHandle user) { boolean includeParams = options.getBoolean(MediaQualityManager.OPTION_INCLUDE_PARAMETERS, false); String selection = BaseParameters.PARAMETER_TYPE + " = ? AND " - + BaseParameters.PARAMETER_ID + " = ?"; - String[] selectionArguments = {String.valueOf(type), id}; + + BaseParameters.PARAMETER_ID + " = ? AND " + + BaseParameters.PARAMETER_PACKAGE + " = ?"; + String[] selectionArguments = {String.valueOf(type), id, getPackageOfCallingUid()}; try ( Cursor cursor = getCursorAfterQuerying( @@ -280,13 +398,42 @@ public class MediaQualityService extends SystemService { return null; } cursor.moveToFirst(); - return getSoundProfileWithTempIdFromCursor(cursor); + return convertCursorToSoundProfileWithTempId(cursor); + } + } + + private SoundProfile getSoundProfile(Long dbId) { + String selection = BaseParameters.PARAMETER_ID + " = ?"; + String[] selectionArguments = {Long.toString(dbId)}; + + try ( + Cursor cursor = getCursorAfterQuerying( + mMediaQualityDbHelper.SOUND_QUALITY_TABLE_NAME, + getMediaProfileColumns(false), selection, selectionArguments) + ) { + int count = cursor.getCount(); + if (count == 0) { + return null; + } + if (count > 1) { + Log.wtf(TAG, String.format(Locale.US, "%d entries found for id=%s " + + "in %s. Should only ever be 0 or 1.", count, dbId, + mMediaQualityDbHelper.SOUND_QUALITY_TABLE_NAME)); + return null; + } + cursor.moveToFirst(); + return convertCursorToSoundProfileWithTempId(cursor); } } @Override public List<SoundProfile> getSoundProfilesByPackage( String packageName, Bundle options, UserHandle user) { + if (!hasGlobalSoundQualityServicePermission()) { + //TODO: error handling + return new ArrayList<>(); + } + boolean includeParams = options.getBoolean(MediaQualityManager.OPTION_INCLUDE_PARAMETERS, false); String selection = BaseParameters.PARAMETER_PACKAGE + " = ?"; @@ -296,24 +443,30 @@ public class MediaQualityService extends SystemService { } @Override - public List<SoundProfile> getAvailableSoundProfiles( - Bundle options, UserHandle user) { - String[] packageNames = mContext.getPackageManager().getPackagesForUid( - Binder.getCallingUid()); - if (packageNames != null && packageNames.length == 1 && !packageNames[0].isEmpty()) { - return getSoundProfilesByPackage(packageNames[0], options, user); + public List<SoundProfile> getAvailableSoundProfiles(Bundle options, UserHandle user) { + String packageName = getPackageOfCallingUid(); + if (packageName != null) { + return getSoundProfilesByPackage(packageName, options, user); } return new ArrayList<>(); } @Override public boolean setDefaultSoundProfile(String profileId, UserHandle user) { + if (!hasGlobalSoundQualityServicePermission()) { + //TODO: error handling + return false; + } // TODO: pass the profile ID to MediaQuality HAL when ready. return false; } @Override public List<String> getSoundProfilePackageNames(UserHandle user) { + if (!hasGlobalSoundQualityServicePermission()) { + //TODO: error handling + return new ArrayList<>(); + } String [] column = {BaseParameters.PARAMETER_NAME}; List<SoundProfile> soundProfiles = getSoundProfilesBasedOnConditions(column, null, null); @@ -323,6 +476,37 @@ public class MediaQualityService extends SystemService { .collect(Collectors.toList()); } + private String getPackageOfCallingUid() { + String[] packageNames = mPackageManager.getPackagesForUid( + Binder.getCallingUid()); + if (packageNames != null && packageNames.length == 1 && !packageNames[0].isEmpty()) { + return packageNames[0]; + } + return null; + } + + private boolean incomingPackageEqualsCallingUidPackage(String incomingPackage) { + return incomingPackage.equalsIgnoreCase(getPackageOfCallingUid()); + } + + private boolean hasGlobalPictureQualityServicePermission() { + return mPackageManager.checkPermission(android.Manifest.permission + .MANAGE_GLOBAL_PICTURE_QUALITY_SERVICE, + mContext.getPackageName()) == mPackageManager.PERMISSION_GRANTED; + } + + private boolean hasGlobalSoundQualityServicePermission() { + return mPackageManager.checkPermission(android.Manifest.permission + .MANAGE_GLOBAL_SOUND_QUALITY_SERVICE, + mContext.getPackageName()) == mPackageManager.PERMISSION_GRANTED; + } + + private boolean hasReadColorZonesPermission() { + return mPackageManager.checkPermission(android.Manifest.permission + .READ_COLOR_ZONES, + mContext.getPackageName()) == mPackageManager.PERMISSION_GRANTED; + } + private void populateTempIdMap(BiMap<Long, String> map, Long id) { if (id != null && map.getValue(id) == null) { String uuid; @@ -430,7 +614,7 @@ public class MediaQualityService extends SystemService { return columns.toArray(new String[0]); } - private PictureProfile getPictureProfileWithTempIdFromCursor(Cursor cursor) { + private PictureProfile convertCursorToPictureProfileWithTempId(Cursor cursor) { return new PictureProfile( getTempId(mPictureProfileTempIdMap, cursor), getType(cursor), @@ -442,7 +626,7 @@ public class MediaQualityService extends SystemService { ); } - private SoundProfile getSoundProfileWithTempIdFromCursor(Cursor cursor) { + private SoundProfile convertCursorToSoundProfileWithTempId(Cursor cursor) { return new SoundProfile( getTempId(mSoundProfileTempIdMap, cursor), getType(cursor), @@ -502,7 +686,7 @@ public class MediaQualityService extends SystemService { ) { List<PictureProfile> pictureProfiles = new ArrayList<>(); while (cursor.moveToNext()) { - pictureProfiles.add(getPictureProfileWithTempIdFromCursor(cursor)); + pictureProfiles.add(convertCursorToPictureProfileWithTempId(cursor)); } return pictureProfiles; } @@ -517,30 +701,64 @@ public class MediaQualityService extends SystemService { ) { List<SoundProfile> soundProfiles = new ArrayList<>(); while (cursor.moveToNext()) { - soundProfiles.add(getSoundProfileWithTempIdFromCursor(cursor)); + soundProfiles.add(convertCursorToSoundProfileWithTempId(cursor)); } return soundProfiles; } } + private void notifyError(String profileId, int errorCode, int uid, int pid) { + UserState userState = getOrCreateUserStateLocked(UserHandle.USER_SYSTEM); + int n = userState.mCallbacks.beginBroadcast(); + + for (int i = 0; i < n; ++i) { + try { + IPictureProfileCallback callback = userState.mCallbacks.getBroadcastItem(i); + Pair<Integer, Integer> pidUid = userState.mCallbackPidUidMap.get(callback); + + if (pidUid.first == pid && pidUid.second == uid) { + userState.mCallbacks.getBroadcastItem(i).onError(profileId, errorCode); + } + } catch (RemoteException e) { + Slog.e(TAG, "failed to report added input to callback", e); + } + } + userState.mCallbacks.finishBroadcast(); + } + @Override public void registerPictureProfileCallback(final IPictureProfileCallback callback) { + int callingPid = Binder.getCallingPid(); + int callingUid = Binder.getCallingUid(); + + UserState userState = getOrCreateUserStateLocked(Binder.getCallingUid()); + userState.mCallbackPidUidMap.put(callback, Pair.create(callingPid, callingUid)); } + @Override public void registerSoundProfileCallback(final ISoundProfileCallback callback) { } @Override public void registerAmbientBacklightCallback(IAmbientBacklightCallback callback) { + if (!hasReadColorZonesPermission()) { + //TODO: error handling + } } @Override public void setAmbientBacklightSettings( AmbientBacklightSettings settings, UserHandle user) { + if (!hasReadColorZonesPermission()) { + //TODO: error handling + } } @Override public void setAmbientBacklightEnabled(boolean enabled, UserHandle user) { + if (!hasReadColorZonesPermission()) { + //TODO: error handling + } } @Override @@ -551,20 +769,34 @@ public class MediaQualityService extends SystemService { @Override public List<String> getPictureProfileAllowList(UserHandle user) { + if (!hasGlobalPictureQualityServicePermission()) { + //TODO: error handling + return new ArrayList<>(); + } return new ArrayList<>(); } @Override public void setPictureProfileAllowList(List<String> packages, UserHandle user) { + if (!hasGlobalPictureQualityServicePermission()) { + //TODO: error handling + } } @Override public List<String> getSoundProfileAllowList(UserHandle user) { + if (!hasGlobalSoundQualityServicePermission()) { + //TODO: error handling + return new ArrayList<>(); + } return new ArrayList<>(); } @Override public void setSoundProfileAllowList(List<String> packages, UserHandle user) { + if (!hasGlobalSoundQualityServicePermission()) { + //TODO: error handling + } } @Override @@ -574,6 +806,9 @@ public class MediaQualityService extends SystemService { @Override public void setAutoPictureQualityEnabled(boolean enabled, UserHandle user) { + if (!hasGlobalPictureQualityServicePermission()) { + //TODO: error handling + } } @Override @@ -583,6 +818,9 @@ public class MediaQualityService extends SystemService { @Override public void setSuperResolutionEnabled(boolean enabled, UserHandle user) { + if (!hasGlobalPictureQualityServicePermission()) { + //TODO: error handling + } } @Override @@ -592,6 +830,9 @@ public class MediaQualityService extends SystemService { @Override public void setAutoSoundQualityEnabled(boolean enabled, UserHandle user) { + if (!hasGlobalSoundQualityServicePermission()) { + //TODO: error handling + } } @Override @@ -604,4 +845,38 @@ public class MediaQualityService extends SystemService { return false; } } + + private class MediaQualityManagerCallbackList extends + RemoteCallbackList<IPictureProfileCallback> { + @Override + public void onCallbackDied(IPictureProfileCallback callback) { + //todo + } + } + + private final class UserState { + // A list of callbacks. + private final MediaQualityManagerCallbackList mCallbacks = + new MediaQualityManagerCallbackList(); + + private final Map<IPictureProfileCallback, Pair<Integer, Integer>> mCallbackPidUidMap = + new HashMap<>(); + + private UserState(Context context, int userId) { + + } + } + + private UserState getOrCreateUserStateLocked(int userId) { + UserState userState = getUserStateLocked(userId); + if (userState == null) { + userState = new UserState(mContext, userId); + mUserStates.put(userId, userState); + } + return userState; + } + + private UserState getUserStateLocked(int userId) { + return mUserStates.get(userId); + } } diff --git a/services/core/java/com/android/server/notification/ConditionProviders.java b/services/core/java/com/android/server/notification/ConditionProviders.java index 0b40d64e3a09..3f2c2228e453 100644 --- a/services/core/java/com/android/server/notification/ConditionProviders.java +++ b/services/core/java/com/android/server/notification/ConditionProviders.java @@ -325,7 +325,7 @@ public class ConditionProviders extends ManagedServices { for (int i = 0; i < N; i++) { final Condition c = conditions[i]; if (mCallback != null) { - mCallback.onConditionChanged(c.id, c); + mCallback.onConditionChanged(c.id, c, info.uid); } } } @@ -515,7 +515,7 @@ public class ConditionProviders extends ManagedServices { public interface Callback { void onServiceAdded(ComponentName component); - void onConditionChanged(Uri id, Condition condition); + void onConditionChanged(Uri id, Condition condition, int callerUid); } } diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index f50e8aa7eb7b..9567c818fa18 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -5903,8 +5903,9 @@ public class NotificationManagerService extends SystemService { // TODO: b/310620812 - Remove getZenRules() when MODES_API is inlined. @Override public List<ZenModeConfig.ZenRule> getZenRules() throws RemoteException { - enforcePolicyAccess(Binder.getCallingUid(), "getZenRules"); - return mZenModeHelper.getZenRules(getCallingZenUser()); + int callingUid = Binder.getCallingUid(); + enforcePolicyAccess(callingUid, "getZenRules"); + return mZenModeHelper.getZenRules(getCallingZenUser(), callingUid); } @Override @@ -5912,15 +5913,17 @@ public class NotificationManagerService extends SystemService { if (!android.app.Flags.modesApi()) { throw new IllegalStateException("getAutomaticZenRules called with flag off!"); } - enforcePolicyAccess(Binder.getCallingUid(), "getAutomaticZenRules"); - return mZenModeHelper.getAutomaticZenRules(getCallingZenUser()); + int callingUid = Binder.getCallingUid(); + enforcePolicyAccess(callingUid, "getAutomaticZenRules"); + return mZenModeHelper.getAutomaticZenRules(getCallingZenUser(), callingUid); } @Override public AutomaticZenRule getAutomaticZenRule(String id) throws RemoteException { Objects.requireNonNull(id, "Id is null"); - enforcePolicyAccess(Binder.getCallingUid(), "getAutomaticZenRule"); - return mZenModeHelper.getAutomaticZenRule(getCallingZenUser(), id); + int callingUid = Binder.getCallingUid(); + enforcePolicyAccess(callingUid, "getAutomaticZenRule"); + return mZenModeHelper.getAutomaticZenRule(getCallingZenUser(), id, callingUid); } @Override @@ -6065,8 +6068,9 @@ public class NotificationManagerService extends SystemService { @Condition.State public int getAutomaticZenRuleState(@NonNull String id) { Objects.requireNonNull(id, "id is null"); - enforcePolicyAccess(Binder.getCallingUid(), "getAutomaticZenRuleState"); - return mZenModeHelper.getAutomaticZenRuleState(getCallingZenUser(), id); + int callingUid = Binder.getCallingUid(); + enforcePolicyAccess(callingUid, "getAutomaticZenRuleState"); + return mZenModeHelper.getAutomaticZenRuleState(getCallingZenUser(), id, callingUid); } @Override diff --git a/services/core/java/com/android/server/notification/ZenModeConditions.java b/services/core/java/com/android/server/notification/ZenModeConditions.java index 52d0c41614d5..d44baeb58a28 100644 --- a/services/core/java/com/android/server/notification/ZenModeConditions.java +++ b/services/core/java/com/android/server/notification/ZenModeConditions.java @@ -113,15 +113,18 @@ public class ZenModeConditions implements ConditionProviders.Callback { } @Override - public void onConditionChanged(Uri id, Condition condition) { + public void onConditionChanged(Uri id, Condition condition, int callingUid) { if (DEBUG) Log.d(TAG, "onConditionChanged " + id + " " + condition); ZenModeConfig config = mHelper.getConfig(); if (config == null) return; - final int callingUid = Binder.getCallingUid(); + if (!Flags.fixCallingUidFromCps()) { + // Old behavior: overwrite with known-bad callingUid (always system_server). + callingUid = Binder.getCallingUid(); + } // This change is known to be for UserHandle.CURRENT because ConditionProviders for // background users are not bound. - mHelper.setAutomaticZenRuleState(UserHandle.CURRENT, id, condition, + mHelper.setAutomaticZenRuleStateFromConditionProvider(UserHandle.CURRENT, id, condition, callingUid == Process.SYSTEM_UID ? ZenModeConfig.ORIGIN_SYSTEM : ZenModeConfig.ORIGIN_APP, callingUid); diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java index b571d62c0cba..0a63f3fb36d0 100644 --- a/services/core/java/com/android/server/notification/ZenModeHelper.java +++ b/services/core/java/com/android/server/notification/ZenModeHelper.java @@ -413,13 +413,13 @@ public class ZenModeHelper { } // TODO: b/310620812 - Make private (or inline) when MODES_API is inlined. - public List<ZenRule> getZenRules(UserHandle user) { + public List<ZenRule> getZenRules(UserHandle user, int callingUid) { List<ZenRule> rules = new ArrayList<>(); synchronized (mConfigLock) { ZenModeConfig config = getConfigLocked(user); if (config == null) return rules; for (ZenRule rule : config.automaticRules.values()) { - if (canManageAutomaticZenRule(rule)) { + if (canManageAutomaticZenRule(rule, callingUid)) { rules.add(rule); } } @@ -432,8 +432,8 @@ public class ZenModeHelper { * (which means the owned rules for a regular app, and every rule for system callers) together * with their ids. */ - Map<String, AutomaticZenRule> getAutomaticZenRules(UserHandle user) { - List<ZenRule> ruleList = getZenRules(user); + Map<String, AutomaticZenRule> getAutomaticZenRules(UserHandle user, int callingUid) { + List<ZenRule> ruleList = getZenRules(user, callingUid); HashMap<String, AutomaticZenRule> rules = new HashMap<>(ruleList.size()); for (ZenRule rule : ruleList) { rules.put(rule.id, zenRuleToAutomaticZenRule(rule)); @@ -441,7 +441,7 @@ public class ZenModeHelper { return rules; } - public AutomaticZenRule getAutomaticZenRule(UserHandle user, String id) { + public AutomaticZenRule getAutomaticZenRule(UserHandle user, String id, int callingUid) { ZenRule rule; synchronized (mConfigLock) { ZenModeConfig config = getConfigLocked(user); @@ -449,7 +449,7 @@ public class ZenModeHelper { rule = config.automaticRules.get(id); } if (rule == null) return null; - if (canManageAutomaticZenRule(rule)) { + if (canManageAutomaticZenRule(rule, callingUid)) { return zenRuleToAutomaticZenRule(rule); } return null; @@ -591,7 +591,7 @@ public class ZenModeHelper { + " reason=" + reason); } ZenModeConfig.ZenRule oldRule = config.automaticRules.get(ruleId); - if (oldRule == null || !canManageAutomaticZenRule(oldRule)) { + if (oldRule == null || !canManageAutomaticZenRule(oldRule, callingUid)) { throw new SecurityException( "Cannot update rules not owned by your condition provider"); } @@ -859,7 +859,7 @@ public class ZenModeHelper { newConfig = config.copy(); ZenRule ruleToRemove = newConfig.automaticRules.get(id); if (ruleToRemove == null) return false; - if (canManageAutomaticZenRule(ruleToRemove)) { + if (canManageAutomaticZenRule(ruleToRemove, callingUid)) { newConfig.automaticRules.remove(id); maybePreserveRemovedRule(newConfig, ruleToRemove, origin); if (ruleToRemove.getPkg() != null @@ -893,7 +893,8 @@ public class ZenModeHelper { newConfig = config.copy(); for (int i = newConfig.automaticRules.size() - 1; i >= 0; i--) { ZenRule rule = newConfig.automaticRules.get(newConfig.automaticRules.keyAt(i)); - if (Objects.equals(rule.getPkg(), packageName) && canManageAutomaticZenRule(rule)) { + if (Objects.equals(rule.getPkg(), packageName) + && canManageAutomaticZenRule(rule, callingUid)) { newConfig.automaticRules.removeAt(i); maybePreserveRemovedRule(newConfig, rule, origin); } @@ -938,14 +939,14 @@ public class ZenModeHelper { } @Condition.State - int getAutomaticZenRuleState(UserHandle user, String id) { + int getAutomaticZenRuleState(UserHandle user, String id, int callingUid) { synchronized (mConfigLock) { ZenModeConfig config = getConfigLocked(user); if (config == null) { return Condition.STATE_UNKNOWN; } ZenRule rule = config.automaticRules.get(id); - if (rule == null || !canManageAutomaticZenRule(rule)) { + if (rule == null || !canManageAutomaticZenRule(rule, callingUid)) { return Condition.STATE_UNKNOWN; } if (Flags.modesApi() && Flags.modesUi()) { @@ -968,7 +969,7 @@ public class ZenModeHelper { newConfig = config.copy(); ZenRule rule = newConfig.automaticRules.get(id); if (Flags.modesApi()) { - if (rule != null && canManageAutomaticZenRule(rule)) { + if (rule != null && canManageAutomaticZenRule(rule, callingUid)) { setAutomaticZenRuleStateLocked(newConfig, Collections.singletonList(rule), condition, origin, callingUid); } @@ -980,8 +981,8 @@ public class ZenModeHelper { } } - void setAutomaticZenRuleState(UserHandle user, Uri ruleDefinition, Condition condition, - @ConfigOrigin int origin, int callingUid) { + void setAutomaticZenRuleStateFromConditionProvider(UserHandle user, Uri ruleDefinition, + Condition condition, @ConfigOrigin int origin, int callingUid) { checkSetRuleStateOrigin("setAutomaticZenRuleState(Uri ruleDefinition)", origin); ZenModeConfig newConfig; synchronized (mConfigLock) { @@ -992,7 +993,7 @@ public class ZenModeHelper { List<ZenRule> matchingRules = findMatchingRules(newConfig, ruleDefinition, condition); if (Flags.modesApi()) { for (int i = matchingRules.size() - 1; i >= 0; i--) { - if (!canManageAutomaticZenRule(matchingRules.get(i))) { + if (!canManageAutomaticZenRule(matchingRules.get(i), callingUid)) { matchingRules.remove(i); } } @@ -1125,15 +1126,21 @@ public class ZenModeHelper { return count; } - public boolean canManageAutomaticZenRule(ZenRule rule) { - final int callingUid = Binder.getCallingUid(); + public boolean canManageAutomaticZenRule(ZenRule rule, int callingUid) { + if (!com.android.server.notification.Flags.fixCallingUidFromCps()) { + // Old behavior: ignore supplied callingUid and instead obtain it here. Will be + // incorrect if not currently handling a Binder call. + callingUid = Binder.getCallingUid(); + } + if (callingUid == 0 || callingUid == Process.SYSTEM_UID) { + // Checked specifically, because checkCallingPermission() will fail. return true; } else if (mContext.checkCallingPermission(android.Manifest.permission.MANAGE_NOTIFICATIONS) == PackageManager.PERMISSION_GRANTED) { return true; } else { - String[] packages = mPm.getPackagesForUid(Binder.getCallingUid()); + String[] packages = mPm.getPackagesForUid(callingUid); if (packages != null) { final int packageCount = packages.length; for (int i = 0; i < packageCount; i++) { @@ -2902,8 +2909,8 @@ public class ZenModeHelper { } /** - * Checks that the {@code origin} supplied to {@link #setAutomaticZenRuleState} overloads makes - * sense. + * Checks that the {@code origin} supplied to {@link #setAutomaticZenRuleState} or + * {@link #setAutomaticZenRuleStateFromConditionProvider} makes sense. */ private static void checkSetRuleStateOrigin(String method, @ConfigOrigin int origin) { if (!Flags.modesApi()) { diff --git a/services/core/java/com/android/server/notification/flags.aconfig b/services/core/java/com/android/server/notification/flags.aconfig index f15c23e110a4..2b4d71e85dc0 100644 --- a/services/core/java/com/android/server/notification/flags.aconfig +++ b/services/core/java/com/android/server/notification/flags.aconfig @@ -196,4 +196,14 @@ flag { metadata { purpose: PURPOSE_BUGFIX } -}
\ No newline at end of file +} + +flag { + name: "fix_calling_uid_from_cps" + namespace: "systemui" + description: "Correctly checks zen rule ownership when a CPS notifies with a Condition" + bug: "379722187" + metadata { + purpose: PURPOSE_BUGFIX + } +} diff --git a/services/core/java/com/android/server/pm/ResilientAtomicFile.java b/services/core/java/com/android/server/pm/ResilientAtomicFile.java index 3aefc5a64926..473ed6136e9a 100644 --- a/services/core/java/com/android/server/pm/ResilientAtomicFile.java +++ b/services/core/java/com/android/server/pm/ResilientAtomicFile.java @@ -23,6 +23,7 @@ import android.os.ParcelFileDescriptor; import android.util.Log; import android.util.Slog; +import com.android.internal.annotations.VisibleForTesting; import com.android.server.security.FileIntegrity; import libcore.io.IoUtils; @@ -121,6 +122,11 @@ final class ResilientAtomicFile implements Closeable { } public void finishWrite(FileOutputStream str) throws IOException { + finishWrite(str, true /* doFsVerity */); + } + + @VisibleForTesting + public void finishWrite(FileOutputStream str, final boolean doFsVerity) throws IOException { if (mMainOutStream != str) { throw new IllegalStateException("Invalid incoming stream."); } @@ -145,13 +151,15 @@ final class ResilientAtomicFile implements Closeable { finalizeOutStream(reserveOutStream); } - // Protect both main and reserve using fs-verity. - try (ParcelFileDescriptor mainPfd = ParcelFileDescriptor.dup(mainInStream.getFD()); - ParcelFileDescriptor copyPfd = ParcelFileDescriptor.dup(reserveInStream.getFD())) { - FileIntegrity.setUpFsVerity(mainPfd); - FileIntegrity.setUpFsVerity(copyPfd); - } catch (IOException e) { - Slog.e(LOG_TAG, "Failed to verity-protect " + mDebugName, e); + if (doFsVerity) { + // Protect both main and reserve using fs-verity. + try (ParcelFileDescriptor mainPfd = ParcelFileDescriptor.dup(mainInStream.getFD()); + ParcelFileDescriptor copyPfd = ParcelFileDescriptor.dup(reserveInStream.getFD())) { + FileIntegrity.setUpFsVerity(mainPfd); + FileIntegrity.setUpFsVerity(copyPfd); + } catch (IOException e) { + Slog.e(LOG_TAG, "Failed to verity-protect " + mDebugName, e); + } } } catch (IOException e) { Slog.e(LOG_TAG, "Failed to write reserve copy " + mDebugName + ": " + mReserveCopy, e); diff --git a/services/core/java/com/android/server/pm/ShortcutPackageItem.java b/services/core/java/com/android/server/pm/ShortcutPackageItem.java index 44789e4c4de2..027da4986ce6 100644 --- a/services/core/java/com/android/server/pm/ShortcutPackageItem.java +++ b/services/core/java/com/android/server/pm/ShortcutPackageItem.java @@ -179,7 +179,7 @@ abstract class ShortcutPackageItem { itemOut.endDocument(); os.flush(); - file.finishWrite(os); + mShortcutUser.mService.injectFinishWrite(file, os); } catch (XmlPullParserException | IOException e) { Slog.e(TAG, "Failed to write to file " + file.getBaseFile(), e); file.failWrite(os); diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java index 2785da5cbdbd..373c1ed3c386 100644 --- a/services/core/java/com/android/server/pm/ShortcutService.java +++ b/services/core/java/com/android/server/pm/ShortcutService.java @@ -1008,7 +1008,7 @@ public class ShortcutService extends IShortcutService.Stub { out.endDocument(); // Close. - file.finishWrite(outs); + injectFinishWrite(file, outs); } catch (IOException e) { Slog.w(TAG, "Failed to write to file " + file.getBaseFile(), e); file.failWrite(outs); @@ -1096,7 +1096,7 @@ public class ShortcutService extends IShortcutService.Stub { saveUserInternalLocked(userId, os, /* forBackup= */ false); } - file.finishWrite(os); + injectFinishWrite(file, os); // Remove all dangling bitmap files. cleanupDanglingBitmapDirectoriesLocked(userId); @@ -5067,6 +5067,12 @@ public class ShortcutService extends IShortcutService.Stub { return Build.FINGERPRINT; } + // Injection point. + void injectFinishWrite(@NonNull final ResilientAtomicFile file, + @NonNull final FileOutputStream os) throws IOException { + file.finishWrite(os); + } + final void wtf(String message) { wtf(message, /* exception= */ null); } diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 5ab59657d4ce..516213b32354 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -562,8 +562,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { volatile boolean mPowerKeyHandled; volatile boolean mBackKeyHandled; volatile boolean mEndCallKeyHandled; - volatile boolean mCameraGestureTriggered; - volatile boolean mCameraGestureTriggeredDuringGoingToSleep; + volatile boolean mPowerButtonLaunchGestureTriggered; + volatile boolean mPowerButtonLaunchGestureTriggeredDuringGoingToSleep; /** * {@code true} if the device is entering a low-power state; {@code false otherwise}. @@ -5893,7 +5893,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { if (mGestureLauncherService == null) { return false; } - mCameraGestureTriggered = false; + mPowerButtonLaunchGestureTriggered = false; final MutableBoolean outLaunched = new MutableBoolean(false); final boolean intercept = mGestureLauncherService.interceptPowerKeyDown(event, interactive, outLaunched); @@ -5903,9 +5903,9 @@ public class PhoneWindowManager implements WindowManagerPolicy { // detector from processing the power key later on. return intercept; } - mCameraGestureTriggered = true; + mPowerButtonLaunchGestureTriggered = true; if (mRequestedOrSleepingDefaultDisplay) { - mCameraGestureTriggeredDuringGoingToSleep = true; + mPowerButtonLaunchGestureTriggeredDuringGoingToSleep = true; // Wake device up early to prevent display doing redundant turning off/on stuff. mWindowWakeUpPolicy.wakeUpFromPowerKeyCameraGesture(); } @@ -6282,13 +6282,13 @@ public class PhoneWindowManager implements WindowManagerPolicy { if (mKeyguardDelegate != null) { mKeyguardDelegate.onFinishedGoingToSleep(pmSleepReason, - mCameraGestureTriggeredDuringGoingToSleep); + mPowerButtonLaunchGestureTriggeredDuringGoingToSleep); } if (mDisplayFoldController != null) { mDisplayFoldController.finishedGoingToSleep(); } - mCameraGestureTriggeredDuringGoingToSleep = false; - mCameraGestureTriggered = false; + mPowerButtonLaunchGestureTriggeredDuringGoingToSleep = false; + mPowerButtonLaunchGestureTriggered = false; } // Called on the PowerManager's Notifier thread. @@ -6319,10 +6319,10 @@ public class PhoneWindowManager implements WindowManagerPolicy { mDefaultDisplayRotation.updateOrientationListener(); if (mKeyguardDelegate != null) { - mKeyguardDelegate.onStartedWakingUp(pmWakeReason, mCameraGestureTriggered); + mKeyguardDelegate.onStartedWakingUp(pmWakeReason, mPowerButtonLaunchGestureTriggered); } - mCameraGestureTriggered = false; + mPowerButtonLaunchGestureTriggered = false; } // Called on the PowerManager's Notifier thread. diff --git a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java index da8b01ac86fb..587447b8af26 100644 --- a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java +++ b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java @@ -198,7 +198,7 @@ public class KeyguardServiceDelegate { if (mKeyguardState.interactiveState == INTERACTIVE_STATE_AWAKE || mKeyguardState.interactiveState == INTERACTIVE_STATE_WAKING) { mKeyguardService.onStartedWakingUp(PowerManager.WAKE_REASON_UNKNOWN, - false /* cameraGestureTriggered */); + false /* powerButtonLaunchGestureTriggered */); } if (mKeyguardState.interactiveState == INTERACTIVE_STATE_AWAKE) { mKeyguardService.onFinishedWakingUp(); @@ -319,10 +319,10 @@ public class KeyguardServiceDelegate { } public void onStartedWakingUp( - @PowerManager.WakeReason int pmWakeReason, boolean cameraGestureTriggered) { + @PowerManager.WakeReason int pmWakeReason, boolean powerButtonLaunchGestureTriggered) { if (mKeyguardService != null) { if (DEBUG) Log.v(TAG, "onStartedWakingUp()"); - mKeyguardService.onStartedWakingUp(pmWakeReason, cameraGestureTriggered); + mKeyguardService.onStartedWakingUp(pmWakeReason, powerButtonLaunchGestureTriggered); } mKeyguardState.interactiveState = INTERACTIVE_STATE_WAKING; } @@ -383,9 +383,11 @@ public class KeyguardServiceDelegate { } public void onFinishedGoingToSleep( - @PowerManager.GoToSleepReason int pmSleepReason, boolean cameraGestureTriggered) { + @PowerManager.GoToSleepReason int pmSleepReason, + boolean powerButtonLaunchGestureTriggered) { if (mKeyguardService != null) { - mKeyguardService.onFinishedGoingToSleep(pmSleepReason, cameraGestureTriggered); + mKeyguardService.onFinishedGoingToSleep(pmSleepReason, + powerButtonLaunchGestureTriggered); } mKeyguardState.interactiveState = INTERACTIVE_STATE_SLEEP; } diff --git a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceWrapper.java b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceWrapper.java index cd789eaed1b3..f2342e0d5688 100644 --- a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceWrapper.java +++ b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceWrapper.java @@ -113,9 +113,10 @@ public class KeyguardServiceWrapper implements IKeyguardService { @Override public void onFinishedGoingToSleep( - @PowerManager.GoToSleepReason int pmSleepReason, boolean cameraGestureTriggered) { + @PowerManager.GoToSleepReason int pmSleepReason, + boolean powerButtonLaunchGestureTriggered) { try { - mService.onFinishedGoingToSleep(pmSleepReason, cameraGestureTriggered); + mService.onFinishedGoingToSleep(pmSleepReason, powerButtonLaunchGestureTriggered); } catch (RemoteException e) { Slog.w(TAG , "Remote Exception", e); } @@ -123,9 +124,9 @@ public class KeyguardServiceWrapper implements IKeyguardService { @Override public void onStartedWakingUp( - @PowerManager.WakeReason int pmWakeReason, boolean cameraGestureTriggered) { + @PowerManager.WakeReason int pmWakeReason, boolean powerButtonLaunchGestureTriggered) { try { - mService.onStartedWakingUp(pmWakeReason, cameraGestureTriggered); + mService.onStartedWakingUp(pmWakeReason, powerButtonLaunchGestureTriggered); } catch (RemoteException e) { Slog.w(TAG , "Remote Exception", e); } diff --git a/services/core/java/com/android/server/resources/ResourcesManagerShellCommand.java b/services/core/java/com/android/server/resources/ResourcesManagerShellCommand.java index a75d110e3cd1..17739712d65a 100644 --- a/services/core/java/com/android/server/resources/ResourcesManagerShellCommand.java +++ b/services/core/java/com/android/server/resources/ResourcesManagerShellCommand.java @@ -88,6 +88,5 @@ public class ResourcesManagerShellCommand extends ShellCommand { out.println(" Print this help text."); out.println(" dump <PROCESS>"); out.println(" Dump the Resources objects in use as well as the history of Resources"); - } } diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 093df8c5075c..29f1f93a844f 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -3209,7 +3209,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A true /* forActivity */)) { return false; } - if (mAppCompatController.mAllowRestrictedResizability.getAsBoolean()) { + if (mAppCompatController.getResizeOverrides().allowRestrictedResizability()) { return false; } // If the user preference respects aspect ratio, then it becomes non-resizable. @@ -3240,8 +3240,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // The caller will check both application and activity level property. return true; } - return !AppCompatController.allowRestrictedResizability(wms.mContext.getPackageManager(), - appInfo.packageName); + return !AppCompatResizeOverrides.allowRestrictedResizability( + wms.mContext.getPackageManager(), appInfo.packageName); } boolean isResizeable() { @@ -8435,8 +8435,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A */ @ActivityInfo.SizeChangesSupportMode private int supportsSizeChanges() { - if (mAppCompatController.getAppCompatResizeOverrides() - .shouldOverrideForceNonResizeApp()) { + final AppCompatResizeOverrides resizeOverrides = mAppCompatController.getResizeOverrides(); + if (resizeOverrides.shouldOverrideForceNonResizeApp()) { return SIZE_CHANGES_UNSUPPORTED_OVERRIDE; } @@ -8444,8 +8444,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A return SIZE_CHANGES_SUPPORTED_METADATA; } - if (mAppCompatController.getAppCompatResizeOverrides() - .shouldOverrideForceResizeApp()) { + if (resizeOverrides.shouldOverrideForceResizeApp()) { return SIZE_CHANGES_SUPPORTED_OVERRIDE; } @@ -10221,7 +10220,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A mAppCompatController.getAppCompatOrientationOverrides() .shouldIgnoreOrientationRequestLoop()); proto.write(SHOULD_OVERRIDE_FORCE_RESIZE_APP, - mAppCompatController.getAppCompatResizeOverrides().shouldOverrideForceResizeApp()); + mAppCompatController.getResizeOverrides().shouldOverrideForceResizeApp()); proto.write(SHOULD_ENABLE_USER_ASPECT_RATIO_SETTINGS, mAppCompatController.getAppCompatAspectRatioOverrides() .shouldEnableUserAspectRatioSettings()); diff --git a/services/core/java/com/android/server/wm/AppCompatController.java b/services/core/java/com/android/server/wm/AppCompatController.java index 4433d64f0d00..0967078deac3 100644 --- a/services/core/java/com/android/server/wm/AppCompatController.java +++ b/services/core/java/com/android/server/wm/AppCompatController.java @@ -15,23 +15,17 @@ */ package com.android.server.wm; -import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_RESTRICTED_RESIZABILITY; - import android.annotation.NonNull; import android.content.pm.PackageManager; import com.android.server.wm.utils.OptPropFactory; import java.io.PrintWriter; -import java.util.function.BooleanSupplier; /** * Allows the interaction with all the app compat policies and configurations */ class AppCompatController { - - @NonNull - private final ActivityRecord mActivityRecord; @NonNull private final TransparentPolicy mTransparentPolicy; @NonNull @@ -50,56 +44,28 @@ class AppCompatController { private final AppCompatLetterboxPolicy mAppCompatLetterboxPolicy; @NonNull private final AppCompatSizeCompatModePolicy mAppCompatSizeCompatModePolicy; - @NonNull - final BooleanSupplier mAllowRestrictedResizability; AppCompatController(@NonNull WindowManagerService wmService, @NonNull ActivityRecord activityRecord) { - mActivityRecord = activityRecord; final PackageManager packageManager = wmService.mContext.getPackageManager(); final OptPropFactory optPropBuilder = new OptPropFactory(packageManager, activityRecord.packageName); mAppCompatDeviceStateQuery = new AppCompatDeviceStateQuery(activityRecord); mTransparentPolicy = new TransparentPolicy(activityRecord, wmService.mAppCompatConfiguration); - mAppCompatOverrides = new AppCompatOverrides(activityRecord, + mAppCompatOverrides = new AppCompatOverrides(activityRecord, packageManager, wmService.mAppCompatConfiguration, optPropBuilder, mAppCompatDeviceStateQuery); mOrientationPolicy = new AppCompatOrientationPolicy(activityRecord, mAppCompatOverrides); mAppCompatAspectRatioPolicy = new AppCompatAspectRatioPolicy(activityRecord, mTransparentPolicy, mAppCompatOverrides); - mAppCompatReachabilityPolicy = new AppCompatReachabilityPolicy(mActivityRecord, + mAppCompatReachabilityPolicy = new AppCompatReachabilityPolicy(activityRecord, wmService.mAppCompatConfiguration); - mAppCompatLetterboxPolicy = new AppCompatLetterboxPolicy(mActivityRecord, + mAppCompatLetterboxPolicy = new AppCompatLetterboxPolicy(activityRecord, wmService.mAppCompatConfiguration); mDesktopAppCompatAspectRatioPolicy = new DesktopAppCompatAspectRatioPolicy(activityRecord, mAppCompatOverrides, mTransparentPolicy, wmService.mAppCompatConfiguration); - mAppCompatSizeCompatModePolicy = new AppCompatSizeCompatModePolicy(mActivityRecord, + mAppCompatSizeCompatModePolicy = new AppCompatSizeCompatModePolicy(activityRecord, mAppCompatOverrides); - mAllowRestrictedResizability = AppCompatUtils.asLazy(() -> { - // Application level. - if (allowRestrictedResizability(packageManager, mActivityRecord.packageName)) { - return true; - } - // Activity level. - try { - return packageManager.getPropertyAsUser( - PROPERTY_COMPAT_ALLOW_RESTRICTED_RESIZABILITY, - mActivityRecord.mActivityComponent.getPackageName(), - mActivityRecord.mActivityComponent.getClassName(), - mActivityRecord.mUserId).getBoolean(); - } catch (PackageManager.NameNotFoundException e) { - return false; - } - }); - } - - static boolean allowRestrictedResizability(PackageManager pm, String packageName) { - try { - return pm.getProperty(PROPERTY_COMPAT_ALLOW_RESTRICTED_RESIZABILITY, packageName) - .getBoolean(); - } catch (PackageManager.NameNotFoundException e) { - return false; - } } @NonNull @@ -138,8 +104,8 @@ class AppCompatController { } @NonNull - AppCompatResizeOverrides getAppCompatResizeOverrides() { - return mAppCompatOverrides.getAppCompatResizeOverrides(); + AppCompatResizeOverrides getResizeOverrides() { + return mAppCompatOverrides.getResizeOverrides(); } @NonNull diff --git a/services/core/java/com/android/server/wm/AppCompatOverrides.java b/services/core/java/com/android/server/wm/AppCompatOverrides.java index 2f03105846bd..58b37becc373 100644 --- a/services/core/java/com/android/server/wm/AppCompatOverrides.java +++ b/services/core/java/com/android/server/wm/AppCompatOverrides.java @@ -17,6 +17,7 @@ package com.android.server.wm; import android.annotation.NonNull; +import android.content.pm.PackageManager; import com.android.server.wm.utils.OptPropFactory; @@ -34,13 +35,14 @@ public class AppCompatOverrides { @NonNull private final AppCompatFocusOverrides mAppCompatFocusOverrides; @NonNull - private final AppCompatResizeOverrides mAppCompatResizeOverrides; + private final AppCompatResizeOverrides mResizeOverrides; @NonNull private final AppCompatReachabilityOverrides mAppCompatReachabilityOverrides; @NonNull private final AppCompatLetterboxOverrides mAppCompatLetterboxOverrides; AppCompatOverrides(@NonNull ActivityRecord activityRecord, + @NonNull PackageManager packageManager, @NonNull AppCompatConfiguration appCompatConfiguration, @NonNull OptPropFactory optPropBuilder, @NonNull AppCompatDeviceStateQuery appCompatDeviceStateQuery) { @@ -55,7 +57,8 @@ public class AppCompatOverrides { mAppCompatReachabilityOverrides); mAppCompatFocusOverrides = new AppCompatFocusOverrides(activityRecord, appCompatConfiguration, optPropBuilder); - mAppCompatResizeOverrides = new AppCompatResizeOverrides(activityRecord, optPropBuilder); + mResizeOverrides = new AppCompatResizeOverrides(activityRecord, packageManager, + optPropBuilder); mAppCompatLetterboxOverrides = new AppCompatLetterboxOverrides(activityRecord, appCompatConfiguration); } @@ -81,8 +84,8 @@ public class AppCompatOverrides { } @NonNull - AppCompatResizeOverrides getAppCompatResizeOverrides() { - return mAppCompatResizeOverrides; + AppCompatResizeOverrides getResizeOverrides() { + return mResizeOverrides; } @NonNull diff --git a/services/core/java/com/android/server/wm/AppCompatResizeOverrides.java b/services/core/java/com/android/server/wm/AppCompatResizeOverrides.java index 60c18254eca7..fa53153dd143 100644 --- a/services/core/java/com/android/server/wm/AppCompatResizeOverrides.java +++ b/services/core/java/com/android/server/wm/AppCompatResizeOverrides.java @@ -19,13 +19,17 @@ package com.android.server.wm; import static android.content.pm.ActivityInfo.FORCE_NON_RESIZE_APP; import static android.content.pm.ActivityInfo.FORCE_RESIZE_APP; import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES; +import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_RESTRICTED_RESIZABILITY; import static com.android.server.wm.AppCompatUtils.isChangeEnabled; import android.annotation.NonNull; +import android.content.pm.PackageManager; import com.android.server.wm.utils.OptPropFactory; +import java.util.function.BooleanSupplier; + /** * Encapsulate app compat logic about resizability. */ @@ -37,11 +41,40 @@ class AppCompatResizeOverrides { @NonNull private final OptPropFactory.OptProp mAllowForceResizeOverrideOptProp; + @NonNull + private final BooleanSupplier mAllowRestrictedResizability; + AppCompatResizeOverrides(@NonNull ActivityRecord activityRecord, + @NonNull PackageManager packageManager, @NonNull OptPropFactory optPropBuilder) { mActivityRecord = activityRecord; mAllowForceResizeOverrideOptProp = optPropBuilder.create( PROPERTY_COMPAT_ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES); + mAllowRestrictedResizability = AppCompatUtils.asLazy(() -> { + // Application level. + if (allowRestrictedResizability(packageManager, mActivityRecord.packageName)) { + return true; + } + // Activity level. + try { + return packageManager.getPropertyAsUser( + PROPERTY_COMPAT_ALLOW_RESTRICTED_RESIZABILITY, + mActivityRecord.mActivityComponent.getPackageName(), + mActivityRecord.mActivityComponent.getClassName(), + mActivityRecord.mUserId).getBoolean(); + } catch (PackageManager.NameNotFoundException e) { + return false; + } + }); + } + + static boolean allowRestrictedResizability(PackageManager pm, String packageName) { + try { + return pm.getProperty(PROPERTY_COMPAT_ALLOW_RESTRICTED_RESIZABILITY, packageName) + .getBoolean(); + } catch (PackageManager.NameNotFoundException e) { + return false; + } } /** @@ -75,4 +108,9 @@ class AppCompatResizeOverrides { return mAllowForceResizeOverrideOptProp.shouldEnableWithOptInOverrideAndOptOutProperty( isChangeEnabled(mActivityRecord, FORCE_NON_RESIZE_APP)); } + + /** @see android.view.WindowManager#PROPERTY_COMPAT_ALLOW_RESTRICTED_RESIZABILITY */ + boolean allowRestrictedResizability() { + return mAllowRestrictedResizability.getAsBoolean(); + } } diff --git a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java index 98ed6f76b2f9..54ae80cfe98a 100644 --- a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java +++ b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java @@ -103,6 +103,8 @@ final class ImeInsetsSourceProvider extends InsetsSourceProvider { // again, so that the control with leash can be eventually dispatched if (!mGivenInsetsReady && isServerVisible() && !givenInsetsPending && mControlTarget != null) { + ProtoLog.d(WM_DEBUG_IME, + "onPostLayout: IME control ready to be dispatched, ws=%s", ws); mGivenInsetsReady = true; ImeTracker.forLogging().onProgress(mStatsToken, ImeTracker.PHASE_WM_POST_LAYOUT_NOTIFY_CONTROLS_CHANGED); @@ -118,6 +120,8 @@ final class ImeInsetsSourceProvider extends InsetsSourceProvider { ImeTracker.PHASE_WM_POST_LAYOUT_NOTIFY_CONTROLS_CHANGED); mStatsToken = null; } else if (wasServerVisible && !isServerVisible()) { + ProtoLog.d(WM_DEBUG_IME, "onPostLayout: setImeShowing(false) was: %s, ws=%s", + isImeShowing(), ws); setImeShowing(false); } } @@ -621,6 +625,7 @@ final class ImeInsetsSourceProvider extends InsetsSourceProvider { // request (cancelling the initial show) or hide request (aborting the initial show). logIsScheduledAndReadyToShowIme(!visible /* aborted */); } + ProtoLog.d(WM_DEBUG_IME, "receiveImeStatsToken: visible=%s", visible); if (visible) { ImeTracker.forLogging().onCancelled( mStatsToken, ImeTracker.PHASE_WM_ABORT_SHOW_IME_POST_LAYOUT); diff --git a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java index 4e030d499c25..3ef360a752f6 100644 --- a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java @@ -112,6 +112,7 @@ import org.mockito.stubbing.Answer; import java.io.BufferedReader; import java.io.ByteArrayOutputStream; import java.io.File; +import java.io.FileOutputStream; import java.io.FileReader; import java.io.IOException; import java.io.InputStreamReader; @@ -556,6 +557,12 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { } @Override + void injectFinishWrite(@NonNull ResilientAtomicFile file, + @NonNull FileOutputStream os) throws IOException { + file.finishWrite(os, false /* doFsVerity */); + } + + @Override void wtf(String message, Throwable th) { // During tests, WTF is fatal. fail(message + " exception: " + th + "\n" + Log.getStackTraceString(th)); diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java index c01283a236c4..0d86d4c3fa28 100644 --- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java +++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java @@ -159,7 +159,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { /** * Test for the first launch path, no settings file available. */ - public void FirstInitialize() { + public void testFirstInitialize() { assertResetTimes(START_TIME, START_TIME + INTERVAL); } @@ -167,7 +167,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { * Test for {@link ShortcutService#getLastResetTimeLocked()} and * {@link ShortcutService#getNextResetTimeLocked()}. */ - public void UpdateAndGetNextResetTimeLocked() { + public void testUpdateAndGetNextResetTimeLocked() { assertResetTimes(START_TIME, START_TIME + INTERVAL); // Advance clock. @@ -196,7 +196,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { /** * Test for the restoration from saved file. */ - public void InitializeFromSavedFile() { + public void testInitializeFromSavedFile() { mInjectedCurrentTimeMillis = START_TIME + 4 * INTERVAL + 50; assertResetTimes(START_TIME + 4 * INTERVAL, START_TIME + 5 * INTERVAL); @@ -220,7 +220,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { // TODO Add various broken cases. } - public void LoadConfig() { + public void testLoadConfig() { mService.updateConfigurationLocked( ConfigConstants.KEY_RESET_INTERVAL_SEC + "=123," + ConfigConstants.KEY_MAX_SHORTCUTS + "=4," @@ -261,22 +261,22 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { // === Test for app side APIs === /** Test for {@link android.content.pm.ShortcutManager#getMaxShortcutCountForActivity()} */ - public void GetMaxDynamicShortcutCount() { + public void testGetMaxDynamicShortcutCount() { assertEquals(MAX_SHORTCUTS, mManager.getMaxShortcutCountForActivity()); } /** Test for {@link android.content.pm.ShortcutManager#getRemainingCallCount()} */ - public void GetRemainingCallCount() { + public void testGetRemainingCallCount() { assertEquals(MAX_UPDATES_PER_INTERVAL, mManager.getRemainingCallCount()); } - public void GetIconMaxDimensions() { + public void testGetIconMaxDimensions() { assertEquals(MAX_ICON_DIMENSION, mManager.getIconMaxWidth()); assertEquals(MAX_ICON_DIMENSION, mManager.getIconMaxHeight()); } /** Test for {@link android.content.pm.ShortcutManager#getRateLimitResetTime()} */ - public void GetRateLimitResetTime() { + public void testGetRateLimitResetTime() { assertEquals(START_TIME + INTERVAL, mManager.getRateLimitResetTime()); mInjectedCurrentTimeMillis = START_TIME + 4 * INTERVAL + 50; @@ -284,7 +284,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { assertEquals(START_TIME + 5 * INTERVAL, mManager.getRateLimitResetTime()); } - public void SetDynamicShortcuts() { + public void testSetDynamicShortcuts() { setCaller(CALLING_PACKAGE_1, USER_10); final Icon icon1 = Icon.createWithResource(getTestContext(), R.drawable.icon1); @@ -354,7 +354,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { }); } - public void AddDynamicShortcuts() { + public void testAddDynamicShortcuts() { setCaller(CALLING_PACKAGE_1, USER_10); final ShortcutInfo si1 = makeShortcut("shortcut1"); @@ -402,7 +402,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { }); } - public void PushDynamicShortcut() { + public void testPushDynamicShortcut() { // Change the max number of shortcuts. mService.updateConfigurationLocked(ConfigConstants.KEY_MAX_SHORTCUTS + "=5," + ShortcutService.ConfigConstants.KEY_SAVE_DELAY_MILLIS + "=1"); @@ -544,7 +544,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { eq(CALLING_PACKAGE_1), eq("s9"), eq(USER_10)); } - public void PushDynamicShortcut_CallsToUsageStatsManagerAreThrottled() + public void testPushDynamicShortcut_CallsToUsageStatsManagerAreThrottled() throws InterruptedException { mService.updateConfigurationLocked( ShortcutService.ConfigConstants.KEY_SAVE_DELAY_MILLIS + "=500"); @@ -576,6 +576,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { Mockito.reset(mMockUsageStatsManagerInternal); for (int i = 2; i <= 10; i++) { final ShortcutInfo si = makeShortcut("s" + i); + setCaller(CALLING_PACKAGE_2, USER_10); mManager.pushDynamicShortcut(si); } verify(mMockUsageStatsManagerInternal, times(0)).reportShortcutUsage( @@ -595,7 +596,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { eq(CALLING_PACKAGE_2), any(), eq(USER_10)); } - public void UnlimitedCalls() { + public void testUnlimitedCalls() { setCaller(CALLING_PACKAGE_1, USER_10); final ShortcutInfo si1 = makeShortcut("shortcut1"); @@ -626,7 +627,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { assertEquals(3, mManager.getRemainingCallCount()); } - public void PublishWithNoActivity() { + public void testPublishWithNoActivity() { // If activity is not explicitly set, use the default one. mRunningUsers.put(USER_11, true); @@ -732,7 +733,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { }); } - public void PublishWithNoActivity_noMainActivityInPackage() { + public void testPublishWithNoActivity_noMainActivityInPackage() { mRunningUsers.put(USER_11, true); runWithCaller(CALLING_PACKAGE_2, USER_11, () -> { @@ -751,7 +752,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { }); } - public void DeleteDynamicShortcuts() { + public void testDeleteDynamicShortcuts() { final ShortcutInfo si1 = makeShortcut("shortcut1"); final ShortcutInfo si2 = makeShortcut("shortcut2"); final ShortcutInfo si3 = makeShortcut("shortcut3"); @@ -792,7 +793,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { assertEquals(2, mManager.getRemainingCallCount()); } - public void DeleteAllDynamicShortcuts() { + public void testDeleteAllDynamicShortcuts() { final ShortcutInfo si1 = makeShortcut("shortcut1"); final ShortcutInfo si2 = makeShortcut("shortcut2"); final ShortcutInfo si3 = makeShortcut("shortcut3"); @@ -821,7 +822,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { assertEquals(1, mManager.getRemainingCallCount()); } - public void Icons() throws IOException { + public void testIcons() throws IOException { final Icon res32x32 = Icon.createWithResource(getTestContext(), R.drawable.black_32x32); final Icon res64x64 = Icon.createWithResource(getTestContext(), R.drawable.black_64x64); final Icon res512x512 = Icon.createWithResource(getTestContext(), R.drawable.black_512x512); @@ -1035,7 +1036,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { */ } - public void CleanupDanglingBitmaps() throws Exception { + public void testCleanupDanglingBitmaps() throws Exception { assertBitmapDirectories(USER_10, EMPTY_STRINGS); assertBitmapDirectories(USER_11, EMPTY_STRINGS); @@ -1204,7 +1205,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { maxSize)); } - public void ShrinkBitmap() { + public void testShrinkBitmap() { checkShrinkBitmap(32, 32, R.drawable.black_512x512, 32); checkShrinkBitmap(511, 511, R.drawable.black_512x512, 511); checkShrinkBitmap(512, 512, R.drawable.black_512x512, 512); @@ -1227,7 +1228,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { return out.getFile(); } - public void OpenIconFileForWrite() throws IOException { + public void testOpenIconFileForWrite() throws IOException { mInjectedCurrentTimeMillis = 1000; final File p10_1_1 = openIconFileForWriteAndGetPath(10, CALLING_PACKAGE_1); @@ -1301,7 +1302,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { assertFalse(p11_1_3.getName().contains("_")); } - public void UpdateShortcuts() { + public void testUpdateShortcuts() { runWithCaller(CALLING_PACKAGE_1, USER_10, () -> { assertTrue(mManager.setDynamicShortcuts(list( makeShortcut("s1"), @@ -1432,7 +1433,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { }); } - public void UpdateShortcuts_icons() { + public void testUpdateShortcuts_icons() { runWithCaller(CALLING_PACKAGE_1, USER_10, () -> { assertTrue(mManager.setDynamicShortcuts(list( makeShortcut("s1") @@ -1526,7 +1527,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { }); } - public void ShortcutManagerGetShortcuts_shortcutTypes() { + public void testShortcutManagerGetShortcuts_shortcutTypes() { // Create 3 manifest and 3 dynamic shortcuts addManifestShortcutResource( @@ -1617,7 +1618,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { assertShortcutIds(mManager.getShortcuts(ShortcutManager.FLAG_MATCH_CACHED), "s1", "s2"); } - public void CachedShortcuts() { + public void testCachedShortcuts() { runWithCaller(CALLING_PACKAGE_1, USER_10, () -> { assertTrue(mManager.setDynamicShortcuts(list(makeShortcut("s1"), makeLongLivedShortcut("s2"), makeLongLivedShortcut("s3"), @@ -1701,7 +1702,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { "s2"); } - public void CachedShortcuts_accessShortcutsPermission() { + public void testCachedShortcuts_accessShortcutsPermission() { runWithCaller(CALLING_PACKAGE_1, USER_10, () -> { assertTrue(mManager.setDynamicShortcuts(list(makeShortcut("s1"), makeLongLivedShortcut("s2"), makeLongLivedShortcut("s3"), @@ -1743,7 +1744,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { assertShortcutIds(mManager.getShortcuts(ShortcutManager.FLAG_MATCH_CACHED), "s3"); } - public void CachedShortcuts_canPassShortcutLimit() { + public void testCachedShortcuts_canPassShortcutLimit() { // Change the max number of shortcuts. mService.updateConfigurationLocked(ConfigConstants.KEY_MAX_SHORTCUTS + "=4"); @@ -1781,7 +1782,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { // === Test for launcher side APIs === - public void GetShortcuts() { + public void testGetShortcuts() { // Set up shortcuts. @@ -1998,7 +1999,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { "s1", "s3"); } - public void GetShortcuts_shortcutKinds() throws Exception { + public void testGetShortcuts_shortcutKinds() throws Exception { // Create 3 manifest and 3 dynamic shortcuts addManifestShortcutResource( new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()), @@ -2109,7 +2110,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { }); } - public void GetShortcuts_resolveStrings() throws Exception { + public void testGetShortcuts_resolveStrings() throws Exception { runWithCaller(CALLING_PACKAGE_1, USER_10, () -> { ShortcutInfo si = new ShortcutInfo.Builder(mClientContext) .setId("id") @@ -2157,7 +2158,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { }); } - public void GetShortcuts_personsFlag() { + public void testGetShortcuts_personsFlag() { ShortcutInfo s = new ShortcutInfo.Builder(mClientContext, "id") .setShortLabel("label") .setActivity(new ComponentName(mClientContext, ShortcutActivity2.class)) @@ -2205,7 +2206,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { } // TODO resource - public void GetShortcutInfo() { + public void testGetShortcutInfo() { // Create shortcuts. setCaller(CALLING_PACKAGE_1); final ShortcutInfo s1_1 = makeShortcut( @@ -2280,7 +2281,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { assertEquals("ABC", findById(list, "s1").getTitle()); } - public void PinShortcutAndGetPinnedShortcuts() { + public void testPinShortcutAndGetPinnedShortcuts() { runWithCaller(CALLING_PACKAGE_1, USER_10, () -> { final ShortcutInfo s1_1 = makeShortcutWithTimestamp("s1", 1000); final ShortcutInfo s1_2 = makeShortcutWithTimestamp("s2", 2000); @@ -2361,7 +2362,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { * This is similar to the above test, except it used "disable" instead of "remove". It also * does "enable". */ - public void DisableAndEnableShortcuts() { + public void testDisableAndEnableShortcuts() { runWithCaller(CALLING_PACKAGE_1, USER_10, () -> { final ShortcutInfo s1_1 = makeShortcutWithTimestamp("s1", 1000); final ShortcutInfo s1_2 = makeShortcutWithTimestamp("s2", 2000); @@ -2486,7 +2487,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { }); } - public void DisableShortcuts_thenRepublish() { + public void testDisableShortcuts_thenRepublish() { runWithCaller(CALLING_PACKAGE_1, USER_10, () -> { assertTrue(mManager.setDynamicShortcuts(list( makeShortcut("s1"), makeShortcut("s2"), makeShortcut("s3")))); @@ -2556,7 +2557,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { }); } - public void PinShortcutAndGetPinnedShortcuts_multi() { + public void testPinShortcutAndGetPinnedShortcuts_multi() { // Create some shortcuts. runWithCaller(CALLING_PACKAGE_1, USER_10, () -> { assertTrue(mManager.setDynamicShortcuts(list( @@ -2832,7 +2833,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { }); } - public void PinShortcutAndGetPinnedShortcuts_assistant() { + public void testPinShortcutAndGetPinnedShortcuts_assistant() { // Create some shortcuts. runWithCaller(CALLING_PACKAGE_1, USER_10, () -> { assertTrue(mManager.setDynamicShortcuts(list( @@ -2888,7 +2889,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { }); } - public void PinShortcutAndGetPinnedShortcuts_crossProfile_plusLaunch() { + public void testPinShortcutAndGetPinnedShortcuts_crossProfile_plusLaunch() { // Create some shortcuts. runWithCaller(CALLING_PACKAGE_1, USER_10, () -> { assertTrue(mManager.setDynamicShortcuts(list( @@ -3477,7 +3478,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { }); } - public void StartShortcut() { + public void testStartShortcut() { // Create some shortcuts. runWithCaller(CALLING_PACKAGE_1, USER_10, () -> { final ShortcutInfo s1_1 = makeShortcut( @@ -3612,7 +3613,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { // TODO Check extra, etc } - public void LauncherCallback() throws Throwable { + public void testLauncherCallback() throws Throwable { // Disable throttling for this test. mService.updateConfigurationLocked( ConfigConstants.KEY_MAX_UPDATES_PER_INTERVAL + "=99999999," @@ -3778,7 +3779,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { .isEmpty(); } - public void LauncherCallback_crossProfile() throws Throwable { + public void testLauncherCallback_crossProfile() throws Throwable { prepareCrossProfileDataSet(); final Handler h = new Handler(Looper.getMainLooper()); @@ -3901,7 +3902,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { // === Test for persisting === - public void SaveAndLoadUser_empty() { + public void testSaveAndLoadUser_empty() { assertTrue(mManager.setDynamicShortcuts(list())); Log.i(TAG, "Saved state"); @@ -3918,7 +3919,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { /** * Try save and load, also stop/start the user. */ - public void SaveAndLoadUser() { + public void testSaveAndLoadUser() { // First, create some shortcuts and save. runWithCaller(CALLING_PACKAGE_1, USER_10, () -> { final Icon icon1 = Icon.createWithResource(getTestContext(), R.drawable.black_64x16); @@ -4059,7 +4060,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { // TODO Check all other fields } - public void LoadCorruptedShortcuts() throws Exception { + public void testLoadCorruptedShortcuts() throws Exception { initService(); addPackage("com.android.chrome", 0, 0); @@ -4073,7 +4074,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { assertNull(ShortcutPackage.loadFromFile(mService, user, corruptedShortcutPackage, false)); } - public void SaveCorruptAndLoadUser() throws Exception { + public void testSaveCorruptAndLoadUser() throws Exception { // First, create some shortcuts and save. runWithCaller(CALLING_PACKAGE_1, USER_10, () -> { final Icon icon1 = Icon.createWithResource(getTestContext(), R.drawable.black_64x16); @@ -4229,7 +4230,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { // TODO Check all other fields } - public void CleanupPackage() { + public void testCleanupPackage() { runWithCaller(CALLING_PACKAGE_1, USER_10, () -> { assertTrue(mManager.setDynamicShortcuts(list( makeShortcut("s0_1")))); @@ -4506,7 +4507,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { mService.saveDirtyInfo(); } - public void CleanupPackage_republishManifests() { + public void testCleanupPackage_republishManifests() { addManifestShortcutResource( new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()), R.xml.shortcut_2); @@ -4574,7 +4575,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { }); } - public void HandleGonePackage_crossProfile() { + public void testHandleGonePackage_crossProfile() { // Create some shortcuts. runWithCaller(CALLING_PACKAGE_1, USER_10, () -> { assertTrue(mManager.setDynamicShortcuts(list( @@ -4846,7 +4847,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { assertEquals(expected, spi.canRestoreTo(mService, pi, true)); } - public void CanRestoreTo() { + public void testCanRestoreTo() { addPackage(CALLING_PACKAGE_1, CALLING_UID_1, 10, "sig1"); addPackage(CALLING_PACKAGE_2, CALLING_UID_2, 10, "sig1", "sig2"); addPackage(CALLING_PACKAGE_3, CALLING_UID_3, 10, "sig1"); @@ -4909,7 +4910,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { checkCanRestoreTo(DISABLED_REASON_BACKUP_NOT_SUPPORTED, spi3, true, 10, true, "sig1"); } - public void HandlePackageDelete() { + public void testHandlePackageDelete() { checkHandlePackageDeleteInner((userId, packageName) -> { uninstallPackage(userId, packageName); mService.mPackageMonitor.onReceive(getTestContext(), @@ -4917,7 +4918,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { }); } - public void HandlePackageDisable() { + public void testHandlePackageDisable() { checkHandlePackageDeleteInner((userId, packageName) -> { disablePackage(userId, packageName); mService.mPackageMonitor.onReceive(getTestContext(), @@ -5049,7 +5050,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { } /** Almost ame as testHandlePackageDelete, except it doesn't uninstall packages. */ - public void HandlePackageClearData() { + public void testHandlePackageClearData() { final Icon bmp32x32 = Icon.createWithBitmap(BitmapFactory.decodeResource( getTestContext().getResources(), R.drawable.black_32x32)); setCaller(CALLING_PACKAGE_1, USER_10); @@ -5125,7 +5126,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { assertTrue(bitmapDirectoryExists(CALLING_PACKAGE_3, USER_11)); } - public void HandlePackageClearData_manifestRepublished() { + public void testHandlePackageClearData_manifestRepublished() { mRunningUsers.put(USER_11, true); @@ -5167,7 +5168,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { }); } - public void HandlePackageUpdate() throws Throwable { + public void testHandlePackageUpdate() throws Throwable { // Set up shortcuts and launchers. final Icon res32x32 = Icon.createWithResource(getTestContext(), R.drawable.black_32x32); @@ -5341,7 +5342,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { /** * Test the case where an updated app has resource IDs changed. */ - public void HandlePackageUpdate_resIdChanged() throws Exception { + public void testHandlePackageUpdate_resIdChanged() throws Exception { final Icon icon1 = Icon.createWithResource(getTestContext(), /* res ID */ 1000); final Icon icon2 = Icon.createWithResource(getTestContext(), /* res ID */ 1001); @@ -5416,7 +5417,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { }); } - public void HandlePackageUpdate_systemAppUpdate() { + public void testHandlePackageUpdate_systemAppUpdate() { // Package1 is a system app. Package 2 is not a system app, so it's not scanned // in this test at all. @@ -5522,7 +5523,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { mService.getUserShortcutsLocked(USER_10).getLastAppScanOsFingerprint()); } - public void HandlePackageChanged() { + public void testHandlePackageChanged() { final ComponentName ACTIVITY1 = new ComponentName(CALLING_PACKAGE_1, "act1"); final ComponentName ACTIVITY2 = new ComponentName(CALLING_PACKAGE_1, "act2"); @@ -5652,7 +5653,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { }); } - public void HandlePackageUpdate_activityNoLongerMain() throws Throwable { + public void testHandlePackageUpdate_activityNoLongerMain() throws Throwable { runWithCaller(CALLING_PACKAGE_1, USER_10, () -> { assertTrue(mManager.setDynamicShortcuts(list( makeShortcutWithActivity("s1a", @@ -5738,7 +5739,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { * - Unpinned dynamic shortcuts * - Bitmaps */ - public void BackupAndRestore() { + public void testBackupAndRestore() { assertFileNotExists("user-0/shortcut_dump/restore-0-start.txt"); assertFileNotExists("user-0/shortcut_dump/restore-1-payload.xml"); @@ -5759,7 +5760,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { checkBackupAndRestore_success(/*firstRestore=*/ true); } - public void BackupAndRestore_backupRestoreTwice() { + public void testBackupAndRestore_backupRestoreTwice() { prepareForBackupTest(); checkBackupAndRestore_success(/*firstRestore=*/ true); @@ -5775,7 +5776,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { checkBackupAndRestore_success(/*firstRestore=*/ false); } - public void BackupAndRestore_restoreToNewVersion() { + public void testBackupAndRestore_restoreToNewVersion() { prepareForBackupTest(); addPackage(CALLING_PACKAGE_1, CALLING_UID_1, 2); @@ -5784,7 +5785,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { checkBackupAndRestore_success(/*firstRestore=*/ true); } - public void BackupAndRestore_restoreToSuperSetSignatures() { + public void testBackupAndRestore_restoreToSuperSetSignatures() { prepareForBackupTest(); // Change package signatures. @@ -5981,7 +5982,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { }); } - public void BackupAndRestore_publisherWrongSignature() { + public void testBackupAndRestore_publisherWrongSignature() { prepareForBackupTest(); addPackage(CALLING_PACKAGE_1, CALLING_UID_1, 10, "sigx"); // different signature @@ -5989,7 +5990,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { checkBackupAndRestore_publisherNotRestored(ShortcutInfo.DISABLED_REASON_SIGNATURE_MISMATCH); } - public void BackupAndRestore_publisherNoLongerBackupTarget() { + public void testBackupAndRestore_publisherNoLongerBackupTarget() { prepareForBackupTest(); updatePackageInfo(CALLING_PACKAGE_1, @@ -6118,7 +6119,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { }); } - public void BackupAndRestore_launcherLowerVersion() { + public void testBackupAndRestore_launcherLowerVersion() { prepareForBackupTest(); addPackage(LAUNCHER_1, LAUNCHER_UID_1, 0); // Lower version @@ -6127,7 +6128,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { checkBackupAndRestore_success(/*firstRestore=*/ true); } - public void BackupAndRestore_launcherWrongSignature() { + public void testBackupAndRestore_launcherWrongSignature() { prepareForBackupTest(); addPackage(LAUNCHER_1, LAUNCHER_UID_1, 10, "sigx"); // different signature @@ -6135,7 +6136,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { checkBackupAndRestore_launcherNotRestored(true); } - public void BackupAndRestore_launcherNoLongerBackupTarget() { + public void testBackupAndRestore_launcherNoLongerBackupTarget() { prepareForBackupTest(); updatePackageInfo(LAUNCHER_1, @@ -6240,7 +6241,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { }); } - public void BackupAndRestore_launcherAndPackageNoLongerBackupTarget() { + public void testBackupAndRestore_launcherAndPackageNoLongerBackupTarget() { prepareForBackupTest(); updatePackageInfo(CALLING_PACKAGE_1, @@ -6338,7 +6339,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { }); } - public void BackupAndRestore_disabled() { + public void testBackupAndRestore_disabled() { prepareCrossProfileDataSet(); // Before doing backup & restore, disable s1. @@ -6403,7 +6404,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { } - public void BackupAndRestore_manifestRePublished() { + public void testBackupAndRestore_manifestRePublished() { // Publish two manifest shortcuts. addManifestShortcutResource( new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()), @@ -6494,7 +6495,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { * logcat. * - if it has allowBackup=false, we don't touch any of the existing shortcuts. */ - public void BackupAndRestore_appAlreadyInstalledWhenRestored() { + public void testBackupAndRestore_appAlreadyInstalledWhenRestored() { // Pre-backup. Same as testBackupAndRestore_manifestRePublished(). // Publish two manifest shortcuts. @@ -6619,7 +6620,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { /** * Test for restoring the pre-P backup format. */ - public void BackupAndRestore_api27format() throws Exception { + public void testBackupAndRestore_api27format() throws Exception { final byte[] payload = readTestAsset("shortcut/shortcut_api27_backup.xml").getBytes(); addPackage(CALLING_PACKAGE_1, CALLING_UID_1, 10, "22222"); @@ -6657,7 +6658,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { } - public void SaveAndLoad_crossProfile() { + public void testSaveAndLoad_crossProfile() { prepareCrossProfileDataSet(); dumpsysOnLogcat("Before save & load"); @@ -6860,7 +6861,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { .getPackageUserId()); } - public void OnApplicationActive_permission() { + public void testOnApplicationActive_permission() { assertExpectException(SecurityException.class, "Missing permission", () -> mManager.onApplicationActive(CALLING_PACKAGE_1, USER_10)); @@ -6869,7 +6870,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { mManager.onApplicationActive(CALLING_PACKAGE_1, USER_10); } - public void GetShareTargets_permission() { + public void testGetShareTargets_permission() { addPackage(CHOOSER_ACTIVITY_PACKAGE, CHOOSER_ACTIVITY_UID, 10, "sig1"); mInjectedChooserActivity = ComponentName.createRelative(CHOOSER_ACTIVITY_PACKAGE, ".ChooserActivity"); @@ -6888,7 +6889,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { }); } - public void HasShareTargets_permission() { + public void testHasShareTargets_permission() { assertExpectException(SecurityException.class, "Missing permission", () -> mManager.hasShareTargets(CALLING_PACKAGE_1)); @@ -6897,7 +6898,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { mManager.hasShareTargets(CALLING_PACKAGE_1); } - public void isSharingShortcut_permission() throws IntentFilter.MalformedMimeTypeException { + public void testisSharingShortcut_permission() throws IntentFilter.MalformedMimeTypeException { setCaller(LAUNCHER_1, USER_10); IntentFilter filter_any = new IntentFilter(); @@ -6912,18 +6913,18 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { mManager.hasShareTargets(CALLING_PACKAGE_1); } - public void Dumpsys_crossProfile() { + public void testDumpsys_crossProfile() { prepareCrossProfileDataSet(); dumpsysOnLogcat("test1", /* force= */ true); } - public void Dumpsys_withIcons() throws IOException { - Icons(); + public void testDumpsys_withIcons() throws IOException { + testIcons(); // Dump after having some icons. dumpsysOnLogcat("test1", /* force= */ true); } - public void ManifestShortcut_publishOnUnlockUser() { + public void testManifestShortcut_publishOnUnlockUser() { addManifestShortcutResource( new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()), R.xml.shortcut_1); @@ -7137,7 +7138,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { assertNull(mService.getPackageShortcutForTest(LAUNCHER_1, USER_10)); } - public void ManifestShortcut_publishOnBroadcast() { + public void testManifestShortcut_publishOnBroadcast() { // First, no packages are installed. uninstallPackage(USER_10, CALLING_PACKAGE_1); uninstallPackage(USER_10, CALLING_PACKAGE_2); @@ -7393,7 +7394,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { }); } - public void ManifestShortcuts_missingMandatoryFields() { + public void testManifestShortcuts_missingMandatoryFields() { // Start with no apps installed. uninstallPackage(USER_10, CALLING_PACKAGE_1); uninstallPackage(USER_10, CALLING_PACKAGE_2); @@ -7462,7 +7463,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { }); } - public void ManifestShortcuts_intentDefinitions() { + public void testManifestShortcuts_intentDefinitions() { addManifestShortcutResource( new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()), R.xml.shortcut_error_4); @@ -7604,7 +7605,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { }); } - public void ManifestShortcuts_checkAllFields() { + public void testManifestShortcuts_checkAllFields() { mService.handleUnlockUser(USER_10); // Package 1 updated, which has one valid manifest shortcut and one invalid. @@ -7709,7 +7710,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { }); } - public void ManifestShortcuts_localeChange() throws InterruptedException { + public void testManifestShortcuts_localeChange() throws InterruptedException { mService.handleUnlockUser(USER_10); // Package 1 updated, which has one valid manifest shortcut and one invalid. @@ -7813,7 +7814,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { }); } - public void ManifestShortcuts_updateAndDisabled_notPinned() { + public void testManifestShortcuts_updateAndDisabled_notPinned() { mService.handleUnlockUser(USER_10); // First, just publish a manifest shortcut. @@ -7853,7 +7854,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { }); } - public void ManifestShortcuts_updateAndDisabled_pinned() { + public void testManifestShortcuts_updateAndDisabled_pinned() { mService.handleUnlockUser(USER_10); // First, just publish a manifest shortcut. @@ -7909,7 +7910,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { }); } - public void ManifestShortcuts_duplicateInSingleActivity() { + public void testManifestShortcuts_duplicateInSingleActivity() { mService.handleUnlockUser(USER_10); // The XML has two shortcuts with the same ID. @@ -7934,7 +7935,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { }); } - public void ManifestShortcuts_duplicateInTwoActivities() { + public void testManifestShortcuts_duplicateInTwoActivities() { mService.handleUnlockUser(USER_10); // ShortcutActivity has shortcut ms1 @@ -7986,7 +7987,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { /** * Manifest shortcuts cannot override shortcuts that were published via the APIs. */ - public void ManifestShortcuts_cannotOverrideNonManifest() { + public void testManifestShortcuts_cannotOverrideNonManifest() { mService.handleUnlockUser(USER_10); // Create a non-pinned dynamic shortcut and a non-dynamic pinned shortcut. @@ -8059,7 +8060,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { /** * Make sure the APIs won't work on manifest shortcuts. */ - public void ManifestShortcuts_immutable() { + public void testManifestShortcuts_immutable() { mService.handleUnlockUser(USER_10); // Create a non-pinned manifest shortcut, a pinned shortcut that was originally @@ -8152,7 +8153,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { /** * Make sure the APIs won't work on manifest shortcuts. */ - public void ManifestShortcuts_tooMany() { + public void testManifestShortcuts_tooMany() { // Change the max number of shortcuts. mService.updateConfigurationLocked(ConfigConstants.KEY_MAX_SHORTCUTS + "=3"); @@ -8171,7 +8172,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { }); } - public void MaxShortcutCount_set() { + public void testMaxShortcutCount_set() { // Change the max number of shortcuts. mService.updateConfigurationLocked(ConfigConstants.KEY_MAX_SHORTCUTS + "=3"); @@ -8252,7 +8253,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { }); } - public void MaxShortcutCount_add() { + public void testMaxShortcutCount_add() { // Change the max number of shortcuts. mService.updateConfigurationLocked(ConfigConstants.KEY_MAX_SHORTCUTS + "=3"); @@ -8379,7 +8380,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { }); } - public void MaxShortcutCount_update() { + public void testMaxShortcutCount_update() { // Change the max number of shortcuts. mService.updateConfigurationLocked(ConfigConstants.KEY_MAX_SHORTCUTS + "=3"); @@ -8470,7 +8471,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { }); } - public void ShortcutsPushedOutByManifest() { + public void testShortcutsPushedOutByManifest() { // Change the max number of shortcuts. mService.updateConfigurationLocked(ConfigConstants.KEY_MAX_SHORTCUTS + "=3"); @@ -8578,7 +8579,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { }); } - public void ReturnedByServer() { + public void testReturnedByServer() { // Package 1 updated, with manifest shortcuts. addManifestShortcutResource( new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()), @@ -8624,7 +8625,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { }); } - public void IsForegroundDefaultLauncher_true() { + public void testIsForegroundDefaultLauncher_true() { // random uid in the USER_10 range. final int uid = 1000024; @@ -8635,7 +8636,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { } - public void IsForegroundDefaultLauncher_defaultButNotForeground() { + public void testIsForegroundDefaultLauncher_defaultButNotForeground() { // random uid in the USER_10 range. final int uid = 1000024; @@ -8645,7 +8646,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { assertFalse(mInternal.isForegroundDefaultLauncher("default", uid)); } - public void IsForegroundDefaultLauncher_foregroundButNotDefault() { + public void testIsForegroundDefaultLauncher_foregroundButNotDefault() { // random uid in the USER_10 range. final int uid = 1000024; @@ -8655,7 +8656,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { assertFalse(mInternal.isForegroundDefaultLauncher("another", uid)); } - public void ParseShareTargetsFromManifest() { + public void testParseShareTargetsFromManifest() { // These values must exactly match the content of shortcuts_share_targets.xml resource List<ShareTargetInfo> expectedValues = new ArrayList<>(); expectedValues.add(new ShareTargetInfo( @@ -8707,7 +8708,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { } } - public void ShareTargetInfo_saveToXml() throws IOException, XmlPullParserException { + public void testShareTargetInfo_saveToXml() throws IOException, XmlPullParserException { List<ShareTargetInfo> expectedValues = new ArrayList<>(); expectedValues.add(new ShareTargetInfo( new ShareTargetInfo.TargetData[]{new ShareTargetInfo.TargetData( @@ -8773,7 +8774,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { } } - public void IsSharingShortcut() throws IntentFilter.MalformedMimeTypeException { + public void testIsSharingShortcut() throws IntentFilter.MalformedMimeTypeException { addManifestShortcutResource( new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()), R.xml.shortcut_share_targets); @@ -8823,7 +8824,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { filter_any)); } - public void IsSharingShortcut_PinnedAndCachedOnlyShortcuts() + public void testIsSharingShortcut_PinnedAndCachedOnlyShortcuts() throws IntentFilter.MalformedMimeTypeException { addManifestShortcutResource( new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()), @@ -8880,7 +8881,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { filter_any)); } - public void AddingShortcuts_ExcludesHiddenFromLauncherShortcuts() { + public void testAddingShortcuts_ExcludesHiddenFromLauncherShortcuts() { final ShortcutInfo s1 = makeShortcutExcludedFromLauncher("s1"); final ShortcutInfo s2 = makeShortcutExcludedFromLauncher("s2"); final ShortcutInfo s3 = makeShortcutExcludedFromLauncher("s3"); @@ -8901,7 +8902,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { }); } - public void UpdateShortcuts_ExcludesHiddenFromLauncherShortcuts() { + public void testUpdateShortcuts_ExcludesHiddenFromLauncherShortcuts() { final ShortcutInfo s1 = makeShortcut("s1"); final ShortcutInfo s2 = makeShortcut("s2"); final ShortcutInfo s3 = makeShortcut("s3"); @@ -8914,7 +8915,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { }); } - public void PinHiddenShortcuts_ThrowsException() { + public void testPinHiddenShortcuts_ThrowsException() { runWithCaller(CALLING_PACKAGE_1, USER_10, () -> { assertThrown(IllegalArgumentException.class, () -> { mManager.requestPinShortcut(makeShortcutExcludedFromLauncher("s1"), null); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ConditionProvidersTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ConditionProvidersTest.java index af7f703e9c31..b33233107766 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/ConditionProvidersTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/ConditionProvidersTest.java @@ -101,9 +101,12 @@ public class ConditionProvidersTest extends UiServiceTestCase { mProviders.notifyConditions("package", msi, conditionsToNotify); - verify(mCallback).onConditionChanged(eq(Uri.parse("a")), eq(conditionsToNotify[0])); - verify(mCallback).onConditionChanged(eq(Uri.parse("b")), eq(conditionsToNotify[1])); - verify(mCallback).onConditionChanged(eq(Uri.parse("c")), eq(conditionsToNotify[2])); + verify(mCallback).onConditionChanged(eq(Uri.parse("a")), eq(conditionsToNotify[0]), + eq(100)); + verify(mCallback).onConditionChanged(eq(Uri.parse("b")), eq(conditionsToNotify[1]), + eq(100)); + verify(mCallback).onConditionChanged(eq(Uri.parse("c")), eq(conditionsToNotify[2]), + eq(100)); verifyNoMoreInteractions(mCallback); } @@ -121,8 +124,10 @@ public class ConditionProvidersTest extends UiServiceTestCase { mProviders.notifyConditions("package", msi, conditionsToNotify); - verify(mCallback).onConditionChanged(eq(Uri.parse("a")), eq(conditionsToNotify[0])); - verify(mCallback).onConditionChanged(eq(Uri.parse("b")), eq(conditionsToNotify[1])); + verify(mCallback).onConditionChanged(eq(Uri.parse("a")), eq(conditionsToNotify[0]), + eq(100)); + verify(mCallback).onConditionChanged(eq(Uri.parse("b")), eq(conditionsToNotify[1]), + eq(100)); verifyNoMoreInteractions(mCallback); } @@ -141,8 +146,10 @@ public class ConditionProvidersTest extends UiServiceTestCase { mProviders.notifyConditions("package", msi, conditionsToNotify); - verify(mCallback).onConditionChanged(eq(Uri.parse("a")), eq(conditionsToNotify[0])); - verify(mCallback).onConditionChanged(eq(Uri.parse("b")), eq(conditionsToNotify[3])); + verify(mCallback).onConditionChanged(eq(Uri.parse("a")), eq(conditionsToNotify[0]), + eq(100)); + verify(mCallback).onConditionChanged(eq(Uri.parse("b")), eq(conditionsToNotify[3]), + eq(100)); verifyNoMoreInteractions(mCallback); } diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java index 301165f8151d..7885c9b902e2 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -11213,7 +11213,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { // Representative used to verify getCallingZenUser(). mBinderService.getAutomaticZenRules(); - verify(zenModeHelper).getAutomaticZenRules(eq(UserHandle.CURRENT)); + verify(zenModeHelper).getAutomaticZenRules(eq(UserHandle.CURRENT), anyInt()); } @Test @@ -11225,7 +11225,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { // Representative used to verify getCallingZenUser(). mBinderService.getAutomaticZenRules(); - verify(zenModeHelper).getAutomaticZenRules(eq(Binder.getCallingUserHandle())); + verify(zenModeHelper).getAutomaticZenRules(eq(Binder.getCallingUserHandle()), anyInt()); } /** Prepares for a zen-related test that uses a mocked {@link ZenModeHelper}. */ diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java index 1884bbd39bb9..6ef078b6da8a 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java @@ -291,7 +291,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { @Parameters(name = "{0}") public static List<FlagsParameterization> getParams() { - return FlagsParameterization.allCombinationsOf(FLAG_MODES_UI, FLAG_BACKUP_RESTORE_LOGGING); + return FlagsParameterization.allCombinationsOf(FLAG_MODES_UI, FLAG_BACKUP_RESTORE_LOGGING, + com.android.server.notification.Flags.FLAG_FIX_CALLING_UID_FROM_CPS); } public ZenModeHelperTest(FlagsParameterization flags) { @@ -2617,7 +2618,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test - public void testSetAutomaticZenRuleState_nullPkg() { + public void testSetAutomaticZenRuleStateFromConditionProvider_nullPkg() { AutomaticZenRule zenRule = new AutomaticZenRule("name", null, new ComponentName(mContext.getPackageName(), "ScheduleConditionProvider"), @@ -2627,10 +2628,9 @@ public class ZenModeHelperTest extends UiServiceTestCase { String id = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT, null, zenRule, ORIGIN_APP, "test", CUSTOM_PKG_UID); - mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, zenRule.getConditionId(), - new Condition(zenRule.getConditionId(), "", STATE_TRUE), - ORIGIN_APP, - CUSTOM_PKG_UID); + mZenModeHelper.setAutomaticZenRuleStateFromConditionProvider(UserHandle.CURRENT, + zenRule.getConditionId(), new Condition(zenRule.getConditionId(), "", STATE_TRUE), + ORIGIN_APP, CUSTOM_PKG_UID); ZenModeConfig.ZenRule ruleInConfig = mZenModeHelper.mConfig.automaticRules.get(id); assertEquals(STATE_TRUE, ruleInConfig.condition.state); @@ -2726,8 +2726,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { ORIGIN_SYSTEM, "test", SYSTEM_UID); Condition condition = new Condition(sharedUri, "", STATE_TRUE); - mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, sharedUri, condition, - ORIGIN_SYSTEM, SYSTEM_UID); + mZenModeHelper.setAutomaticZenRuleStateFromConditionProvider(UserHandle.CURRENT, sharedUri, + condition, ORIGIN_SYSTEM, SYSTEM_UID); for (ZenModeConfig.ZenRule rule : mZenModeHelper.mConfig.automaticRules.values()) { if (rule.id.equals(id)) { @@ -2741,8 +2741,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { } condition = new Condition(sharedUri, "", STATE_FALSE); - mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, sharedUri, condition, - ORIGIN_SYSTEM, SYSTEM_UID); + mZenModeHelper.setAutomaticZenRuleStateFromConditionProvider(UserHandle.CURRENT, sharedUri, + condition, ORIGIN_SYSTEM, SYSTEM_UID); for (ZenModeConfig.ZenRule rule : mZenModeHelper.mConfig.automaticRules.values()) { if (rule.id.equals(id)) { @@ -2780,9 +2780,10 @@ public class ZenModeHelperTest extends UiServiceTestCase { .setOwner(OWNER) .setDeviceEffects(zde) .build(), - ORIGIN_APP, "reasons", 0); + ORIGIN_APP, "reasons", CUSTOM_PKG_UID); - AutomaticZenRule savedRule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId); + AutomaticZenRule savedRule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId, + CUSTOM_PKG_UID); assertThat(savedRule.getDeviceEffects()).isEqualTo( new ZenDeviceEffects.Builder() .setShouldDisplayGrayscale(true) @@ -2814,9 +2815,10 @@ public class ZenModeHelperTest extends UiServiceTestCase { .setOwner(OWNER) .setDeviceEffects(zde) .build(), - ORIGIN_SYSTEM, "reasons", 0); + ORIGIN_SYSTEM, "reasons", SYSTEM_UID); - AutomaticZenRule savedRule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId); + AutomaticZenRule savedRule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId, + SYSTEM_UID); assertThat(savedRule.getDeviceEffects()).isEqualTo(zde); } @@ -2845,7 +2847,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { ORIGIN_USER_IN_SYSTEMUI, "reasons", 0); - AutomaticZenRule savedRule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId); + AutomaticZenRule savedRule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId, + SYSTEM_UID); assertThat(savedRule.getDeviceEffects()).isEqualTo(zde); } @@ -2863,7 +2866,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { .setOwner(OWNER) .setDeviceEffects(original) .build(), - ORIGIN_SYSTEM, "reasons", 0); + ORIGIN_SYSTEM, "reasons", SYSTEM_UID); ZenDeviceEffects updateFromApp = new ZenDeviceEffects.Builder() .setShouldUseNightMode(true) // Good @@ -2875,9 +2878,10 @@ public class ZenModeHelperTest extends UiServiceTestCase { .setOwner(OWNER) .setDeviceEffects(updateFromApp) .build(), - ORIGIN_APP, "reasons", 0); + ORIGIN_APP, "reasons", CUSTOM_PKG_UID); - AutomaticZenRule savedRule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId); + AutomaticZenRule savedRule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId, + CUSTOM_PKG_UID); assertThat(savedRule.getDeviceEffects()).isEqualTo( new ZenDeviceEffects.Builder() .setShouldUseNightMode(true) // From update. @@ -2898,7 +2902,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { .setOwner(OWNER) .setDeviceEffects(original) .build(), - ORIGIN_SYSTEM, "reasons", 0); + ORIGIN_SYSTEM, "reasons", SYSTEM_UID); ZenDeviceEffects updateFromSystem = new ZenDeviceEffects.Builder() .setShouldUseNightMode(true) // Good @@ -2908,9 +2912,10 @@ public class ZenModeHelperTest extends UiServiceTestCase { new AutomaticZenRule.Builder("Rule", CONDITION_ID) .setDeviceEffects(updateFromSystem) .build(), - ORIGIN_SYSTEM, "reasons", 0); + ORIGIN_SYSTEM, "reasons", SYSTEM_UID); - AutomaticZenRule savedRule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId); + AutomaticZenRule savedRule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId, + SYSTEM_UID); assertThat(savedRule.getDeviceEffects()).isEqualTo(updateFromSystem); } @@ -2926,7 +2931,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { .setOwner(OWNER) .setDeviceEffects(original) .build(), - ORIGIN_SYSTEM, "reasons", 0); + ORIGIN_SYSTEM, "reasons", SYSTEM_UID); ZenDeviceEffects updateFromUser = new ZenDeviceEffects.Builder() .setShouldUseNightMode(true) @@ -2939,9 +2944,10 @@ public class ZenModeHelperTest extends UiServiceTestCase { new AutomaticZenRule.Builder("Rule", CONDITION_ID) .setDeviceEffects(updateFromUser) .build(), - ORIGIN_USER_IN_SYSTEMUI, "reasons", 0); + ORIGIN_USER_IN_SYSTEMUI, "reasons", SYSTEM_UID); - AutomaticZenRule savedRule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId); + AutomaticZenRule savedRule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId, + SYSTEM_UID); assertThat(savedRule.getDeviceEffects()).isEqualTo(updateFromUser); } @@ -2959,15 +2965,16 @@ public class ZenModeHelperTest extends UiServiceTestCase { .allowCalls(ZenPolicy.PEOPLE_TYPE_NONE) // default is stars .build()) .build(), - ORIGIN_APP, "reasons", 0); + ORIGIN_APP, "reasons", CUSTOM_PKG_UID); mZenModeHelper.updateAutomaticZenRule(UserHandle.CURRENT, ruleId, new AutomaticZenRule.Builder("Rule", CONDITION_ID) // no zen policy .build(), - ORIGIN_APP, "reasons", 0); + ORIGIN_APP, "reasons", CUSTOM_PKG_UID); - AutomaticZenRule savedRule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId); + AutomaticZenRule savedRule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId, + CUSTOM_PKG_UID); assertThat(savedRule.getZenPolicy().getPriorityCategoryCalls()) .isEqualTo(STATE_DISALLOW); } @@ -2988,7 +2995,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { .allowReminders(true) .build()) .build(), - ORIGIN_SYSTEM, "reasons", 0); + ORIGIN_SYSTEM, "reasons", SYSTEM_UID); mZenModeHelper.updateAutomaticZenRule(UserHandle.CURRENT, ruleId, new AutomaticZenRule.Builder("Rule", CONDITION_ID) @@ -2996,9 +3003,10 @@ public class ZenModeHelperTest extends UiServiceTestCase { .allowCalls(ZenPolicy.PEOPLE_TYPE_CONTACTS) .build()) .build(), - ORIGIN_APP, "reasons", 0); + ORIGIN_APP, "reasons", CUSTOM_PKG_UID); - AutomaticZenRule savedRule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId); + AutomaticZenRule savedRule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId, + CUSTOM_PKG_UID); assertThat(savedRule.getZenPolicy().getPriorityCategoryCalls()) .isEqualTo(STATE_ALLOW); // from update assertThat(savedRule.getZenPolicy().getPriorityCallSenders()) @@ -4441,7 +4449,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { rule.triggerDescription = TRIGGER_DESC; mZenModeHelper.mConfig.automaticRules.put(rule.id, rule); - AutomaticZenRule actual = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, rule.id); + AutomaticZenRule actual = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, rule.id, + SYSTEM_UID); assertEquals(NAME, actual.getName()); assertEquals(OWNER, actual.getOwner()); @@ -4508,16 +4517,17 @@ public class ZenModeHelperTest extends UiServiceTestCase { .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) .build(); String ruleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT, - mContext.getPackageName(), azrBase, ORIGIN_APP, "reason", SYSTEM_UID); - AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId); + mContext.getPackageName(), azrBase, ORIGIN_APP, "reason", CUSTOM_PKG_UID); + AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId, + CUSTOM_PKG_UID); // Checks the name can be changed by the app because the user has not modified it. AutomaticZenRule azrUpdate = new AutomaticZenRule.Builder(rule) .setName("NewName") .build(); mZenModeHelper.updateAutomaticZenRule(UserHandle.CURRENT, ruleId, azrUpdate, ORIGIN_APP, - "reason", SYSTEM_UID); - rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId); + "reason", CUSTOM_PKG_UID); + rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId, CUSTOM_PKG_UID); assertThat(rule.getName()).isEqualTo("NewName"); // The user modifies some other field in the rule, which makes the rule as a whole not @@ -4534,8 +4544,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { .setName("NewAppName") .build(); mZenModeHelper.updateAutomaticZenRule(UserHandle.CURRENT, ruleId, azrUpdate, ORIGIN_APP, - "reason", SYSTEM_UID); - rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId); + "reason", CUSTOM_PKG_UID); + rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId, CUSTOM_PKG_UID); assertThat(rule.getName()).isEqualTo("NewAppName"); // The user modifies the name. @@ -4544,7 +4554,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { .build(); mZenModeHelper.updateAutomaticZenRule(UserHandle.CURRENT, ruleId, azrUpdate, ORIGIN_USER_IN_SYSTEMUI, "reason", SYSTEM_UID); - rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId); + rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId, CUSTOM_PKG_UID); assertThat(rule.getName()).isEqualTo("UserProvidedName"); // The app is no longer able to modify the name. @@ -4552,8 +4562,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { .setName("NewAppName") .build(); mZenModeHelper.updateAutomaticZenRule(UserHandle.CURRENT, ruleId, azrUpdate, ORIGIN_APP, - "reason", SYSTEM_UID); - rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId); + "reason", CUSTOM_PKG_UID); + rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId, CUSTOM_PKG_UID); assertThat(rule.getName()).isEqualTo("UserProvidedName"); } @@ -4568,8 +4578,9 @@ public class ZenModeHelperTest extends UiServiceTestCase { .build(); // Adds the rule using the app, to avoid having any user modified bits set. String ruleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT, - mContext.getPackageName(), azrBase, ORIGIN_APP, "reason", SYSTEM_UID); - AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId); + mContext.getPackageName(), azrBase, ORIGIN_APP, "reason", CUSTOM_PKG_UID); + AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId, + CUSTOM_PKG_UID); // Modifies the filter, icon, zen policy, and device effects ZenPolicy policy = new ZenPolicy.Builder(rule.getZenPolicy()) @@ -4589,7 +4600,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { // Update the rule with the AZR from origin user. mZenModeHelper.updateAutomaticZenRule(UserHandle.CURRENT, ruleId, azrUpdate, ORIGIN_USER_IN_SYSTEMUI, "reason", SYSTEM_UID); - rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId); + rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId, CUSTOM_PKG_UID); // UPDATE_ORIGIN_USER should change the bitmask and change the values. assertThat(rule.getInterruptionFilter()).isEqualTo(INTERRUPTION_FILTER_PRIORITY); @@ -4625,8 +4636,9 @@ public class ZenModeHelperTest extends UiServiceTestCase { .build(); // Adds the rule using the app, to avoid having any user modified bits set. String ruleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT, - mContext.getPackageName(), azrBase, ORIGIN_APP, "reason", SYSTEM_UID); - AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId); + mContext.getPackageName(), azrBase, ORIGIN_APP, "reason", CUSTOM_PKG_UID); + AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId, + CUSTOM_PKG_UID); // Modifies the icon, zen policy and device effects ZenPolicy policy = new ZenPolicy.Builder(rule.getZenPolicy()) @@ -4646,7 +4658,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { // Update the rule with the AZR from origin systemUI. mZenModeHelper.updateAutomaticZenRule(UserHandle.CURRENT, ruleId, azrUpdate, ORIGIN_SYSTEM, "reason", SYSTEM_UID); - rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId); + rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId, CUSTOM_PKG_UID); // UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI should change the value but NOT update the bitmask. assertThat(rule.getIconResId()).isEqualTo(ICON_RES_ID); @@ -4675,8 +4687,9 @@ public class ZenModeHelperTest extends UiServiceTestCase { .build(); // Adds the rule using the app, to avoid having any user modified bits set. String ruleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT, - mContext.getPackageName(), azrBase, ORIGIN_APP, "reason", SYSTEM_UID); - AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId); + mContext.getPackageName(), azrBase, ORIGIN_APP, "reason", CUSTOM_PKG_UID); + AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId, + CUSTOM_PKG_UID); ZenPolicy policy = new ZenPolicy.Builder() .allowReminders(true) @@ -4693,7 +4706,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { // Since the rule is not already user modified, UPDATE_ORIGIN_APP can modify the rule. // The bitmask is not modified. mZenModeHelper.updateAutomaticZenRule(UserHandle.CURRENT, ruleId, azrUpdate, ORIGIN_APP, - "reason", SYSTEM_UID); + "reason", CUSTOM_PKG_UID); ZenRule storedRule = mZenModeHelper.mConfig.automaticRules.get(ruleId); assertThat(storedRule.userModifiedFields).isEqualTo(0); @@ -4717,9 +4730,9 @@ public class ZenModeHelperTest extends UiServiceTestCase { // Zen rule update coming from the app again. This cannot fully update the rule, because // the rule is already considered user modified. mZenModeHelper.updateAutomaticZenRule(UserHandle.CURRENT, ruleIdUser, azrUpdate, ORIGIN_APP, - "reason", SYSTEM_UID); + "reason", CUSTOM_PKG_UID); AutomaticZenRule ruleUser = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, - ruleIdUser); + ruleIdUser, CUSTOM_PKG_UID); // The app can only change the value if the rule is not already user modified, // so the rule is not changed, and neither is the bitmask. @@ -4749,8 +4762,9 @@ public class ZenModeHelperTest extends UiServiceTestCase { .build()) .build(); String ruleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT, - mContext.getPackageName(), azrBase, ORIGIN_APP, "reason", SYSTEM_UID); - AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId); + mContext.getPackageName(), azrBase, ORIGIN_APP, "reason", CUSTOM_PKG_UID); + AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId, + CUSTOM_PKG_UID); // The values are modified but the bitmask is not. assertThat(rule.getZenPolicy().getPriorityCategoryReminders()) @@ -4771,7 +4785,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { .build(); // Adds the rule using the app, to avoid having any user modified bits set. String ruleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT, - mContext.getPackageName(), azrBase, ORIGIN_APP, "reason", SYSTEM_UID); + mContext.getPackageName(), azrBase, ORIGIN_APP, "reason", CUSTOM_PKG_UID); AutomaticZenRule azr = new AutomaticZenRule.Builder(azrBase) // Sets Device Effects to null @@ -4781,8 +4795,9 @@ public class ZenModeHelperTest extends UiServiceTestCase { // Zen rule update coming from app, but since the rule isn't already // user modified, it can be updated. mZenModeHelper.updateAutomaticZenRule(UserHandle.CURRENT, ruleId, azr, ORIGIN_APP, "reason", - SYSTEM_UID); - AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId); + CUSTOM_PKG_UID); + AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId, + CUSTOM_PKG_UID); // When AZR's ZenDeviceEffects is null, the updated rule's device effects are kept. assertThat(rule.getDeviceEffects()).isEqualTo(zde); @@ -4797,8 +4812,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { .build(); // Adds the rule using the app, to avoid having any user modified bits set. String ruleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT, - mContext.getPackageName(), azrBase, ORIGIN_APP, "reason", SYSTEM_UID); - AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId); + mContext.getPackageName(), azrBase, ORIGIN_APP, "reason", CUSTOM_PKG_UID); AutomaticZenRule azr = new AutomaticZenRule.Builder(azrBase) // Set zen policy to null @@ -4808,8 +4822,9 @@ public class ZenModeHelperTest extends UiServiceTestCase { // Zen rule update coming from app, but since the rule isn't already // user modified, it can be updated. mZenModeHelper.updateAutomaticZenRule(UserHandle.CURRENT, ruleId, azr, ORIGIN_APP, "reason", - SYSTEM_UID); - rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId); + CUSTOM_PKG_UID); + AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId, + CUSTOM_PKG_UID); // When AZR's ZenPolicy is null, we expect the updated rule's policy to be unchanged // (equivalent to the provided policy, with additional fields filled in with defaults). @@ -4829,8 +4844,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { .build(); // Adds the rule using the app, to avoid having any user modified bits set. String ruleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT, - mContext.getPackageName(), azrBase, ORIGIN_APP, "reason", SYSTEM_UID); - AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId); + mContext.getPackageName(), azrBase, ORIGIN_APP, "reason", CUSTOM_PKG_UID); // Create a fully populated ZenPolicy. ZenPolicy policy = new ZenPolicy.Builder() @@ -4860,7 +4874,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { // Default config defined in getDefaultConfigParser() is used as the original rule. mZenModeHelper.updateAutomaticZenRule(UserHandle.CURRENT, ruleId, azr, ORIGIN_USER_IN_SYSTEMUI, "reason", SYSTEM_UID); - rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId); + AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId, + CUSTOM_PKG_UID); // New ZenPolicy differs from the default config assertThat(rule.getZenPolicy()).isNotNull(); @@ -4890,8 +4905,9 @@ public class ZenModeHelperTest extends UiServiceTestCase { .build(); // Adds the rule using the app, to avoid having any user modified bits set. String ruleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT, - mContext.getPackageName(), azrBase, ORIGIN_APP, "reason", SYSTEM_UID); - AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId); + mContext.getPackageName(), azrBase, ORIGIN_APP, "reason", CUSTOM_PKG_UID); + AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId, + CUSTOM_PKG_UID); ZenDeviceEffects deviceEffects = new ZenDeviceEffects.Builder() .setShouldDisplayGrayscale(true) @@ -4903,7 +4919,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { // Applies the update to the rule. mZenModeHelper.updateAutomaticZenRule(UserHandle.CURRENT, ruleId, azr, ORIGIN_USER_IN_SYSTEMUI, "reason", SYSTEM_UID); - rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId); + rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId, CUSTOM_PKG_UID); // New ZenDeviceEffects is used; all fields considered set, since previously were null. assertThat(rule.getDeviceEffects()).isNotNull(); @@ -5286,7 +5302,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { mZenModeHelper.updateAutomaticZenRule(UserHandle.CURRENT, ruleId, update, ORIGIN_USER_IN_SYSTEMUI, "reason", SYSTEM_UID); - AutomaticZenRule result = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId); + AutomaticZenRule result = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId, + SYSTEM_UID); assertThat(result).isNotNull(); assertThat(result.getOwner().getClassName()).isEqualTo("brand.new.cps"); } @@ -5306,7 +5323,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { mZenModeHelper.updateAutomaticZenRule(UserHandle.CURRENT, ruleId, update, ORIGIN_USER_IN_SYSTEMUI, "reason", SYSTEM_UID); - AutomaticZenRule result = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId); + AutomaticZenRule result = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId, + CUSTOM_PKG_UID); assertThat(result).isNotNull(); assertThat(result.getOwner().getClassName()).isEqualTo("old.third.party.cps"); } @@ -5518,8 +5536,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { .build(); String ruleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT, mContext.getPackageName(), rule, ORIGIN_APP, "add it", CUSTOM_PKG_UID); - assertThat(mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId).getCreationTime()) - .isEqualTo(1000); + assertThat(mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId, + CUSTOM_PKG_UID).getCreationTime()).isEqualTo(1000); // User customizes it. AutomaticZenRule userUpdate = new AutomaticZenRule.Builder(rule) @@ -5546,7 +5564,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { // - ZenPolicy is the one that the user had set. // - rule still has the user-modified fields. AutomaticZenRule finalRule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, - newRuleId); + newRuleId, CUSTOM_PKG_UID); assertThat(finalRule.getCreationTime()).isEqualTo(1000); // And not 3000. assertThat(newRuleId).isEqualTo(ruleId); assertThat(finalRule.getInterruptionFilter()).isEqualTo(INTERRUPTION_FILTER_ALARMS); @@ -5575,8 +5593,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { .build(); String ruleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT, mContext.getPackageName(), rule, ORIGIN_APP, "add it", CUSTOM_PKG_UID); - assertThat(mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId).getCreationTime()) - .isEqualTo(1000); + assertThat(mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId, + CUSTOM_PKG_UID).getCreationTime()).isEqualTo(1000); // App deletes it. mTestClock.advanceByMillis(1000); @@ -5592,7 +5610,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { // Verify that the rule was recreated. This means id and creation time are new. AutomaticZenRule finalRule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, - newRuleId); + newRuleId, CUSTOM_PKG_UID); assertThat(finalRule.getCreationTime()).isEqualTo(3000); assertThat(newRuleId).isNotEqualTo(ruleId); } @@ -5609,8 +5627,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { .build(); String ruleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT, mContext.getPackageName(), rule, ORIGIN_APP, "add it", CUSTOM_PKG_UID); - assertThat(mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId).getCreationTime()) - .isEqualTo(1000); + assertThat(mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId, CUSTOM_PKG_UID) + .getCreationTime()).isEqualTo(1000); // User customizes it. mTestClock.advanceByMillis(1000); @@ -5637,7 +5655,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { // Verify that the rule was recreated. This means id and creation time are new, and the rule // matches the latest data supplied to addAZR. AutomaticZenRule finalRule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, - newRuleId); + newRuleId, CUSTOM_PKG_UID); assertThat(finalRule.getCreationTime()).isEqualTo(4000); assertThat(newRuleId).isNotEqualTo(ruleId); assertThat(finalRule.getInterruptionFilter()).isEqualTo(INTERRUPTION_FILTER_PRIORITY); @@ -5660,8 +5678,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { .build(); String ruleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT, mContext.getPackageName(), rule, ORIGIN_APP, "add it", CUSTOM_PKG_UID); - assertThat(mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId).getCreationTime()) - .isEqualTo(1000); + assertThat(mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId, + CUSTOM_PKG_UID).getCreationTime()).isEqualTo(1000); // User customizes it. mTestClock.advanceByMillis(1000); @@ -5686,7 +5704,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { // Verify that the rule was recreated. This means id and creation time are new. AutomaticZenRule finalRule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, - newRuleId); + newRuleId, CUSTOM_PKG_UID); assertThat(finalRule.getCreationTime()).isEqualTo(4000); assertThat(newRuleId).isNotEqualTo(ruleId); } @@ -5728,7 +5746,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { // Verify that the rule was NOT restored: assertThat(newRuleId).isNotEqualTo(ruleId); AutomaticZenRule finalRule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, - newRuleId); + newRuleId, CUSTOM_PKG_UID); assertThat(finalRule.getInterruptionFilter()).isEqualTo(INTERRUPTION_FILTER_ALARMS); assertThat(finalRule.getOwner()).isEqualTo(new ComponentName("second", "owner")); @@ -5869,7 +5887,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { // The rule is restored... assertThat(newRuleId).isEqualTo(ruleId); AutomaticZenRule finalRule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, - newRuleId); + newRuleId, CUSTOM_PKG_UID); assertThat(finalRule.getInterruptionFilter()).isEqualTo(INTERRUPTION_FILTER_ALARMS); // ... but it is NOT active @@ -5923,7 +5941,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { // The rule is restored... assertThat(newRuleId).isEqualTo(ruleId); AutomaticZenRule finalRule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, - newRuleId); + newRuleId, CUSTOM_PKG_UID); assertThat(finalRule.getInterruptionFilter()).isEqualTo(INTERRUPTION_FILTER_ALARMS); // ... but it is NEITHER active NOR snoozed. @@ -6005,22 +6023,22 @@ public class ZenModeHelperTest extends UiServiceTestCase { ORIGIN_APP, "reasons", CUSTOM_PKG_UID); // Null condition -> STATE_FALSE - assertThat(mZenModeHelper.getAutomaticZenRuleState(UserHandle.CURRENT, id)) + assertThat(mZenModeHelper.getAutomaticZenRuleState(UserHandle.CURRENT, id, CUSTOM_PKG_UID)) .isEqualTo(Condition.STATE_FALSE); mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, id, CONDITION_TRUE, ORIGIN_APP, CUSTOM_PKG_UID); - assertThat(mZenModeHelper.getAutomaticZenRuleState(UserHandle.CURRENT, id)) + assertThat(mZenModeHelper.getAutomaticZenRuleState(UserHandle.CURRENT, id, CUSTOM_PKG_UID)) .isEqualTo(Condition.STATE_TRUE); mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, id, CONDITION_FALSE, ORIGIN_APP, CUSTOM_PKG_UID); - assertThat(mZenModeHelper.getAutomaticZenRuleState(UserHandle.CURRENT, id)) + assertThat(mZenModeHelper.getAutomaticZenRuleState(UserHandle.CURRENT, id, CUSTOM_PKG_UID)) .isEqualTo(Condition.STATE_FALSE); mZenModeHelper.removeAutomaticZenRule(UserHandle.CURRENT, id, ORIGIN_APP, "", CUSTOM_PKG_UID); - assertThat(mZenModeHelper.getAutomaticZenRuleState(UserHandle.CURRENT, id)) + assertThat(mZenModeHelper.getAutomaticZenRuleState(UserHandle.CURRENT, id, CUSTOM_PKG_UID)) .isEqualTo(Condition.STATE_UNKNOWN); } @@ -6036,8 +6054,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { mZenModeHelper.setConfig(config, null, ORIGIN_INIT, "", SYSTEM_UID); assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_ALARMS); - assertThat(mZenModeHelper.getAutomaticZenRuleState(UserHandle.CURRENT, "systemRule")) - .isEqualTo(Condition.STATE_UNKNOWN); + assertThat(mZenModeHelper.getAutomaticZenRuleState(UserHandle.CURRENT, "systemRule", + CUSTOM_PKG_UID)).isEqualTo(Condition.STATE_UNKNOWN); } @Test @@ -6063,7 +6081,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { @Test @EnableFlags(FLAG_MODES_API) - public void setAutomaticZenRuleState_conditionForNotOwnedRule_ignored() { + public void setAutomaticZenRuleStateFromConditionProvider_conditionForNotOwnedRule_ignored() { // Assume existence of an other-package-owned rule that is currently ACTIVE. assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_OFF); ZenRule otherRule = newZenRule("another.package", Instant.now(), null); @@ -6075,7 +6093,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_ALARMS); // Should be ignored. - mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, otherRule.conditionId, + mZenModeHelper.setAutomaticZenRuleStateFromConditionProvider(UserHandle.CURRENT, + otherRule.conditionId, new Condition(otherRule.conditionId, "off", Condition.STATE_FALSE), ORIGIN_APP, CUSTOM_PKG_UID); @@ -6182,7 +6201,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { .isEqualTo(ZEN_MODE_IMPORTANT_INTERRUPTIONS); // From user, update that rule's interruption filter. - AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId); + AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId, + SYSTEM_UID); AutomaticZenRule userUpdateRule = new AutomaticZenRule.Builder(rule) .setInterruptionFilter(INTERRUPTION_FILTER_ALARMS) .build(); @@ -6214,7 +6234,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { .isEqualTo(ZEN_MODE_IMPORTANT_INTERRUPTIONS); // From user, update something in that rule, but not the interruption filter. - AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId); + AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId, + SYSTEM_UID); AutomaticZenRule userUpdateRule = new AutomaticZenRule.Builder(rule) .setName("Renamed") .build(); @@ -6315,7 +6336,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { String ruleId = ZenModeConfig.implicitRuleId(mContext.getPackageName()); // User chooses a new name. - AutomaticZenRule azr = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId); + AutomaticZenRule azr = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId, + SYSTEM_UID); mZenModeHelper.updateAutomaticZenRule(UserHandle.CURRENT, ruleId, new AutomaticZenRule.Builder(azr).setName("User chose this").build(), ORIGIN_USER_IN_SYSTEMUI, "reason", SYSTEM_UID); @@ -6414,7 +6436,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { mZenModeHelper.mConfig.getZenPolicy()).allowMedia(true).build(); // From user, update that rule's policy. - AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId); + AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId, + SYSTEM_UID); ZenPolicy userUpdateZenPolicy = new ZenPolicy.Builder().disallowAllSounds() .allowAlarms(true).build(); AutomaticZenRule userUpdateRule = new AutomaticZenRule.Builder(rule) @@ -6456,7 +6479,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { mZenModeHelper.mConfig.getZenPolicy()).allowMedia(true).build(); // From user, update something in that rule, but not the ZenPolicy. - AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId); + AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId, + SYSTEM_UID); AutomaticZenRule userUpdateRule = new AutomaticZenRule.Builder(rule) .setName("Rule renamed, not touching policy") .build(); @@ -6509,7 +6533,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { String ruleId = ZenModeConfig.implicitRuleId(mContext.getPackageName()); // User chooses a new name. - AutomaticZenRule azr = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId); + AutomaticZenRule azr = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId, + SYSTEM_UID); mZenModeHelper.updateAutomaticZenRule(UserHandle.CURRENT, ruleId, new AutomaticZenRule.Builder(azr).setName("User chose this").build(), ORIGIN_USER_IN_SYSTEMUI, "reason", SYSTEM_UID); @@ -6645,7 +6670,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { new AutomaticZenRule.Builder("Rule", CONDITION_ID).setIconResId(resourceId).build(), ORIGIN_APP, "reason", CUSTOM_PKG_UID); AutomaticZenRule storedRule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, - ruleId); + ruleId, CUSTOM_PKG_UID); assertThat(storedRule.getIconResId()).isEqualTo(0); } @@ -7087,8 +7112,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { ORIGIN_USER_IN_SYSTEMUI, SYSTEM_UID); implicitRule = getZenRule(implicitRuleId(CUSTOM_PKG_NAME)); - assertThat(mZenModeHelper.getAutomaticZenRuleState(UserHandle.CURRENT, implicitRule.id)) - .isEqualTo(STATE_TRUE); + assertThat(mZenModeHelper.getAutomaticZenRuleState(UserHandle.CURRENT, implicitRule.id, + CUSTOM_PKG_UID)).isEqualTo(STATE_TRUE); assertThat(implicitRule.isActive()).isTrue(); assertThat(implicitRule.getConditionOverride()).isEqualTo(OVERRIDE_NONE); } @@ -7108,8 +7133,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { ORIGIN_USER_IN_SYSTEMUI, SYSTEM_UID); implicitRule = getZenRule(implicitRuleId(CUSTOM_PKG_NAME)); - assertThat(mZenModeHelper.getAutomaticZenRuleState(UserHandle.CURRENT, implicitRule.id)) - .isEqualTo(STATE_FALSE); + assertThat(mZenModeHelper.getAutomaticZenRuleState(UserHandle.CURRENT, implicitRule.id, + CUSTOM_PKG_UID)).isEqualTo(STATE_FALSE); assertThat(implicitRule.isActive()).isFalse(); assertThat(implicitRule.getConditionOverride()).isEqualTo(OVERRIDE_NONE); } @@ -7177,7 +7202,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, ruleId, new Condition(rule.getConditionId(), "manual-on", STATE_TRUE, SOURCE_USER_ACTION), ORIGIN_USER_IN_SYSTEMUI, SYSTEM_UID); - assertThat(mZenModeHelper.getAutomaticZenRuleState(UserHandle.CURRENT, ruleId)) + assertThat(mZenModeHelper.getAutomaticZenRuleState(UserHandle.CURRENT, ruleId, SYSTEM_UID)) .isEqualTo(STATE_TRUE); ZenRule zenRule = mZenModeHelper.mConfig.automaticRules.get(ruleId); assertThat(zenRule.getConditionOverride()).isEqualTo(OVERRIDE_ACTIVATE); @@ -7192,14 +7217,14 @@ public class ZenModeHelperTest extends UiServiceTestCase { mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL, null); if (Flags.modesUi()) { - assertThat(mZenModeHelper.getAutomaticZenRuleState(UserHandle.CURRENT, ruleId)) - .isEqualTo(STATE_TRUE); + assertThat(mZenModeHelper.getAutomaticZenRuleState(UserHandle.CURRENT, ruleId, + SYSTEM_UID)).isEqualTo(STATE_TRUE); zenRule = mZenModeHelper.mConfig.automaticRules.get(ruleId); assertThat(zenRule.getConditionOverride()).isEqualTo(OVERRIDE_ACTIVATE); assertThat(zenRule.condition).isNull(); } else { - assertThat(mZenModeHelper.getAutomaticZenRuleState(UserHandle.CURRENT, ruleId)) - .isEqualTo(STATE_FALSE); + assertThat(mZenModeHelper.getAutomaticZenRuleState(UserHandle.CURRENT, ruleId, + SYSTEM_UID)).isEqualTo(STATE_FALSE); } } @@ -7218,7 +7243,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, ruleId, new Condition(rule.getConditionId(), "snooze", STATE_FALSE, SOURCE_USER_ACTION), ORIGIN_USER_IN_SYSTEMUI, SYSTEM_UID); - assertThat(mZenModeHelper.getAutomaticZenRuleState(UserHandle.CURRENT, ruleId)) + assertThat( + mZenModeHelper.getAutomaticZenRuleState(UserHandle.CURRENT, ruleId, CUSTOM_PKG_UID)) .isEqualTo(STATE_FALSE); ZenRule zenRule = mZenModeHelper.mConfig.automaticRules.get(ruleId); assertThat(zenRule.getConditionOverride()).isEqualTo(OVERRIDE_DEACTIVATE); @@ -7232,7 +7258,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { TypedXmlPullParser parser = getParserForByteStream(xmlBytes); mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL, null); - assertThat(mZenModeHelper.getAutomaticZenRuleState(UserHandle.CURRENT, ruleId)) + assertThat( + mZenModeHelper.getAutomaticZenRuleState(UserHandle.CURRENT, ruleId, CUSTOM_PKG_UID)) .isEqualTo(STATE_TRUE); zenRule = mZenModeHelper.mConfig.automaticRules.get(ruleId); assertThat(zenRule.getConditionOverride()).isEqualTo(OVERRIDE_NONE); diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatResizeOverridesTest.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatResizeOverridesTest.java index b8d554b405d1..98a4fb3c473f 100644 --- a/services/tests/wmtests/src/com/android/server/wm/AppCompatResizeOverridesTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatResizeOverridesTest.java @@ -184,12 +184,12 @@ public class AppCompatResizeOverridesTest extends WindowTestsBase { void checkShouldOverrideForceResizeApp(boolean expected) { Assert.assertEquals(expected, activity().top().mAppCompatController - .getAppCompatResizeOverrides().shouldOverrideForceResizeApp()); + .getResizeOverrides().shouldOverrideForceResizeApp()); } void checkShouldOverrideForceNonResizeApp(boolean expected) { Assert.assertEquals(expected, activity().top().mAppCompatController - .getAppCompatResizeOverrides().shouldOverrideForceNonResizeApp()); + .getResizeOverrides().shouldOverrideForceNonResizeApp()); } } diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java index b6e393d7be0c..03d904283e83 100644 --- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java @@ -342,8 +342,8 @@ public class AppTransitionTests extends WindowTestsBase { public void testCancelRemoteAnimationWhenFreeze() { final DisplayContent dc = createNewDisplay(Display.STATE_ON); doReturn(false).when(dc).onDescendantOrientationChanged(any()); - final WindowState exitingAppWindow = createWindow(null /* parent */, TYPE_BASE_APPLICATION, - dc, "exiting app"); + final WindowState exitingAppWindow = newWindowBuilder("exiting app", + TYPE_BASE_APPLICATION).setDisplay(dc).build(); final ActivityRecord exitingActivity = exitingAppWindow.mActivityRecord; // Wait until everything in animation handler get executed to prevent the exiting window // from being removed during WindowSurfacePlacer Traversal. diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentDeferredUpdateTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentDeferredUpdateTests.java index 14276ae21899..7033d79d0ee1 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentDeferredUpdateTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentDeferredUpdateTests.java @@ -266,10 +266,10 @@ public class DisplayContentDeferredUpdateTests extends WindowTestsBase { mSetFlagsRule.enableFlags(Flags.FLAG_WAIT_FOR_TRANSITION_ON_DISPLAY_SWITCH); prepareSecondaryDisplay(); - final WindowState defaultDisplayWindow = createWindow(/* parent= */ null, - TYPE_BASE_APPLICATION, mDisplayContent, "DefaultDisplayWindow"); - final WindowState secondaryDisplayWindow = createWindow(/* parent= */ null, - TYPE_BASE_APPLICATION, mSecondaryDisplayContent, "SecondaryDisplayWindow"); + final WindowState defaultDisplayWindow = newWindowBuilder("DefaultDisplayWindow", + TYPE_BASE_APPLICATION).setDisplay(mDisplayContent).build(); + final WindowState secondaryDisplayWindow = newWindowBuilder("SecondaryDisplayWindow", + TYPE_BASE_APPLICATION).setDisplay(mSecondaryDisplayContent).build(); makeWindowVisibleAndNotDrawn(defaultDisplayWindow, secondaryDisplayWindow); // Mark as display switching only for the default display as we filter out diff --git a/services/tests/wmtests/src/com/android/server/wm/DualDisplayAreaGroupPolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/DualDisplayAreaGroupPolicyTest.java index bd15bc42e811..347d1bc1becc 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DualDisplayAreaGroupPolicyTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/DualDisplayAreaGroupPolicyTest.java @@ -379,13 +379,11 @@ public class DualDisplayAreaGroupPolicyTest extends WindowTestsBase { assertThat(imeContainer.getRootDisplayArea()).isEqualTo(mDisplay); assertThat(mDisplay.findAreaForTokenInLayer(imeToken)).isEqualTo(imeContainer); - final WindowState firstActivityWin = - createWindow(null /* parent */, TYPE_APPLICATION_STARTING, mFirstActivity, - "firstActivityWin"); + final WindowState firstActivityWin = newWindowBuilder("firstActivityWin", + TYPE_APPLICATION_STARTING).setWindowToken(mFirstActivity).build(); spyOn(firstActivityWin); - final WindowState secondActivityWin = - createWindow(null /* parent */, TYPE_APPLICATION_STARTING, mSecondActivity, - "firstActivityWin"); + final WindowState secondActivityWin = newWindowBuilder("secondActivityWin", + TYPE_APPLICATION_STARTING).setWindowToken(mSecondActivity).build(); spyOn(secondActivityWin); // firstActivityWin should be the target @@ -424,13 +422,11 @@ public class DualDisplayAreaGroupPolicyTest extends WindowTestsBase { setupImeWindow(); final DisplayArea.Tokens imeContainer = mDisplay.getImeContainer(); final WindowToken imeToken = tokenOfType(TYPE_INPUT_METHOD); - final WindowState firstActivityWin = - createWindow(null /* parent */, TYPE_APPLICATION_STARTING, mFirstActivity, - "firstActivityWin"); + final WindowState firstActivityWin = newWindowBuilder("firstActivityWin", + TYPE_APPLICATION_STARTING).setWindowToken(mFirstActivity).build(); spyOn(firstActivityWin); - final WindowState secondActivityWin = - createWindow(null /* parent */, TYPE_APPLICATION_STARTING, mSecondActivity, - "secondActivityWin"); + final WindowState secondActivityWin = newWindowBuilder("secondActivityWin", + TYPE_APPLICATION_STARTING).setWindowToken(mSecondActivity).build(); spyOn(secondActivityWin); // firstActivityWin should be the target @@ -464,9 +460,8 @@ public class DualDisplayAreaGroupPolicyTest extends WindowTestsBase { assertThat(imeContainer.getRootDisplayArea()).isEqualTo(mDisplay); assertThat(mDisplay.findAreaForTokenInLayer(imeToken)).isEqualTo(imeContainer); - final WindowState firstActivityWin = - createWindow(null /* parent */, TYPE_APPLICATION_STARTING, mFirstActivity, - "firstActivityWin"); + final WindowState firstActivityWin = newWindowBuilder("firstActivityWin", + TYPE_APPLICATION_STARTING).setWindowToken(mFirstActivity).build(); spyOn(firstActivityWin); // firstActivityWin should be the target doReturn(true).when(firstActivityWin).canBeImeTarget(); @@ -499,9 +494,8 @@ public class DualDisplayAreaGroupPolicyTest extends WindowTestsBase { assertThat(imeContainer.getRootDisplayArea()).isEqualTo(mDisplay); // firstActivityWin should be the target - final WindowState firstActivityWin = - createWindow(null /* parent */, TYPE_APPLICATION_STARTING, mFirstActivity, - "firstActivityWin"); + final WindowState firstActivityWin = newWindowBuilder("firstActivityWin", + TYPE_APPLICATION_STARTING).setWindowToken(mFirstActivity).build(); spyOn(firstActivityWin); doReturn(true).when(firstActivityWin).canBeImeTarget(); WindowState imeTarget = mDisplay.computeImeTarget(true /* updateImeTarget */); @@ -560,8 +554,8 @@ public class DualDisplayAreaGroupPolicyTest extends WindowTestsBase { } private void setupImeWindow() { - final WindowState imeWindow = createWindow(null /* parent */, - TYPE_INPUT_METHOD, mDisplay, "mImeWindow"); + final WindowState imeWindow = newWindowBuilder("mImeWindow", TYPE_INPUT_METHOD).setDisplay( + mDisplay).build(); imeWindow.mAttrs.flags |= FLAG_NOT_FOCUSABLE; mDisplay.mInputMethodWindow = imeWindow; } diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java index dc4adcc4315b..299717393028 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java @@ -878,8 +878,10 @@ public class TaskFragmentTest extends WindowTestsBase { .build(); final ActivityRecord activity0 = tf0.getTopMostActivity(); final ActivityRecord activity1 = tf1.getTopMostActivity(); - final WindowState win0 = createWindow(null, TYPE_BASE_APPLICATION, activity0, "win0"); - final WindowState win1 = createWindow(null, TYPE_BASE_APPLICATION, activity1, "win1"); + final WindowState win0 = newWindowBuilder("win0", TYPE_BASE_APPLICATION).setWindowToken( + activity0).build(); + final WindowState win1 = newWindowBuilder("win1", TYPE_BASE_APPLICATION).setWindowToken( + activity1).build(); doReturn(false).when(mDisplayContent).shouldImeAttachedToApp(); mDisplayContent.setImeInputTarget(win0); @@ -1174,8 +1176,8 @@ public class TaskFragmentTest extends WindowTestsBase { } private WindowState createAppWindow(ActivityRecord app, String name) { - final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, app, name, - 0 /* ownerId */, false /* ownerCanAddInternalSystemWindow */, new TestIWindow()); + final WindowState win = newWindowBuilder(name, TYPE_BASE_APPLICATION).setWindowToken( + app).setClientWindow(new TestIWindow()).build(); mWm.mWindowMap.put(win.mClient.asBinder(), win); return win; } diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotCacheTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotCacheTest.java index f145b40d2292..f9250f9ecf5d 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotCacheTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotCacheTest.java @@ -63,7 +63,7 @@ public class TaskSnapshotCacheTest extends TaskSnapshotPersisterTestBase { @Test public void testAppRemoved() { - final WindowState window = createWindow(null, FIRST_APPLICATION_WINDOW, "window"); + final WindowState window = newWindowBuilder("window", FIRST_APPLICATION_WINDOW).build(); mCache.putSnapshot(window.getTask(), createSnapshot()); assertNotNull(mCache.getSnapshot(window.getTask().mTaskId, false /* isLowResolution */)); mCache.onAppRemoved(window.mActivityRecord); @@ -72,7 +72,7 @@ public class TaskSnapshotCacheTest extends TaskSnapshotPersisterTestBase { @Test public void testAppDied() { - final WindowState window = createWindow(null, FIRST_APPLICATION_WINDOW, "window"); + final WindowState window = newWindowBuilder("window", FIRST_APPLICATION_WINDOW).build(); mCache.putSnapshot(window.getTask(), createSnapshot()); assertNotNull(mCache.getSnapshot(window.getTask().mTaskId, false /* isLowResolution */)); mCache.onAppDied(window.mActivityRecord); @@ -81,7 +81,7 @@ public class TaskSnapshotCacheTest extends TaskSnapshotPersisterTestBase { @Test public void testTaskRemoved() { - final WindowState window = createWindow(null, FIRST_APPLICATION_WINDOW, "window"); + final WindowState window = newWindowBuilder("window", FIRST_APPLICATION_WINDOW).build(); mCache.putSnapshot(window.getTask(), createSnapshot()); assertNotNull(mCache.getSnapshot(window.getTask().mTaskId, false /* isLowResolution */)); mCache.onIdRemoved(window.getTask().mTaskId); @@ -90,7 +90,7 @@ public class TaskSnapshotCacheTest extends TaskSnapshotPersisterTestBase { @Test public void testReduced_notCached() { - final WindowState window = createWindow(null, FIRST_APPLICATION_WINDOW, "window"); + final WindowState window = newWindowBuilder("window", FIRST_APPLICATION_WINDOW).build(); mPersister.persistSnapshot(window.getTask().mTaskId, mWm.mCurrentUserId, createSnapshot()); mSnapshotPersistQueue.waitForQueueEmpty(); assertNull(mCache.getSnapshot(window.getTask().mTaskId, false /* isLowResolution */)); @@ -105,7 +105,7 @@ public class TaskSnapshotCacheTest extends TaskSnapshotPersisterTestBase { @Test public void testRestoreFromDisk() { - final WindowState window = createWindow(null, FIRST_APPLICATION_WINDOW, "window"); + final WindowState window = newWindowBuilder("window", FIRST_APPLICATION_WINDOW).build(); mPersister.persistSnapshot(window.getTask().mTaskId, mWm.mCurrentUserId, createSnapshot()); mSnapshotPersistQueue.waitForQueueEmpty(); assertNull(mCache.getSnapshot(window.getTask().mTaskId, false /* isLowResolution */)); @@ -117,7 +117,7 @@ public class TaskSnapshotCacheTest extends TaskSnapshotPersisterTestBase { @Test public void testClearCache() { - final WindowState window = createWindow(null, FIRST_APPLICATION_WINDOW, "window"); + final WindowState window = newWindowBuilder("window", FIRST_APPLICATION_WINDOW).build(); mCache.putSnapshot(window.getTask(), mSnapshot); assertEquals(mSnapshot, mCache.getSnapshot(window.getTask().mTaskId, false /* isLowResolution */)); diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java index c6b2a6b8d42f..1bca53aff034 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java @@ -74,8 +74,8 @@ public class TaskSnapshotControllerTest extends WindowTestsBase { @Test public void testGetClosingApps_closing() { - final WindowState closingWindow = createWindow(null, FIRST_APPLICATION_WINDOW, - "closingWindow"); + final WindowState closingWindow = newWindowBuilder("closingWindow", + FIRST_APPLICATION_WINDOW).build(); closingWindow.mActivityRecord.commitVisibility( false /* visible */, true /* performLayout */); final ArraySet<ActivityRecord> closingApps = new ArraySet<>(); @@ -88,8 +88,8 @@ public class TaskSnapshotControllerTest extends WindowTestsBase { @Test public void testGetClosingApps_notClosing() { - final WindowState closingWindow = createWindow(null, FIRST_APPLICATION_WINDOW, - "closingWindow"); + final WindowState closingWindow = newWindowBuilder("closingWindow", + FIRST_APPLICATION_WINDOW).build(); final WindowState openingWindow = createAppWindow(closingWindow.getTask(), FIRST_APPLICATION_WINDOW, "openingWindow"); closingWindow.mActivityRecord.commitVisibility( @@ -105,8 +105,8 @@ public class TaskSnapshotControllerTest extends WindowTestsBase { @Test public void testGetClosingApps_skipClosingAppsSnapshotTasks() { - final WindowState closingWindow = createWindow(null, FIRST_APPLICATION_WINDOW, - "closingWindow"); + final WindowState closingWindow = newWindowBuilder("closingWindow", + FIRST_APPLICATION_WINDOW).build(); closingWindow.mActivityRecord.commitVisibility( false /* visible */, true /* performLayout */); final ArraySet<ActivityRecord> closingApps = new ArraySet<>(); @@ -133,19 +133,19 @@ public class TaskSnapshotControllerTest extends WindowTestsBase { @Test public void testGetSnapshotMode() { - final WindowState disabledWindow = createWindow(null, - FIRST_APPLICATION_WINDOW, mDisplayContent, "disabledWindow"); + final WindowState disabledWindow = newWindowBuilder("disabledWindow", + FIRST_APPLICATION_WINDOW).setDisplay(mDisplayContent).build(); disabledWindow.mActivityRecord.setRecentsScreenshotEnabled(false); assertEquals(SNAPSHOT_MODE_APP_THEME, mWm.mTaskSnapshotController.getSnapshotMode(disabledWindow.getTask())); - final WindowState normalWindow = createWindow(null, - FIRST_APPLICATION_WINDOW, mDisplayContent, "normalWindow"); + final WindowState normalWindow = newWindowBuilder("normalWindow", + FIRST_APPLICATION_WINDOW).setDisplay(mDisplayContent).build(); assertEquals(SNAPSHOT_MODE_REAL, mWm.mTaskSnapshotController.getSnapshotMode(normalWindow.getTask())); - final WindowState secureWindow = createWindow(null, - FIRST_APPLICATION_WINDOW, mDisplayContent, "secureWindow"); + final WindowState secureWindow = newWindowBuilder("secureWindow", + FIRST_APPLICATION_WINDOW).setDisplay(mDisplayContent).build(); secureWindow.mAttrs.flags |= FLAG_SECURE; assertEquals(SNAPSHOT_MODE_APP_THEME, mWm.mTaskSnapshotController.getSnapshotMode(secureWindow.getTask())); @@ -297,8 +297,8 @@ public class TaskSnapshotControllerTest extends WindowTestsBase { spyOn(mWm.mTaskSnapshotController); doReturn(false).when(mWm.mTaskSnapshotController).shouldDisableSnapshots(); - final WindowState normalWindow = createWindow(null, - FIRST_APPLICATION_WINDOW, mDisplayContent, "normalWindow"); + final WindowState normalWindow = newWindowBuilder("normalWindow", + FIRST_APPLICATION_WINDOW).setDisplay(mDisplayContent).build(); final TaskSnapshot snapshot = new TaskSnapshotPersisterTestBase.TaskSnapshotBuilder() .setTopActivityComponent(normalWindow.mActivityRecord.mActivityComponent).build(); doReturn(snapshot).when(mWm.mTaskSnapshotController).snapshot(any()); diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotLowResDisabledTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotLowResDisabledTest.java index 9bde0663d4a3..51ea498811fc 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotLowResDisabledTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotLowResDisabledTest.java @@ -41,7 +41,7 @@ import java.io.File; * Test class for {@link TaskSnapshotPersister} and {@link AppSnapshotLoader} * * Build/Install/Run: - * atest TaskSnapshotPersisterLoaderTest + * atest TaskSnapshotLowResDisabledTest */ @MediumTest @Presubmit @@ -126,7 +126,7 @@ public class TaskSnapshotLowResDisabledTest extends TaskSnapshotPersisterTestBas @Test public void testReduced_notCached() { - final WindowState window = createWindow(null, FIRST_APPLICATION_WINDOW, "window"); + final WindowState window = newWindowBuilder("window", FIRST_APPLICATION_WINDOW).build(); mPersister.persistSnapshot(window.getTask().mTaskId, mWm.mCurrentUserId, createSnapshot()); mSnapshotPersistQueue.waitForQueueEmpty(); assertNull(mCache.getSnapshot(window.getTask().mTaskId, false /* isLowResolution */)); diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java index 1fa657822189..5ed2df30518b 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java @@ -504,8 +504,8 @@ public class WindowContainerTests extends WindowTestsBase { assertTrue(child.isAnimating(PARENTS, ANIMATION_TYPE_APP_TRANSITION)); assertFalse(child.isAnimating(PARENTS, ANIMATION_TYPE_SCREEN_ROTATION)); - final WindowState windowState = createWindow(null /* parent */, TYPE_BASE_APPLICATION, - mDisplayContent, "TestWindowState"); + final WindowState windowState = newWindowBuilder("TestWindowState", + TYPE_BASE_APPLICATION).setDisplay(mDisplayContent).build(); WindowContainer parent = windowState.getParent(); spyOn(windowState.mSurfaceAnimator); doReturn(true).when(windowState.mSurfaceAnimator).isAnimating(); @@ -1045,8 +1045,8 @@ public class WindowContainerTests extends WindowTestsBase { // An animating window with mRemoveOnExit can be removed by handleCompleteDeferredRemoval // once it no longer animates. - final WindowState exitingWindow = createWindow(null, TYPE_APPLICATION_OVERLAY, - displayContent, "exiting window"); + final WindowState exitingWindow = newWindowBuilder("exiting window", + TYPE_APPLICATION_OVERLAY).setDisplay(displayContent).build(); exitingWindow.startAnimation(exitingWindow.getPendingTransaction(), mock(AnimationAdapter.class), false /* hidden */, SurfaceAnimator.ANIMATION_TYPE_WINDOW_ANIMATION); @@ -1063,7 +1063,7 @@ public class WindowContainerTests extends WindowTestsBase { final ActivityRecord r = new TaskBuilder(mSupervisor).setCreateActivity(true) .setDisplay(displayContent).build().getTopMostActivity(); // Add a window and make the activity animating so the removal of activity is deferred. - createWindow(null, TYPE_BASE_APPLICATION, r, "win"); + newWindowBuilder("win", TYPE_BASE_APPLICATION).setWindowToken(r).build(); doReturn(true).when(r).isAnimating(anyInt(), anyInt()); displayContent.remove(); @@ -1216,7 +1216,8 @@ public class WindowContainerTests extends WindowTestsBase { public void testFreezeInsets() { final Task task = createTask(mDisplayContent); final ActivityRecord activity = createActivityRecord(mDisplayContent, task); - final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, activity, "win"); + final WindowState win = newWindowBuilder("win", TYPE_BASE_APPLICATION).setWindowToken( + activity).build(); // Set visibility to false, verify the main window of the task will be set the frozen // insets state immediately. @@ -1233,7 +1234,8 @@ public class WindowContainerTests extends WindowTestsBase { final Task rootTask = createTask(mDisplayContent); final Task task = createTaskInRootTask(rootTask, 0 /* userId */); final ActivityRecord activity = createActivityRecord(mDisplayContent, task); - final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, activity, "win"); + final WindowState win = newWindowBuilder("win", TYPE_BASE_APPLICATION).setWindowToken( + activity).build(); task.getDisplayContent().prepareAppTransition(TRANSIT_CLOSE); spyOn(win); doReturn(true).when(task).okToAnimate(); diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTraversalTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTraversalTests.java index 72935cb546d9..8606581539ff 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTraversalTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTraversalTests.java @@ -49,9 +49,10 @@ public class WindowContainerTraversalTests extends WindowTestsBase { @SetupWindows(addWindows = { W_DOCK_DIVIDER, W_INPUT_METHOD }) @Test public void testDockedDividerPosition() { - final WindowState splitScreenWindow = createWindow(null, - WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD, TYPE_BASE_APPLICATION, - mDisplayContent, "splitScreenWindow"); + final WindowState splitScreenWindow = newWindowBuilder("splitScreenWindow", + TYPE_BASE_APPLICATION).setWindowingMode( + WINDOWING_MODE_MULTI_WINDOW).setActivityType(ACTIVITY_TYPE_STANDARD).setDisplay( + mDisplayContent).build(); mDisplayContent.setImeLayeringTarget(splitScreenWindow); diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java index 50e0e181cd68..513ba1d49258 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java @@ -154,9 +154,11 @@ public class WindowStateTests extends WindowTestsBase { @Test public void testIsParentWindowHidden() { - final WindowState parentWindow = createWindow(null, TYPE_APPLICATION, "parentWindow"); - final WindowState child1 = createWindow(parentWindow, FIRST_SUB_WINDOW, "child1"); - final WindowState child2 = createWindow(parentWindow, FIRST_SUB_WINDOW, "child2"); + final WindowState parentWindow = newWindowBuilder("parentWindow", TYPE_APPLICATION).build(); + final WindowState child1 = newWindowBuilder("child1", FIRST_SUB_WINDOW).setParent( + parentWindow).build(); + final WindowState child2 = newWindowBuilder("child2", FIRST_SUB_WINDOW).setParent( + parentWindow).build(); // parentWindow is initially set to hidden. assertTrue(parentWindow.mHidden); @@ -172,10 +174,12 @@ public class WindowStateTests extends WindowTestsBase { @Test public void testIsChildWindow() { - final WindowState parentWindow = createWindow(null, TYPE_APPLICATION, "parentWindow"); - final WindowState child1 = createWindow(parentWindow, FIRST_SUB_WINDOW, "child1"); - final WindowState child2 = createWindow(parentWindow, FIRST_SUB_WINDOW, "child2"); - final WindowState randomWindow = createWindow(null, TYPE_APPLICATION, "randomWindow"); + final WindowState parentWindow = newWindowBuilder("parentWindow", TYPE_APPLICATION).build(); + final WindowState child1 = newWindowBuilder("child1", FIRST_SUB_WINDOW).setParent( + parentWindow).build(); + final WindowState child2 = newWindowBuilder("child2", FIRST_SUB_WINDOW).setParent( + parentWindow).build(); + final WindowState randomWindow = newWindowBuilder("randomWindow", TYPE_APPLICATION).build(); assertFalse(parentWindow.isChildWindow()); assertTrue(child1.isChildWindow()); @@ -185,12 +189,15 @@ public class WindowStateTests extends WindowTestsBase { @Test public void testHasChild() { - final WindowState win1 = createWindow(null, TYPE_APPLICATION, "win1"); - final WindowState win11 = createWindow(win1, FIRST_SUB_WINDOW, "win11"); - final WindowState win12 = createWindow(win1, FIRST_SUB_WINDOW, "win12"); - final WindowState win2 = createWindow(null, TYPE_APPLICATION, "win2"); - final WindowState win21 = createWindow(win2, FIRST_SUB_WINDOW, "win21"); - final WindowState randomWindow = createWindow(null, TYPE_APPLICATION, "randomWindow"); + final WindowState win1 = newWindowBuilder("win1", TYPE_APPLICATION).build(); + final WindowState win11 = newWindowBuilder("win11", FIRST_SUB_WINDOW).setParent( + win1).build(); + final WindowState win12 = newWindowBuilder("win12", FIRST_SUB_WINDOW).setParent( + win1).build(); + final WindowState win2 = newWindowBuilder("win2", TYPE_APPLICATION).build(); + final WindowState win21 = newWindowBuilder("win21", FIRST_SUB_WINDOW).setParent( + win2).build(); + final WindowState randomWindow = newWindowBuilder("randomWindow", TYPE_APPLICATION).build(); assertTrue(win1.hasChild(win11)); assertTrue(win1.hasChild(win12)); @@ -206,9 +213,11 @@ public class WindowStateTests extends WindowTestsBase { @Test public void testGetParentWindow() { - final WindowState parentWindow = createWindow(null, TYPE_APPLICATION, "parentWindow"); - final WindowState child1 = createWindow(parentWindow, FIRST_SUB_WINDOW, "child1"); - final WindowState child2 = createWindow(parentWindow, FIRST_SUB_WINDOW, "child2"); + final WindowState parentWindow = newWindowBuilder("parentWindow", TYPE_APPLICATION).build(); + final WindowState child1 = newWindowBuilder("child1", FIRST_SUB_WINDOW).setParent( + parentWindow).build(); + final WindowState child2 = newWindowBuilder("child2", FIRST_SUB_WINDOW).setParent( + parentWindow).build(); assertNull(parentWindow.getParentWindow()); assertEquals(parentWindow, child1.getParentWindow()); @@ -217,8 +226,8 @@ public class WindowStateTests extends WindowTestsBase { @Test public void testOverlayWindowHiddenWhenSuspended() { - final WindowState overlayWindow = spy(createWindow(null, TYPE_APPLICATION_OVERLAY, - "overlayWindow")); + final WindowState overlayWindow = spy( + newWindowBuilder("overlayWindow", TYPE_APPLICATION_OVERLAY).build()); overlayWindow.setHiddenWhileSuspended(true); verify(overlayWindow).hide(true /* doAnimation */, true /* requestAnim */); overlayWindow.setHiddenWhileSuspended(false); @@ -227,9 +236,11 @@ public class WindowStateTests extends WindowTestsBase { @Test public void testGetTopParentWindow() { - final WindowState root = createWindow(null, TYPE_APPLICATION, "root"); - final WindowState child1 = createWindow(root, FIRST_SUB_WINDOW, "child1"); - final WindowState child2 = createWindow(child1, FIRST_SUB_WINDOW, "child2"); + final WindowState root = newWindowBuilder("root", TYPE_APPLICATION).build(); + final WindowState child1 = newWindowBuilder("child1", FIRST_SUB_WINDOW).setParent( + root).build(); + final WindowState child2 = newWindowBuilder("child2", FIRST_SUB_WINDOW).setParent( + child1).build(); assertEquals(root, root.getTopParentWindow()); assertEquals(root, child1.getTopParentWindow()); @@ -244,7 +255,7 @@ public class WindowStateTests extends WindowTestsBase { @Test public void testIsOnScreen_hiddenByPolicy() { - final WindowState window = createWindow(null, TYPE_APPLICATION, "window"); + final WindowState window = newWindowBuilder("window", TYPE_APPLICATION).build(); window.setHasSurface(true); assertTrue(window.isOnScreen()); window.hide(false /* doAnimation */, false /* requestAnim */); @@ -273,8 +284,8 @@ public class WindowStateTests extends WindowTestsBase { @Test public void testCanBeImeTarget() { - final WindowState appWindow = createWindow(null, TYPE_APPLICATION, "appWindow"); - final WindowState imeWindow = createWindow(null, TYPE_INPUT_METHOD, "imeWindow"); + final WindowState appWindow = newWindowBuilder("appWindow", TYPE_APPLICATION).build(); + final WindowState imeWindow = newWindowBuilder("imeWindow", TYPE_INPUT_METHOD).build(); // Setting FLAG_NOT_FOCUSABLE prevents the window from being an IME target. appWindow.mAttrs.flags |= FLAG_NOT_FOCUSABLE; @@ -328,16 +339,17 @@ public class WindowStateTests extends WindowTestsBase { @Test public void testGetWindow() { - final WindowState root = createWindow(null, TYPE_APPLICATION, "root"); - final WindowState mediaChild = createWindow(root, TYPE_APPLICATION_MEDIA, "mediaChild"); - final WindowState mediaOverlayChild = createWindow(root, - TYPE_APPLICATION_MEDIA_OVERLAY, "mediaOverlayChild"); - final WindowState attachedDialogChild = createWindow(root, - TYPE_APPLICATION_ATTACHED_DIALOG, "attachedDialogChild"); - final WindowState subPanelChild = createWindow(root, - TYPE_APPLICATION_SUB_PANEL, "subPanelChild"); - final WindowState aboveSubPanelChild = createWindow(root, - TYPE_APPLICATION_ABOVE_SUB_PANEL, "aboveSubPanelChild"); + final WindowState root = newWindowBuilder("root", TYPE_APPLICATION).build(); + final WindowState mediaChild = newWindowBuilder("mediaChild", + TYPE_APPLICATION_MEDIA).setParent(root).build(); + final WindowState mediaOverlayChild = newWindowBuilder("mediaOverlayChild", + TYPE_APPLICATION_MEDIA_OVERLAY).setParent(root).build(); + final WindowState attachedDialogChild = newWindowBuilder("attachedDialogChild", + TYPE_APPLICATION_ATTACHED_DIALOG).setParent(root).build(); + final WindowState subPanelChild = newWindowBuilder("subPanelChild", + TYPE_APPLICATION_SUB_PANEL).setParent(root).build(); + final WindowState aboveSubPanelChild = newWindowBuilder("aboveSubPanelChild", + TYPE_APPLICATION_ABOVE_SUB_PANEL).setParent(root).build(); final LinkedList<WindowState> windows = new LinkedList<>(); @@ -358,7 +370,7 @@ public class WindowStateTests extends WindowTestsBase { @Test public void testDestroySurface() { - final WindowState win = createWindow(null, TYPE_APPLICATION, "win"); + final WindowState win = newWindowBuilder("win", TYPE_APPLICATION).build(); win.mHasSurface = win.mAnimatingExit = true; win.mWinAnimator.mSurfaceControl = mock(SurfaceControl.class); win.onExitAnimationDone(); @@ -384,8 +396,10 @@ public class WindowStateTests extends WindowTestsBase { // Call prepareWindowToDisplayDuringRelayout for a window without FLAG_TURN_SCREEN_ON before // calling setCurrentLaunchCanTurnScreenOn for windows with flag in the same activity. final ActivityRecord activity = createActivityRecord(mDisplayContent); - final WindowState first = createWindow(null, TYPE_APPLICATION, activity, "first"); - final WindowState second = createWindow(null, TYPE_APPLICATION, activity, "second"); + final WindowState first = newWindowBuilder("first", TYPE_APPLICATION).setWindowToken( + activity).build(); + final WindowState second = newWindowBuilder("second", TYPE_APPLICATION).setWindowToken( + activity).build(); testPrepareWindowToDisplayDuringRelayout(first, false /* expectedWakeupCalled */, true /* expectedCurrentLaunchCanTurnScreenOn */); @@ -423,10 +437,10 @@ public class WindowStateTests extends WindowTestsBase { // Call prepareWindowToDisplayDuringRelayout for a windows that are not children of an // activity. Both windows have the FLAG_TURNS_SCREEN_ON so both should call wakeup final WindowToken windowToken = createTestWindowToken(FIRST_SUB_WINDOW, mDisplayContent); - final WindowState firstWindow = createWindow(null, TYPE_APPLICATION, windowToken, - "firstWindow"); - final WindowState secondWindow = createWindow(null, TYPE_APPLICATION, windowToken, - "secondWindow"); + final WindowState firstWindow = newWindowBuilder("firstWindow", + TYPE_APPLICATION).setWindowToken(windowToken).build(); + final WindowState secondWindow = newWindowBuilder("secondWindow", + TYPE_APPLICATION).setWindowToken(windowToken).build(); firstWindow.mAttrs.flags |= WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON; secondWindow.mAttrs.flags |= WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON; @@ -459,7 +473,7 @@ public class WindowStateTests extends WindowTestsBase { @Test public void testCanAffectSystemUiFlags() { - final WindowState app = createWindow(null, TYPE_APPLICATION, "app"); + final WindowState app = newWindowBuilder("app", TYPE_APPLICATION).build(); app.mActivityRecord.setVisible(true); assertTrue(app.canAffectSystemUiFlags()); app.mActivityRecord.setVisible(false); @@ -471,7 +485,7 @@ public class WindowStateTests extends WindowTestsBase { @Test public void testCanAffectSystemUiFlags_starting() { - final WindowState app = createWindow(null, TYPE_APPLICATION_STARTING, "app"); + final WindowState app = newWindowBuilder("app", TYPE_APPLICATION_STARTING).build(); app.mActivityRecord.setVisible(true); app.mStartingData = new SnapshotStartingData(mWm, null, 0); assertFalse(app.canAffectSystemUiFlags()); @@ -481,7 +495,7 @@ public class WindowStateTests extends WindowTestsBase { @Test public void testCanAffectSystemUiFlags_disallow() { - final WindowState app = createWindow(null, TYPE_APPLICATION, "app"); + final WindowState app = newWindowBuilder("app", TYPE_APPLICATION).build(); app.mActivityRecord.setVisible(true); assertTrue(app.canAffectSystemUiFlags()); app.getTask().setCanAffectSystemUiFlags(false); @@ -538,9 +552,11 @@ public class WindowStateTests extends WindowTestsBase { @Test public void testIsSelfOrAncestorWindowAnimating() { - final WindowState root = createWindow(null, TYPE_APPLICATION, "root"); - final WindowState child1 = createWindow(root, FIRST_SUB_WINDOW, "child1"); - final WindowState child2 = createWindow(child1, FIRST_SUB_WINDOW, "child2"); + final WindowState root = newWindowBuilder("root", TYPE_APPLICATION).build(); + final WindowState child1 = newWindowBuilder("child1", FIRST_SUB_WINDOW).setParent( + root).build(); + final WindowState child2 = newWindowBuilder("child2", FIRST_SUB_WINDOW).setParent( + child1).build(); assertFalse(child2.isSelfOrAncestorWindowAnimatingExit()); child2.mAnimatingExit = true; assertTrue(child2.isSelfOrAncestorWindowAnimatingExit()); @@ -551,7 +567,7 @@ public class WindowStateTests extends WindowTestsBase { @Test public void testDeferredRemovalByAnimating() { - final WindowState appWindow = createWindow(null, TYPE_APPLICATION, "appWindow"); + final WindowState appWindow = newWindowBuilder("appWindow", TYPE_APPLICATION).build(); makeWindowVisible(appWindow); spyOn(appWindow.mWinAnimator); doReturn(true).when(appWindow.mWinAnimator).getShown(); @@ -571,8 +587,9 @@ public class WindowStateTests extends WindowTestsBase { @Test public void testOnExitAnimationDone() { - final WindowState parent = createWindow(null, TYPE_APPLICATION, "parent"); - final WindowState child = createWindow(parent, TYPE_APPLICATION_PANEL, "child"); + final WindowState parent = newWindowBuilder("parent", TYPE_APPLICATION).build(); + final WindowState child = newWindowBuilder("child", TYPE_APPLICATION_PANEL).setParent( + parent).build(); final SurfaceControl.Transaction t = parent.getPendingTransaction(); child.startAnimation(t, mock(AnimationAdapter.class), false /* hidden */, SurfaceAnimator.ANIMATION_TYPE_WINDOW_ANIMATION); @@ -609,7 +626,7 @@ public class WindowStateTests extends WindowTestsBase { @Test public void testLayoutSeqResetOnReparent() { - final WindowState app = createWindow(null, TYPE_APPLICATION, "app"); + final WindowState app = newWindowBuilder("app", TYPE_APPLICATION).build(); app.mLayoutSeq = 1; mDisplayContent.mLayoutSeq = 1; @@ -622,7 +639,7 @@ public class WindowStateTests extends WindowTestsBase { @Test public void testDisplayIdUpdatedOnReparent() { - final WindowState app = createWindow(null, TYPE_APPLICATION, "app"); + final WindowState app = newWindowBuilder("app", TYPE_APPLICATION).build(); // fake a different display app.mInputWindowHandle.setDisplayId(mDisplayContent.getDisplayId() + 1); app.onDisplayChanged(mDisplayContent); @@ -633,7 +650,7 @@ public class WindowStateTests extends WindowTestsBase { @Test public void testApplyWithNextDraw() { - final WindowState win = createWindow(null, TYPE_APPLICATION_OVERLAY, "app"); + final WindowState win = newWindowBuilder("app", TYPE_APPLICATION_OVERLAY).build(); final SurfaceControl.Transaction[] handledT = { null }; // The normal case that the draw transaction is applied with finishing drawing. win.applyWithNextDraw(t -> handledT[0] = t); @@ -657,7 +674,7 @@ public class WindowStateTests extends WindowTestsBase { @Test public void testSeamlesslyRotateWindow() { - final WindowState app = createWindow(null, TYPE_APPLICATION, "app"); + final WindowState app = newWindowBuilder("app", TYPE_APPLICATION).build(); final SurfaceControl.Transaction t = spy(StubTransaction.class); makeWindowVisible(app); @@ -707,7 +724,7 @@ public class WindowStateTests extends WindowTestsBase { @Test public void testVisibilityChangeSwitchUser() { - final WindowState window = createWindow(null, TYPE_APPLICATION, "app"); + final WindowState window = newWindowBuilder("app", TYPE_APPLICATION).build(); window.mHasSurface = true; spyOn(window); doReturn(false).when(window).showForAllUsers(); @@ -729,8 +746,9 @@ public class WindowStateTests extends WindowTestsBase { final CompatModePackages cmp = mWm.mAtmService.mCompatModePackages; spyOn(cmp); doReturn(overrideScale).when(cmp).getCompatScale(anyString(), anyInt()); - final WindowState w = createWindow(null, TYPE_APPLICATION_OVERLAY, "win"); - final WindowState child = createWindow(w, TYPE_APPLICATION_PANEL, "child"); + final WindowState w = newWindowBuilder("win", TYPE_APPLICATION_OVERLAY).build(); + final WindowState child = newWindowBuilder("child", TYPE_APPLICATION_PANEL).setParent( + w).build(); assertTrue(w.hasCompatScale()); assertTrue(child.hasCompatScale()); @@ -788,7 +806,8 @@ public class WindowStateTests extends WindowTestsBase { // Child window without scale (e.g. different app) should apply inverse scale of parent. doReturn(1f).when(cmp).getCompatScale(anyString(), anyInt()); - final WindowState child2 = createWindow(w, TYPE_APPLICATION_SUB_PANEL, "child2"); + final WindowState child2 = newWindowBuilder("child2", TYPE_APPLICATION_SUB_PANEL).setParent( + w).build(); makeWindowVisible(w, child2); clearInvocations(t); child2.prepareSurfaces(); @@ -798,10 +817,10 @@ public class WindowStateTests extends WindowTestsBase { @SetupWindows(addWindows = { W_ABOVE_ACTIVITY, W_NOTIFICATION_SHADE }) @Test public void testRequestDrawIfNeeded() { - final WindowState startingApp = createWindow(null /* parent */, - TYPE_BASE_APPLICATION, "startingApp"); - final WindowState startingWindow = createWindow(null /* parent */, - TYPE_APPLICATION_STARTING, startingApp.mToken, "starting"); + final WindowState startingApp = newWindowBuilder("startingApp", + TYPE_BASE_APPLICATION).build(); + final WindowState startingWindow = newWindowBuilder("starting", + TYPE_APPLICATION_STARTING).setWindowToken(startingApp.mToken).build(); startingApp.mActivityRecord.mStartingWindow = startingWindow; final WindowState keyguardHostWindow = mNotificationShadeWindow; final WindowState allDrawnApp = mAppWindow; @@ -878,7 +897,7 @@ public class WindowStateTests extends WindowTestsBase { @Test public void testRequestResizeForBlastSync() { - final WindowState win = createWindow(null, TYPE_APPLICATION, "window"); + final WindowState win = newWindowBuilder("window", TYPE_APPLICATION).build(); makeWindowVisible(win); makeLastConfigReportedToClient(win, true /* visible */); win.mLayoutSeq = win.getDisplayContent().mLayoutSeq; @@ -926,8 +945,8 @@ public class WindowStateTests extends WindowTestsBase { final Task task = createTask(mDisplayContent); final TaskFragment embeddedTf = createTaskFragmentWithEmbeddedActivity(task, organizer); final ActivityRecord embeddedActivity = embeddedTf.getTopMostActivity(); - final WindowState win = createWindow(null /* parent */, TYPE_APPLICATION, embeddedActivity, - "App window"); + final WindowState win = newWindowBuilder("App window", TYPE_APPLICATION).setWindowToken( + embeddedActivity).build(); doReturn(true).when(embeddedActivity).isVisible(); embeddedActivity.setVisibleRequested(true); makeWindowVisible(win); @@ -949,14 +968,14 @@ public class WindowStateTests extends WindowTestsBase { @Test public void testCantReceiveTouchWhenAppTokenHiddenRequested() { - final WindowState win0 = createWindow(null, TYPE_APPLICATION, "win0"); + final WindowState win0 = newWindowBuilder("win0", TYPE_APPLICATION).build(); win0.mActivityRecord.setVisibleRequested(false); assertFalse(win0.canReceiveTouchInput()); } @Test public void testCantReceiveTouchWhenNotFocusable() { - final WindowState win0 = createWindow(null, TYPE_APPLICATION, "win0"); + final WindowState win0 = newWindowBuilder("win0", TYPE_APPLICATION).build(); final Task rootTask = win0.mActivityRecord.getRootTask(); spyOn(rootTask); when(rootTask.shouldIgnoreInput()).thenReturn(true); @@ -969,7 +988,7 @@ public class WindowStateTests extends WindowTestsBase { @Test public void testUpdateInputWindowHandle() { - final WindowState win = createWindow(null, TYPE_APPLICATION, "win"); + final WindowState win = newWindowBuilder("win", TYPE_APPLICATION).build(); win.mAttrs.inputFeatures = WindowManager.LayoutParams.INPUT_FEATURE_DISABLE_USER_ACTIVITY; win.mAttrs.flags = FLAG_WATCH_OUTSIDE_TOUCH | FLAG_SPLIT_TOUCH; final InputWindowHandle handle = new InputWindowHandle( @@ -1026,7 +1045,7 @@ public class WindowStateTests extends WindowTestsBase { @DisableFlags(Flags.FLAG_SCROLLING_FROM_LETTERBOX) @Test public void testTouchRegionUsesLetterboxBoundsIfTransformedBoundsAndLetterboxScrolling() { - final WindowState win = createWindow(null, TYPE_APPLICATION, "win"); + final WindowState win = newWindowBuilder("win", TYPE_APPLICATION).build(); // Transformed bounds used for size of touchable region if letterbox inner bounds are empty. final Rect transformedBounds = new Rect(0, 0, 300, 500); @@ -1051,7 +1070,7 @@ public class WindowStateTests extends WindowTestsBase { @DisableFlags(Flags.FLAG_SCROLLING_FROM_LETTERBOX) @Test public void testTouchRegionUsesLetterboxBoundsIfNullTransformedBoundsAndLetterboxScrolling() { - final WindowState win = createWindow(null, TYPE_APPLICATION, "win"); + final WindowState win = newWindowBuilder("win", TYPE_APPLICATION).build(); // Fragment bounds used for size of touchable region if letterbox inner bounds are empty // and Transform bounds are null. @@ -1083,7 +1102,7 @@ public class WindowStateTests extends WindowTestsBase { @EnableFlags(Flags.FLAG_SCROLLING_FROM_LETTERBOX) @Test public void testTouchRegionUsesTransformedBoundsIfLetterboxScrolling() { - final WindowState win = createWindow(null, TYPE_APPLICATION, "win"); + final WindowState win = newWindowBuilder("win", TYPE_APPLICATION).build(); // Transformed bounds used for size of touchable region if letterbox inner bounds are empty. final Rect transformedBounds = new Rect(0, 0, 300, 500); @@ -1109,7 +1128,7 @@ public class WindowStateTests extends WindowTestsBase { public void testHasActiveVisibleWindow() { final int uid = ActivityBuilder.DEFAULT_FAKE_UID; - final WindowState app = createWindow(null, TYPE_APPLICATION, "app", uid); + final WindowState app = newWindowBuilder("app", TYPE_APPLICATION).setOwnerId(uid).build(); app.mActivityRecord.setVisible(false); app.mActivityRecord.setVisibility(false); assertFalse(mAtm.hasActiveVisibleWindow(uid)); @@ -1120,15 +1139,17 @@ public class WindowStateTests extends WindowTestsBase { // Make the activity invisible and add a visible toast. The uid should have no active // visible window because toast can be misused by legacy app to bypass background check. app.mActivityRecord.setVisibility(false); - final WindowState overlay = createWindow(null, TYPE_APPLICATION_OVERLAY, "overlay", uid); - final WindowState toast = createWindow(null, TYPE_TOAST, app.mToken, "toast", uid); + final WindowState overlay = newWindowBuilder("overlay", + TYPE_APPLICATION_OVERLAY).setOwnerId(uid).build(); + final WindowState toast = newWindowBuilder("toast", TYPE_TOAST).setWindowToken( + app.mToken).setOwnerId(uid).build(); toast.onSurfaceShownChanged(true); assertFalse(mAtm.hasActiveVisibleWindow(uid)); // Though starting window should belong to system. Make sure it is ignored to avoid being // allow-list unexpectedly, see b/129563343. - final WindowState starting = - createWindow(null, TYPE_APPLICATION_STARTING, app.mToken, "starting", uid); + final WindowState starting = newWindowBuilder("starting", + TYPE_APPLICATION_STARTING).setWindowToken(app.mToken).setOwnerId(uid).build(); starting.onSurfaceShownChanged(true); assertFalse(mAtm.hasActiveVisibleWindow(uid)); @@ -1145,8 +1166,8 @@ public class WindowStateTests extends WindowTestsBase { @SetupWindows(addWindows = { W_ACTIVITY, W_INPUT_METHOD }) @Test public void testNeedsRelativeLayeringToIme_notAttached() { - WindowState sameTokenWindow = createWindow(null, TYPE_BASE_APPLICATION, mAppWindow.mToken, - "SameTokenWindow"); + WindowState sameTokenWindow = newWindowBuilder("SameTokenWindow", + TYPE_BASE_APPLICATION).setWindowToken(mAppWindow.mToken).build(); mDisplayContent.setImeLayeringTarget(mAppWindow); makeWindowVisible(mImeWindow); sameTokenWindow.mActivityRecord.getRootTask().setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); @@ -1158,8 +1179,8 @@ public class WindowStateTests extends WindowTestsBase { @SetupWindows(addWindows = { W_ACTIVITY, W_INPUT_METHOD }) @Test public void testNeedsRelativeLayeringToIme_startingWindow() { - WindowState sameTokenWindow = createWindow(null, TYPE_APPLICATION_STARTING, - mAppWindow.mToken, "SameTokenWindow"); + WindowState sameTokenWindow = newWindowBuilder("SameTokenWindow", + TYPE_APPLICATION_STARTING).setWindowToken(mAppWindow.mToken).build(); mDisplayContent.setImeLayeringTarget(mAppWindow); makeWindowVisible(mImeWindow); sameTokenWindow.mActivityRecord.getRootTask().setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); @@ -1169,9 +1190,9 @@ public class WindowStateTests extends WindowTestsBase { @UseTestDisplay(addWindows = {W_ACTIVITY, W_INPUT_METHOD}) @Test public void testNeedsRelativeLayeringToIme_systemDialog() { - WindowState systemDialogWindow = createWindow(null, TYPE_SECURE_SYSTEM_OVERLAY, - mDisplayContent, - "SystemDialog", true); + WindowState systemDialogWindow = newWindowBuilder("SystemDialog", + TYPE_SECURE_SYSTEM_OVERLAY).setDisplay( + mDisplayContent).setOwnerCanAddInternalSystemWindow(true).build(); mDisplayContent.setImeLayeringTarget(mAppWindow); mAppWindow.getTask().setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); makeWindowVisible(mImeWindow); @@ -1182,20 +1203,21 @@ public class WindowStateTests extends WindowTestsBase { @UseTestDisplay(addWindows = {W_INPUT_METHOD}) @Test public void testNeedsRelativeLayeringToIme_notificationShadeShouldNotHideSystemDialog() { - WindowState systemDialogWindow = createWindow(null, TYPE_SECURE_SYSTEM_OVERLAY, - mDisplayContent, - "SystemDialog", true); + WindowState systemDialogWindow = newWindowBuilder("SystemDialog", + TYPE_SECURE_SYSTEM_OVERLAY).setDisplay( + mDisplayContent).setOwnerCanAddInternalSystemWindow(true).build(); mDisplayContent.setImeLayeringTarget(systemDialogWindow); makeWindowVisible(mImeWindow); - WindowState notificationShade = createWindow(null, TYPE_NOTIFICATION_SHADE, - mDisplayContent, "NotificationShade", true); + WindowState notificationShade = newWindowBuilder("NotificationShade", + TYPE_NOTIFICATION_SHADE).setDisplay( + mDisplayContent).setOwnerCanAddInternalSystemWindow(true).build(); notificationShade.mAttrs.flags |= FLAG_ALT_FOCUSABLE_IM; assertFalse(notificationShade.needsRelativeLayeringToIme()); } @Test public void testSetFreezeInsetsState() { - final WindowState app = createWindow(null, TYPE_APPLICATION, "app"); + final WindowState app = newWindowBuilder("app", TYPE_APPLICATION).build(); spyOn(app); doReturn(true).when(app).isVisible(); @@ -1216,7 +1238,7 @@ public class WindowStateTests extends WindowTestsBase { verify(app).notifyInsetsChanged(); // Verify that invisible non-activity window won't dispatch insets changed. - final WindowState overlay = createWindow(null, TYPE_APPLICATION_OVERLAY, "overlay"); + final WindowState overlay = newWindowBuilder("overlay", TYPE_APPLICATION_OVERLAY).build(); makeWindowVisible(overlay); assertTrue(overlay.isReadyToDispatchInsetsState()); overlay.mHasSurface = false; @@ -1244,9 +1266,9 @@ public class WindowStateTests extends WindowTestsBase { @Test public void testAdjustImeInsetsVisibilityWhenSwitchingApps() { - final WindowState app = createWindow(null, TYPE_APPLICATION, "app"); - final WindowState app2 = createWindow(null, TYPE_APPLICATION, "app2"); - final WindowState imeWindow = createWindow(null, TYPE_APPLICATION, "imeWindow"); + final WindowState app = newWindowBuilder("app", TYPE_APPLICATION).build(); + final WindowState app2 = newWindowBuilder("app2", TYPE_APPLICATION).build(); + final WindowState imeWindow = newWindowBuilder("imeWindow", TYPE_APPLICATION).build(); spyOn(imeWindow); doReturn(true).when(imeWindow).isVisible(); mDisplayContent.mInputMethodWindow = imeWindow; @@ -1279,10 +1301,11 @@ public class WindowStateTests extends WindowTestsBase { @Test public void testAdjustImeInsetsVisibilityWhenSwitchingApps_toAppInMultiWindowMode() { - final WindowState app = createWindow(null, TYPE_APPLICATION, "app"); - final WindowState app2 = createWindow(null, WINDOWING_MODE_MULTI_WINDOW, - ACTIVITY_TYPE_STANDARD, TYPE_APPLICATION, mDisplayContent, "app2"); - final WindowState imeWindow = createWindow(null, TYPE_APPLICATION, "imeWindow"); + final WindowState app = newWindowBuilder("app", TYPE_APPLICATION).build(); + final WindowState app2 = newWindowBuilder("app2", TYPE_APPLICATION).setWindowingMode( + WINDOWING_MODE_MULTI_WINDOW).setActivityType(ACTIVITY_TYPE_STANDARD).setDisplay( + mDisplayContent).build(); + final WindowState imeWindow = newWindowBuilder("imeWindow", TYPE_APPLICATION).build(); spyOn(imeWindow); doReturn(true).when(imeWindow).isVisible(); mDisplayContent.mInputMethodWindow = imeWindow; @@ -1321,8 +1344,8 @@ public class WindowStateTests extends WindowTestsBase { @SetupWindows(addWindows = W_ACTIVITY) @Test public void testUpdateImeControlTargetWhenLeavingMultiWindow() { - WindowState app = createWindow(null, TYPE_BASE_APPLICATION, - mAppWindow.mToken, "app"); + WindowState app = newWindowBuilder("app", TYPE_BASE_APPLICATION).setWindowToken( + mAppWindow.mToken).build(); mDisplayContent.setRemoteInsetsController(createDisplayWindowInsetsController()); spyOn(app); @@ -1349,8 +1372,8 @@ public class WindowStateTests extends WindowTestsBase { @SetupWindows(addWindows = { W_ACTIVITY, W_INPUT_METHOD, W_NOTIFICATION_SHADE }) @Test public void testNotificationShadeHasImeInsetsWhenMultiWindow() { - WindowState app = createWindow(null, TYPE_BASE_APPLICATION, - mAppWindow.mToken, "app"); + WindowState app = newWindowBuilder("app", TYPE_BASE_APPLICATION).setWindowToken( + mAppWindow.mToken).build(); // Simulate entering multi-window mode and windowing mode is multi-window. app.mActivityRecord.getRootTask().setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); @@ -1376,7 +1399,7 @@ public class WindowStateTests extends WindowTestsBase { @Test public void testRequestedVisibility() { - final WindowState app = createWindow(null, TYPE_APPLICATION, "app"); + final WindowState app = newWindowBuilder("app", TYPE_APPLICATION).build(); app.mActivityRecord.setVisible(false); app.mActivityRecord.setVisibility(false); assertFalse(app.isVisibleRequested()); @@ -1391,7 +1414,7 @@ public class WindowStateTests extends WindowTestsBase { @Test public void testKeepClearAreas() { - final WindowState window = createWindow(null, TYPE_APPLICATION, "window"); + final WindowState window = newWindowBuilder("window", TYPE_APPLICATION).build(); makeWindowVisible(window); final Rect keepClearArea1 = new Rect(0, 0, 10, 10); @@ -1433,7 +1456,7 @@ public class WindowStateTests extends WindowTestsBase { @Test public void testUnrestrictedKeepClearAreas() { - final WindowState window = createWindow(null, TYPE_APPLICATION, "window"); + final WindowState window = newWindowBuilder("window", TYPE_APPLICATION).build(); makeWindowVisible(window); final Rect keepClearArea1 = new Rect(0, 0, 10, 10); @@ -1481,8 +1504,9 @@ public class WindowStateTests extends WindowTestsBase { final InputMethodManagerInternal immi = InputMethodManagerInternal.get(); spyOn(immi); - final WindowState imeTarget = createWindow(null /* parent */, TYPE_BASE_APPLICATION, - createActivityRecord(mDisplayContent), "imeTarget"); + final WindowState imeTarget = newWindowBuilder("imeTarget", + TYPE_BASE_APPLICATION).setWindowToken( + createActivityRecord(mDisplayContent)).build(); imeTarget.mActivityRecord.setVisibleRequested(true); makeWindowVisible(imeTarget); @@ -1562,8 +1586,8 @@ public class WindowStateTests extends WindowTestsBase { @Test public void testIsSecureLocked_flagSecureSet() { - WindowState window = createWindow(null /* parent */, TYPE_APPLICATION, "test-window", - 1 /* ownerId */); + WindowState window = newWindowBuilder("test-window", TYPE_APPLICATION).setOwnerId( + 1).build(); window.mAttrs.flags |= WindowManager.LayoutParams.FLAG_SECURE; assertTrue(window.isSecureLocked()); @@ -1571,8 +1595,8 @@ public class WindowStateTests extends WindowTestsBase { @Test public void testIsSecureLocked_flagSecureNotSet() { - WindowState window = createWindow(null /* parent */, TYPE_APPLICATION, "test-window", - 1 /* ownerId */); + WindowState window = newWindowBuilder("test-window", TYPE_APPLICATION).setOwnerId( + 1).build(); assertFalse(window.isSecureLocked()); } @@ -1581,8 +1605,8 @@ public class WindowStateTests extends WindowTestsBase { public void testIsSecureLocked_disableSecureWindows() { assumeTrue(Build.IS_DEBUGGABLE); - WindowState window = createWindow(null /* parent */, TYPE_APPLICATION, "test-window", - 1 /* ownerId */); + WindowState window = newWindowBuilder("test-window", TYPE_APPLICATION).setOwnerId( + 1).build(); window.mAttrs.flags |= WindowManager.LayoutParams.FLAG_SECURE; ContentResolver cr = useFakeSettingsProvider(); @@ -1617,8 +1641,10 @@ public class WindowStateTests extends WindowTestsBase { String testPackage = "test"; int ownerId1 = 20; int ownerId2 = 21; - final WindowState window1 = createWindow(null, TYPE_APPLICATION, "window1", ownerId1); - final WindowState window2 = createWindow(null, TYPE_APPLICATION, "window2", ownerId2); + final WindowState window1 = newWindowBuilder("window1", TYPE_APPLICATION).setOwnerId( + ownerId1).build(); + final WindowState window2 = newWindowBuilder("window2", TYPE_APPLICATION).setOwnerId( + ownerId2).build(); // Setting packagename for targeted feature window1.mAttrs.packageName = testPackage; @@ -1638,7 +1664,8 @@ public class WindowStateTests extends WindowTestsBase { public void testIsSecureLocked_sensitiveContentBlockOrClearScreenCaptureForApp() { String testPackage = "test"; int ownerId = 20; - final WindowState window = createWindow(null, TYPE_APPLICATION, "window", ownerId); + final WindowState window = newWindowBuilder("window", TYPE_APPLICATION).setOwnerId( + ownerId).build(); window.mAttrs.packageName = testPackage; assertFalse(window.isSecureLocked()); diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java index f226b9d29ca0..a02c3db1e636 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java @@ -74,11 +74,16 @@ public class WindowTokenTests extends WindowTestsBase { assertEquals(0, token.getWindowsCount()); - final WindowState window1 = createWindow(null, TYPE_APPLICATION, token, "window1"); - final WindowState window11 = createWindow(window1, FIRST_SUB_WINDOW, token, "window11"); - final WindowState window12 = createWindow(window1, FIRST_SUB_WINDOW, token, "window12"); - final WindowState window2 = createWindow(null, TYPE_APPLICATION, token, "window2"); - final WindowState window3 = createWindow(null, TYPE_APPLICATION, token, "window3"); + final WindowState window1 = newWindowBuilder("window1", TYPE_APPLICATION).setWindowToken( + token).build(); + final WindowState window11 = newWindowBuilder("window11", FIRST_SUB_WINDOW).setParent( + window1).setWindowToken(token).build(); + final WindowState window12 = newWindowBuilder("window12", FIRST_SUB_WINDOW).setParent( + window1).setWindowToken(token).build(); + final WindowState window2 = newWindowBuilder("window2", TYPE_APPLICATION).setWindowToken( + token).build(); + final WindowState window3 = newWindowBuilder("window3", TYPE_APPLICATION).setWindowToken( + token).build(); token.addWindow(window1); // NOTE: Child windows will not be added to the token as window containers can only @@ -105,8 +110,10 @@ public class WindowTokenTests extends WindowTestsBase { public void testAddWindow_assignsLayers() { final TestWindowToken token1 = createTestWindowToken(0, mDisplayContent); final TestWindowToken token2 = createTestWindowToken(0, mDisplayContent); - final WindowState window1 = createWindow(null, TYPE_STATUS_BAR, token1, "window1"); - final WindowState window2 = createWindow(null, TYPE_STATUS_BAR, token2, "window2"); + final WindowState window1 = newWindowBuilder("window1", TYPE_STATUS_BAR).setWindowToken( + token1).build(); + final WindowState window2 = newWindowBuilder("window2", TYPE_STATUS_BAR).setWindowToken( + token2).build(); token1.addWindow(window1); token2.addWindow(window2); @@ -122,8 +129,10 @@ public class WindowTokenTests extends WindowTestsBase { assertEquals(token, dc.getWindowToken(token.token)); - final WindowState window1 = createWindow(null, TYPE_APPLICATION, token, "window1"); - final WindowState window2 = createWindow(null, TYPE_APPLICATION, token, "window2"); + final WindowState window1 = newWindowBuilder("window1", TYPE_APPLICATION).setWindowToken( + token).build(); + final WindowState window2 = newWindowBuilder("window2", TYPE_APPLICATION).setWindowToken( + token).build(); window2.removeImmediately(); // The token should still be mapped in the display content since it still has a child. @@ -147,8 +156,10 @@ public class WindowTokenTests extends WindowTestsBase { // Verify that the token is on the display assertNotNull(mDisplayContent.getWindowToken(token.token)); - final WindowState window1 = createWindow(null, TYPE_TOAST, token, "window1"); - final WindowState window2 = createWindow(null, TYPE_TOAST, token, "window2"); + final WindowState window1 = newWindowBuilder("window1", TYPE_TOAST).setWindowToken( + token).build(); + final WindowState window2 = newWindowBuilder("window2", TYPE_TOAST).setWindowToken( + token).build(); mDisplayContent.removeWindowToken(token.token, true /* animateExit */); // Verify that the token is no longer mapped on the display @@ -231,7 +242,8 @@ public class WindowTokenTests extends WindowTestsBase { assertNull(fromClientToken.mSurfaceControl); - createWindow(null, TYPE_APPLICATION_OVERLAY, fromClientToken, "window"); + newWindowBuilder("window", TYPE_APPLICATION_OVERLAY).setWindowToken( + fromClientToken).build(); assertNotNull(fromClientToken.mSurfaceControl); final WindowToken nonClientToken = new WindowToken.Builder(mDisplayContent.mWmService, @@ -285,7 +297,7 @@ public class WindowTokenTests extends WindowTestsBase { // Simulate an app window to be the IME layering target, assume the app window has no // frozen insets state by default. - final WindowState app = createWindow(null, TYPE_APPLICATION, "app"); + final WindowState app = newWindowBuilder("app", TYPE_APPLICATION).build(); mDisplayContent.setImeLayeringTarget(app); assertNull(app.getFrozenInsetsState()); assertTrue(app.isImeLayeringTarget()); @@ -299,7 +311,8 @@ public class WindowTokenTests extends WindowTestsBase { @Test public void testRemoveWindowToken_noAnimateExitWhenSet() { final TestWindowToken token = createTestWindowToken(0, mDisplayContent); - final WindowState win = createWindow(null, TYPE_APPLICATION, token, "win"); + final WindowState win = newWindowBuilder("win", TYPE_APPLICATION).setWindowToken( + token).build(); makeWindowVisible(win); assertTrue(win.isOnScreen()); spyOn(win); diff --git a/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java b/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java index 4f60106db93d..84e21181a7b9 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java @@ -221,7 +221,7 @@ public class ZOrderingTests extends WindowTestsBase { } WindowState createWindow(String name) { - return createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, name); + return newWindowBuilder(name, TYPE_BASE_APPLICATION).setDisplay(mDisplayContent).build(); } @Test @@ -263,12 +263,12 @@ public class ZOrderingTests extends WindowTestsBase { @Test public void testAssignWindowLayers_ForImeWithAppTargetWithChildWindows() { final WindowState imeAppTarget = createWindow("imeAppTarget"); - final WindowState imeAppTargetChildAboveWindow = createWindow(imeAppTarget, - TYPE_APPLICATION_ATTACHED_DIALOG, imeAppTarget.mToken, - "imeAppTargetChildAboveWindow"); - final WindowState imeAppTargetChildBelowWindow = createWindow(imeAppTarget, - TYPE_APPLICATION_MEDIA_OVERLAY, imeAppTarget.mToken, - "imeAppTargetChildBelowWindow"); + final WindowState imeAppTargetChildAboveWindow = newWindowBuilder( + "imeAppTargetChildAboveWindow", TYPE_APPLICATION_ATTACHED_DIALOG).setParent( + imeAppTarget).setWindowToken(imeAppTarget.mToken).build(); + final WindowState imeAppTargetChildBelowWindow = newWindowBuilder( + "imeAppTargetChildBelowWindow", TYPE_APPLICATION_MEDIA_OVERLAY).setParent( + imeAppTarget).setWindowToken(imeAppTarget.mToken).build(); mDisplayContent.setImeLayeringTarget(imeAppTarget); makeWindowVisible(mImeWindow); @@ -313,9 +313,9 @@ public class ZOrderingTests extends WindowTestsBase { @Test public void testAssignWindowLayers_ForImeNonAppImeTarget() { - final WindowState imeSystemOverlayTarget = createWindow(null, TYPE_SYSTEM_OVERLAY, - mDisplayContent, "imeSystemOverlayTarget", - true /* ownerCanAddInternalSystemWindow */); + final WindowState imeSystemOverlayTarget = newWindowBuilder("imeSystemOverlayTarget", + TYPE_SYSTEM_OVERLAY).setDisplay(mDisplayContent).setOwnerCanAddInternalSystemWindow( + true).build(); mDisplayContent.setImeLayeringTarget(imeSystemOverlayTarget); mDisplayContent.assignChildLayers(mTransaction); @@ -354,18 +354,19 @@ public class ZOrderingTests extends WindowTestsBase { @Test public void testStackLayers() { final WindowState anyWindow1 = createWindow("anyWindow"); - final WindowState pinnedStackWindow = createWindow(null, WINDOWING_MODE_PINNED, - ACTIVITY_TYPE_STANDARD, TYPE_BASE_APPLICATION, mDisplayContent, - "pinnedStackWindow"); - final WindowState dockedStackWindow = createWindow(null, - WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD, TYPE_BASE_APPLICATION, - mDisplayContent, "dockedStackWindow"); - final WindowState assistantStackWindow = createWindow(null, - WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_ASSISTANT, TYPE_BASE_APPLICATION, - mDisplayContent, "assistantStackWindow"); - final WindowState homeActivityWindow = createWindow(null, WINDOWING_MODE_FULLSCREEN, - ACTIVITY_TYPE_HOME, TYPE_BASE_APPLICATION, - mDisplayContent, "homeActivityWindow"); + final WindowState pinnedStackWindow = newWindowBuilder("pinnedStackWindow", + TYPE_BASE_APPLICATION).setWindowingMode(WINDOWING_MODE_PINNED).setActivityType( + ACTIVITY_TYPE_STANDARD).setDisplay(mDisplayContent).build(); + final WindowState dockedStackWindow = newWindowBuilder("dockedStackWindow", + TYPE_BASE_APPLICATION).setWindowingMode( + WINDOWING_MODE_MULTI_WINDOW).setActivityType(ACTIVITY_TYPE_STANDARD).setDisplay( + mDisplayContent).build(); + final WindowState assistantStackWindow = newWindowBuilder("assistantStackWindow", + TYPE_BASE_APPLICATION).setWindowingMode(WINDOWING_MODE_FULLSCREEN).setActivityType( + ACTIVITY_TYPE_ASSISTANT).setDisplay(mDisplayContent).build(); + final WindowState homeActivityWindow = newWindowBuilder("homeActivityWindow", + TYPE_BASE_APPLICATION).setWindowingMode(WINDOWING_MODE_FULLSCREEN).setActivityType( + ACTIVITY_TYPE_HOME).setDisplay(mDisplayContent).build(); final WindowState anyWindow2 = createWindow("anyWindow2"); mDisplayContent.assignChildLayers(mTransaction); @@ -383,13 +384,12 @@ public class ZOrderingTests extends WindowTestsBase { @Test public void testAssignWindowLayers_ForSysUiPanels() { - final WindowState navBarPanel = - createWindow(null, TYPE_NAVIGATION_BAR_PANEL, mDisplayContent, "NavBarPanel"); - final WindowState statusBarPanel = - createWindow(null, TYPE_STATUS_BAR_ADDITIONAL, mDisplayContent, - "StatusBarAdditional"); - final WindowState statusBarSubPanel = - createWindow(null, TYPE_STATUS_BAR_SUB_PANEL, mDisplayContent, "StatusBarSubPanel"); + final WindowState navBarPanel = newWindowBuilder("NavBarPanel", + TYPE_NAVIGATION_BAR_PANEL).setDisplay(mDisplayContent).build(); + final WindowState statusBarPanel = newWindowBuilder("StatusBarAdditional", + TYPE_STATUS_BAR_ADDITIONAL).setDisplay(mDisplayContent).build(); + final WindowState statusBarSubPanel = newWindowBuilder("StatusBarSubPanel", + TYPE_STATUS_BAR_SUB_PANEL).setDisplay(mDisplayContent).build(); mDisplayContent.assignChildLayers(mTransaction); // Ime should be above all app windows and below system windows if it is targeting an app @@ -401,15 +401,16 @@ public class ZOrderingTests extends WindowTestsBase { @Test public void testAssignWindowLayers_ForImeOnPopupImeLayeringTarget() { - final WindowState imeAppTarget = createWindow(null, TYPE_APPLICATION, - mAppWindow.mActivityRecord, "imeAppTarget"); + final WindowState imeAppTarget = newWindowBuilder("imeAppTarget", + TYPE_APPLICATION).setWindowToken(mAppWindow.mActivityRecord).build(); mDisplayContent.setImeInputTarget(imeAppTarget); mDisplayContent.setImeLayeringTarget(imeAppTarget); mDisplayContent.setImeControlTarget(imeAppTarget); // Set a popup IME layering target and keeps the original IME control target behinds it. - final WindowState popupImeTargetWin = createWindow(imeAppTarget, - TYPE_APPLICATION_SUB_PANEL, mAppWindow.mActivityRecord, "popupImeTargetWin"); + final WindowState popupImeTargetWin = newWindowBuilder("popupImeTargetWin", + TYPE_APPLICATION_SUB_PANEL).setParent(imeAppTarget).setWindowToken( + mAppWindow.mActivityRecord).build(); mDisplayContent.setImeLayeringTarget(popupImeTargetWin); mDisplayContent.updateImeParent(); @@ -424,11 +425,11 @@ public class ZOrderingTests extends WindowTestsBase { // then we can drop all negative layering on the windowing side. final WindowState anyWindow = createWindow("anyWindow"); - final WindowState child = createWindow(anyWindow, TYPE_APPLICATION_MEDIA, mDisplayContent, - "TypeApplicationMediaChild"); - final WindowState mediaOverlayChild = createWindow(anyWindow, - TYPE_APPLICATION_MEDIA_OVERLAY, - mDisplayContent, "TypeApplicationMediaOverlayChild"); + final WindowState child = newWindowBuilder("TypeApplicationMediaChild", + TYPE_APPLICATION_MEDIA).setParent(anyWindow).setDisplay(mDisplayContent).build(); + final WindowState mediaOverlayChild = newWindowBuilder("TypeApplicationMediaOverlayChild", + TYPE_APPLICATION_MEDIA_OVERLAY).setParent(anyWindow).setDisplay( + mDisplayContent).build(); mDisplayContent.assignChildLayers(mTransaction); @@ -440,14 +441,17 @@ public class ZOrderingTests extends WindowTestsBase { public void testAssignWindowLayers_ForPostivelyZOrderedSubtype() { final WindowState anyWindow = createWindow("anyWindow"); final ArrayList<WindowState> childList = new ArrayList<>(); - childList.add(createWindow(anyWindow, TYPE_APPLICATION_PANEL, mDisplayContent, - "TypeApplicationPanelChild")); - childList.add(createWindow(anyWindow, TYPE_APPLICATION_SUB_PANEL, mDisplayContent, - "TypeApplicationSubPanelChild")); - childList.add(createWindow(anyWindow, TYPE_APPLICATION_ATTACHED_DIALOG, mDisplayContent, - "TypeApplicationAttachedDialogChild")); - childList.add(createWindow(anyWindow, TYPE_APPLICATION_ABOVE_SUB_PANEL, mDisplayContent, - "TypeApplicationAboveSubPanelPanelChild")); + childList.add(newWindowBuilder("TypeApplicationPanelChild", + TYPE_APPLICATION_PANEL).setParent(anyWindow).setDisplay(mDisplayContent).build()); + childList.add(newWindowBuilder("TypeApplicationSubPanelChild", + TYPE_APPLICATION_SUB_PANEL).setParent(anyWindow).setDisplay( + mDisplayContent).build()); + childList.add(newWindowBuilder("TypeApplicationAttachedDialogChild", + TYPE_APPLICATION_ATTACHED_DIALOG).setParent(anyWindow).setDisplay( + mDisplayContent).build()); + childList.add(newWindowBuilder("TypeApplicationAboveSubPanelPanelChild", + TYPE_APPLICATION_ABOVE_SUB_PANEL).setParent(anyWindow).setDisplay( + mDisplayContent).build()); final LayerRecordingTransaction t = mTransaction; mDisplayContent.assignChildLayers(t); @@ -469,8 +473,8 @@ public class ZOrderingTests extends WindowTestsBase { // Create a popupWindow assertWindowHigher(mImeWindow, mAppWindow); - final WindowState popupWindow = createWindow(mAppWindow, TYPE_APPLICATION_PANEL, - mDisplayContent, "PopupWindow"); + final WindowState popupWindow = newWindowBuilder("PopupWindow", + TYPE_APPLICATION_PANEL).setParent(mAppWindow).setDisplay(mDisplayContent).build(); spyOn(popupWindow); mDisplayContent.assignChildLayers(mTransaction); @@ -492,8 +496,9 @@ public class ZOrderingTests extends WindowTestsBase { makeWindowVisible(mImeWindow); // Create a popupWindow - final WindowState systemDialogWindow = createWindow(null, TYPE_SECURE_SYSTEM_OVERLAY, - mDisplayContent, "SystemDialog", true); + final WindowState systemDialogWindow = newWindowBuilder("SystemDialog", + TYPE_SECURE_SYSTEM_OVERLAY).setDisplay( + mDisplayContent).setOwnerCanAddInternalSystemWindow(true).build(); systemDialogWindow.mAttrs.flags |= FLAG_ALT_FOCUSABLE_IM; spyOn(systemDialogWindow); diff --git a/telephony/java/android/telephony/satellite/SatelliteTransmissionUpdateCallback.java b/telephony/java/android/telephony/satellite/SatelliteTransmissionUpdateCallback.java index b5dfb631609c..e18fad3eda79 100644 --- a/telephony/java/android/telephony/satellite/SatelliteTransmissionUpdateCallback.java +++ b/telephony/java/android/telephony/satellite/SatelliteTransmissionUpdateCallback.java @@ -78,6 +78,9 @@ public interface SatelliteTransmissionUpdateCallback { /** * Called when framework receives a request to send a datagram. * + * Informs external apps that device is working on sending a datagram out and is in the process + * of checking if all the conditions required to send datagrams are met. + * * @param datagramType The type of the requested datagram. */ @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS) diff --git a/tools/aapt2/cmd/Command.cpp b/tools/aapt2/cmd/Command.cpp index 449d93dd8c0b..20315561cceb 100644 --- a/tools/aapt2/cmd/Command.cpp +++ b/tools/aapt2/cmd/Command.cpp @@ -53,61 +53,67 @@ std::string GetSafePath(StringPiece arg) { void Command::AddRequiredFlag(StringPiece name, StringPiece description, std::string* value, uint32_t flags) { - auto func = [value, flags](StringPiece arg) -> bool { + auto func = [value, flags](StringPiece arg, std::ostream*) -> bool { *value = (flags & Command::kPath) ? GetSafePath(arg) : std::string(arg); return true; }; - flags_.emplace_back(Flag(name, description, /* required */ true, /* num_args */ 1, func)); + flags_.emplace_back( + Flag(name, description, /* required */ true, /* num_args */ 1, std::move(func))); } void Command::AddRequiredFlagList(StringPiece name, StringPiece description, std::vector<std::string>* value, uint32_t flags) { - auto func = [value, flags](StringPiece arg) -> bool { + auto func = [value, flags](StringPiece arg, std::ostream*) -> bool { value->push_back((flags & Command::kPath) ? GetSafePath(arg) : std::string(arg)); return true; }; - flags_.emplace_back(Flag(name, description, /* required */ true, /* num_args */ 1, func)); + flags_.emplace_back( + Flag(name, description, /* required */ true, /* num_args */ 1, std::move(func))); } void Command::AddOptionalFlag(StringPiece name, StringPiece description, std::optional<std::string>* value, uint32_t flags) { - auto func = [value, flags](StringPiece arg) -> bool { + auto func = [value, flags](StringPiece arg, std::ostream*) -> bool { *value = (flags & Command::kPath) ? GetSafePath(arg) : std::string(arg); return true; }; - flags_.emplace_back(Flag(name, description, /* required */ false, /* num_args */ 1, func)); + flags_.emplace_back( + Flag(name, description, /* required */ false, /* num_args */ 1, std::move(func))); } void Command::AddOptionalFlagList(StringPiece name, StringPiece description, std::vector<std::string>* value, uint32_t flags) { - auto func = [value, flags](StringPiece arg) -> bool { + auto func = [value, flags](StringPiece arg, std::ostream*) -> bool { value->push_back((flags & Command::kPath) ? GetSafePath(arg) : std::string(arg)); return true; }; - flags_.emplace_back(Flag(name, description, /* required */ false, /* num_args */ 1, func)); + flags_.emplace_back( + Flag(name, description, /* required */ false, /* num_args */ 1, std::move(func))); } void Command::AddOptionalFlagList(StringPiece name, StringPiece description, std::unordered_set<std::string>* value) { - auto func = [value](StringPiece arg) -> bool { + auto func = [value](StringPiece arg, std::ostream* out_error) -> bool { value->emplace(arg); return true; }; - flags_.emplace_back(Flag(name, description, /* required */ false, /* num_args */ 1, func)); + flags_.emplace_back( + Flag(name, description, /* required */ false, /* num_args */ 1, std::move(func))); } void Command::AddOptionalSwitch(StringPiece name, StringPiece description, bool* value) { - auto func = [value](StringPiece arg) -> bool { + auto func = [value](StringPiece arg, std::ostream* out_error) -> bool { *value = true; return true; }; - flags_.emplace_back(Flag(name, description, /* required */ false, /* num_args */ 0, func)); + flags_.emplace_back( + Flag(name, description, /* required */ false, /* num_args */ 0, std::move(func))); } void Command::AddOptionalSubcommand(std::unique_ptr<Command>&& subcommand, bool experimental) { @@ -172,19 +178,74 @@ void Command::Usage(std::ostream* out) { argline = " "; } } - *out << " " << std::setw(kWidth) << std::left << "-h" - << "Displays this help menu\n"; out->flush(); } -int Command::Execute(const std::vector<StringPiece>& args, std::ostream* out_error) { +const std::string& Command::addEnvironmentArg(const Flag& flag, const char* env) { + if (*env && flag.num_args > 0) { + return environment_args_.emplace_back(flag.name + '=' + env); + } + return flag.name; +} + +// +// Looks for the flags specified in the environment and adds them to |args|. +// Expected format: +// - _AAPT2_UPPERCASE_NAME are added before all of the command line flags, so it's +// a default for the flag that may get overridden by the command line. +// - AAPT2_UPPERCASE_NAME_ are added after them, making this to be the final value +// even if there was something on the command line. +// - All dashes in the flag name get replaced with underscores, the rest of it is +// intact. +// +// E.g. +// --set-some-flag becomes either _AAPT2_SET_SOME_FLAG or AAPT2_SET_SOME_FLAG_ +// --set-param=2 is _AAPT2_SET_SOME_FLAG=2 +// +// Values get passed as it, with no processing or quoting. +// +// This way one can make sure aapt2 has the flags they need even when it is +// launched in a way they can't control, e.g. deep inside a build. +// +void Command::parseFlagsFromEnvironment(std::vector<StringPiece>& args) { + // If the first argument is a subcommand then skip it and prepend the flags past that (the root + // command should only have a single '-h' flag anyway). + const int insert_pos = args.empty() ? 0 : args.front().starts_with('-') ? 0 : 1; + + std::string env_name; + for (const Flag& flag : flags_) { + // First, the prefix version. + env_name.assign("_AAPT2_"); + // Append the uppercased flag name, skipping all dashes in front and replacing them with + // underscores later. + auto name_start = flag.name.begin(); + while (name_start != flag.name.end() && *name_start == '-') { + ++name_start; + } + std::transform(name_start, flag.name.end(), std::back_inserter(env_name), + [](char c) { return c == '-' ? '_' : toupper(c); }); + if (auto prefix_env = getenv(env_name.c_str())) { + args.insert(args.begin() + insert_pos, addEnvironmentArg(flag, prefix_env)); + } + // Now reuse the same name variable to construct a suffix version: append the + // underscore and just skip the one in front. + env_name += '_'; + if (auto suffix_env = getenv(env_name.c_str() + 1)) { + args.push_back(addEnvironmentArg(flag, suffix_env)); + } + } +} + +int Command::Execute(std::vector<StringPiece>& args, std::ostream* out_error) { TRACE_NAME_ARGS("Command::Execute", args); std::vector<std::string> file_args; + parseFlagsFromEnvironment(args); + for (size_t i = 0; i < args.size(); i++) { StringPiece arg = args[i]; if (*(arg.data()) != '-') { - // Continue parsing as the subcommand if the first argument matches one of the subcommands + // Continue parsing as a subcommand if the first argument matches one of the subcommands if (i == 0) { for (auto& subcommand : subcommands_) { if (arg == subcommand->name_ || (!subcommand->short_name_.empty() @@ -211,37 +272,67 @@ int Command::Execute(const std::vector<StringPiece>& args, std::ostream* out_err return 1; } + static constexpr auto matchShortArg = [](std::string_view arg, const Flag& flag) static { + return flag.name.starts_with("--") && + arg.compare(0, 2, std::string_view(flag.name.c_str() + 1, 2)) == 0; + }; + bool match = false; for (Flag& flag : flags_) { - // Allow both "--arg value" and "--arg=value" syntax. + // Allow both "--arg value" and "--arg=value" syntax, and look for the cases where we can + // safely deduce the "--arg" flag from the short "-a" version when there's no value expected + bool matched_current = false; if (arg.starts_with(flag.name) && (arg.size() == flag.name.size() || (flag.num_args > 0 && arg[flag.name.size()] == '='))) { - if (flag.num_args > 0) { - if (arg.size() == flag.name.size()) { - i++; - if (i >= args.size()) { - *out_error << flag.name << " missing argument.\n\n"; - Usage(out_error); - return 1; - } - arg = args[i]; - } else { - arg.remove_prefix(flag.name.size() + 1); - // Disallow empty arguments after '='. - if (arg.empty()) { - *out_error << flag.name << " has empty argument.\n\n"; - Usage(out_error); - return 1; - } + matched_current = true; + } else if (flag.num_args == 0 && matchShortArg(arg, flag)) { + matched_current = true; + // It matches, now need to make sure no other flag would match as well. + // This is really inefficient, but we don't expect to have enough flags for it to matter + // (famous last words). + for (const Flag& other_flag : flags_) { + if (&other_flag == &flag) { + continue; + } + if (matchShortArg(arg, other_flag)) { + matched_current = false; // ambiguous, skip this match + break; + } + } + } + if (!matched_current) { + continue; + } + + if (flag.num_args > 0) { + if (arg.size() == flag.name.size()) { + i++; + if (i >= args.size()) { + *out_error << flag.name << " missing argument.\n\n"; + Usage(out_error); + return 1; } - flag.action(arg); + arg = args[i]; } else { - flag.action({}); + arg.remove_prefix(flag.name.size() + 1); + // Disallow empty arguments after '='. + if (arg.empty()) { + *out_error << flag.name << " has empty argument.\n\n"; + Usage(out_error); + return 1; + } + } + if (!flag.action(arg, out_error)) { + return 1; + } + } else { + if (!flag.action({}, out_error)) { + return 1; } - flag.found = true; - match = true; - break; } + flag.found = true; + match = true; + break; } if (!match) { diff --git a/tools/aapt2/cmd/Command.h b/tools/aapt2/cmd/Command.h index 1416e980ed19..767ca9b0de9f 100644 --- a/tools/aapt2/cmd/Command.h +++ b/tools/aapt2/cmd/Command.h @@ -14,10 +14,11 @@ * limitations under the License. */ -#ifndef AAPT_COMMAND_H -#define AAPT_COMMAND_H +#pragma once +#include <deque> #include <functional> +#include <memory> #include <optional> #include <ostream> #include <string> @@ -30,10 +31,17 @@ namespace aapt { class Command { public: - explicit Command(android::StringPiece name) : name_(name), full_subcommand_name_(name){}; + explicit Command(android::StringPiece name) : Command(name, {}) { + } explicit Command(android::StringPiece name, android::StringPiece short_name) - : name_(name), short_name_(short_name), full_subcommand_name_(name){}; + : name_(name), short_name_(short_name), full_subcommand_name_(name) { + flags_.emplace_back("--help", "Displays this help menu", false, 0, + [this](android::StringPiece arg, std::ostream* out) { + Usage(out); + return false; + }); + } Command(Command&&) = default; Command& operator=(Command&&) = default; @@ -76,41 +84,51 @@ class Command { // Parses the command line arguments, sets the flag variable values, and runs the action of // the command. If the arguments fail to parse to the command and its subcommands, then the action // will not be run and the usage will be printed instead. - int Execute(const std::vector<android::StringPiece>& args, std::ostream* outError); + int Execute(std::vector<android::StringPiece>& args, std::ostream* out_error); + + // Same, but for a temporary vector of args. + int Execute(std::vector<android::StringPiece>&& args, std::ostream* out_error) { + return Execute(args, out_error); + } // The action to preform when the command is executed. virtual int Action(const std::vector<std::string>& args) = 0; private: struct Flag { - explicit Flag(android::StringPiece name, android::StringPiece description, - const bool is_required, const size_t num_args, - std::function<bool(android::StringPiece value)>&& action) + explicit Flag(android::StringPiece name, android::StringPiece description, bool is_required, + const size_t num_args, + std::function<bool(android::StringPiece value, std::ostream* out_err)>&& action) : name(name), description(description), - is_required(is_required), + action(std::move(action)), num_args(num_args), - action(std::move(action)) { + is_required(is_required) { } - const std::string name; - const std::string description; - const bool is_required; - const size_t num_args; - const std::function<bool(android::StringPiece value)> action; + std::string name; + std::string description; + std::function<bool(android::StringPiece value, std::ostream* out_error)> action; + size_t num_args; + bool is_required; bool found = false; }; + const std::string& addEnvironmentArg(const Flag& flag, const char* env); + void parseFlagsFromEnvironment(std::vector<android::StringPiece>& args); + std::string name_; std::string short_name_; - std::string description_ = ""; + std::string description_; std::string full_subcommand_name_; std::vector<Flag> flags_; std::vector<std::unique_ptr<Command>> subcommands_; std::vector<std::unique_ptr<Command>> experimental_subcommands_; + // A collection of arguments loaded from environment variables, with stable positions + // in memory - we add them to the vector of string views so the pointers may not change, + // with or without short string buffer utilization in std::string. + std::deque<std::string> environment_args_; }; } // namespace aapt - -#endif // AAPT_COMMAND_H diff --git a/tools/aapt2/cmd/Command_test.cpp b/tools/aapt2/cmd/Command_test.cpp index 20d87e0025c3..2a3cb2a0c65d 100644 --- a/tools/aapt2/cmd/Command_test.cpp +++ b/tools/aapt2/cmd/Command_test.cpp @@ -118,4 +118,45 @@ TEST(CommandTest, OptionsWithValues) { EXPECT_NE(0, command.Execute({"--flag1"s, "2"s}, &std::cerr)); } +TEST(CommandTest, ShortOptions) { + TestCommand command; + bool flag = false; + command.AddOptionalSwitch("--flag", "", &flag); + + ASSERT_EQ(0, command.Execute({"--flag"s}, &std::cerr)); + EXPECT_TRUE(flag); + + // Short version of a switch should work. + flag = false; + ASSERT_EQ(0, command.Execute({"-f"s}, &std::cerr)); + EXPECT_TRUE(flag); + + // Ambiguous names shouldn't parse via short options. + command.AddOptionalSwitch("--flag-2", "", &flag); + ASSERT_NE(0, command.Execute({"-f"s}, &std::cerr)); + + // But when we have a proper flag like that it should still work. + flag = false; + command.AddOptionalSwitch("-f", "", &flag); + ASSERT_EQ(0, command.Execute({"-f"s}, &std::cerr)); + EXPECT_TRUE(flag); + + // A regular short flag works fine as well. + flag = false; + command.AddOptionalSwitch("-d", "", &flag); + ASSERT_EQ(0, command.Execute({"-d"s}, &std::cerr)); + EXPECT_TRUE(flag); + + // A flag with a value only works via its long name syntax. + std::optional<std::string> val; + command.AddOptionalFlag("--with-val", "", &val); + ASSERT_EQ(0, command.Execute({"--with-val"s, "1"s}, &std::cerr)); + EXPECT_TRUE(val); + EXPECT_STREQ("1", val->c_str()); + + // Make sure the flags that require a value can't be parsed via short syntax, -w=blah + // looks weird. + ASSERT_NE(0, command.Execute({"-w"s, "2"s}, &std::cerr)); +} + } // namespace aapt
\ No newline at end of file |