diff options
532 files changed, 6918 insertions, 6089 deletions
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg index b42f7bc0ca94..e8571757c6f7 100644 --- a/PREUPLOAD.cfg +++ b/PREUPLOAD.cfg @@ -1,6 +1,7 @@ [Builtin Hooks] clang_format = true bpfmt = true +ktfmt = true [Builtin Hooks Options] # Only turn on clang-format check for the following subfolders. @@ -17,6 +18,7 @@ clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp tests/ tools/ bpfmt = -d +ktfmt = --kotlinlang-style --include-dirs=services/permission,packages/SystemUI [Hook Scripts] checkstyle_hook = ${REPO_ROOT}/prebuilts/checkstyle/checkstyle.py --sha ${PREUPLOAD_COMMIT} @@ -25,9 +27,10 @@ hidden_api_txt_checksorted_hook = ${REPO_ROOT}/tools/platform-compat/hiddenapi/c hidden_api_txt_exclude_hook = ${REPO_ROOT}/frameworks/base/tools/hiddenapi/exclude.sh ${PREUPLOAD_COMMIT} ${REPO_ROOT} -ktfmt_hook = ${REPO_ROOT}/external/ktfmt/ktfmt.py --check -i ${REPO_ROOT}/frameworks/base/ktfmt_includes.txt ${PREUPLOAD_FILES} - ktlint_hook = ${REPO_ROOT}/prebuilts/ktlint/ktlint.py --no-verify-format -f ${PREUPLOAD_FILES} # This flag check hook runs only for "packages/SystemUI" subdirectory. If you want to include this check for other subdirectories, please modify flag_check.py. flag_hook = ${REPO_ROOT}/frameworks/base/packages/SystemUI/flag_check.py --msg=${PREUPLOAD_COMMIT_MESSAGE} --files=${PREUPLOAD_FILES} --project=${REPO_PATH} + +[Tool Paths] +ktfmt = ${REPO_ROOT}/prebuilts/build-tools/common/framework/ktfmt.jar diff --git a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java index 11fa7b75182f..c2aeadaea65c 100644 --- a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java +++ b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java @@ -619,6 +619,7 @@ public class DeviceIdleController extends SystemService * List of end times for app-IDs that are temporarily marked as being allowed to access * the network and acquire wakelocks. Times are in milliseconds. */ + @GuardedBy("this") private final SparseArray<Pair<MutableLong, String>> mTempWhitelistAppIdEndTimes = new SparseArray<>(); @@ -5010,7 +5011,9 @@ public class DeviceIdleController extends SystemService if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) { return -1; } - dumpTempWhitelistSchedule(pw, false); + synchronized (this) { + dumpTempWhitelistScheduleLocked(pw, false); + } } } else if ("except-idle-whitelist".equals(cmd)) { getContext().enforceCallingOrSelfPermission( @@ -5294,7 +5297,7 @@ public class DeviceIdleController extends SystemService pw.println(); } } - dumpTempWhitelistSchedule(pw, true); + dumpTempWhitelistScheduleLocked(pw, true); size = mTempWhitelistAppIdArray != null ? mTempWhitelistAppIdArray.length : 0; if (size > 0) { @@ -5422,7 +5425,8 @@ public class DeviceIdleController extends SystemService } } - void dumpTempWhitelistSchedule(PrintWriter pw, boolean printTitle) { + @GuardedBy("this") + void dumpTempWhitelistScheduleLocked(PrintWriter pw, boolean printTitle) { final int size = mTempWhitelistAppIdEndTimes.size(); if (size > 0) { String prefix = ""; diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/IdleController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/IdleController.java index adee322f60cf..f722e41c6195 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/IdleController.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/IdleController.java @@ -48,6 +48,7 @@ public final class IdleController extends RestrictingController implements Idlen private static final String TAG = "JobScheduler.IdleController"; // Policy: we decide that we're "idle" if the device has been unused / // screen off or dreaming or wireless charging dock idle for at least this long + @GuardedBy("mLock") final ArraySet<JobStatus> mTrackedTasks = new ArraySet<>(); IdlenessTracker mIdleTracker; private final FlexibilityController mFlexibilityController; @@ -118,8 +119,10 @@ public final class IdleController extends RestrictingController implements Idlen for (int i = mTrackedTasks.size()-1; i >= 0; i--) { mTrackedTasks.valueAt(i).setIdleConstraintSatisfied(nowElapsed, isIdle); } + if (!mTrackedTasks.isEmpty()) { + mStateChangedListener.onControllerStateChanged(mTrackedTasks); + } } - mStateChangedListener.onControllerStateChanged(mTrackedTasks); } /** diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index 2887d228e1b6..fa8fe3bf5458 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -1755,6 +1755,12 @@ public class ActivityManager { private int mNavigationBarColor; @Appearance private int mSystemBarsAppearance; + /** + * Similar to {@link TaskDescription#mSystemBarsAppearance}, but is taken from the topmost + * fully opaque (i.e. non transparent) activity in the task. + */ + @Appearance + private int mTopOpaqueSystemBarsAppearance; private boolean mEnsureStatusBarContrastWhenTransparent; private boolean mEnsureNavigationBarContrastWhenTransparent; private int mResizeMode; @@ -1855,7 +1861,7 @@ public class ActivityManager { final Icon icon = mIconRes == Resources.ID_NULL ? null : Icon.createWithResource(ActivityThread.currentPackageName(), mIconRes); return new TaskDescription(mLabel, icon, mPrimaryColor, mBackgroundColor, - mStatusBarColor, mNavigationBarColor, 0, false, false, + mStatusBarColor, mNavigationBarColor, 0, 0, false, false, RESIZE_MODE_RESIZEABLE, -1, -1, 0); } } @@ -1874,7 +1880,7 @@ public class ActivityManager { @Deprecated public TaskDescription(String label, @DrawableRes int iconRes, int colorPrimary) { this(label, Icon.createWithResource(ActivityThread.currentPackageName(), iconRes), - colorPrimary, 0, 0, 0, 0, false, false, RESIZE_MODE_RESIZEABLE, -1, -1, 0); + colorPrimary, 0, 0, 0, 0, 0, false, false, RESIZE_MODE_RESIZEABLE, -1, -1, 0); if ((colorPrimary != 0) && (Color.alpha(colorPrimary) != 255)) { throw new RuntimeException("A TaskDescription's primary color should be opaque"); } @@ -1892,7 +1898,7 @@ public class ActivityManager { @Deprecated public TaskDescription(String label, @DrawableRes int iconRes) { this(label, Icon.createWithResource(ActivityThread.currentPackageName(), iconRes), - 0, 0, 0, 0, 0, false, false, RESIZE_MODE_RESIZEABLE, -1, -1, 0); + 0, 0, 0, 0, 0, 0, false, false, RESIZE_MODE_RESIZEABLE, -1, -1, 0); } /** @@ -1904,7 +1910,7 @@ public class ActivityManager { */ @Deprecated public TaskDescription(String label) { - this(label, null, 0, 0, 0, 0, 0, false, false, RESIZE_MODE_RESIZEABLE, -1, -1, 0); + this(label, null, 0, 0, 0, 0, 0, 0, false, false, RESIZE_MODE_RESIZEABLE, -1, -1, 0); } /** @@ -1914,7 +1920,7 @@ public class ActivityManager { */ @Deprecated public TaskDescription() { - this(null, null, 0, 0, 0, 0, 0, false, false, RESIZE_MODE_RESIZEABLE, -1, -1, 0); + this(null, null, 0, 0, 0, 0, 0, 0, false, false, RESIZE_MODE_RESIZEABLE, -1, -1, 0); } /** @@ -1930,7 +1936,7 @@ public class ActivityManager { @Deprecated public TaskDescription(String label, Bitmap icon, int colorPrimary) { this(label, icon != null ? Icon.createWithBitmap(icon) : null, colorPrimary, 0, 0, 0, - 0, false, false, RESIZE_MODE_RESIZEABLE, -1, -1, 0); + 0, 0, false, false, RESIZE_MODE_RESIZEABLE, -1, -1, 0); if ((colorPrimary != 0) && (Color.alpha(colorPrimary) != 255)) { throw new RuntimeException("A TaskDescription's primary color should be opaque"); } @@ -1946,7 +1952,7 @@ public class ActivityManager { */ @Deprecated public TaskDescription(String label, Bitmap icon) { - this(label, icon != null ? Icon.createWithBitmap(icon) : null, 0, 0, 0, 0, 0, false, + this(label, icon != null ? Icon.createWithBitmap(icon) : null, 0, 0, 0, 0, 0, 0, false, false, RESIZE_MODE_RESIZEABLE, -1, -1, 0); } @@ -1955,6 +1961,7 @@ public class ActivityManager { int colorPrimary, int colorBackground, int statusBarColor, int navigationBarColor, @Appearance int systemBarsAppearance, + @Appearance int topOpaqueSystemBarsAppearance, boolean ensureStatusBarContrastWhenTransparent, boolean ensureNavigationBarContrastWhenTransparent, int resizeMode, int minWidth, int minHeight, int colorBackgroundFloating) { @@ -1965,6 +1972,7 @@ public class ActivityManager { mStatusBarColor = statusBarColor; mNavigationBarColor = navigationBarColor; mSystemBarsAppearance = systemBarsAppearance; + mTopOpaqueSystemBarsAppearance = topOpaqueSystemBarsAppearance; mEnsureStatusBarContrastWhenTransparent = ensureStatusBarContrastWhenTransparent; mEnsureNavigationBarContrastWhenTransparent = ensureNavigationBarContrastWhenTransparent; @@ -1994,6 +2002,7 @@ public class ActivityManager { mStatusBarColor = other.mStatusBarColor; mNavigationBarColor = other.mNavigationBarColor; mSystemBarsAppearance = other.mSystemBarsAppearance; + mTopOpaqueSystemBarsAppearance = other.mTopOpaqueSystemBarsAppearance; mEnsureStatusBarContrastWhenTransparent = other.mEnsureStatusBarContrastWhenTransparent; mEnsureNavigationBarContrastWhenTransparent = other.mEnsureNavigationBarContrastWhenTransparent; @@ -2026,6 +2035,9 @@ public class ActivityManager { if (other.mSystemBarsAppearance != 0) { mSystemBarsAppearance = other.mSystemBarsAppearance; } + if (other.mTopOpaqueSystemBarsAppearance != 0) { + mTopOpaqueSystemBarsAppearance = other.mTopOpaqueSystemBarsAppearance; + } mEnsureStatusBarContrastWhenTransparent = other.mEnsureStatusBarContrastWhenTransparent; mEnsureNavigationBarContrastWhenTransparent = @@ -2305,6 +2317,14 @@ public class ActivityManager { /** * @hide */ + @Appearance + public int getTopOpaqueSystemBarsAppearance() { + return mTopOpaqueSystemBarsAppearance; + } + + /** + * @hide + */ public void setEnsureStatusBarContrastWhenTransparent( boolean ensureStatusBarContrastWhenTransparent) { mEnsureStatusBarContrastWhenTransparent = ensureStatusBarContrastWhenTransparent; @@ -2320,6 +2340,13 @@ public class ActivityManager { /** * @hide */ + public void setTopOpaqueSystemBarsAppearance(int topOpaqueSystemBarsAppearance) { + mTopOpaqueSystemBarsAppearance = topOpaqueSystemBarsAppearance; + } + + /** + * @hide + */ public boolean getEnsureNavigationBarContrastWhenTransparent() { return mEnsureNavigationBarContrastWhenTransparent; } @@ -2442,6 +2469,7 @@ public class ActivityManager { dest.writeInt(mStatusBarColor); dest.writeInt(mNavigationBarColor); dest.writeInt(mSystemBarsAppearance); + dest.writeInt(mTopOpaqueSystemBarsAppearance); dest.writeBoolean(mEnsureStatusBarContrastWhenTransparent); dest.writeBoolean(mEnsureNavigationBarContrastWhenTransparent); dest.writeInt(mResizeMode); @@ -2466,6 +2494,7 @@ public class ActivityManager { mStatusBarColor = source.readInt(); mNavigationBarColor = source.readInt(); mSystemBarsAppearance = source.readInt(); + mTopOpaqueSystemBarsAppearance = source.readInt(); mEnsureStatusBarContrastWhenTransparent = source.readBoolean(); mEnsureNavigationBarContrastWhenTransparent = source.readBoolean(); mResizeMode = source.readInt(); @@ -2498,7 +2527,8 @@ public class ActivityManager { + " resizeMode: " + ActivityInfo.resizeModeToString(mResizeMode) + " minWidth: " + mMinWidth + " minHeight: " + mMinHeight + " colorBackgrounFloating: " + mColorBackgroundFloating - + " systemBarsAppearance: " + mSystemBarsAppearance; + + " systemBarsAppearance: " + mSystemBarsAppearance + + " topOpaqueSystemBarsAppearance: " + mTopOpaqueSystemBarsAppearance; } @Override @@ -2519,6 +2549,7 @@ public class ActivityManager { result = result * 31 + mStatusBarColor; result = result * 31 + mNavigationBarColor; result = result * 31 + mSystemBarsAppearance; + result = result * 31 + mTopOpaqueSystemBarsAppearance; result = result * 31 + (mEnsureStatusBarContrastWhenTransparent ? 1 : 0); result = result * 31 + (mEnsureNavigationBarContrastWhenTransparent ? 1 : 0); result = result * 31 + mResizeMode; @@ -2542,6 +2573,7 @@ public class ActivityManager { && mStatusBarColor == other.mStatusBarColor && mNavigationBarColor == other.mNavigationBarColor && mSystemBarsAppearance == other.mSystemBarsAppearance + && mTopOpaqueSystemBarsAppearance == other.mTopOpaqueSystemBarsAppearance && mEnsureStatusBarContrastWhenTransparent == other.mEnsureStatusBarContrastWhenTransparent && mEnsureNavigationBarContrastWhenTransparent diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index 4c839f1762cb..329fb00c1d9b 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -1632,6 +1632,10 @@ public class Notification implements Parcelable private Icon mSmallIcon; @UnsupportedAppUsage private Icon mLargeIcon; + private Icon mAppIcon; + + /** Cache for whether the notification was posted by a headless system app. */ + private Boolean mBelongsToHeadlessSystemApp = null; @UnsupportedAppUsage private String mChannelId; @@ -3079,25 +3083,17 @@ public class Notification implements Parcelable return name.toString(); } } - // If not, try getting the app info from extras. + // If not, try getting the name from the app info. if (context == null) { return null; } - final PackageManager pm = context.getPackageManager(); if (TextUtils.isEmpty(name)) { - if (extras.containsKey(EXTRA_BUILDER_APPLICATION_INFO)) { - final ApplicationInfo info = extras.getParcelable( - EXTRA_BUILDER_APPLICATION_INFO, - ApplicationInfo.class); - if (info != null) { - name = pm.getApplicationLabel(info); - } + ApplicationInfo info = getApplicationInfo(context); + if (info != null) { + final PackageManager pm = context.getPackageManager(); + name = pm.getApplicationLabel(getApplicationInfo(context)); } } - // If that's still empty, use the one from the context directly. - if (TextUtils.isEmpty(name)) { - name = pm.getApplicationLabel(context.getApplicationInfo()); - } // If there's still nothing, ¯\_(ツ)_/¯ if (TextUtils.isEmpty(name)) { return null; @@ -3109,9 +3105,89 @@ public class Notification implements Parcelable } /** + * Whether this notification was posted by a headless system app. + * + * If we don't have enough information to figure this out, this will return false. Therefore, + * false negatives are possible, but false positives should not be. + * * @hide */ - public int loadHeaderAppIconRes(Context context) { + public boolean belongsToHeadlessSystemApp(Context context) { + Trace.beginSection("Notification#belongsToHeadlessSystemApp"); + + try { + if (mBelongsToHeadlessSystemApp != null) { + return mBelongsToHeadlessSystemApp; + } + + if (context == null) { + // Without a valid context, we don't know exactly. Let's assume it doesn't belong to + // a system app, but not cache the value. + return false; + } + + ApplicationInfo info = getApplicationInfo(context); + if (info != null) { + if ((info.flags & ApplicationInfo.FLAG_SYSTEM) == 0) { + // It's not a system app at all. + mBelongsToHeadlessSystemApp = false; + } else { + // If there's no launch intent, it's probably a headless app. + final PackageManager pm = context.getPackageManager(); + mBelongsToHeadlessSystemApp = pm.getLaunchIntentForPackage(info.packageName) + == null; + } + } else { + // If for some reason we don't have the app info, we don't know; best assume it's + // not a system app. + return false; + } + return mBelongsToHeadlessSystemApp; + } finally { + Trace.endSection(); + } + } + + /** + * Get the resource ID of the app icon from application info. + * @hide + */ + public int getHeaderAppIconRes(Context context) { + ApplicationInfo info = getApplicationInfo(context); + if (info != null) { + return info.icon; + } + return 0; + } + + /** + * Load the app icon drawable from the package manager. This could result in a binder call. + * @hide + */ + public Drawable loadHeaderAppIcon(Context context) { + Trace.beginSection("Notification#loadHeaderAppIcon"); + + try { + if (context == null) { + Log.e(TAG, "Cannot load the app icon drawable with a null context"); + return null; + } + final PackageManager pm = context.getPackageManager(); + ApplicationInfo info = getApplicationInfo(context); + if (info == null) { + Log.e(TAG, "Cannot load the app icon drawable: no application info"); + return null; + } + return pm.getApplicationIcon(info); + } finally { + Trace.endSection(); + } + } + + /** + * Fetch the application info from the notification, or the context if that isn't available. + */ + private ApplicationInfo getApplicationInfo(Context context) { ApplicationInfo info = null; if (extras.containsKey(EXTRA_BUILDER_APPLICATION_INFO)) { info = extras.getParcelable( @@ -3119,12 +3195,12 @@ public class Notification implements Parcelable ApplicationInfo.class); } if (info == null) { + if (context == null) { + return null; + } info = context.getApplicationInfo(); } - if (info != null) { - return info.icon; - } - return 0; + return info; } /** @@ -4124,6 +4200,55 @@ public class Notification implements Parcelable } /** + * The colored app icon that can replace the small icon in the notification starting in V. + * + * Before using this value, you should first check whether it's actually being used by the + * notification by calling {@link Notification#shouldUseAppIcon()}. + * + * @hide + */ + public Icon getAppIcon() { + if (mAppIcon != null) { + return mAppIcon; + } + // If the app icon hasn't been loaded yet, check if we can load it without a context. + if (extras.containsKey(EXTRA_BUILDER_APPLICATION_INFO)) { + final ApplicationInfo info = extras.getParcelable( + EXTRA_BUILDER_APPLICATION_INFO, + ApplicationInfo.class); + if (info != null) { + int appIconRes = info.icon; + if (appIconRes == 0) { + Log.w(TAG, "Failed to get the app icon: no icon in application info"); + return null; + } + mAppIcon = Icon.createWithResource(info.packageName, appIconRes); + return mAppIcon; + } else { + Log.e(TAG, "Failed to get the app icon: " + + "there's an EXTRA_BUILDER_APPLICATION_INFO in extras but it's null"); + } + } else { + Log.w(TAG, "Failed to get the app icon: no application info in extras"); + } + return null; + } + + /** + * Whether the notification is using the app icon instead of the small icon. + * @hide + */ + public boolean shouldUseAppIcon() { + if (Flags.notificationsUseAppIconInRow()) { + if (belongsToHeadlessSystemApp(/* context = */ null)) { + return false; + } + return getAppIcon() != null; + } + return false; + } + + /** * The large icon shown in this notification's content view. * @see Builder#getLargeIcon() * @see Builder#setLargeIcon(Icon) @@ -6116,16 +6241,30 @@ public class Notification implements Parcelable if (Flags.notificationsUseAppIcon()) { // Override small icon with app icon mN.mSmallIcon = Icon.createWithResource(mContext, - mN.loadHeaderAppIconRes(mContext)); + mN.getHeaderAppIconRes(mContext)); } else if (mN.mSmallIcon == null && mN.icon != 0) { mN.mSmallIcon = Icon.createWithResource(mContext, mN.icon); } - contentView.setImageViewIcon(R.id.icon, mN.mSmallIcon); + boolean usingAppIcon = false; + if (Flags.notificationsUseAppIconInRow() && !mN.belongsToHeadlessSystemApp(mContext)) { + // Use the app icon in the view + int appIconRes = mN.getHeaderAppIconRes(mContext); + if (appIconRes != 0) { + mN.mAppIcon = Icon.createWithResource(mContext, appIconRes); + contentView.setImageViewIcon(R.id.icon, mN.mAppIcon); + usingAppIcon = true; + } else { + Log.w(TAG, "bindSmallIcon: could not get the app icon"); + } + } + if (!usingAppIcon) { + contentView.setImageViewIcon(R.id.icon, mN.mSmallIcon); + } contentView.setInt(R.id.icon, "setImageLevel", mN.iconLevel); // Don't change color if we're using the app icon. - if (!Flags.notificationsUseAppIcon()) { + if (!Flags.notificationsUseAppIcon() && !usingAppIcon) { processSmallIconColor(mN.mSmallIcon, contentView, p); } } diff --git a/core/java/android/app/notification.aconfig b/core/java/android/app/notification.aconfig index 55c3bb60e9c7..6edae0b60fd9 100644 --- a/core/java/android/app/notification.aconfig +++ b/core/java/android/app/notification.aconfig @@ -52,14 +52,32 @@ flag { bug: "281044385" } +# vvv Prototypes for using app icons in notifications vvv + flag { name: "notifications_use_app_icon" namespace: "systemui" - description: "Experiment to replace the small icon in a notification with the app icon." + description: "Experiment to replace the small icon in a notification with the app icon. This includes the status bar, AOD, shelf and notification row itself." + bug: "335211019" +} + +flag { + name: "notifications_use_app_icon_in_row" + namespace: "systemui" + description: "Experiment to replace the small icon in a notification row with the app icon." bug: "335211019" } flag { + name: "notifications_use_monochrome_app_icon" + namespace: "systemui" + description: "Experiment to replace the notification icon in the status bar and shelf with the monochrome app icon, if available." + bug: "335211019" +} + +# ^^^ Prototypes for using app icons in notifications ^^^ + +flag { name: "notification_expansion_optional" namespace: "systemui" description: "Experiment to restore the pre-S behavior where standard notifications are not expandable unless they have actions." diff --git a/core/java/android/app/servertransaction/WindowStateInsetsControlChangeItem.java b/core/java/android/app/servertransaction/WindowStateInsetsControlChangeItem.java index 1c8e497edd0a..ed18a057118c 100644 --- a/core/java/android/app/servertransaction/WindowStateInsetsControlChangeItem.java +++ b/core/java/android/app/servertransaction/WindowStateInsetsControlChangeItem.java @@ -69,7 +69,12 @@ public class WindowStateInsetsControlChangeItem extends WindowStateTransactionIt } instance.setWindow(window); instance.mInsetsState = new InsetsState(insetsState, true /* copySources */); - instance.mActiveControls = new InsetsSourceControl.Array(activeControls); + instance.mActiveControls = new InsetsSourceControl.Array( + activeControls, true /* copyControls */); + // This source control is an extra copy if the client is not local. By setting + // PARCELABLE_WRITE_RETURN_VALUE, the leash will be released at the end of + // SurfaceControl.writeToParcel. + instance.mActiveControls.setParcelableFlags(PARCELABLE_WRITE_RETURN_VALUE); return instance; } diff --git a/core/java/android/companion/virtual/flags/flags.aconfig b/core/java/android/companion/virtual/flags/flags.aconfig index 006226eb8c31..ed5d66227574 100644 --- a/core/java/android/companion/virtual/flags/flags.aconfig +++ b/core/java/android/companion/virtual/flags/flags.aconfig @@ -52,4 +52,15 @@ flag { description: "Makes MediaDrm APIs device-aware" bug: "303535376" is_fixed_read_only: true -}
\ No newline at end of file +} + +flag { + namespace: "virtual_devices" + name: "virtual_display_multi_window_mode_support" + description: "Add support for WINDOWING_MODE_MULTI_WINDOW to virtual displays by default" + is_fixed_read_only: true + bug: "341151395" + metadata { + purpose: PURPOSE_BUGFIX + } +} diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 2d9881abc4a5..282ede385ba3 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -4397,8 +4397,6 @@ public abstract class PackageManager { * {@link #hasSystemFeature}: The device supports freeform window management. * Windows have title bars and can be moved and resized. */ - // If this feature is present, you also need to set - // com.android.internal.R.config_freeformWindowManagement to true in your configuration overlay. @SdkConstant(SdkConstantType.FEATURE) public static final String FEATURE_FREEFORM_WINDOW_MANAGEMENT = "android.software.freeform_window_management"; diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java index a720b6473be5..248ef1d4d9c4 100644 --- a/core/java/android/content/res/Resources.java +++ b/core/java/android/content/res/Resources.java @@ -2745,17 +2745,6 @@ public class Resources { ar.recycle(); Log.i(TAG, "...preloaded " + numberOfEntries + " resources in " + (SystemClock.uptimeMillis() - startTime) + "ms."); - - if (sysRes.getBoolean( - com.android.internal.R.bool.config_freeformWindowManagement)) { - startTime = SystemClock.uptimeMillis(); - ar = sysRes.obtainTypedArray( - com.android.internal.R.array.preloaded_freeform_multi_window_drawables); - numberOfEntries = preloadDrawables(sysRes, ar); - ar.recycle(); - Log.i(TAG, "...preloaded " + numberOfEntries + " resource in " - + (SystemClock.uptimeMillis() - startTime) + "ms."); - } } sysRes.finishPreloading(); } catch (RuntimeException e) { diff --git a/core/java/android/hardware/SystemSensorManager.java b/core/java/android/hardware/SystemSensorManager.java index 60ad8e81fcf4..2d3d25217357 100644 --- a/core/java/android/hardware/SystemSensorManager.java +++ b/core/java/android/hardware/SystemSensorManager.java @@ -523,23 +523,25 @@ public class SystemSensorManager extends SensorManager { Handler mainHandler = new Handler(mContext.getMainLooper()); - for (Map.Entry<DynamicSensorCallback, Handler> entry : - mDynamicSensorCallbacks.entrySet()) { - final DynamicSensorCallback callback = entry.getKey(); - Handler handler = - entry.getValue() == null ? mainHandler : entry.getValue(); - - handler.post(new Runnable() { - @Override - public void run() { - for (Sensor s: addedList) { - callback.onDynamicSensorConnected(s); + synchronized (mDynamicSensorCallbacks) { + for (Map.Entry<DynamicSensorCallback, Handler> entry : + mDynamicSensorCallbacks.entrySet()) { + final DynamicSensorCallback callback = entry.getKey(); + Handler handler = + entry.getValue() == null ? mainHandler : entry.getValue(); + + handler.post(new Runnable() { + @Override + public void run() { + for (Sensor s: addedList) { + callback.onDynamicSensorConnected(s); + } + for (Sensor s: removedList) { + callback.onDynamicSensorDisconnected(s); + } } - for (Sensor s: removedList) { - callback.onDynamicSensorDisconnected(s); - } - } - }); + }); + } } for (Sensor s: removedList) { @@ -658,13 +660,15 @@ public class SystemSensorManager extends SensorManager { if (callback == null) { throw new IllegalArgumentException("callback cannot be null"); } - if (mDynamicSensorCallbacks.containsKey(callback)) { - // has been already registered, ignore - return; - } + synchronized (mDynamicSensorCallbacks) { + if (mDynamicSensorCallbacks.containsKey(callback)) { + // has been already registered, ignore + return; + } - setupDynamicSensorBroadcastReceiver(); - mDynamicSensorCallbacks.put(callback, handler); + setupDynamicSensorBroadcastReceiver(); + mDynamicSensorCallbacks.put(callback, handler); + } } /** @hide */ @@ -673,7 +677,9 @@ public class SystemSensorManager extends SensorManager { if (DEBUG_DYNAMIC_SENSOR) { Log.i(TAG, "Removing dynamic sensor listener"); } - mDynamicSensorCallbacks.remove(callback); + synchronized (mDynamicSensorCallbacks) { + mDynamicSensorCallbacks.remove(callback); + } } /* diff --git a/core/java/android/hardware/biometrics/flags.aconfig b/core/java/android/hardware/biometrics/flags.aconfig index 4284ad09e251..047d1fa4f49a 100644 --- a/core/java/android/hardware/biometrics/flags.aconfig +++ b/core/java/android/hardware/biometrics/flags.aconfig @@ -32,3 +32,10 @@ flag { description: "Feature flag for adding a custom content view API to BiometricPrompt.Builder." bug: "302735104" } + +flag { + name: "mandatory_biometrics" + namespace: "biometrics_framework" + description: "This flag controls whether LSKF fallback is removed from biometric prompt when the phone is outside trusted locations" + bug: "322081563" +} diff --git a/core/java/android/os/IBinder.java b/core/java/android/os/IBinder.java index 91c2965c2505..c9f207cf26e8 100644 --- a/core/java/android/os/IBinder.java +++ b/core/java/android/os/IBinder.java @@ -305,15 +305,28 @@ public interface IBinder { /** * Interface for receiving a callback when the process hosting an IBinder * has gone away. - * + * * @see #linkToDeath */ public interface DeathRecipient { public void binderDied(); /** - * Interface for receiving a callback when the process hosting an IBinder + * The function called when the process hosting an IBinder * has gone away. + * + * This callback will be called from any binder thread like any other binder + * transaction. If the process receiving this notification is multithreaded + * then synchronization may be required because other threads may be executing + * at the same time. + * + * No locks are held in libbinder when {@link binderDied} is called. + * + * There is no need to call {@link unlinkToDeath} in the binderDied callback. + * The binder is already dead so {@link unlinkToDeath} is a no-op. + * It will be unlinked when the last local reference of that binder proxy is + * dropped. + * * @param who The IBinder that has become invalid */ default void binderDied(@NonNull IBinder who) { diff --git a/core/java/android/view/HandwritingInitiator.java b/core/java/android/view/HandwritingInitiator.java index beb4d95263be..57d1b8d18fe1 100644 --- a/core/java/android/view/HandwritingInitiator.java +++ b/core/java/android/view/HandwritingInitiator.java @@ -227,7 +227,10 @@ public class HandwritingInitiator { mState.mStylusDownY, /* isHover */ false); if (candidateView != null && candidateView.isEnabled()) { boolean candidateHasFocus = candidateView.hasFocus(); - if (shouldShowHandwritingUnavailableMessageForView(candidateView)) { + if (!candidateView.isStylusHandwritingAvailable()) { + mState.mShouldInitHandwriting = false; + return false; + } else if (shouldShowHandwritingUnavailableMessageForView(candidateView)) { int messagesResId = (candidateView instanceof TextView tv && tv.isAnyPasswordInputType()) ? R.string.error_handwriting_unsupported_password diff --git a/core/java/android/view/InsetsSourceControl.java b/core/java/android/view/InsetsSourceControl.java index 4e5cb58a00b5..588e9e0a3b12 100644 --- a/core/java/android/view/InsetsSourceControl.java +++ b/core/java/android/view/InsetsSourceControl.java @@ -269,22 +269,54 @@ public class InsetsSourceControl implements Parcelable { public Array() { } - public Array(@NonNull Array other) { - mControls = other.mControls; + /** + * @param copyControls whether or not to make a copy of the each {@link InsetsSourceControl} + */ + public Array(@NonNull Array other, boolean copyControls) { + setTo(other, copyControls); } - public Array(Parcel in) { + public Array(@NonNull Parcel in) { readFromParcel(in); } - public void set(@Nullable InsetsSourceControl[] controls) { - mControls = controls; + /** Updates the current Array to the given Array. */ + public void setTo(@NonNull Array other, boolean copyControls) { + set(other.mControls, copyControls); } + /** Updates the current controls to the given controls. */ + public void set(@Nullable InsetsSourceControl[] controls, boolean copyControls) { + if (controls == null || !copyControls) { + mControls = controls; + return; + } + // Make a copy of the array. + mControls = new InsetsSourceControl[controls.length]; + for (int i = mControls.length - 1; i >= 0; i--) { + if (controls[i] != null) { + mControls[i] = new InsetsSourceControl(controls[i]); + } + } + } + + /** Gets the controls. */ public @Nullable InsetsSourceControl[] get() { return mControls; } + /** Sets the given flags to all controls. */ + public void setParcelableFlags(int parcelableFlags) { + if (mControls == null) { + return; + } + for (InsetsSourceControl control : mControls) { + if (control != null) { + control.setParcelableFlags(parcelableFlags); + } + } + } + @Override public int describeContents() { return 0; diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 1cb276568244..95c9d7bb72c5 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -23705,12 +23705,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, mPrivateFlags |= PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID; mPrivateFlags &= ~PFLAG_DIRTY_MASK; - // // For VRR to vote the preferred frame rate - if (sToolkitSetFrameRateReadOnlyFlagValue - && sToolkitFrameRateViewEnablingReadOnlyFlagValue) { - votePreferredFrameRate(); - } - mPrivateFlags4 |= PFLAG4_HAS_DRAWN; // Fast path for layouts with no backgrounds @@ -23727,6 +23721,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback, draw(canvas); } } + + // For VRR to vote the preferred frame rate + if (sToolkitSetFrameRateReadOnlyFlagValue + && sToolkitFrameRateViewEnablingReadOnlyFlagValue) { + votePreferredFrameRate(); + } } finally { renderNode.endRecording(); setDisplayListProperties(renderNode); diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 139285a44817..b54e052cf538 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -2276,6 +2276,33 @@ public final class ViewRootImpl implements ViewParent, requestLayout(); } + /** Handles messages {@link #MSG_INSETS_CONTROL_CHANGED}. */ + private void handleInsetsControlChanged(@NonNull InsetsState insetsState, + @NonNull InsetsSourceControl.Array activeControls) { + final InsetsSourceControl[] controls = activeControls.get(); + + if (mTranslator != null) { + mTranslator.translateInsetsStateInScreenToAppWindow(insetsState); + mTranslator.translateSourceControlsInScreenToAppWindow(controls); + } + + // Deliver state change before control change, such that: + // a) When gaining control, controller can compare with server state to evaluate + // whether it needs to run animation. + // b) When loosing control, controller can restore server state by taking last + // dispatched state as truth. + mInsetsController.onStateChanged(insetsState); + if (mAdded) { + mInsetsController.onControlsChanged(controls); + } else if (controls != null) { + for (InsetsSourceControl control : controls) { + if (control != null) { + control.release(SurfaceControl::release); + } + } + } + } + private final DisplayListener mDisplayListener = new DisplayListener() { @Override public void onDisplayChanged(int displayId) { @@ -6591,24 +6618,11 @@ public final class ViewRootImpl implements ViewParent, break; } case MSG_INSETS_CONTROL_CHANGED: { - SomeArgs args = (SomeArgs) msg.obj; - - // Deliver state change before control change, such that: - // a) When gaining control, controller can compare with server state to evaluate - // whether it needs to run animation. - // b) When loosing control, controller can restore server state by taking last - // dispatched state as truth. - mInsetsController.onStateChanged((InsetsState) args.arg1); - InsetsSourceControl[] controls = (InsetsSourceControl[]) args.arg2; - if (mAdded) { - mInsetsController.onControlsChanged(controls); - } else if (controls != null) { - for (InsetsSourceControl control : controls) { - if (control != null) { - control.release(SurfaceControl::release); - } - } - } + final SomeArgs args = (SomeArgs) msg.obj; + final InsetsState insetsState = (InsetsState) args.arg1; + final InsetsSourceControl.Array activeControls = + (InsetsSourceControl.Array) args.arg2; + handleInsetsControlChanged(insetsState, activeControls); args.recycle(); break; } @@ -9828,25 +9842,9 @@ public final class ViewRootImpl implements ViewParent, mHandler.sendMessage(msg); } - private void dispatchInsetsControlChanged(InsetsState insetsState, - InsetsSourceControl[] activeControls) { - if (Binder.getCallingPid() == android.os.Process.myPid()) { - insetsState = new InsetsState(insetsState, true /* copySource */); - if (activeControls != null) { - for (int i = activeControls.length - 1; i >= 0; i--) { - activeControls[i] = new InsetsSourceControl(activeControls[i]); - } - } - } - if (mTranslator != null) { - mTranslator.translateInsetsStateInScreenToAppWindow(insetsState); - mTranslator.translateSourceControlsInScreenToAppWindow(activeControls); - } - if (insetsState != null && insetsState.isSourceOrDefaultVisible(ID_IME, Type.ime())) { - ImeTracing.getInstance().triggerClientDump("ViewRootImpl#dispatchInsetsControlChanged", - getInsetsController().getHost().getInputMethodManager(), null /* icProto */); - } - SomeArgs args = SomeArgs.obtain(); + private void dispatchInsetsControlChanged(@NonNull InsetsState insetsState, + @NonNull InsetsSourceControl.Array activeControls) { + final SomeArgs args = SomeArgs.obtain(); args.arg1 = insetsState; args.arg2 = activeControls; mHandler.obtainMessage(MSG_INSETS_CONTROL_CHANGED, args).sendToTarget(); @@ -11289,9 +11287,9 @@ public final class ViewRootImpl implements ViewParent, return; } // The the parameters from WindowStateResizeItem are already copied. - final boolean needCopy = + final boolean needsCopy = !isFromResizeItem && (Binder.getCallingPid() == Process.myPid()); - if (needCopy) { + if (needsCopy) { insetsState = new InsetsState(insetsState, true /* copySource */); frames = new ClientWindowFrames(frames); mergedConfiguration = new MergedConfiguration(mergedConfiguration); @@ -11307,10 +11305,32 @@ public final class ViewRootImpl implements ViewParent, final boolean isFromInsetsControlChangeItem = mIsFromTransactionItem; mIsFromTransactionItem = false; final ViewRootImpl viewAncestor = mViewAncestor.get(); - if (viewAncestor != null) { - viewAncestor.dispatchInsetsControlChanged(insetsState, activeControls.get()); + if (viewAncestor == null) { + return; + } + if (insetsState.isSourceOrDefaultVisible(ID_IME, Type.ime())) { + ImeTracing.getInstance().triggerClientDump( + "ViewRootImpl#dispatchInsetsControlChanged", + viewAncestor.getInsetsController().getHost().getInputMethodManager(), + null /* icProto */); + } + // If the UI thread is the same as the current thread that is dispatching + // WindowStateInsetsControlChangeItem, then it can run directly. + if (isFromInsetsControlChangeItem && viewAncestor.mHandler.getLooper() + == ActivityThread.currentActivityThread().getLooper()) { + viewAncestor.handleInsetsControlChanged(insetsState, activeControls); + return; } - // TODO(b/339380439): no need to post if the call is from InsetsControlChangeItem + // The parameters from WindowStateInsetsControlChangeItem are already copied. + final boolean needsCopy = + !isFromInsetsControlChangeItem && (Binder.getCallingPid() == Process.myPid()); + if (needsCopy) { + insetsState = new InsetsState(insetsState, true /* copySource */); + activeControls = new InsetsSourceControl.Array( + activeControls, true /* copyControls */); + } + + viewAncestor.dispatchInsetsControlChanged(insetsState, activeControls); } @Override diff --git a/core/java/android/widget/RemoteViewsService.java b/core/java/android/widget/RemoteViewsService.java index 07d6acbe38a8..c79eac605e64 100644 --- a/core/java/android/widget/RemoteViewsService.java +++ b/core/java/android/widget/RemoteViewsService.java @@ -132,7 +132,8 @@ public abstract class RemoteViewsService extends Service { RemoteViews.RemoteCollectionItems items = new RemoteViews.RemoteCollectionItems .Builder().build(); Parcel capSizeTestParcel = Parcel.obtain(); - capSizeTestParcel.allowSquashing(); + // restore allowSquashing to reduce the noise in error messages + boolean prevAllowSquashing = capSizeTestParcel.allowSquashing(); try { RemoteViews.RemoteCollectionItems.Builder itemsBuilder = @@ -154,6 +155,7 @@ public abstract class RemoteViewsService extends Service { items = itemsBuilder.build(); } finally { + capSizeTestParcel.restoreAllowSquashing(prevAllowSquashing); // Recycle the parcel capSizeTestParcel.recycle(); } diff --git a/core/java/android/window/DisplayWindowPolicyController.java b/core/java/android/window/DisplayWindowPolicyController.java index 8d71a8e998bd..9cd2a716e498 100644 --- a/core/java/android/window/DisplayWindowPolicyController.java +++ b/core/java/android/window/DisplayWindowPolicyController.java @@ -23,6 +23,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; import android.app.WindowConfiguration; +import android.companion.virtualdevice.flags.Flags; import android.content.ComponentName; import android.content.Intent; import android.content.pm.ActivityInfo; @@ -66,6 +67,9 @@ public abstract class DisplayWindowPolicyController { public DisplayWindowPolicyController() { synchronized (mSupportedWindowingModes) { mSupportedWindowingModes.add(WindowConfiguration.WINDOWING_MODE_FULLSCREEN); + if (Flags.virtualDisplayMultiWindowModeSupport()) { + mSupportedWindowingModes.add(WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW); + } } } diff --git a/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig b/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig index 983f46c58c4b..5b99ff9703e0 100644 --- a/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig +++ b/core/java/android/window/flags/large_screen_experiences_app_compat.aconfig @@ -73,6 +73,16 @@ flag { } flag { + name: "immersive_app_repositioning" + namespace: "large_screen_experiences_app_compat" + description: "Fix immersive apps changing size when repositioning" + bug: "334076352" + metadata { + purpose: PURPOSE_BUGFIX + } +} + +flag { name: "camera_compat_for_freeform" namespace: "large_screen_experiences_app_compat" description: "Whether to apply Camera Compat treatment to fixed-orientation apps in freeform windowing mode" diff --git a/core/java/android/window/flags/windowing_sdk.aconfig b/core/java/android/window/flags/windowing_sdk.aconfig index 21aa4800237c..9e69f8910bab 100644 --- a/core/java/android/window/flags/windowing_sdk.aconfig +++ b/core/java/android/window/flags/windowing_sdk.aconfig @@ -158,4 +158,14 @@ flag { metadata { purpose: PURPOSE_BUGFIX } -}
\ No newline at end of file +} + +flag { + namespace: "windowing_sdk" + name: "move_animation_options_to_change" + description: "Move AnimationOptions from TransitionInfo to each Change" + bug: "327332488" + metadata { + purpose: PURPOSE_BUGFIX + } +} diff --git a/core/java/com/android/internal/policy/BackdropFrameRenderer.java b/core/java/com/android/internal/policy/BackdropFrameRenderer.java deleted file mode 100644 index c6e8bf75dbcd..000000000000 --- a/core/java/com/android/internal/policy/BackdropFrameRenderer.java +++ /dev/null @@ -1,398 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -package com.android.internal.policy; - -import android.graphics.Insets; -import android.graphics.RecordingCanvas; -import android.graphics.Rect; -import android.graphics.RenderNode; -import android.graphics.drawable.ColorDrawable; -import android.graphics.drawable.Drawable; -import android.os.Looper; -import android.view.Choreographer; -import android.view.ThreadedRenderer; - -/** - * The thread which draws a fill in background while the app is resizing in areas where the app - * content draw is lagging behind the resize operation. - * It starts with the creation and it ends once someone calls destroy(). - * Any size changes can be passed by a call to setTargetRect will passed to the thread and - * executed via the Choreographer. - * @hide - */ -public class BackdropFrameRenderer extends Thread implements Choreographer.FrameCallback { - - private DecorView mDecorView; - - // This is containing the last requested size by a resize command. Note that this size might - // or might not have been applied to the output already. - private final Rect mTargetRect = new Rect(); - - // The render nodes for the multi threaded renderer. - private ThreadedRenderer mRenderer; - private RenderNode mFrameAndBackdropNode; - private RenderNode mSystemBarBackgroundNode; - - private final Rect mOldTargetRect = new Rect(); - private final Rect mNewTargetRect = new Rect(); - - private Choreographer mChoreographer; - - // Cached size values from the last render for the case that the view hierarchy is gone - // during a configuration change. - private int mLastContentWidth; - private int mLastContentHeight; - private int mLastXOffset; - private int mLastYOffset; - - // Whether to report when next frame is drawn or not. - private boolean mReportNextDraw; - - private Drawable mCaptionBackgroundDrawable; - private Drawable mUserCaptionBackgroundDrawable; - private Drawable mResizingBackgroundDrawable; - private ColorDrawable mStatusBarColor; - private ColorDrawable mNavigationBarColor; - private boolean mOldFullscreen; - private boolean mFullscreen; - private final Rect mOldSystemBarInsets = new Rect(); - private final Rect mSystemBarInsets = new Rect(); - private final Rect mTmpRect = new Rect(); - - public BackdropFrameRenderer(DecorView decorView, ThreadedRenderer renderer, Rect initialBounds, - Drawable resizingBackgroundDrawable, Drawable captionBackgroundDrawable, - Drawable userCaptionBackgroundDrawable, int statusBarColor, int navigationBarColor, - boolean fullscreen, Insets systemBarInsets) { - setName("ResizeFrame"); - - mRenderer = renderer; - onResourcesLoaded(decorView, resizingBackgroundDrawable, captionBackgroundDrawable, - userCaptionBackgroundDrawable, statusBarColor, navigationBarColor); - - // Create a render node for the content and frame backdrop - // which can be resized independently from the content. - mFrameAndBackdropNode = RenderNode.create("FrameAndBackdropNode", null); - - mRenderer.addRenderNode(mFrameAndBackdropNode, true); - - // Set the initial bounds and draw once so that we do not get a broken frame. - mTargetRect.set(initialBounds); - mFullscreen = fullscreen; - mOldFullscreen = fullscreen; - mSystemBarInsets.set(systemBarInsets.toRect()); - mOldSystemBarInsets.set(systemBarInsets.toRect()); - - // Kick off our draw thread. - start(); - } - - void onResourcesLoaded(DecorView decorView, Drawable resizingBackgroundDrawable, - Drawable captionBackgroundDrawableDrawable, Drawable userCaptionBackgroundDrawable, - int statusBarColor, int navigationBarColor) { - synchronized (this) { - mDecorView = decorView; - mResizingBackgroundDrawable = resizingBackgroundDrawable != null - && resizingBackgroundDrawable.getConstantState() != null - ? resizingBackgroundDrawable.getConstantState().newDrawable() - : null; - mCaptionBackgroundDrawable = captionBackgroundDrawableDrawable != null - && captionBackgroundDrawableDrawable.getConstantState() != null - ? captionBackgroundDrawableDrawable.getConstantState().newDrawable() - : null; - mUserCaptionBackgroundDrawable = userCaptionBackgroundDrawable != null - && userCaptionBackgroundDrawable.getConstantState() != null - ? userCaptionBackgroundDrawable.getConstantState().newDrawable() - : null; - if (mCaptionBackgroundDrawable == null) { - mCaptionBackgroundDrawable = mResizingBackgroundDrawable; - } - if (statusBarColor != 0) { - mStatusBarColor = new ColorDrawable(statusBarColor); - addSystemBarNodeIfNeeded(); - } else { - mStatusBarColor = null; - } - if (navigationBarColor != 0) { - mNavigationBarColor = new ColorDrawable(navigationBarColor); - addSystemBarNodeIfNeeded(); - } else { - mNavigationBarColor = null; - } - } - } - - private void addSystemBarNodeIfNeeded() { - if (mSystemBarBackgroundNode != null) { - return; - } - mSystemBarBackgroundNode = RenderNode.create("SystemBarBackgroundNode", null); - mRenderer.addRenderNode(mSystemBarBackgroundNode, false); - } - - /** - * Call this function asynchronously when the window size has been changed or when the insets - * have changed or whether window switched between a fullscreen or non-fullscreen layout. - * The change will be picked up once per frame and the frame will be re-rendered accordingly. - * - * @param newTargetBounds The new target bounds. - * @param fullscreen Whether the window is currently drawing in fullscreen. - * @param systemBarInsets The current visible system insets for the window. - */ - public void setTargetRect(Rect newTargetBounds, boolean fullscreen, Rect systemBarInsets) { - synchronized (this) { - mFullscreen = fullscreen; - mTargetRect.set(newTargetBounds); - mSystemBarInsets.set(systemBarInsets); - // Notify of a bounds change. - pingRenderLocked(false /* drawImmediate */); - } - } - - /** - * The window got replaced due to a configuration change. - */ - public void onConfigurationChange() { - synchronized (this) { - if (mRenderer != null) { - // Enforce a window redraw. - mOldTargetRect.set(0, 0, 0, 0); - pingRenderLocked(false /* drawImmediate */); - } - } - } - - /** - * All resources of the renderer will be released. This function can be called from the - * the UI thread as well as the renderer thread. - */ - void releaseRenderer() { - synchronized (this) { - if (mRenderer != null) { - // Invalidate the current content bounds. - mRenderer.setContentDrawBounds(0, 0, 0, 0); - - // Remove the render node again - // (see comment above - better to do that only once). - mRenderer.removeRenderNode(mFrameAndBackdropNode); - if (mSystemBarBackgroundNode != null) { - mRenderer.removeRenderNode(mSystemBarBackgroundNode); - } - - mRenderer = null; - - // Exit the renderer loop. - pingRenderLocked(false /* drawImmediate */); - } - } - } - - @Override - public void run() { - try { - Looper.prepare(); - synchronized (this) { - if (mRenderer == null) { - // This can happen if 'releaseRenderer' is called immediately after 'start'. - return; - } - mChoreographer = Choreographer.getInstance(); - } - Looper.loop(); - } finally { - releaseRenderer(); - } - synchronized (this) { - // Make sure no more messages are being sent. - mChoreographer = null; - Choreographer.releaseInstance(); - } - } - - /** - * The implementation of the FrameCallback. - * @param frameTimeNanos The time in nanoseconds when the frame started being rendered, - * in the {@link System#nanoTime()} timebase. Divide this value by {@code 1000000} - */ - @Override - public void doFrame(long frameTimeNanos) { - synchronized (this) { - if (mRenderer == null) { - reportDrawIfNeeded(); - // Tell the looper to stop. We are done. - Looper.myLooper().quit(); - return; - } - doFrameUncheckedLocked(); - } - } - - private void doFrameUncheckedLocked() { - mNewTargetRect.set(mTargetRect); - if (!mNewTargetRect.equals(mOldTargetRect) - || mOldFullscreen != mFullscreen - || !mSystemBarInsets.equals(mOldSystemBarInsets) - || mReportNextDraw) { - mOldFullscreen = mFullscreen; - mOldTargetRect.set(mNewTargetRect); - mOldSystemBarInsets.set(mSystemBarInsets); - redrawLocked(mNewTargetRect, mFullscreen); - } - } - - /** - * The content is about to be drawn and we got the location of where it will be shown. - * If a "redrawLocked" call has already been processed, we will re-issue the call - * if the previous call was ignored since the size was unknown. - * @param xOffset The x offset where the content is drawn to. - * @param yOffset The y offset where the content is drawn to. - * @param xSize The width size of the content. This should not be 0. - * @param ySize The height of the content. - * @return true if a frame should be requested after the content is drawn; false otherwise. - */ - boolean onContentDrawn(int xOffset, int yOffset, int xSize, int ySize) { - synchronized (this) { - final boolean firstCall = mLastContentWidth == 0; - // The current content buffer is drawn here. - mLastContentWidth = xSize; - mLastContentHeight = ySize; - mLastXOffset = xOffset; - mLastYOffset = yOffset; - - // Inform the renderer of the content's new bounds - mRenderer.setContentDrawBounds( - mLastXOffset, - mLastYOffset, - mLastXOffset + mLastContentWidth, - mLastYOffset + mLastContentHeight); - - // If this was the first call and redrawLocked got already called prior - // to us, we should re-issue a redrawLocked now. - return firstCall; - } - } - - void onRequestDraw(boolean reportNextDraw) { - synchronized (this) { - mReportNextDraw = reportNextDraw; - mOldTargetRect.set(0, 0, 0, 0); - pingRenderLocked(true /* drawImmediate */); - } - } - - /** - * Redraws the background, the caption and the system inset backgrounds if something changed. - * - * @param newBounds The window bounds which needs to be drawn. - * @param fullscreen Whether the window is currently drawing in fullscreen. - */ - private void redrawLocked(Rect newBounds, boolean fullscreen) { - - // Make sure that the other thread has already prepared the render draw calls for the - // content. If any size is 0, we have to wait for it to be drawn first. - if (mLastContentWidth == 0 || mLastContentHeight == 0) { - return; - } - - // Content may not be drawn at the surface origin, so we want to keep the offset when we're - // resizing it. - final int left = mLastXOffset + newBounds.left; - final int top = mLastYOffset + newBounds.top; - final int width = newBounds.width(); - final int height = newBounds.height(); - - mFrameAndBackdropNode.setLeftTopRightBottom(left, top, left + width, top + height); - - // Draw the caption and content backdrops in to our render node. - RecordingCanvas canvas = mFrameAndBackdropNode.beginRecording(width, height); - final Drawable drawable = mUserCaptionBackgroundDrawable != null - ? mUserCaptionBackgroundDrawable : mCaptionBackgroundDrawable; - - if (drawable != null) { - drawable.setBounds(0, 0, left + width, top); - drawable.draw(canvas); - } - - // The backdrop: clear everything with the background. Clipping is done elsewhere. - if (mResizingBackgroundDrawable != null) { - mResizingBackgroundDrawable.setBounds(0, 0, left + width, top + height); - mResizingBackgroundDrawable.draw(canvas); - } - mFrameAndBackdropNode.endRecording(); - - drawColorViews(left, top, width, height, fullscreen); - - // We need to render the node explicitly - mRenderer.drawRenderNode(mFrameAndBackdropNode); - - reportDrawIfNeeded(); - } - - private void drawColorViews(int left, int top, int width, int height, boolean fullscreen) { - if (mSystemBarBackgroundNode == null) { - return; - } - RecordingCanvas canvas = mSystemBarBackgroundNode.beginRecording(width, height); - mSystemBarBackgroundNode.setLeftTopRightBottom(left, top, left + width, top + height); - final int topInset = mSystemBarInsets.top; - if (mStatusBarColor != null) { - mStatusBarColor.setBounds(0, 0, left + width, topInset); - mStatusBarColor.draw(canvas); - } - - // We only want to draw the navigation bar if our window is currently fullscreen because we - // don't want the navigation bar background be moving around when resizing in docked mode. - // However, we need it for the transitions into/out of docked mode. - if (mNavigationBarColor != null && fullscreen) { - DecorView.getNavigationBarRect(width, height, mSystemBarInsets, mTmpRect, 1f); - mNavigationBarColor.setBounds(mTmpRect); - mNavigationBarColor.draw(canvas); - } - mSystemBarBackgroundNode.endRecording(); - mRenderer.drawRenderNode(mSystemBarBackgroundNode); - } - - /** Notify view root that a frame has been drawn by us, if it has requested so. */ - private void reportDrawIfNeeded() { - if (mReportNextDraw) { - if (mDecorView.isAttachedToWindow()) { - mDecorView.getViewRootImpl().reportDrawFinish(); - } - mReportNextDraw = false; - } - } - - /** - * Sends a message to the renderer to wake up and perform the next action which can be - * either the next rendering or the self destruction if mRenderer is null. - * Note: This call must be synchronized. - * - * @param drawImmediate if we should draw immediately instead of scheduling a frame - */ - private void pingRenderLocked(boolean drawImmediate) { - if (mChoreographer != null && !drawImmediate) { - mChoreographer.postFrameCallback(this); - } else { - doFrameUncheckedLocked(); - } - } - - void setUserCaptionBackgroundDrawable(Drawable userCaptionBackgroundDrawable) { - synchronized (this) { - mUserCaptionBackgroundDrawable = userCaptionBackgroundDrawable; - } - } -} diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java index 74c2325d45a2..c14a6c1ca5ba 100644 --- a/core/java/com/android/internal/policy/DecorView.java +++ b/core/java/com/android/internal/policy/DecorView.java @@ -57,7 +57,6 @@ import android.graphics.Paint; import android.graphics.PixelFormat; import android.graphics.RecordingCanvas; import android.graphics.Rect; -import android.graphics.Region; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.InsetDrawable; @@ -238,11 +237,8 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind private Rect mTempRect; private boolean mWindowResizeCallbacksAdded = false; - private Drawable.Callback mLastBackgroundDrawableCb = null; - private BackdropFrameRenderer mBackdropFrameRenderer = null; private Drawable mOriginalBackgroundDrawable; private Drawable mLastOriginalBackgroundDrawable; - private Drawable mResizingBackgroundDrawable; private BackgroundBlurDrawable mBackgroundBlurDrawable; private BackgroundBlurDrawable mLastBackgroundBlurDrawable; @@ -253,8 +249,6 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind */ @Nullable private Drawable mPendingWindowBackground; - private Drawable mCaptionBackgroundDrawable; - private Drawable mUserCaptionBackgroundDrawable; String mLogTag = TAG; private final Rect mFloatingInsets = new Rect(); @@ -329,26 +323,6 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind } @Override - public boolean gatherTransparentRegion(Region region) { - boolean statusOpaque = gatherTransparentRegion(mStatusColorViewState, region); - boolean navOpaque = gatherTransparentRegion(mNavigationColorViewState, region); - boolean decorOpaque = super.gatherTransparentRegion(region); - - // combine bools after computation, so each method above always executes - return statusOpaque || navOpaque || decorOpaque; - } - - boolean gatherTransparentRegion(ColorViewState colorViewState, Region region) { - if (colorViewState.view != null && colorViewState.visible && isResizing()) { - // If a visible ColorViewState is in a resizing host DecorView, forcibly register its - // opaque area, since it's drawn by a different root RenderNode. It would otherwise be - // rejected by ViewGroup#gatherTransparentRegion() for the view not being VISIBLE. - return colorViewState.view.gatherTransparentRegion(region); - } - return false; // no opaque area added - } - - @Override public void onDraw(Canvas c) { super.onDraw(c); @@ -838,7 +812,7 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind final MenuHelper helper; final boolean isPopup = !Float.isNaN(x) && !Float.isNaN(y); if (isPopup) { - helper = mWindow.mContextMenu.showPopup(getContext(), originalView, x, y); + helper = mWindow.mContextMenu.showPopup(originalView.getContext(), originalView, x, y); } else { helper = mWindow.mContextMenu.showDialog(originalView, originalView.getWindowToken()); } @@ -977,15 +951,11 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind updateColorViews(null /* insets */, false /* animate */); } if (drawable != null) { - mResizingBackgroundDrawable = enforceNonTranslucentBackground(drawable, - mWindow.isTranslucent() || mWindow.isShowingWallpaper()); - } else { - mResizingBackgroundDrawable = getResizingBackgroundDrawable( - mWindow.mBackgroundDrawable, mWindow.mBackgroundFallbackDrawable, - mWindow.isTranslucent() || mWindow.isShowingWallpaper()); - } - if (mResizingBackgroundDrawable != null) { - mResizingBackgroundDrawable.getPadding(mBackgroundPadding); + drawable.getPadding(mBackgroundPadding); + } else if (mWindow.mBackgroundDrawable != null) { + mWindow.mBackgroundDrawable.getPadding(mBackgroundPadding); + } else if (mWindow.mBackgroundFallbackDrawable != null) { + mWindow.mBackgroundFallbackDrawable.getPadding(mBackgroundPadding); } else { mBackgroundPadding.setEmpty(); } @@ -1451,7 +1421,7 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind mWindow.getAttributes().flags, force); boolean show = state.attributes.isVisible(state.present, color, mWindow.getAttributes().flags, force); - boolean showView = show && !isResizing() && size > 0; + boolean showView = show && size > 0; boolean visibilityChanged = false; View view = state.view; @@ -1505,7 +1475,7 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind } if (visibilityChanged) { view.animate().cancel(); - if (animate && !isResizing()) { + if (animate) { if (showView) { if (view.getVisibility() != VISIBLE) { view.setVisibility(VISIBLE); @@ -1834,10 +1804,6 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind // Note that our ViewRootImpl object will not change. getViewRootImpl().addWindowCallbacks(this); mWindowResizeCallbacksAdded = true; - } else if (mBackdropFrameRenderer != null) { - // We are resizing and this call happened due to a configuration change. Tell the - // renderer about it. - mBackdropFrameRenderer.onConfigurationChange(); } updateBackgroundBlurRadius(); @@ -1877,8 +1843,6 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind st.menu.close(); } - releaseThreadedRenderer(); - if (mWindowResizeCallbacksAdded) { getViewRootImpl().removeWindowCallbacks(this); mWindowResizeCallbacksAdded = false; @@ -2158,14 +2122,6 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind } void onResourcesLoaded(LayoutInflater inflater, int layoutResource) { - if (mBackdropFrameRenderer != null) { - loadBackgroundDrawablesIfNeeded(); - mBackdropFrameRenderer.onResourcesLoaded( - this, mResizingBackgroundDrawable, mCaptionBackgroundDrawable, - mUserCaptionBackgroundDrawable, getCurrentColor(mStatusColorViewState), - getCurrentColor(mNavigationColorViewState)); - } - final View root = inflater.inflate(layoutResource, null); // Put it below the color views. @@ -2174,63 +2130,6 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind initializeElevation(); } - private void loadBackgroundDrawablesIfNeeded() { - if (mResizingBackgroundDrawable == null) { - mResizingBackgroundDrawable = getResizingBackgroundDrawable(mWindow.mBackgroundDrawable, - mWindow.mBackgroundFallbackDrawable, mWindow.isTranslucent() - || mWindow.isShowingWallpaper()); - if (mResizingBackgroundDrawable == null) { - // We shouldn't really get here as the background fallback should be always - // available since it is defaulted by the system. - Log.w(mLogTag, "Failed to find background drawable for PhoneWindow=" + mWindow); - } - } - if (mCaptionBackgroundDrawable == null) { - mCaptionBackgroundDrawable = getContext().getDrawable( - R.drawable.decor_caption_title_focused); - } - if (mResizingBackgroundDrawable != null) { - mLastBackgroundDrawableCb = mResizingBackgroundDrawable.getCallback(); - mResizingBackgroundDrawable.setCallback(null); - } - } - - /** - * Returns the color used to fill areas the app has not rendered content to yet when the - * user is resizing the window of an activity in multi-window mode. - */ - public static Drawable getResizingBackgroundDrawable(@Nullable Drawable backgroundDrawable, - @Nullable Drawable fallbackDrawable, boolean windowTranslucent) { - if (backgroundDrawable != null) { - return enforceNonTranslucentBackground(backgroundDrawable, windowTranslucent); - } - - if (fallbackDrawable != null) { - return enforceNonTranslucentBackground(fallbackDrawable, windowTranslucent); - } - return new ColorDrawable(Color.BLACK); - } - - /** - * Enforces a drawable to be non-translucent to act as a background if needed, i.e. if the - * window is not translucent. - */ - private static Drawable enforceNonTranslucentBackground(Drawable drawable, - boolean windowTranslucent) { - if (!windowTranslucent && drawable instanceof ColorDrawable) { - ColorDrawable colorDrawable = (ColorDrawable) drawable; - int color = colorDrawable.getColor(); - if (Color.alpha(color) != 255) { - ColorDrawable copy = (ColorDrawable) colorDrawable.getConstantState().newDrawable() - .mutate(); - copy.setColor( - Color.argb(255, Color.red(color), Color.green(color), Color.blue(color))); - return copy; - } - } - return drawable; - } - void clearContentView() { for (int i = getChildCount() - 1; i >= 0; i--) { View v = getChildAt(i); @@ -2243,21 +2142,13 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind @Override public void onWindowSizeIsChanging(Rect newBounds, boolean fullscreen, Rect systemInsets, - Rect stableInsets) { - if (mBackdropFrameRenderer != null) { - mBackdropFrameRenderer.setTargetRect(newBounds, fullscreen, systemInsets); - } - } + Rect stableInsets) {} @Override public void onWindowDragResizeStart(Rect initialBounds, boolean fullscreen, Rect systemInsets, Rect stableInsets) { if (mWindow.isDestroyed()) { // If the owner's window is gone, we should not be able to come here anymore. - releaseThreadedRenderer(); - return; - } - if (mBackdropFrameRenderer != null) { return; } getViewRootImpl().requestInvalidateRootRenderNode(); @@ -2265,28 +2156,23 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind @Override public void onWindowDragResizeEnd() { - releaseThreadedRenderer(); updateColorViews(null /* insets */, false); getViewRootImpl().requestInvalidateRootRenderNode(); } @Override public boolean onContentDrawn(int offsetX, int offsetY, int sizeX, int sizeY) { - if (mBackdropFrameRenderer == null) { - return false; - } - return mBackdropFrameRenderer.onContentDrawn(offsetX, offsetY, sizeX, sizeY); + return false; } @Override public void onRequestDraw(boolean reportNextDraw) { - if (mBackdropFrameRenderer != null) { - mBackdropFrameRenderer.onRequestDraw(reportNextDraw); - } else if (reportNextDraw) { - // If render thread is gone, just report immediately. - if (isAttachedToWindow()) { - getViewRootImpl().reportDrawFinish(); - } + if (!reportNextDraw) { + return; + } + // If render thread is gone, just report immediately. + if (isAttachedToWindow()) { + getViewRootImpl().reportDrawFinish(); } } @@ -2307,25 +2193,6 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind mLegacyNavigationBarBackgroundPaint); } - /** Release the renderer thread which is usually done when the user stops resizing. */ - private void releaseThreadedRenderer() { - if (mResizingBackgroundDrawable != null && mLastBackgroundDrawableCb != null) { - mResizingBackgroundDrawable.setCallback(mLastBackgroundDrawableCb); - mLastBackgroundDrawableCb = null; - } - - if (mBackdropFrameRenderer != null) { - mBackdropFrameRenderer.releaseRenderer(); - mBackdropFrameRenderer = null; - // Bring the shadow back. - updateElevation(); - } - } - - private boolean isResizing() { - return mBackdropFrameRenderer != null; - } - /** * The elevation gets set for the first time and the framework needs to be informed that * the surface layer gets created with the shadow size in mind. @@ -2348,7 +2215,7 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind final boolean wasAdjustedForStack = mElevationAdjustedForStack; // Do not use a shadow when we are in resizing mode (mBackdropFrameRenderer not null) // since the shadow is bound to the content size and not the target size. - if ((windowingMode == WINDOWING_MODE_FREEFORM) && !isResizing()) { + if (windowingMode == WINDOWING_MODE_FREEFORM) { elevation = hasWindowFocus() ? DECOR_SHADOW_FOCUSED_HEIGHT_IN_DIP : DECOR_SHADOW_UNFOCUSED_HEIGHT_IN_DIP; // Add a maximum shadow height value to the top level view. @@ -2367,16 +2234,8 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind // Don't change the elevation if we didn't previously adjust it for the stack it was in // or it didn't change. - if ((wasAdjustedForStack || mElevationAdjustedForStack) - && getElevation() != elevation) { - if (!isResizing()) { - mWindow.setElevation(elevation); - } else { - // Just suppress the shadow when resizing, don't adjust surface insets because it'll - // cause a flicker when drag resize for freeform window starts. #onContentDrawn() - // will compensate the offset when passing to BackdropFrameRenderer. - setElevation(elevation); - } + if ((wasAdjustedForStack || mElevationAdjustedForStack) && getElevation() != elevation) { + mWindow.setElevation(elevation); } } @@ -2390,16 +2249,6 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind getResources().getDisplayMetrics()); } - /** - * Provide an override of the caption background drawable. - */ - void setUserCaptionBackgroundDrawable(Drawable drawable) { - mUserCaptionBackgroundDrawable = drawable; - if (mBackdropFrameRenderer != null) { - mBackdropFrameRenderer.setUserCaptionBackgroundDrawable(drawable); - } - } - private static String getTitleSuffix(WindowManager.LayoutParams params) { if (params == null) { return ""; diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java index a091e19e1985..2194c897ff0d 100644 --- a/core/java/com/android/internal/policy/PhoneWindow.java +++ b/core/java/com/android/internal/policy/PhoneWindow.java @@ -4055,7 +4055,8 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { @Override public void setResizingCaptionDrawable(Drawable drawable) { - mDecor.setUserCaptionBackgroundDrawable(drawable); + // TODO(b/333724879): Deprecate this public API. The new caption in WM shell allows the app + // content to draw behind it directly if requested. } @Override diff --git a/core/java/com/android/internal/policy/ScreenDecorationsUtils.java b/core/java/com/android/internal/policy/ScreenDecorationsUtils.java index ec6283922807..067e5e8813a7 100644 --- a/core/java/com/android/internal/policy/ScreenDecorationsUtils.java +++ b/core/java/com/android/internal/policy/ScreenDecorationsUtils.java @@ -18,6 +18,9 @@ package com.android.internal.policy; import android.content.Context; import android.content.res.Resources; +import android.util.DisplayUtils; +import android.view.Display; +import android.view.DisplayInfo; import android.view.RoundedCorners; import com.android.internal.R; @@ -57,11 +60,31 @@ public class ScreenDecorationsUtils { bottomRadius = defaultRadius; } + // If the physical pixels are scaled, apply it here + float scale = getPhysicalPixelDisplaySizeRatio(context); + if (scale != 1f) { + topRadius = topRadius * scale; + bottomRadius = bottomRadius * scale; + } + // Always use the smallest radius to make sure the rounded corners will // completely cover the display. return Math.min(topRadius, bottomRadius); } + static float getPhysicalPixelDisplaySizeRatio(Context context) { + DisplayInfo displayInfo = new DisplayInfo(); + context.getDisplay().getDisplayInfo(displayInfo); + final Display.Mode maxDisplayMode = + DisplayUtils.getMaximumResolutionDisplayMode(displayInfo.supportedModes); + if (maxDisplayMode == null) { + return 1f; + } + return DisplayUtils.getPhysicalPixelDisplaySizeRatio( + maxDisplayMode.getPhysicalWidth(), maxDisplayMode.getPhysicalHeight(), + displayInfo.getNaturalWidth(), displayInfo.getNaturalHeight()); + } + /** * If live rounded corners are supported on windows. */ diff --git a/core/java/com/android/internal/policy/TransitionAnimation.java b/core/java/com/android/internal/policy/TransitionAnimation.java index 2f09a5550fd4..66b2a9c8a424 100644 --- a/core/java/com/android/internal/policy/TransitionAnimation.java +++ b/core/java/com/android/internal/policy/TransitionAnimation.java @@ -1299,6 +1299,21 @@ public class TransitionAnimation { == HardwareBuffer.USAGE_PROTECTED_CONTENT; } + /** + * Returns the luminance in 0~1. The surface control is the source of the hardware buffer, + * which will be used if the buffer is protected from reading. + */ + public static float getBorderLuma(@NonNull HardwareBuffer hwBuffer, + @NonNull ColorSpace colorSpace, @NonNull SurfaceControl sourceSurfaceControl) { + if (hasProtectedContent(hwBuffer)) { + // The buffer cannot be read. Capture another buffer which excludes protected content + // from the source surface. + return getBorderLuma(sourceSurfaceControl, hwBuffer.getWidth(), hwBuffer.getHeight()); + } + // Use the existing buffer directly. + return getBorderLuma(hwBuffer, colorSpace); + } + /** Returns the luminance in 0~1. */ public static float getBorderLuma(SurfaceControl surfaceControl, int w, int h) { final ScreenCapture.ScreenshotHardwareBuffer buffer = diff --git a/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java b/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java index 37b72880dd0c..42fa6ac0407d 100644 --- a/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java +++ b/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java @@ -135,7 +135,7 @@ public class PerfettoProtoLogImpl implements IProtoLog { new DataSourceParams.Builder() .setBufferExhaustedPolicy( DataSourceParams - .PERFETTO_DS_BUFFER_EXHAUSTED_POLICY_STALL_AND_ABORT) + .PERFETTO_DS_BUFFER_EXHAUSTED_POLICY_DROP) .build(); mDataSource.register(params); this.mViewerConfigInputStreamProvider = viewerConfigInputStreamProvider; diff --git a/core/java/com/android/internal/widget/NotificationRowIconView.java b/core/java/com/android/internal/widget/NotificationRowIconView.java index 0f4615a12ea2..58bddaecd3e7 100644 --- a/core/java/com/android/internal/widget/NotificationRowIconView.java +++ b/core/java/com/android/internal/widget/NotificationRowIconView.java @@ -59,7 +59,7 @@ public class NotificationRowIconView extends CachingIconView { @Override protected void onFinishInflate() { // If showing the app icon, we don't need background or padding. - if (Flags.notificationsUseAppIcon()) { + if (Flags.notificationsUseAppIcon() || Flags.notificationsUseAppIconInRow()) { setPadding(0, 0, 0, 0); setBackground(null); } diff --git a/core/jni/com_android_internal_content_FileSystemUtils.cpp b/core/jni/com_android_internal_content_FileSystemUtils.cpp index 31f4e641b69e..d426f1240a7f 100644 --- a/core/jni/com_android_internal_content_FileSystemUtils.cpp +++ b/core/jni/com_android_internal_content_FileSystemUtils.cpp @@ -88,7 +88,7 @@ bool punchHoles(const char *filePath, const uint64_t offset, ALOGD("Total number of LOAD segments %zu", programHeaders.size()); ALOGD("Size before punching holes st_blocks: %" PRIu64 - ", st_blksize: %ld, st_size: %" PRIu64 "", + ", st_blksize: %d, st_size: %" PRIu64 "", beforePunch.st_blocks, beforePunch.st_blksize, static_cast<uint64_t>(beforePunch.st_size)); } @@ -193,7 +193,7 @@ bool punchHoles(const char *filePath, const uint64_t offset, ALOGD("lstat64 failed for filePath %s, error:%d", filePath, errno); return false; } - ALOGD("Size after punching holes st_blocks: %" PRIu64 ", st_blksize: %ld, st_size: %" PRIu64 + ALOGD("Size after punching holes st_blocks: %" PRIu64 ", st_blksize: %d, st_size: %" PRIu64 "", afterPunch.st_blocks, afterPunch.st_blksize, static_cast<uint64_t>(afterPunch.st_size)); @@ -271,7 +271,7 @@ bool punchHolesInZip(const char *filePath, uint64_t offset, uint16_t extraFieldL uint64_t blockSize = beforePunch.st_blksize; IF_ALOGD() { ALOGD("Extra field length: %hu, Size before punching holes st_blocks: %" PRIu64 - ", st_blksize: %ld, st_size: %" PRIu64 "", + ", st_blksize: %d, st_size: %" PRIu64 "", extraFieldLen, beforePunch.st_blocks, beforePunch.st_blksize, static_cast<uint64_t>(beforePunch.st_size)); } @@ -346,7 +346,7 @@ bool punchHolesInZip(const char *filePath, uint64_t offset, uint16_t extraFieldL return false; } ALOGD("punchHolesInApk:: Size after punching holes st_blocks: %" PRIu64 - ", st_blksize: %ld, st_size: %" PRIu64 "", + ", st_blksize: %d, st_size: %" PRIu64 "", afterPunch.st_blocks, afterPunch.st_blksize, static_cast<uint64_t>(afterPunch.st_size)); } diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 7b9235cdc691..6dbe44b483d2 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -4702,6 +4702,11 @@ <permission android:name="android.permission.REQUEST_UNIQUE_ID_ATTESTATION" android:protectionLevel="signature" /> + <!-- Allows an application to use the RemoteKeyProvisioningService. + @hide --> + <permission android:name="android.permission.BIND_RKP_SERVICE" + android:protectionLevel="signature" /> + <!-- Allows an application to get enabled credential manager providers. @hide --> <permission android:name="android.permission.LIST_ENABLED_CREDENTIAL_PROVIDERS" diff --git a/core/res/res/drawable/decor_caption_title.xml b/core/res/res/drawable/decor_caption_title.xml deleted file mode 100644 index 591605d33fae..000000000000 --- a/core/res/res/drawable/decor_caption_title.xml +++ /dev/null @@ -1,21 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2015 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> - -<selector xmlns:android="http://schemas.android.com/apk/res/android"> - <item android:state_window_focused="true" - android:drawable="@drawable/decor_caption_title_focused" /> - <item android:drawable="@drawable/decor_caption_title_unfocused" /> -</selector> diff --git a/core/res/res/drawable/decor_caption_title_focused.xml b/core/res/res/drawable/decor_caption_title_focused.xml deleted file mode 100644 index 7d1c23052bdb..000000000000 --- a/core/res/res/drawable/decor_caption_title_focused.xml +++ /dev/null @@ -1,23 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2015 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. ---> - -<shape android:shape="rectangle" - android:tintMode="multiply" - android:tint="#D8D8D8" - xmlns:android="http://schemas.android.com/apk/res/android"> - <!-- Fading the primary color to 85% blackness --> - <solid android:color="?android:attr/colorPrimary" /> -</shape> diff --git a/core/res/res/drawable/decor_caption_title_unfocused.xml b/core/res/res/drawable/decor_caption_title_unfocused.xml deleted file mode 100644 index 2846d8ca6baa..000000000000 --- a/core/res/res/drawable/decor_caption_title_unfocused.xml +++ /dev/null @@ -1,23 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2015 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. ---> - -<shape android:shape="rectangle" - android:tintMode="multiply" - android:tint="#F2F2F2" - xmlns:android="http://schemas.android.com/apk/res/android"> - <!-- Fading the primary color to 95% blackness --> - <solid android:color="?android:attr/colorPrimary"/> -</shape> diff --git a/core/res/res/drawable/decor_close_button_dark.xml b/core/res/res/drawable/decor_close_button_dark.xml deleted file mode 100644 index 950e4fdaba0b..000000000000 --- a/core/res/res/drawable/decor_close_button_dark.xml +++ /dev/null @@ -1,31 +0,0 @@ -<!-- -Copyright (C) 2015 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> -<vector xmlns:android="http://schemas.android.com/apk/res/android" - android:width="32.0dp" - android:height="32.0dp" - android:viewportWidth="32.0" - android:viewportHeight="32.0" - android:tint="@color/decor_button_dark_color" - > - <group android:scaleX="0.5" - android:scaleY="0.5" - android:translateX="8.0" - android:translateY="8.0" > - <path - android:fillColor="@color/white" - android:pathData="M6.9,4.0l-2.9,2.9 9.1,9.1 -9.1,9.200001 2.9,2.799999 9.1,-9.1 9.1,9.1 2.9,-2.799999 -9.1,-9.200001 9.1,-9.1 -2.9,-2.9 -9.1,9.2z"/> - </group> -</vector> diff --git a/core/res/res/drawable/decor_close_button_light.xml b/core/res/res/drawable/decor_close_button_light.xml deleted file mode 100644 index d75cd25b23ee..000000000000 --- a/core/res/res/drawable/decor_close_button_light.xml +++ /dev/null @@ -1,31 +0,0 @@ -<!-- -Copyright (C) 2015 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> -<vector xmlns:android="http://schemas.android.com/apk/res/android" - android:width="32.0dp" - android:height="32.0dp" - android:viewportWidth="32.0" - android:viewportHeight="32.0" - android:tint="@color/decor_button_light_color" - > - <group android:scaleX="0.5" - android:scaleY="0.5" - android:translateX="8.0" - android:translateY="8.0" > - <path - android:fillColor="@color/white" - android:pathData="M6.9,4.0l-2.9,2.9 9.1,9.1 -9.1,9.200001 2.9,2.799999 9.1,-9.1 9.1,9.1 2.9,-2.799999 -9.1,-9.200001 9.1,-9.1 -2.9,-2.9 -9.1,9.2z"/> - </group> -</vector> diff --git a/core/res/res/drawable/decor_maximize_button_dark.xml b/core/res/res/drawable/decor_maximize_button_dark.xml deleted file mode 100644 index 619b460ce787..000000000000 --- a/core/res/res/drawable/decor_maximize_button_dark.xml +++ /dev/null @@ -1,37 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2015 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> - -<vector xmlns:android="http://schemas.android.com/apk/res/android" - android:width="32.0dp" - android:height="32.0dp" - android:viewportWidth="32.0" - android:viewportHeight="32.0" - android:tint="@color/decor_button_dark_color" - > - <group android:scaleX="0.5" - android:scaleY="0.5" - android:translateX="8.0" - android:translateY="8.0" > - <path - android:fillColor="@color/white" - android:pathData="M2.0,4.0l0.0,16.0l28.0,0.0L30.0,4.0L2.0,4.0zM26.0,16.0L6.0,16.0L6.0,8.0l20.0,0.0L26.0,16.0z"/> - <path - android:fillColor="@color/white" - android:pathData="M2.0,24.0l28.0,0.0l0.0,4.0l-28.0,0.0z"/> - </group> -</vector> - - diff --git a/core/res/res/drawable/decor_maximize_button_light.xml b/core/res/res/drawable/decor_maximize_button_light.xml deleted file mode 100644 index 5b55fd20ee87..000000000000 --- a/core/res/res/drawable/decor_maximize_button_light.xml +++ /dev/null @@ -1,35 +0,0 @@ -<!-- -Copyright (C) 2015 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> - -<vector xmlns:android="http://schemas.android.com/apk/res/android" - android:width="32.0dp" - android:height="32.0dp" - android:viewportWidth="32.0" - android:viewportHeight="32.0" - android:tint="@color/decor_button_light_color" - > - <group android:scaleX="0.5" - android:scaleY="0.5" - android:translateX="8.0" - android:translateY="8.0" > - <path - android:fillColor="@color/white" - android:pathData="M2.0,4.0l0.0,16.0l28.0,0.0L30.0,4.0L2.0,4.0zM26.0,16.0L6.0,16.0L6.0,8.0l20.0,0.0L26.0,16.0z"/> - <path - android:fillColor="@color/white" - android:pathData="M2.0,24.0l28.0,0.0l0.0,4.0l-28.0,0.0z"/> - </group> -</vector> diff --git a/core/res/res/layout/decor_caption.xml b/core/res/res/layout/decor_caption.xml deleted file mode 100644 index 02467369825d..000000000000 --- a/core/res/res/layout/decor_caption.xml +++ /dev/null @@ -1,52 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -** -** Copyright 2015, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ ---> - -<com.android.internal.widget.DecorCaptionView xmlns:android="http://schemas.android.com/apk/res/android" - android:orientation="vertical" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:descendantFocusability="beforeDescendants" > - <LinearLayout - android:id="@+id/caption" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:gravity="end" - android:background="@drawable/decor_caption_title" - android:focusable="false" - android:descendantFocusability="blocksDescendants" > - <Button - android:id="@+id/maximize_window" - android:layout_width="32dp" - android:layout_height="32dp" - android:layout_margin="5dp" - android:padding="4dp" - android:layout_gravity="center_vertical|end" - android:contentDescription="@string/maximize_button_text" - android:background="@drawable/decor_maximize_button_dark" /> - <Button - android:id="@+id/close_window" - android:layout_width="32dp" - android:layout_height="32dp" - android:layout_margin="5dp" - android:padding="4dp" - android:layout_gravity="center_vertical|end" - android:contentDescription="@string/close_button_text" - android:background="@drawable/decor_close_button_dark" /> - </LinearLayout> -</com.android.internal.widget.DecorCaptionView> diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml index b532ad718cec..ca6a384bd96f 100644 --- a/core/res/res/values-af/strings.xml +++ b/core/res/res/values-af/strings.xml @@ -201,10 +201,8 @@ <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"Jou werkprofiel is nie meer op hierdie toestel beskikbaar nie"</string> <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"Te veel wagwoordpogings"</string> <string name="device_ownership_relinquished" msgid="4080886992183195724">"Administrateur het toestel vir persoonlike gebruik afgestaan"</string> - <!-- no translation found for private_space_deleted_by_admin (1484365588862066939) --> - <skip /> - <!-- no translation found for private_space_deleted_by_admin_details (7007781735201818689) --> - <skip /> + <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"Privaat ruimte is verwyder"</string> + <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"Jou organisasie laat nie privaat ruimtes op hierdie bestuurde toestel toe nie."</string> <string name="network_logging_notification_title" msgid="554983187553845004">"Toestel word bestuur"</string> <string name="network_logging_notification_text" msgid="1327373071132562512">"Jou organisasie bestuur hierdie toestel en kan netwerkverkeer monitor. Tik vir besonderhede."</string> <string name="location_changed_notification_title" msgid="3620158742816699316">"Programme kan toegang tot jou ligging kry"</string> @@ -2198,7 +2196,6 @@ <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"D-paneel links"</string> <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"D-paneel regs"</string> <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"D-paneel middel"</string> - <string name="accessibility_freeform_caption" msgid="8377519323496290122">"<xliff:g id="APP_NAME">%1$s</xliff:g> se onderskrifbalk."</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> is in die BEPERK-groep geplaas"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"het \'n prent gestuur"</string> diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml index 060c8cb0cf3c..218b13ae3de7 100644 --- a/core/res/res/values-am/strings.xml +++ b/core/res/res/values-am/strings.xml @@ -201,10 +201,8 @@ <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"የሥራ መገለጫዎ ከዚህ በኋላ በዚህ መሣሪያ ላይ አይገኝም"</string> <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"በጣም ብዙ የይለፍ ቃል ሙከራዎች"</string> <string name="device_ownership_relinquished" msgid="4080886992183195724">"አስተዳዳሪ መሣሪያዎን ለግል ጥቅም ትተውታል"</string> - <!-- no translation found for private_space_deleted_by_admin (1484365588862066939) --> - <skip /> - <!-- no translation found for private_space_deleted_by_admin_details (7007781735201818689) --> - <skip /> + <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"የግል ቦታ ተወግዷል"</string> + <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"ድርጅትዎ የግል ቦታዎችን በዚህ የሚተዳደር መሣሪያ ላይ አይፈቅድም።"</string> <string name="network_logging_notification_title" msgid="554983187553845004">"መሣሪያው የሚተዳደር ነው"</string> <string name="network_logging_notification_text" msgid="1327373071132562512">"የእርስዎ ድርጅት ይህን መሣሪያ ያስተዳድራል፣ እና የአውታረ መረብ ትራፊክን ሊከታተል ይችላል። ዝርዝሮችን ለማግኘት መታ ያድርጉ።"</string> <string name="location_changed_notification_title" msgid="3620158742816699316">"መተግበሪያዎች የእርስዎን አካባቢ መድረስ ይችላሉ"</string> @@ -2198,7 +2196,6 @@ <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"ከDpad በስተግራ"</string> <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"ከDpad በስተቀኝ"</string> <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"የDpad ማዕከል"</string> - <string name="accessibility_freeform_caption" msgid="8377519323496290122">"የ<xliff:g id="APP_NAME">%1$s</xliff:g> የሥዕል ገላጭ ጽሁፍ አሞሌ።"</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> ወደ የRESTRICTED ባልዲ ተከትቷል"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>፦"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"አንድ ምስል ልከዋል"</string> diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml index f59a8cb15506..d0e28b7b243a 100644 --- a/core/res/res/values-ar/strings.xml +++ b/core/res/res/values-ar/strings.xml @@ -205,10 +205,8 @@ <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"لم يعد ملفك الشخصي للعمل متاحًا على هذا الجهاز"</string> <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"تم إجراء محاولات كثيرة جدًا لإدخال كلمة المرور"</string> <string name="device_ownership_relinquished" msgid="4080886992183195724">"تنازل المشرف عن الجهاز للاستخدام الشخصي"</string> - <!-- no translation found for private_space_deleted_by_admin (1484365588862066939) --> - <skip /> - <!-- no translation found for private_space_deleted_by_admin_details (7007781735201818689) --> - <skip /> + <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"تمت إزالة المساحة الخاصّة"</string> + <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"لا تسمح مؤسستك بالمساحات الخاصة على هذا الجهاز المُدار."</string> <string name="network_logging_notification_title" msgid="554983187553845004">"تتم إدارة الجهاز"</string> <string name="network_logging_notification_text" msgid="1327373071132562512">"تدير مؤسستك هذا الجهاز ويمكنها مراقبة حركة بيانات الشبكة. يمكنك النقر للحصول على تفاصيل."</string> <string name="location_changed_notification_title" msgid="3620158742816699316">"يمكن للتطبيقات الوصول إلى موقعك الجغرافي"</string> @@ -2202,7 +2200,6 @@ <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"زرّ الاتجاه لليسار"</string> <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"زرّ الاتجاه لليمين"</string> <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"الزرّ المركزي"</string> - <string name="accessibility_freeform_caption" msgid="8377519323496290122">"شريط الشرح لتطبيق <xliff:g id="APP_NAME">%1$s</xliff:g>."</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"تم وضع <xliff:g id="PACKAGE_NAME">%1$s</xliff:g> في الحزمة \"محظورة\"."</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"هذا المستخدم أرسل صورة"</string> diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml index c20d5ae00624..42b0af9d4490 100644 --- a/core/res/res/values-as/strings.xml +++ b/core/res/res/values-as/strings.xml @@ -201,10 +201,8 @@ <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"আপোনাৰ কৰ্মস্থানৰ প্ৰ\'ফাইল এই ডিভাইচটোত আৰু উপলব্ধ নহয়"</string> <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"বহুতবাৰ ভুলকৈ পাছৱৰ্ড দিয়া হৈছে"</string> <string name="device_ownership_relinquished" msgid="4080886992183195724">"প্ৰশাসকে ডিভাইচটো ব্যক্তিগত ব্যৱহাৰৰ বাবে বাজেয়প্ত কৰিছে"</string> - <!-- no translation found for private_space_deleted_by_admin (1484365588862066939) --> - <skip /> - <!-- no translation found for private_space_deleted_by_admin_details (7007781735201818689) --> - <skip /> + <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"প্ৰাইভেট স্পে’চ আঁতৰোৱা হৈছে"</string> + <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"আপোনাৰ প্ৰতিষ্ঠানে এই পৰিচালিত ডিভাইচত প্ৰাইভেট স্পে’চৰ অনুমতি নিদিয়ে।"</string> <string name="network_logging_notification_title" msgid="554983187553845004">"পৰিচালিত ডিভাইচ"</string> <string name="network_logging_notification_text" msgid="1327373071132562512">"আপোনাৰ প্ৰতিষ্ঠানটোৱে এই ডিভাইচটো পৰিচালনা কৰে আৰু ই নেটৱৰ্কৰ ট্ৰেফিক পৰ্যবেক্ষণ কৰিব পাৰে। সবিশেষ জানিবলৈ টিপক।"</string> <string name="location_changed_notification_title" msgid="3620158742816699316">"এপ্সমূহে আপোনাৰ অৱস্থান এক্সেছ কৰিব পাৰে"</string> @@ -2198,7 +2196,6 @@ <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"ডিপেডৰ বাওঁফালৰ বুটাম"</string> <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"ডিপেডৰ সোঁফালৰ বুটাম"</string> <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"ডিপেডৰ মাজৰ বুটাম"</string> - <string name="accessibility_freeform_caption" msgid="8377519323496290122">"<xliff:g id="APP_NAME">%1$s</xliff:g>ৰ কেপশ্বন বাৰ।"</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g>ক সীমাবদ্ধ বাকেটটোত ৰখা হৈছে"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"এখন প্ৰতিচ্ছবি পঠিয়াইছে"</string> @@ -2399,7 +2396,7 @@ <string name="keyboard_layout_notification_more_than_three_selected_message" msgid="1581834181578206937">"কীব’ৰ্ডৰ লে’আউট <xliff:g id="LAYOUT_1">%1$s</xliff:g>, <xliff:g id="LAYOUT_2">%2$s</xliff:g>, <xliff:g id="LAYOUT_3">%3$s</xliff:g> হিচাপে ছেট কৰা হৈছে… সলনি কৰিবলৈ টিপক।"</string> <string name="keyboard_layout_notification_multiple_selected_title" msgid="5242444914367024499">"ভৌতিক কীব’ৰ্ড কনফিগাৰ কৰা হৈছে"</string> <string name="keyboard_layout_notification_multiple_selected_message" msgid="6576533454124419202">"কীব’ৰ্ড চাবলৈ টিপক"</string> - <string name="profile_label_private" msgid="6463418670715290696">"ব্যক্তিগত"</string> + <string name="profile_label_private" msgid="6463418670715290696">"প্ৰাইভেট"</string> <string name="profile_label_clone" msgid="769106052210954285">"ক্ল’ন"</string> <string name="profile_label_work" msgid="3495359133038584618">"কৰ্মস্থান"</string> <string name="profile_label_work_2" msgid="4691533661598632135">"কৰ্মস্থান ২"</string> diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml index 9d4475906fea..5633c06f6b92 100644 --- a/core/res/res/values-az/strings.xml +++ b/core/res/res/values-az/strings.xml @@ -2196,7 +2196,6 @@ <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Dpad Sola"</string> <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Dpad Sağa"</string> <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Dpad Mərkəzə"</string> - <string name="accessibility_freeform_caption" msgid="8377519323496290122">"<xliff:g id="APP_NAME">%1$s</xliff:g> başlıq paneli."</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> MƏHDUDLAŞDIRILMIŞ səbətinə yerləşdirilib"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"şəkil göndərdi"</string> @@ -2397,7 +2396,7 @@ <string name="keyboard_layout_notification_more_than_three_selected_message" msgid="1581834181578206937">"Klaviatura düzəni <xliff:g id="LAYOUT_1">%1$s</xliff:g>, <xliff:g id="LAYOUT_2">%2$s</xliff:g>, <xliff:g id="LAYOUT_3">%3$s</xliff:g> kimi ayarlanıb… Dəyişmək üçün toxunun."</string> <string name="keyboard_layout_notification_multiple_selected_title" msgid="5242444914367024499">"Fiziki klaviaturalar konfiqurasiya edilib"</string> <string name="keyboard_layout_notification_multiple_selected_message" msgid="6576533454124419202">"Klaviaturalara baxmaq üçün toxunun"</string> - <string name="profile_label_private" msgid="6463418670715290696">"Şəxsi"</string> + <string name="profile_label_private" msgid="6463418670715290696">"Məxfi"</string> <string name="profile_label_clone" msgid="769106052210954285">"Klon"</string> <string name="profile_label_work" msgid="3495359133038584618">"İş"</string> <string name="profile_label_work_2" msgid="4691533661598632135">"İş 2"</string> diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml index 140ca4aa3f39..e38706ea866c 100644 --- a/core/res/res/values-b+sr+Latn/strings.xml +++ b/core/res/res/values-b+sr+Latn/strings.xml @@ -202,10 +202,8 @@ <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"Poslovni profil više nije dostupan na ovom uređaju"</string> <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"Previše pokušaja unosa lozinke"</string> <string name="device_ownership_relinquished" msgid="4080886992183195724">"Administrator je ustupio uređaj za ličnu upotrebu"</string> - <!-- no translation found for private_space_deleted_by_admin (1484365588862066939) --> - <skip /> - <!-- no translation found for private_space_deleted_by_admin_details (7007781735201818689) --> - <skip /> + <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"Privatan prostor je uklonjen"</string> + <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"Organizacija ne dozvoljava privatne prostore na ovom upravljanom uređaju."</string> <string name="network_logging_notification_title" msgid="554983187553845004">"Uređajem se upravlja"</string> <string name="network_logging_notification_text" msgid="1327373071132562512">"Organizacija upravlja ovim uređajem i može da nadgleda mrežni saobraćaj. Dodirnite za detalje."</string> <string name="location_changed_notification_title" msgid="3620158742816699316">"Aplikacije mogu da pristupaju vašoj lokaciji"</string> @@ -2199,7 +2197,6 @@ <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"nalevo na D-pad-u"</string> <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"nadesno na D-pad-u"</string> <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"centar na D-pad-u"</string> - <string name="accessibility_freeform_caption" msgid="8377519323496290122">"Traka sa naslovima aplikacije <xliff:g id="APP_NAME">%1$s</xliff:g>."</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"Paket <xliff:g id="PACKAGE_NAME">%1$s</xliff:g> je dodat u segment OGRANIČENO"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"je poslao/la sliku"</string> diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml index 116a83621934..25ae5a764360 100644 --- a/core/res/res/values-be/strings.xml +++ b/core/res/res/values-be/strings.xml @@ -203,10 +203,8 @@ <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"Ваш працоўны профіль больш не даступны на гэтай прыладзе"</string> <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"Занадта шмат спроб уводу пароля"</string> <string name="device_ownership_relinquished" msgid="4080886992183195724">"Адміністратар пераналадзіў прыладу для асабістага выкарыстання"</string> - <!-- no translation found for private_space_deleted_by_admin (1484365588862066939) --> - <skip /> - <!-- no translation found for private_space_deleted_by_admin_details (7007781735201818689) --> - <skip /> + <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"Прыватная прастора выдалена"</string> + <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"Ваша арганізацыя не дазваляе прыватныя прасторы на гэтай прыладзе пад яе кіраваннем."</string> <string name="network_logging_notification_title" msgid="554983187553845004">"Прылада знаходзіцца пад кіраваннем"</string> <string name="network_logging_notification_text" msgid="1327373071132562512">"Ваша арганізацыя кіруе гэтай прыладай і можа сачыць за сеткавым трафікам. Дакраніцеся для атрымання дадатковай інфармацыі."</string> <string name="location_changed_notification_title" msgid="3620158742816699316">"Праграмы могуць атрымліваць даныя пра ваша месцазнаходжанне"</string> @@ -2200,7 +2198,6 @@ <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Улева на панэлі кіравання"</string> <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Управа на панэлі кіравання"</string> <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"У цэнтр на панэлі кіравання"</string> - <string name="accessibility_freeform_caption" msgid="8377519323496290122">"Панэль субцітраў праграмы \"<xliff:g id="APP_NAME">%1$s</xliff:g>\"."</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"Пакет \"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g>\" дададзены ў АБМЕЖАВАНУЮ групу"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"адпраўлены відарыс"</string> diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml index 086cb63009c1..6b388d8d6e6d 100644 --- a/core/res/res/values-bg/strings.xml +++ b/core/res/res/values-bg/strings.xml @@ -201,10 +201,8 @@ <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"Служебният ви потребителски профил вече не е налице на това устройство"</string> <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"Опитите за паролата са твърде много"</string> <string name="device_ownership_relinquished" msgid="4080886992183195724">"Администраторът предостави устройствотото за лична употреба"</string> - <!-- no translation found for private_space_deleted_by_admin (1484365588862066939) --> - <skip /> - <!-- no translation found for private_space_deleted_by_admin_details (7007781735201818689) --> - <skip /> + <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"Частното пространство бе премахнато"</string> + <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"Организацията ви не допуска частни пространства на това управлявано устройство."</string> <string name="network_logging_notification_title" msgid="554983187553845004">"Устройството се управлява"</string> <string name="network_logging_notification_text" msgid="1327373071132562512">"Организацията ви управлява това устройство и може да наблюдава мрежовия трафик. Докоснете за подробности."</string> <string name="location_changed_notification_title" msgid="3620158742816699316">"Прилож. имат достъп до местоположението ви"</string> @@ -2198,7 +2196,6 @@ <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Контролен пад – ляво"</string> <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Контролен пад – дясно"</string> <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Контролен пад – център"</string> - <string name="accessibility_freeform_caption" msgid="8377519323496290122">"Лента за надписи на <xliff:g id="APP_NAME">%1$s</xliff:g>."</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"Пакетът <xliff:g id="PACKAGE_NAME">%1$s</xliff:g> е поставен в ОГРАНИЧЕНИЯ контейнер"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"изпратено изображение"</string> diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml index 679cf5737c0c..3f338cce08aa 100644 --- a/core/res/res/values-bn/strings.xml +++ b/core/res/res/values-bn/strings.xml @@ -201,10 +201,8 @@ <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"আপনার কর্মস্থলের প্রোফাইলটি আর এই ডিভাইসে নেই"</string> <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"বহুবার ভুল পাসওয়ার্ড দিয়েছেন"</string> <string name="device_ownership_relinquished" msgid="4080886992183195724">"ব্যক্তিগত কাজের জন্য অ্যাডমিন এই ডিভাইস ব্যবহার করার অনুমতি দেয়নি"</string> - <!-- no translation found for private_space_deleted_by_admin (1484365588862066939) --> - <skip /> - <!-- no translation found for private_space_deleted_by_admin_details (7007781735201818689) --> - <skip /> + <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"প্রাইভেট স্পেস সরিয়ে দেওয়া হয়েছে"</string> + <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"এই ম্যানেজ করা ডিভাইসে আপনার সংস্থা প্রাইভেট স্পেসের অনুমতি দেয় না।"</string> <string name="network_logging_notification_title" msgid="554983187553845004">"ডিভাইসটি পরিচালনা করা হচ্ছে"</string> <string name="network_logging_notification_text" msgid="1327373071132562512">"আপনার প্রতিষ্ঠান এই ডিভাইসটি পরিচালনা করে এবং এটির নেটওয়ার্ক ট্রাফিকের উপরে নজর রাখতে পারে। বিশদ বিবরণের জন্য ট্যাপ করুন।,"</string> <string name="location_changed_notification_title" msgid="3620158742816699316">"অ্যাপগুলি আপনার লোকেশন অ্যাক্সেস করতে পারবে"</string> @@ -2198,7 +2196,6 @@ <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"ডিপ্যাড (Dpad)-এর বাঁদিকে"</string> <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"ডিপ্যাড (Dpad)-এর ডানদিকে"</string> <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"ডিপ্যাড (Dpad)-এর মাঝখানে"</string> - <string name="accessibility_freeform_caption" msgid="8377519323496290122">"<xliff:g id="APP_NAME">%1$s</xliff:g>-এর ক্যাপশন বার।"</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> সীমাবদ্ধ গ্রুপে অন্তর্ভুক্ত করা হয়েছে"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"একটি ছবি পাঠানো হয়েছে"</string> @@ -2399,7 +2396,7 @@ <string name="keyboard_layout_notification_more_than_three_selected_message" msgid="1581834181578206937">"কীবোর্ড লেআউট <xliff:g id="LAYOUT_1">%1$s</xliff:g>, <xliff:g id="LAYOUT_2">%2$s</xliff:g>, <xliff:g id="LAYOUT_3">%3$s</xliff:g>-এ সেট করা আছে… পালটাতে ট্যাপ করুন।"</string> <string name="keyboard_layout_notification_multiple_selected_title" msgid="5242444914367024499">"ফিজিক্যাল কীবোর্ড কনফিগার করা হয়েছে"</string> <string name="keyboard_layout_notification_multiple_selected_message" msgid="6576533454124419202">"কীবোর্ড দেখতে ট্যাপ করুন"</string> - <string name="profile_label_private" msgid="6463418670715290696">"ব্যক্তিগত"</string> + <string name="profile_label_private" msgid="6463418670715290696">"প্রাইভেট"</string> <string name="profile_label_clone" msgid="769106052210954285">"ক্লোন করুন"</string> <string name="profile_label_work" msgid="3495359133038584618">"অফিস"</string> <string name="profile_label_work_2" msgid="4691533661598632135">"২য় অফিস"</string> diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml index a4d1b46b54b7..0438302d762b 100644 --- a/core/res/res/values-bs/strings.xml +++ b/core/res/res/values-bs/strings.xml @@ -202,10 +202,8 @@ <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"Radni profil više nije dostupan na ovom uređaju"</string> <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"Previše puta ste pokušali otključati uređaj"</string> <string name="device_ownership_relinquished" msgid="4080886992183195724">"Administrator je ustupio uređaj za ličnu upotrebu"</string> - <!-- no translation found for private_space_deleted_by_admin (1484365588862066939) --> - <skip /> - <!-- no translation found for private_space_deleted_by_admin_details (7007781735201818689) --> - <skip /> + <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"Privatni prostor je uklonjen"</string> + <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"Organizacija ne dozvoljava privatne prostore na ovom uređaju kojim se upravlja."</string> <string name="network_logging_notification_title" msgid="554983187553845004">"Uređajem se upravlja."</string> <string name="network_logging_notification_text" msgid="1327373071132562512">"Vaša organizacija upravlja ovim uređajem i može pratiti mrežni saobraćaj. Dodirnite za detalje."</string> <string name="location_changed_notification_title" msgid="3620158742816699316">"Aplikacije mogu pristupiti vašoj lokaciji"</string> @@ -2199,7 +2197,6 @@ <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Upravljač lijevo"</string> <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Upravljač desno"</string> <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Upravljač sredina"</string> - <string name="accessibility_freeform_caption" msgid="8377519323496290122">"Traka za natpis aplikacije <xliff:g id="APP_NAME">%1$s</xliff:g>."</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"Paket <xliff:g id="PACKAGE_NAME">%1$s</xliff:g> je stavljen u odjeljak OGRANIČENO"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"je poslao/la sliku"</string> diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml index 3219c566f80e..21f397b0e093 100644 --- a/core/res/res/values-ca/strings.xml +++ b/core/res/res/values-ca/strings.xml @@ -202,10 +202,8 @@ <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"El teu perfil de treball ja no està disponible en aquest dispositiu"</string> <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"Has intentat introduir la contrasenya massa vegades"</string> <string name="device_ownership_relinquished" msgid="4080886992183195724">"L\'administrador ha cedit el dispositiu per a ús personal"</string> - <!-- no translation found for private_space_deleted_by_admin (1484365588862066939) --> - <skip /> - <!-- no translation found for private_space_deleted_by_admin_details (7007781735201818689) --> - <skip /> + <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"S\'ha suprimit l\'espai privat"</string> + <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"La teva organització no permet espais privats en aquest dispositiu gestionat."</string> <string name="network_logging_notification_title" msgid="554983187553845004">"El dispositiu està gestionat"</string> <string name="network_logging_notification_text" msgid="1327373071132562512">"La teva organització gestiona aquest dispositiu i és possible que supervisi el trànsit de xarxa. Toca per obtenir més informació."</string> <string name="location_changed_notification_title" msgid="3620158742816699316">"Les aplicacions poden accedir a la teva ubicació"</string> @@ -2199,7 +2197,6 @@ <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Creu direccional: esquerra"</string> <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Creu direccional: dreta"</string> <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Creu direccional: centre"</string> - <string name="accessibility_freeform_caption" msgid="8377519323496290122">"Barra de títol de l\'aplicació <xliff:g id="APP_NAME">%1$s</xliff:g>."</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> s\'ha transferit al segment RESTRINGIT"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"ha enviat una imatge"</string> diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml index 2305f6a64175..4013a034a5c3 100644 --- a/core/res/res/values-cs/strings.xml +++ b/core/res/res/values-cs/strings.xml @@ -203,10 +203,8 @@ <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"Váš pracovní profil v tomto zařízení již není k dispozici"</string> <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"Příliš mnoho pokusů o zadání hesla"</string> <string name="device_ownership_relinquished" msgid="4080886992183195724">"Administrátor zařízení uvolnil k osobnímu používání"</string> - <!-- no translation found for private_space_deleted_by_admin (1484365588862066939) --> - <skip /> - <!-- no translation found for private_space_deleted_by_admin_details (7007781735201818689) --> - <skip /> + <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"Soukromý prostor byl odstraněn"</string> + <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"Vaše organizace na tomto spravovaném zařízení soukromé prostory nepovoluje."</string> <string name="network_logging_notification_title" msgid="554983187553845004">"Zařízení je spravováno"</string> <string name="network_logging_notification_text" msgid="1327373071132562512">"Toto zařízení je spravováno vaší organizací, která může sledovat síťový provoz. Podrobnosti zobrazíte klepnutím."</string> <string name="location_changed_notification_title" msgid="3620158742816699316">"Aplikace mají přístup k vaší poloze"</string> @@ -2200,7 +2198,6 @@ <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Dpad doleva"</string> <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Dpad doprava"</string> <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Dpad střed"</string> - <string name="accessibility_freeform_caption" msgid="8377519323496290122">"Popisek aplikace <xliff:g id="APP_NAME">%1$s</xliff:g>."</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"Balíček <xliff:g id="PACKAGE_NAME">%1$s</xliff:g> byl vložen do sekce OMEZENO"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"posílá obrázek"</string> @@ -2401,9 +2398,9 @@ <string name="keyboard_layout_notification_more_than_three_selected_message" msgid="1581834181578206937">"Rozložení klávesnice je nastaveno na <xliff:g id="LAYOUT_1">%1$s</xliff:g>, <xliff:g id="LAYOUT_2">%2$s</xliff:g>, <xliff:g id="LAYOUT_3">%3$s</xliff:g>… Klepnutím jej změníte."</string> <string name="keyboard_layout_notification_multiple_selected_title" msgid="5242444914367024499">"Fyzické klávesnice byly nakonfigurovány"</string> <string name="keyboard_layout_notification_multiple_selected_message" msgid="6576533454124419202">"Klepnutím zobrazíte klávesnice"</string> - <string name="profile_label_private" msgid="6463418670715290696">"Soukromé"</string> + <string name="profile_label_private" msgid="6463418670715290696">"Soukromý"</string> <string name="profile_label_clone" msgid="769106052210954285">"Klonovat"</string> - <string name="profile_label_work" msgid="3495359133038584618">"Práce"</string> + <string name="profile_label_work" msgid="3495359133038584618">"Pracovní"</string> <string name="profile_label_work_2" msgid="4691533661598632135">"Práce 2"</string> <string name="profile_label_work_3" msgid="4834572253956798917">"Práce 3"</string> <string name="profile_label_test" msgid="9168641926186071947">"Test"</string> diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml index dd506350da28..5226d562384c 100644 --- a/core/res/res/values-da/strings.xml +++ b/core/res/res/values-da/strings.xml @@ -2196,7 +2196,6 @@ <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"D-pad, venstre"</string> <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"D-pad, højre"</string> <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"D-pad, midten"</string> - <string name="accessibility_freeform_caption" msgid="8377519323496290122">"Titellinje for <xliff:g id="APP_NAME">%1$s</xliff:g>."</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> er blevet placeret i samlingen BEGRÆNSET"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"sendte et billede"</string> diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml index af5a20da975f..949150ac18c4 100644 --- a/core/res/res/values-de/strings.xml +++ b/core/res/res/values-de/strings.xml @@ -201,10 +201,8 @@ <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"Dein Arbeitsprofil ist auf diesem Gerät nicht mehr verfügbar"</string> <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"Zu viele falsche Passworteingaben"</string> <string name="device_ownership_relinquished" msgid="4080886992183195724">"Administrator hat das Gerät zur persönlichen Nutzung abgegeben"</string> - <!-- no translation found for private_space_deleted_by_admin (1484365588862066939) --> - <skip /> - <!-- no translation found for private_space_deleted_by_admin_details (7007781735201818689) --> - <skip /> + <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"Vertrauliches Profil entfernt"</string> + <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"Deine Organisation erlaubt auf diesem verwalteten Gerät keine vertraulichen Profile."</string> <string name="network_logging_notification_title" msgid="554983187553845004">"Dies ist ein verwaltetes Gerät"</string> <string name="network_logging_notification_text" msgid="1327373071132562512">"Deine Organisation verwaltet dieses Gerät und überprüft unter Umständen den Netzwerkverkehr. Tippe hier, um weitere Informationen zu erhalten."</string> <string name="location_changed_notification_title" msgid="3620158742816699316">"Apps können auf deinen Standort zugreifen"</string> @@ -2198,7 +2196,6 @@ <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Steuerkreuz nach links"</string> <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Steuerkreuz nach rechts"</string> <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Steuerkreuz Mitte"</string> - <string name="accessibility_freeform_caption" msgid="8377519323496290122">"Untertitelleiste von <xliff:g id="APP_NAME">%1$s</xliff:g>."</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> wurde in den BESCHRÄNKT-Bucket gelegt"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"hat ein Bild gesendet"</string> diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml index 31a52c856e36..11dac6c4e16b 100644 --- a/core/res/res/values-el/strings.xml +++ b/core/res/res/values-el/strings.xml @@ -201,10 +201,8 @@ <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"Το προφίλ εργασίας σας δεν είναι πια διαθέσιμο σε αυτήν τη συσκευή"</string> <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"Πάρα πολλές προσπάθειες εισαγωγής κωδικού πρόσβασης"</string> <string name="device_ownership_relinquished" msgid="4080886992183195724">"Συσκευή από την οποία αποσύρθηκε ο διαχειριστής για προσωπική χρήση"</string> - <!-- no translation found for private_space_deleted_by_admin (1484365588862066939) --> - <skip /> - <!-- no translation found for private_space_deleted_by_admin_details (7007781735201818689) --> - <skip /> + <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"Ο ιδιωτικός χώρος καταργήθηκε"</string> + <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"Ο οργανισμός σας δεν επιτρέπει ιδιωτικούς χώρους σε αυτή τη διαχειριζόμενη συσκευή."</string> <string name="network_logging_notification_title" msgid="554983187553845004">"Η συσκευή είναι διαχειριζόμενη"</string> <string name="network_logging_notification_text" msgid="1327373071132562512">"Ο οργανισμός σας διαχειρίζεται αυτήν τη συσκευή και ενδέχεται να παρακολουθεί την επισκεψιμότητα δικτύου. Πατήστε για λεπτομέρειες."</string> <string name="location_changed_notification_title" msgid="3620158742816699316">"Οι εφαρμογές μπορούν να αποκτήσουν πρόσβαση στην τοποθεσία σας"</string> @@ -2198,14 +2196,13 @@ <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Dpad αριστερά"</string> <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Dpad δεξιά"</string> <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Dpad κέντρο"</string> - <string name="accessibility_freeform_caption" msgid="8377519323496290122">"Γραμμή υποτίτλων για την εφαρμογή <xliff:g id="APP_NAME">%1$s</xliff:g>."</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"Το πακέτο <xliff:g id="PACKAGE_NAME">%1$s</xliff:g> τοποθετήθηκε στον κάδο ΠΕΡΙΟΡΙΣΜΕΝΗΣ ΠΡΟΣΒΑΣΗΣ."</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"έστειλε μια εικόνα"</string> <string name="conversation_title_fallback_one_to_one" msgid="1980753619726908614">"Συνομιλία"</string> <string name="conversation_title_fallback_group_chat" msgid="456073374993104303">"Ομαδική συνομιλία"</string> <string name="unread_convo_overflow" msgid="920517615597353833">"<xliff:g id="MAX_UNREAD_COUNT">%1$d</xliff:g>+"</string> - <string name="resolver_personal_tab" msgid="2051260504014442073">"Προσωπικό"</string> + <string name="resolver_personal_tab" msgid="2051260504014442073">"Προσωπικός"</string> <string name="resolver_work_tab" msgid="2690019516263167035">"Εργασία"</string> <string name="resolver_personal_tab_accessibility" msgid="5739524949153091224">"Προσωπική προβολή"</string> <string name="resolver_work_tab_accessibility" msgid="4753168230363802734">"Προβολή εργασίας"</string> @@ -2399,7 +2396,7 @@ <string name="keyboard_layout_notification_more_than_three_selected_message" msgid="1581834181578206937">"Η διάταξη πληκτρολογίου ορίστηκε σε <xliff:g id="LAYOUT_1">%1$s</xliff:g>, <xliff:g id="LAYOUT_2">%2$s</xliff:g>, <xliff:g id="LAYOUT_3">%3$s</xliff:g>… Πατήστε για αλλαγή."</string> <string name="keyboard_layout_notification_multiple_selected_title" msgid="5242444914367024499">"Τα φυσικά πληκτρολόγια διαμορφώθηκαν"</string> <string name="keyboard_layout_notification_multiple_selected_message" msgid="6576533454124419202">"Πατήστε για να δείτε πληκτρολόγια"</string> - <string name="profile_label_private" msgid="6463418670715290696">"Ιδιωτικό"</string> + <string name="profile_label_private" msgid="6463418670715290696">"Ιδιωτικός"</string> <string name="profile_label_clone" msgid="769106052210954285">"Κλώνος"</string> <string name="profile_label_work" msgid="3495359133038584618">"Εργασία"</string> <string name="profile_label_work_2" msgid="4691533661598632135">"Εργασία 2"</string> diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml index a0511552f9c2..1effe7c8f7d1 100644 --- a/core/res/res/values-en-rAU/strings.xml +++ b/core/res/res/values-en-rAU/strings.xml @@ -2196,7 +2196,6 @@ <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Dpad left"</string> <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Dpad right"</string> <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Dpad centre"</string> - <string name="accessibility_freeform_caption" msgid="8377519323496290122">"Caption bar of <xliff:g id="APP_NAME">%1$s</xliff:g>."</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> has been put into the RESTRICTED bucket"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"sent an image"</string> diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml index 8f3f623dcb5d..4abc5816a744 100644 --- a/core/res/res/values-en-rCA/strings.xml +++ b/core/res/res/values-en-rCA/strings.xml @@ -2196,7 +2196,6 @@ <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Dpad Left"</string> <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Dpad Right"</string> <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Dpad Center"</string> - <string name="accessibility_freeform_caption" msgid="8377519323496290122">"Caption bar of <xliff:g id="APP_NAME">%1$s</xliff:g>."</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> has been put into the RESTRICTED bucket"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"sent an image"</string> diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml index ff3c20a25a5f..425d88ce49ea 100644 --- a/core/res/res/values-en-rGB/strings.xml +++ b/core/res/res/values-en-rGB/strings.xml @@ -2196,7 +2196,6 @@ <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Dpad left"</string> <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Dpad right"</string> <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Dpad centre"</string> - <string name="accessibility_freeform_caption" msgid="8377519323496290122">"Caption bar of <xliff:g id="APP_NAME">%1$s</xliff:g>."</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> has been put into the RESTRICTED bucket"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"sent an image"</string> diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml index daa61abfd79d..56ff14aa994c 100644 --- a/core/res/res/values-en-rIN/strings.xml +++ b/core/res/res/values-en-rIN/strings.xml @@ -2196,7 +2196,6 @@ <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Dpad left"</string> <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Dpad right"</string> <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Dpad centre"</string> - <string name="accessibility_freeform_caption" msgid="8377519323496290122">"Caption bar of <xliff:g id="APP_NAME">%1$s</xliff:g>."</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> has been put into the RESTRICTED bucket"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"sent an image"</string> diff --git a/core/res/res/values-en-rXC/strings.xml b/core/res/res/values-en-rXC/strings.xml index 98cac20dd061..e599c0355c8b 100644 --- a/core/res/res/values-en-rXC/strings.xml +++ b/core/res/res/values-en-rXC/strings.xml @@ -2196,7 +2196,6 @@ <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Dpad Left"</string> <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Dpad Right"</string> <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Dpad Center"</string> - <string name="accessibility_freeform_caption" msgid="8377519323496290122">"Caption bar of <xliff:g id="APP_NAME">%1$s</xliff:g>."</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> has been put into the RESTRICTED bucket"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"sent an image"</string> diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml index 98a2c5055805..f5859bf78846 100644 --- a/core/res/res/values-es-rUS/strings.xml +++ b/core/res/res/values-es-rUS/strings.xml @@ -202,10 +202,8 @@ <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"Tu perfil de trabajo ya no está disponible en este dispositivo"</string> <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"Demasiados intentos para ingresar la contraseña"</string> <string name="device_ownership_relinquished" msgid="4080886992183195724">"El administrador no permite hacer un uso personal del dispositivo"</string> - <!-- no translation found for private_space_deleted_by_admin (1484365588862066939) --> - <skip /> - <!-- no translation found for private_space_deleted_by_admin_details (7007781735201818689) --> - <skip /> + <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"Se quitó el espacio privado"</string> + <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"Tu organización no permite espacios privados en este dispositivo administrado."</string> <string name="network_logging_notification_title" msgid="554983187553845004">"Dispositivo administrado"</string> <string name="network_logging_notification_text" msgid="1327373071132562512">"Tu organización administra este dispositivo y es posible que controle el tráfico de red. Presiona para obtener más información."</string> <string name="location_changed_notification_title" msgid="3620158742816699316">"Las apps pueden acceder a tu ubicación"</string> @@ -2199,7 +2197,6 @@ <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Pad direccional: izquierda"</string> <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Pad direccional: derecha"</string> <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Pad direccional: centro"</string> - <string name="accessibility_freeform_caption" msgid="8377519323496290122">"Barra de subtítulos de <xliff:g id="APP_NAME">%1$s</xliff:g>."</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"Se colocó <xliff:g id="PACKAGE_NAME">%1$s</xliff:g> en el bucket RESTRICTED"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"envió una imagen"</string> diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml index 2e230422493d..d8b1f59a721d 100644 --- a/core/res/res/values-es/strings.xml +++ b/core/res/res/values-es/strings.xml @@ -202,10 +202,8 @@ <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"Tu perfil de trabajo ya no está disponible en este dispositivo"</string> <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"Has fallado demasiadas veces al introducir la contraseña"</string> <string name="device_ownership_relinquished" msgid="4080886992183195724">"El administrador no permite hacer un uso personal del dispositivo"</string> - <!-- no translation found for private_space_deleted_by_admin (1484365588862066939) --> - <skip /> - <!-- no translation found for private_space_deleted_by_admin_details (7007781735201818689) --> - <skip /> + <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"Espacio privado eliminado"</string> + <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"Tu organización no permite espacios privados en este dispositivo gestionado."</string> <string name="network_logging_notification_title" msgid="554983187553845004">"El dispositivo está administrado"</string> <string name="network_logging_notification_text" msgid="1327373071132562512">"Tu organización administra este dispositivo y puede supervisar el tráfico de red. Toca la notificación para obtener más información."</string> <string name="location_changed_notification_title" msgid="3620158742816699316">"Las aplicaciones pueden acceder a tu ubicación"</string> @@ -2199,7 +2197,6 @@ <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Cruceta: izquierda"</string> <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Cruceta: derecha"</string> <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Cruceta: centro"</string> - <string name="accessibility_freeform_caption" msgid="8377519323496290122">"Barra de subtítulos de <xliff:g id="APP_NAME">%1$s</xliff:g>."</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> se ha incluido en el grupo de restringidos"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"ha enviado una imagen"</string> diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml index 6f80461fc692..77e665aa87a9 100644 --- a/core/res/res/values-et/strings.xml +++ b/core/res/res/values-et/strings.xml @@ -2196,7 +2196,6 @@ <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Suunaklahvistiku vasaknool"</string> <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Suunaklahvistiku paremnool"</string> <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Suunaklahvistiku keskmine nupp"</string> - <string name="accessibility_freeform_caption" msgid="8377519323496290122">"Rakenduse <xliff:g id="APP_NAME">%1$s</xliff:g> pealkirjariba."</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> on lisatud salve PIIRANGUTEGA"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"saatis kujutise"</string> diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml index 2a544f3410db..9bf2a4d0380e 100644 --- a/core/res/res/values-eu/strings.xml +++ b/core/res/res/values-eu/strings.xml @@ -2196,7 +2196,6 @@ <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Norabide-kontrolagailuko ezkerreko botoia"</string> <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Norabide-kontrolagailuko eskuineko botoia"</string> <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Norabide-kontrolagailuko erdiko botoia"</string> - <string name="accessibility_freeform_caption" msgid="8377519323496290122">"<xliff:g id="APP_NAME">%1$s</xliff:g> aplikazioko azpitituluen barra."</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"Murriztuen edukiontzian ezarri da <xliff:g id="PACKAGE_NAME">%1$s</xliff:g>"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"erabiltzaileak irudi bat bidali du"</string> diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml index 96d120398a4d..308260f0019d 100644 --- a/core/res/res/values-fa/strings.xml +++ b/core/res/res/values-fa/strings.xml @@ -2196,7 +2196,6 @@ <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"پد کنترل چپ"</string> <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"پد کنترل راست"</string> <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"پد کنترل وسط"</string> - <string name="accessibility_freeform_caption" msgid="8377519323496290122">"نوار شرح <xliff:g id="APP_NAME">%1$s</xliff:g>."</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> در سطل «محدودشده» قرار گرفت"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"تصویری ارسال کرد"</string> diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml index 876094803703..db579f223781 100644 --- a/core/res/res/values-fi/strings.xml +++ b/core/res/res/values-fi/strings.xml @@ -2196,7 +2196,6 @@ <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Suuntanäppäimistö: vasen painike"</string> <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Suuntanäppäimistö: oikea painike"</string> <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Suuntanäppäimistö: keskipainike"</string> - <string name="accessibility_freeform_caption" msgid="8377519323496290122">"Tekstityspalkki: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> on nyt rajoitettujen ryhmässä"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"lähetti kuvan"</string> diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml index 4aeb86ed55b4..8888005be1d3 100644 --- a/core/res/res/values-fr-rCA/strings.xml +++ b/core/res/res/values-fr-rCA/strings.xml @@ -202,10 +202,8 @@ <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"Votre profil professionnel n\'est plus accessible sur cet appareil"</string> <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"Trop de tentatives d\'entrée du mot de passe"</string> <string name="device_ownership_relinquished" msgid="4080886992183195724">"L\'administrateur a libéré l\'appareil pour un usage personnel"</string> - <!-- no translation found for private_space_deleted_by_admin (1484365588862066939) --> - <skip /> - <!-- no translation found for private_space_deleted_by_admin_details (7007781735201818689) --> - <skip /> + <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"Espace privé retiré"</string> + <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"Votre organisation n\'autorise pas les espaces privés sur cet appareil géré."</string> <string name="network_logging_notification_title" msgid="554983187553845004">"L\'appareil est géré"</string> <string name="network_logging_notification_text" msgid="1327373071132562512">"Votre organisation gère cet appareil et peut surveiller le trafic réseau. Touchez ici pour obtenir plus d\'information."</string> <string name="location_changed_notification_title" msgid="3620158742816699316">"Les applications peuvent accéder à votre position"</string> @@ -2199,7 +2197,6 @@ <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Pavé directionnel – gauche"</string> <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Pavé directionnel – droite"</string> <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Pavé directionnel – centre"</string> - <string name="accessibility_freeform_caption" msgid="8377519323496290122">"Barre de légende de l\'application <xliff:g id="APP_NAME">%1$s</xliff:g>."</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> a été placé dans le compartiment RESTREINT"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g> :"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"a envoyé une image"</string> diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml index f3801334fa8c..4c2835d4f1cb 100644 --- a/core/res/res/values-fr/strings.xml +++ b/core/res/res/values-fr/strings.xml @@ -202,10 +202,8 @@ <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"Votre profil professionnel n\'est plus disponible sur cet appareil"</string> <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"Trop de tentatives de saisie du mot de passe"</string> <string name="device_ownership_relinquished" msgid="4080886992183195724">"L\'administrateur a mis l\'appareil à disposition pour un usage personnel"</string> - <!-- no translation found for private_space_deleted_by_admin (1484365588862066939) --> - <skip /> - <!-- no translation found for private_space_deleted_by_admin_details (7007781735201818689) --> - <skip /> + <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"Espace privé supprimé"</string> + <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"Votre organisation n\'autorise pas les espaces privés sur cet appareil géré."</string> <string name="network_logging_notification_title" msgid="554983187553845004">"L\'appareil est géré"</string> <string name="network_logging_notification_text" msgid="1327373071132562512">"Votre organisation gère cet appareil et peut surveiller le trafic réseau. Appuyez ici pour obtenir plus d\'informations."</string> <string name="location_changed_notification_title" msgid="3620158742816699316">"Des applications peuvent accéder à votre position"</string> @@ -2199,7 +2197,6 @@ <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Pavé directionnel - Gauche"</string> <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Pavé directionnel - Droite"</string> <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Pavé directionnel - Centre"</string> - <string name="accessibility_freeform_caption" msgid="8377519323496290122">"Barre de légende de l\'application <xliff:g id="APP_NAME">%1$s</xliff:g>."</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> a été placé dans le bucket RESTRICTED"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g> :"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"a envoyé une image"</string> diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml index 342d76b03589..cb8e8046db81 100644 --- a/core/res/res/values-gl/strings.xml +++ b/core/res/res/values-gl/strings.xml @@ -201,10 +201,8 @@ <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"O teu perfil de traballo xa non está dispoñible neste dispositivo"</string> <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"Demasiados intentos de introdución do contrasinal"</string> <string name="device_ownership_relinquished" msgid="4080886992183195724">"O administrador renunciou ao dispositivo para uso persoal"</string> - <!-- no translation found for private_space_deleted_by_admin (1484365588862066939) --> - <skip /> - <!-- no translation found for private_space_deleted_by_admin_details (7007781735201818689) --> - <skip /> + <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"Quitouse o espazo privado"</string> + <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"A túa organización non permite espazos privados neste dispositivo xestionado."</string> <string name="network_logging_notification_title" msgid="554983187553845004">"O dispositivo está xestionado"</string> <string name="network_logging_notification_text" msgid="1327373071132562512">"A túa organización xestiona este dispositivo e pode controlar o tráfico de rede. Toca para obter máis detalles."</string> <string name="location_changed_notification_title" msgid="3620158742816699316">"As aplicacións poden acceder á túa localización"</string> @@ -2198,7 +2196,6 @@ <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Cruceta: esquerda"</string> <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Cruceta: dereita"</string> <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Cruceta: centro"</string> - <string name="accessibility_freeform_caption" msgid="8377519323496290122">"Barra de subtítulos de <xliff:g id="APP_NAME">%1$s</xliff:g>."</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> incluíuse no grupo RESTRINXIDO"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"enviouse unha imaxe"</string> diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml index 5aca7d63be70..55032c9301af 100644 --- a/core/res/res/values-gu/strings.xml +++ b/core/res/res/values-gu/strings.xml @@ -201,10 +201,8 @@ <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"આ ઉપકરણ પર તમારી કાર્યાલયની પ્રોફાઇલ હવે ઉપલબ્ધ નથી"</string> <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"પાસવર્ડના ઘણા વધુ પ્રયત્નો"</string> <string name="device_ownership_relinquished" msgid="4080886992183195724">"વ્યવસ્થાપકે ડિવાઇસ વ્યક્તિગત ઉપયોગ માટે આપી દીધું છે"</string> - <!-- no translation found for private_space_deleted_by_admin (1484365588862066939) --> - <skip /> - <!-- no translation found for private_space_deleted_by_admin_details (7007781735201818689) --> - <skip /> + <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"ખાનગી સ્પેસ કાઢી નાખી"</string> + <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"મેનેજ કરેલા ડિવાઇસ પર, તમારી સંસ્થા દ્વારા ખાનગી સ્પેસને મંજૂરી આપવામાં આવતી નથી."</string> <string name="network_logging_notification_title" msgid="554983187553845004">"ડિવાઇસ મેનેજ થયેલ છે"</string> <string name="network_logging_notification_text" msgid="1327373071132562512">"તમારી સંસ્થા આ ઉપકરણનું સંચાલન કરે છે અને નેટવર્ક ટ્રાફિફનું નિયમન કરી શકે છે. વિગતો માટે ટૅપ કરો."</string> <string name="location_changed_notification_title" msgid="3620158742816699316">"ઍપ તમારા સ્થાનને ઍક્સેસ કરી શકે છે"</string> @@ -2198,7 +2196,6 @@ <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"ડી-પૅડ ડાબે"</string> <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"ડી-પૅડ જમણે"</string> <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"ડી-પૅડ મધ્યમાં"</string> - <string name="accessibility_freeform_caption" msgid="8377519323496290122">"<xliff:g id="APP_NAME">%1$s</xliff:g>નું કૅપ્શન બાર."</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g>ને પ્રતિબંધિત સમૂહમાં મૂકવામાં આવ્યું છે"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"છબી મોકલી"</string> diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml index 3af0279c847d..e47715ab25f2 100644 --- a/core/res/res/values-hi/strings.xml +++ b/core/res/res/values-hi/strings.xml @@ -201,10 +201,8 @@ <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"आपकी वर्क प्रोफ़ाइल अब इस डिवाइस पर उपलब्ध नहीं है"</string> <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"कई बार गलत पासवर्ड डाला गया"</string> <string name="device_ownership_relinquished" msgid="4080886992183195724">"एडमिन ने निजी इस्तेमाल के लिए डिवाइस दे दिया है"</string> - <!-- no translation found for private_space_deleted_by_admin (1484365588862066939) --> - <skip /> - <!-- no translation found for private_space_deleted_by_admin_details (7007781735201818689) --> - <skip /> + <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"प्राइवेट स्पेस हटाया गया"</string> + <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"आपका संगठन मैनेज किए जा रहे इस डिवाइस पर प्राइवेट स्पेस रखने की अनुमति नहीं देता है."</string> <string name="network_logging_notification_title" msgid="554983187553845004">"डिवाइस प्रबंधित है"</string> <string name="network_logging_notification_text" msgid="1327373071132562512">"आपका संगठन इस डिवाइस का प्रबंधन करता है और वह नेटवर्क ट्रैफ़िक की निगरानी भी कर सकता है. विवरण के लिए टैप करें."</string> <string name="location_changed_notification_title" msgid="3620158742816699316">"ऐप्लिकेशन आपकी जगह की जानकारी ऐक्सेस कर सकते हैं"</string> @@ -2198,7 +2196,6 @@ <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"डी-पैड का बाईं ओर वाला बटन"</string> <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"डी-पैड का दाईं ओर वाला बटन"</string> <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"डी-पैड का बीच वाला बटन"</string> - <string name="accessibility_freeform_caption" msgid="8377519323496290122">"<xliff:g id="APP_NAME">%1$s</xliff:g> का कैप्शन बार."</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> को प्रतिबंधित बकेट में रखा गया है"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"एक इमेज भेजी गई"</string> diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml index 57caeecb6488..0b9b662397f2 100644 --- a/core/res/res/values-hr/strings.xml +++ b/core/res/res/values-hr/strings.xml @@ -202,10 +202,8 @@ <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"Vaš poslovni profil više nije dostupan na ovom uređaju"</string> <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"Previše pokušaja unosa zaporke"</string> <string name="device_ownership_relinquished" msgid="4080886992183195724">"Administrator je ustupio uređaj za osobnu upotrebu"</string> - <!-- no translation found for private_space_deleted_by_admin (1484365588862066939) --> - <skip /> - <!-- no translation found for private_space_deleted_by_admin_details (7007781735201818689) --> - <skip /> + <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"Privatni prostor je uklonjen"</string> + <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"Vaša organizacija ne dopušta privatne prostore na ovom upravljanom uređaju."</string> <string name="network_logging_notification_title" msgid="554983187553845004">"Uređaj je upravljan"</string> <string name="network_logging_notification_text" msgid="1327373071132562512">"Vaša organizacija upravlja ovim uređajem i može nadzirati mrežni promet. Dodirnite za pojedinosti."</string> <string name="location_changed_notification_title" msgid="3620158742816699316">"Aplikacije mogu pristupiti vašoj lokaciji"</string> @@ -2199,7 +2197,6 @@ <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Lijevo na plohi za smjerove"</string> <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Desno na plohi za smjerove"</string> <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"U središtu plohe za smjerove"</string> - <string name="accessibility_freeform_caption" msgid="8377519323496290122">"Traka naslova aplikacije <xliff:g id="APP_NAME">%1$s</xliff:g>."</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"Paket <xliff:g id="PACKAGE_NAME">%1$s</xliff:g> premješten je u spremnik OGRANIČENO"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"šalje sliku"</string> diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml index 8447d798b07e..de6777e8bce7 100644 --- a/core/res/res/values-hu/strings.xml +++ b/core/res/res/values-hu/strings.xml @@ -201,10 +201,8 @@ <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"Munkaprofilja már nem hozzáférhető ezen az eszközön."</string> <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"Túl sok jelszómegadási kísérlet"</string> <string name="device_ownership_relinquished" msgid="4080886992183195724">"Az adminisztrátor átadta az eszközt személyes használatra"</string> - <!-- no translation found for private_space_deleted_by_admin (1484365588862066939) --> - <skip /> - <!-- no translation found for private_space_deleted_by_admin_details (7007781735201818689) --> - <skip /> + <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"Privát terület eltávolítva"</string> + <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"A szervezete nem engedélyez privát területeket ezen a kezelt eszközön."</string> <string name="network_logging_notification_title" msgid="554983187553845004">"Felügyelt eszköz"</string> <string name="network_logging_notification_text" msgid="1327373071132562512">"Ezt az eszközt szervezete kezeli, és lehetséges, hogy a hálózati forgalmat is figyelik. További részletekért koppintson."</string> <string name="location_changed_notification_title" msgid="3620158742816699316">"Az alkalmazások hozzáférhetnek az Ön tartózkodási helyéhez"</string> @@ -2198,7 +2196,6 @@ <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"D-pad – balra"</string> <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"D-pad – jobbra"</string> <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"D-pad – középre"</string> - <string name="accessibility_freeform_caption" msgid="8377519323496290122">"A(z) <xliff:g id="APP_NAME">%1$s</xliff:g> alkalmazás címsora."</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"A következő csomag a KORLÁTOZOTT csoportba került: <xliff:g id="PACKAGE_NAME">%1$s</xliff:g>"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"képet küldött"</string> diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml index e8827942140c..58331c7a1b3b 100644 --- a/core/res/res/values-hy/strings.xml +++ b/core/res/res/values-hy/strings.xml @@ -201,10 +201,8 @@ <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"Ձեր աշխատանքային պրոֆիլն այս սարքում այլևս հասանելի չէ"</string> <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"Գաղտնաբառը մուտքագրելու չափից շատ փորձեր են կատարվել"</string> <string name="device_ownership_relinquished" msgid="4080886992183195724">"Ադմինիստրատորը տրամադրել է սարքը անձնական օգտագործման համար"</string> - <!-- no translation found for private_space_deleted_by_admin (1484365588862066939) --> - <skip /> - <!-- no translation found for private_space_deleted_by_admin_details (7007781735201818689) --> - <skip /> + <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"Մասնավոր տարածքը հեռացվել է"</string> + <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"Ձեր կազմակերպությունն արգելում է մասնավոր տարածքներն այս կառավարվող սարքում։"</string> <string name="network_logging_notification_title" msgid="554983187553845004">"Սարքը կառավարվում է"</string> <string name="network_logging_notification_text" msgid="1327373071132562512">"Ձեր կազմակերպությունը կառավարում է այս սարքը և կարող է վերահսկել ցանցի թրաֆիկը: Հպեք՝ մանրամասները դիտելու համար:"</string> <string name="location_changed_notification_title" msgid="3620158742816699316">"Հավելվածներին հասանելի է ձեր տեղադրությունը"</string> @@ -2198,7 +2196,6 @@ <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Dpad-ի «Ձախ» կոճակ"</string> <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Dpad-ի «Աջ» կոճակ"</string> <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Dpad-ի «Կենտրոն» կոճակ"</string> - <string name="accessibility_freeform_caption" msgid="8377519323496290122">"<xliff:g id="APP_NAME">%1$s</xliff:g> հավելվածի ենթագրերի գոտին։"</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> փաթեթը գցվեց ՍԱՀՄԱՆԱՓԱԿՎԱԾ զամբյուղի մեջ"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>՝"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"օգտատերը պատկեր է ուղարկել"</string> diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml index a075749f333c..eeaf2b6514fa 100644 --- a/core/res/res/values-in/strings.xml +++ b/core/res/res/values-in/strings.xml @@ -201,10 +201,8 @@ <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"Profil kerja tidak tersedia lagi di perangkat ini"</string> <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"Terlalu banyak kesalahan sandi"</string> <string name="device_ownership_relinquished" msgid="4080886992183195724">"Admin melepaskan perangkat untuk penggunaan pribadi"</string> - <!-- no translation found for private_space_deleted_by_admin (1484365588862066939) --> - <skip /> - <!-- no translation found for private_space_deleted_by_admin_details (7007781735201818689) --> - <skip /> + <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"Ruang privasi dihapus"</string> + <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"Organisasi Anda tidak mengizinkan adanya ruang privasi di perangkat terkelola ini."</string> <string name="network_logging_notification_title" msgid="554983187553845004">"Perangkat ini ada yang mengelola"</string> <string name="network_logging_notification_text" msgid="1327373071132562512">"Organisasi mengelola perangkat ini dan mungkin memantau traffic jaringan. Ketuk untuk melihat detailnya."</string> <string name="location_changed_notification_title" msgid="3620158742816699316">"Aplikasi dapat mengakses lokasi Anda"</string> @@ -2198,7 +2196,6 @@ <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Dpad Kiri"</string> <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Dpad Kanan"</string> <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Dpad Tengah"</string> - <string name="accessibility_freeform_caption" msgid="8377519323496290122">"Kolom teks <xliff:g id="APP_NAME">%1$s</xliff:g>."</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> telah dimasukkan ke dalam bucket DIBATASI"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"mengirim gambar"</string> diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml index 8b6f7c7e3969..7e68a06cc9f8 100644 --- a/core/res/res/values-is/strings.xml +++ b/core/res/res/values-is/strings.xml @@ -2196,14 +2196,13 @@ <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Vinstrihnappur stýriflatar"</string> <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Hægrihnappur stýriflatar"</string> <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Miðjuhnappur stýriflatar"</string> - <string name="accessibility_freeform_caption" msgid="8377519323496290122">"Skjátextastika <xliff:g id="APP_NAME">%1$s</xliff:g>."</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> var sett í flokkinn TAKMARKAÐ"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"sendi mynd"</string> <string name="conversation_title_fallback_one_to_one" msgid="1980753619726908614">"Samtal"</string> <string name="conversation_title_fallback_group_chat" msgid="456073374993104303">"Hópsamtal"</string> <string name="unread_convo_overflow" msgid="920517615597353833">"<xliff:g id="MAX_UNREAD_COUNT">%1$d</xliff:g>+"</string> - <string name="resolver_personal_tab" msgid="2051260504014442073">"Persónulegt"</string> + <string name="resolver_personal_tab" msgid="2051260504014442073">"Einkasnið"</string> <string name="resolver_work_tab" msgid="2690019516263167035">"Vinna"</string> <string name="resolver_personal_tab_accessibility" msgid="5739524949153091224">"Persónulegt yfirlit"</string> <string name="resolver_work_tab_accessibility" msgid="4753168230363802734">"Vinnuyfirlit"</string> @@ -2397,7 +2396,7 @@ <string name="keyboard_layout_notification_more_than_three_selected_message" msgid="1581834181578206937">"Lyklaskipan er stillt á <xliff:g id="LAYOUT_1">%1$s</xliff:g>, <xliff:g id="LAYOUT_2">%2$s</xliff:g>, <xliff:g id="LAYOUT_3">%3$s</xliff:g>… Ýttu til að breyta."</string> <string name="keyboard_layout_notification_multiple_selected_title" msgid="5242444914367024499">"Vélbúnaðarlyklaborð eru stillt"</string> <string name="keyboard_layout_notification_multiple_selected_message" msgid="6576533454124419202">"Ýttu til að sjá lyklaborð"</string> - <string name="profile_label_private" msgid="6463418670715290696">"Lokað"</string> + <string name="profile_label_private" msgid="6463418670715290696">"Leynirými"</string> <string name="profile_label_clone" msgid="769106052210954285">"Afrit"</string> <string name="profile_label_work" msgid="3495359133038584618">"Vinna"</string> <string name="profile_label_work_2" msgid="4691533661598632135">"Vinna 2"</string> diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml index abd1c29b1a8d..037e148cd60a 100644 --- a/core/res/res/values-it/strings.xml +++ b/core/res/res/values-it/strings.xml @@ -2199,7 +2199,6 @@ <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"D-pad - Sinistra"</string> <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"D-pad - Destra"</string> <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"D-pad - Centro"</string> - <string name="accessibility_freeform_caption" msgid="8377519323496290122">"Barra del titolo di <xliff:g id="APP_NAME">%1$s</xliff:g>."</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> è stato inserito nel bucket RESTRICTED"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"ha inviato un\'immagine"</string> diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml index 0c70e911ffa0..a1e72da42cae 100644 --- a/core/res/res/values-iw/strings.xml +++ b/core/res/res/values-iw/strings.xml @@ -202,10 +202,8 @@ <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"פרופיל העבודה שלך אינו זמין עוד במכשיר הזה"</string> <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"בוצעו ניסיונות רבים מדי להזנת סיסמה"</string> <string name="device_ownership_relinquished" msgid="4080886992183195724">"מנהל המערכת ביטל את האפשרות לשימוש במכשיר לצרכים אישיים"</string> - <!-- no translation found for private_space_deleted_by_admin (1484365588862066939) --> - <skip /> - <!-- no translation found for private_space_deleted_by_admin_details (7007781735201818689) --> - <skip /> + <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"המרחב הפרטי הוסר"</string> + <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"הארגון שלך לא מאפשר שימוש במרחבים פרטיים במכשיר המנוהל הזה."</string> <string name="network_logging_notification_title" msgid="554983187553845004">"המכשיר מנוהל"</string> <string name="network_logging_notification_text" msgid="1327373071132562512">"הארגון שלך מנהל את המכשיר הזה והוא עשוי לנטר את התנועה ברשת. יש להקיש לקבלת פרטים."</string> <string name="location_changed_notification_title" msgid="3620158742816699316">"לאפליקציות יש הרשאת גישה למיקום שלך"</string> @@ -2199,7 +2197,6 @@ <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"לחצן שמאלי ב-Dpad"</string> <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"לחצן ימני ב-Dpad"</string> <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"לחצן אמצעי ב-Dpad"</string> - <string name="accessibility_freeform_caption" msgid="8377519323496290122">"סרגל כיתוב של <xliff:g id="APP_NAME">%1$s</xliff:g>."</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> התווספה לקטגוריה \'מוגבל\'"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"נשלחה תמונה"</string> @@ -2400,7 +2397,7 @@ <string name="keyboard_layout_notification_more_than_three_selected_message" msgid="1581834181578206937">"פריסת המקלדת מוגדרת ל<xliff:g id="LAYOUT_1">%1$s</xliff:g>, <xliff:g id="LAYOUT_2">%2$s</xliff:g>, <xliff:g id="LAYOUT_3">%3$s</xliff:g>… אפשר להקיש כדי לשנות את ההגדרה הזו."</string> <string name="keyboard_layout_notification_multiple_selected_title" msgid="5242444914367024499">"הוגדרו מקלדות פיזיות"</string> <string name="keyboard_layout_notification_multiple_selected_message" msgid="6576533454124419202">"יש להקיש כדי להציג את המקלדות"</string> - <string name="profile_label_private" msgid="6463418670715290696">"פרופיל פרטי"</string> + <string name="profile_label_private" msgid="6463418670715290696">"פרטי"</string> <string name="profile_label_clone" msgid="769106052210954285">"שכפול"</string> <string name="profile_label_work" msgid="3495359133038584618">"פרופיל עבודה"</string> <string name="profile_label_work_2" msgid="4691533661598632135">"פרופיל עבודה 2"</string> diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml index 4704e380993a..04e47a782e12 100644 --- a/core/res/res/values-ja/strings.xml +++ b/core/res/res/values-ja/strings.xml @@ -201,10 +201,8 @@ <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"お使いの仕事用プロファイルはこのデバイスで使用できなくなりました"</string> <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"パスワード入力回数が上限に達しました"</string> <string name="device_ownership_relinquished" msgid="4080886992183195724">"管理者により、デバイスの個人使用が許可されました"</string> - <!-- no translation found for private_space_deleted_by_admin (1484365588862066939) --> - <skip /> - <!-- no translation found for private_space_deleted_by_admin_details (7007781735201818689) --> - <skip /> + <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"プライベート スペースの削除"</string> + <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"この管理対象デバイス上のプライベート スペースは組織で許可されていません。"</string> <string name="network_logging_notification_title" msgid="554983187553845004">"管理対象のデバイス"</string> <string name="network_logging_notification_text" msgid="1327373071132562512">"このデバイスは組織によって管理され、ネットワーク トラフィックが監視される場合があります。詳しくはタップしてください。"</string> <string name="location_changed_notification_title" msgid="3620158742816699316">"アプリに位置情報へのアクセスを許可しました"</string> @@ -2198,7 +2196,6 @@ <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"D-pad: 左"</string> <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"D-pad: 右"</string> <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"D-pad: 中央"</string> - <string name="accessibility_freeform_caption" msgid="8377519323496290122">"<xliff:g id="APP_NAME">%1$s</xliff:g> のキャプション バーです。"</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> は RESTRICTED バケットに移動しました。"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"画像を送信しました"</string> diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml index e0a507ebc35c..4f87855446d6 100644 --- a/core/res/res/values-ka/strings.xml +++ b/core/res/res/values-ka/strings.xml @@ -201,10 +201,8 @@ <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"თქვენი სამსახურის პროფილი აღარ არის ხელმისაწვდომი ამ მოწყობილობაზე"</string> <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"დაფიქსირდა პაროლის შეყვანის ზედმეტად ბევრი მცდელობა"</string> <string name="device_ownership_relinquished" msgid="4080886992183195724">"ადმინისტრატორმა გაათავისუფლა მოწყობილობა პირადი გამოყენებისთვის"</string> - <!-- no translation found for private_space_deleted_by_admin (1484365588862066939) --> - <skip /> - <!-- no translation found for private_space_deleted_by_admin_details (7007781735201818689) --> - <skip /> + <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"კერძო სივრცე ამოშლილია"</string> + <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"თქვენი ორგანიზაცია არ უშვებს ამ მართულ მოწყობილობაზე პირად სივრცეებს."</string> <string name="network_logging_notification_title" msgid="554983187553845004">"მოწყობილობა მართულია"</string> <string name="network_logging_notification_text" msgid="1327373071132562512">"ამ მოწყობილობას თქვენი ორგანიზაცია მართავს და მას ქსელის ტრაფიკის მონიტორინგი შეუძლია. შეეხეთ დამატებითი დეტალებისთვის."</string> <string name="location_changed_notification_title" msgid="3620158742816699316">"აპებს შეუძლია თქვენს მდებარეობაზე წვდომა"</string> @@ -2198,7 +2196,6 @@ <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Dpad მარცხნივ"</string> <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Dpad მარჯვნივ"</string> <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Dpad ცენტრი"</string> - <string name="accessibility_freeform_caption" msgid="8377519323496290122">"<xliff:g id="APP_NAME">%1$s</xliff:g>-ის სუბტიტრების ზოლი."</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> მოთავსდა კალათაში „შეზღუდული“"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"გაიგზავნა სურათი"</string> diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml index 04c99a2ae6ad..f36d9fca2a35 100644 --- a/core/res/res/values-kk/strings.xml +++ b/core/res/res/values-kk/strings.xml @@ -201,10 +201,8 @@ <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"Жұмыс профиліңіз осы құрылғыда енді қолжетімді емес"</string> <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"Құпия сөз көп рет қате енгізілді"</string> <string name="device_ownership_relinquished" msgid="4080886992183195724">"Әкімші құрылғыны жеке пайдалануға ұсынды."</string> - <!-- no translation found for private_space_deleted_by_admin (1484365588862066939) --> - <skip /> - <!-- no translation found for private_space_deleted_by_admin_details (7007781735201818689) --> - <skip /> + <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"Құпия кеңістік өшірілді"</string> + <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"Ұйымыңыз басқаратын құрылғыда құпия кеңістік мүмкіндіктерін пайдалануға рұқсат етілмейді."</string> <string name="network_logging_notification_title" msgid="554983187553845004">"Құрылғы басқарылады"</string> <string name="network_logging_notification_text" msgid="1327373071132562512">"Ұйымыңыз осы құрылғыны басқарады және желі трафигін бақылауы мүмкін. Мәліметтер алу үшін түртіңіз."</string> <string name="location_changed_notification_title" msgid="3620158742816699316">"Қолданбалар геодерегіңізді пайдалана алады"</string> @@ -2198,7 +2196,6 @@ <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Сол жақ Dpad түймесі"</string> <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Оң жақ Dpad түймесі"</string> <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Ортаңғы Dpad түймесі"</string> - <string name="accessibility_freeform_caption" msgid="8377519323496290122">"<xliff:g id="APP_NAME">%1$s</xliff:g> қолданбасының жазу жолағы."</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> ШЕКТЕЛГЕН себетке салынды."</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"сурет жіберілді"</string> @@ -2399,7 +2396,7 @@ <string name="keyboard_layout_notification_more_than_three_selected_message" msgid="1581834181578206937">"Пернетақта схемасы \"<xliff:g id="LAYOUT_1">%1$s</xliff:g>\", \"<xliff:g id="LAYOUT_2">%2$s</xliff:g>\", \"<xliff:g id="LAYOUT_3">%3$s</xliff:g>\" деп орнатылды… Өзгерту үшін түртіңіз."</string> <string name="keyboard_layout_notification_multiple_selected_title" msgid="5242444914367024499">"Физикалық пернетақталар конфигурацияланды"</string> <string name="keyboard_layout_notification_multiple_selected_message" msgid="6576533454124419202">"Пернетақталарды көру үшін түртіңіз."</string> - <string name="profile_label_private" msgid="6463418670715290696">"Жеке"</string> + <string name="profile_label_private" msgid="6463418670715290696">"Құпия"</string> <string name="profile_label_clone" msgid="769106052210954285">"Клон"</string> <string name="profile_label_work" msgid="3495359133038584618">"Жұмыс"</string> <string name="profile_label_work_2" msgid="4691533661598632135">"Жұмыс 2"</string> diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml index 527745c1bb90..195eb133c872 100644 --- a/core/res/res/values-km/strings.xml +++ b/core/res/res/values-km/strings.xml @@ -201,10 +201,8 @@ <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"កម្រងព័ត៌មានការងាររបស់អ្នកលែងមាននៅលើឧបករណ៍នេះទៀតហើយ"</string> <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"ការព្យាយាមបញ្ចូលពាក្យសម្ងាត់ច្រើនដងពេកហើយ"</string> <string name="device_ownership_relinquished" msgid="4080886992183195724">"អ្នកគ្រប់គ្រងបានបោះបង់ឧបករណ៍ចោលដោយសារការប្រើប្រាស់ផ្ទាល់ខ្លួន"</string> - <!-- no translation found for private_space_deleted_by_admin (1484365588862066939) --> - <skip /> - <!-- no translation found for private_space_deleted_by_admin_details (7007781735201818689) --> - <skip /> + <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"លំហឯកជនត្រូវបានដកចេញ"</string> + <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"ស្ថាប័នរបស់អ្នកមិនអនុញ្ញាតលំហឯកជននៅលើឧបករណ៍ដែលស្ថិតក្រោមការគ្រប់គ្រងនេះទេ។"</string> <string name="network_logging_notification_title" msgid="554983187553845004">"ឧបករណ៍ស្ថិតក្រោមការគ្រប់គ្រង"</string> <string name="network_logging_notification_text" msgid="1327373071132562512">"ស្ថាប័នរបស់អ្នកគ្រប់គ្រងឧបករណ៍នេះ ហើយអាចនឹងតាមដានចរាចរណ៍បណ្តាញ។ ចុចដើម្បីទទួលបានព័ត៌មានលម្អិត។"</string> <string name="location_changed_notification_title" msgid="3620158742816699316">"កម្មវិធីអាចចូលប្រើទីតាំងរបស់អ្នកបាន"</string> @@ -2198,7 +2196,6 @@ <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Dpad ឆ្វេង"</string> <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Dpad ស្ដាំ"</string> <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Dpad កណ្ដាល"</string> - <string name="accessibility_freeform_caption" msgid="8377519323496290122">"របារពណ៌នាអំពី <xliff:g id="APP_NAME">%1$s</xliff:g>។"</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> ត្រូវបានដាក់ទៅក្នុងធុងដែលបានដាក់កំហិត"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>៖"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"បានផ្ញើរូបភាព"</string> diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml index ade6f76e5647..073e44885f6c 100644 --- a/core/res/res/values-kn/strings.xml +++ b/core/res/res/values-kn/strings.xml @@ -201,10 +201,8 @@ <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"ನಿಮ್ಮ ಉದ್ಯೋಗ ಪ್ರೊಫೈಲ್ ಈ ಸಾಧನದಲ್ಲಿ ಈಗ ಲಭ್ಯವಿಲ್ಲ"</string> <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"ಹಲವಾರು ಪಾಸ್ವರ್ಡ್ ಪ್ರಯತ್ನಗಳು"</string> <string name="device_ownership_relinquished" msgid="4080886992183195724">"ವೈಯಕ್ತಿಕ ಬಳಕೆಗಾಗಿ ನಿರ್ವಾಹಕರು ತೊರೆದ ಸಾಧನ"</string> - <!-- no translation found for private_space_deleted_by_admin (1484365588862066939) --> - <skip /> - <!-- no translation found for private_space_deleted_by_admin_details (7007781735201818689) --> - <skip /> + <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"ಪ್ರೈವೆಟ್ ಸ್ಪೇಸ್ ಅನ್ನು ತೆಗೆದುಹಾಕಲಾಗಿದೆ"</string> + <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"ಈ ನಿರ್ವಹಿಸಲಾದ ಸಾಧನದಲ್ಲಿ ಪ್ರೈವೆಟ್ ಸ್ಪೇಸ್ಗಳನ್ನು ನಿಮ್ಮ ಸಂಸ್ಥೆಯು ಅನುಮತಿಸುವುದಿಲ್ಲ."</string> <string name="network_logging_notification_title" msgid="554983187553845004">"ಸಾಧನವನ್ನು ನಿರ್ವಹಿಸಲಾಗುತ್ತಿದೆ"</string> <string name="network_logging_notification_text" msgid="1327373071132562512">"ನಿಮ್ಮ ಸಂಸ್ಥೆಯು ಈ ಸಾಧನವನ್ನು ನಿರ್ವಹಿಸುತ್ತದೆ ಮತ್ತು ಅದು ನೆಟ್ವರ್ಕ್ ಟ್ರಾಫಿಕ್ ಮೇಲೆ ಗಮನವಿರಿಸಬಹುದು. ವಿವರಗಳಿಗಾಗಿ ಟ್ಯಾಪ್ ಮಾಡಿ."</string> <string name="location_changed_notification_title" msgid="3620158742816699316">"ಆ್ಯಪ್ಗಳು ನಿಮ್ಮ ಸ್ಥಳವನ್ನು ಪ್ರವೇಶಿಸಬಹುದು"</string> @@ -2198,7 +2196,6 @@ <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Dpad ನ ಎಡಭಾಗದ ಬಟನ್"</string> <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Dpad ನ ಬಲಭಾಗದ ಬಟನ್"</string> <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Dpad ನ ಮಧ್ಯದ ಬಟನ್"</string> - <string name="accessibility_freeform_caption" msgid="8377519323496290122">"<xliff:g id="APP_NAME">%1$s</xliff:g> ಆ್ಯಪ್ನ ಶೀರ್ಷಿಕೆಯ ಪಟ್ಟಿ."</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> ಅನ್ನು ನಿರ್ಬಂಧಿತ ಬಕೆಟ್ಗೆ ಹಾಕಲಾಗಿದೆ"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"ಚಿತ್ರವನ್ನು ಕಳುಹಿಸಲಾಗಿದೆ"</string> diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml index c60482f12d03..bbe8aef4bc70 100644 --- a/core/res/res/values-ko/strings.xml +++ b/core/res/res/values-ko/strings.xml @@ -2196,7 +2196,6 @@ <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"방향 패드 왼쪽"</string> <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"방향 패드 오른쪽"</string> <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"방향 패드 가운데"</string> - <string name="accessibility_freeform_caption" msgid="8377519323496290122">"<xliff:g id="APP_NAME">%1$s</xliff:g>의 자막 표시줄입니다."</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> 항목이 RESTRICTED 버킷으로 이동함"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"이미지 보냄"</string> diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml index c23e49a781f1..9686a141d528 100644 --- a/core/res/res/values-ky/strings.xml +++ b/core/res/res/values-ky/strings.xml @@ -2196,7 +2196,6 @@ <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Dpad\'дын сол баскычы"</string> <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Dpad\'дын оң баскычы"</string> <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Dpad\'дын ортоңку баскычы"</string> - <string name="accessibility_freeform_caption" msgid="8377519323496290122">"<xliff:g id="APP_NAME">%1$s</xliff:g> колдонмосунун маалымат тилкеси."</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> ЧЕКТЕЛГЕН чакага коюлган"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"сүрөт жөнөттү"</string> diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml index 12f59b7fb8a6..b6bd4b2dec51 100644 --- a/core/res/res/values-lo/strings.xml +++ b/core/res/res/values-lo/strings.xml @@ -2196,7 +2196,6 @@ <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Dpad ຊ້າຍ"</string> <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Dpad ຂວາ"</string> <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Dpad ກາງ"</string> - <string name="accessibility_freeform_caption" msgid="8377519323496290122">"ແຖບຄຳບັນຍາຍຂອງ <xliff:g id="APP_NAME">%1$s</xliff:g>."</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> ຖືກວາງໄວ້ໃນກະຕ່າ \"ຈຳກັດ\" ແລ້ວ"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"ສົ່ງຮູບແລ້ວ"</string> diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml index c4e228c34fc4..2b4f370ffc45 100644 --- a/core/res/res/values-lt/strings.xml +++ b/core/res/res/values-lt/strings.xml @@ -203,10 +203,8 @@ <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"Darbo profilis nebepasiekiamas šiame įrenginyje"</string> <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"Per daug slaptažodžio bandymų"</string> <string name="device_ownership_relinquished" msgid="4080886992183195724">"Administratorius atmetė prašymą įrenginį naudoti asmeniniais tikslais"</string> - <!-- no translation found for private_space_deleted_by_admin (1484365588862066939) --> - <skip /> - <!-- no translation found for private_space_deleted_by_admin_details (7007781735201818689) --> - <skip /> + <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"Privati erdvė pašalinta"</string> + <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"Jūsų organizacija neleidžia naudoti privačių erdvių šiame tvarkomame įrenginyje."</string> <string name="network_logging_notification_title" msgid="554983187553845004">"Įrenginys yra tvarkomas"</string> <string name="network_logging_notification_text" msgid="1327373071132562512">"Šį įrenginį tvarko organizacija ir gali stebėti tinklo srautą. Palieskite, kad gautumėte daugiau informacijos."</string> <string name="location_changed_notification_title" msgid="3620158742816699316">"Programos gali pasiekti jūsų vietovę"</string> @@ -2200,7 +2198,6 @@ <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Valdymo pultas – kairėn"</string> <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Valdymo pultas – dešinėn"</string> <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Valdymo pultas – centras"</string> - <string name="accessibility_freeform_caption" msgid="8377519323496290122">"Programos „<xliff:g id="APP_NAME">%1$s</xliff:g>“ antraštės juosta."</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"„<xliff:g id="PACKAGE_NAME">%1$s</xliff:g>“ įkeltas į grupę APRIBOTA"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"išsiuntė vaizdą"</string> diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml index c70abadc1ba7..3669f041f877 100644 --- a/core/res/res/values-lv/strings.xml +++ b/core/res/res/values-lv/strings.xml @@ -202,10 +202,8 @@ <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"Jūsu darba profils šai ierīcē vairs nav pieejams."</string> <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"Veikts pārāk daudz paroles ievadīšanas mēģinājumu."</string> <string name="device_ownership_relinquished" msgid="4080886992183195724">"Administrators atteicās no tādas ierīces pārvaldības, ko var izmantot personiskām vajadzībām"</string> - <!-- no translation found for private_space_deleted_by_admin (1484365588862066939) --> - <skip /> - <!-- no translation found for private_space_deleted_by_admin_details (7007781735201818689) --> - <skip /> + <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"Privātā telpa ir noņemta"</string> + <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"Jūsu organizācija neatļauj izmantot privātās telpas šajā pārvaldītajā ierīcē."</string> <string name="network_logging_notification_title" msgid="554983187553845004">"Ierīce tiek pārvaldīta"</string> <string name="network_logging_notification_text" msgid="1327373071132562512">"Jūsu organizācija pārvalda šo ierīci un var uzraudzīt tīkla datplūsmu. Pieskarieties, lai saņemtu detalizētu informāciju."</string> <string name="location_changed_notification_title" msgid="3620158742816699316">"Lietotne var piekļūt jūsu atrašanās vietas datiem"</string> @@ -2199,14 +2197,13 @@ <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Virzienu slēdzis — pa kreisi"</string> <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Virzienu slēdzis — pa labi"</string> <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Virzienu slēdzis — centrs"</string> - <string name="accessibility_freeform_caption" msgid="8377519323496290122">"Lietotnes <xliff:g id="APP_NAME">%1$s</xliff:g> subtitru josla."</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"Pakotne “<xliff:g id="PACKAGE_NAME">%1$s</xliff:g>” ir ievietota ierobežotā kopā."</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"nosūtīts attēls"</string> <string name="conversation_title_fallback_one_to_one" msgid="1980753619726908614">"Saruna"</string> <string name="conversation_title_fallback_group_chat" msgid="456073374993104303">"Grupas saruna"</string> <string name="unread_convo_overflow" msgid="920517615597353833">"<xliff:g id="MAX_UNREAD_COUNT">%1$d</xliff:g>+"</string> - <string name="resolver_personal_tab" msgid="2051260504014442073">"Privātais profils"</string> + <string name="resolver_personal_tab" msgid="2051260504014442073">"Personīgais"</string> <string name="resolver_work_tab" msgid="2690019516263167035">"Darba profils"</string> <string name="resolver_personal_tab_accessibility" msgid="5739524949153091224">"Personisks skats"</string> <string name="resolver_work_tab_accessibility" msgid="4753168230363802734">"Darba skats"</string> diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml index b3b2d73c3264..8902619d341d 100644 --- a/core/res/res/values-mk/strings.xml +++ b/core/res/res/values-mk/strings.xml @@ -201,10 +201,8 @@ <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"Вашиот работен профил веќе не е достапен на уредов"</string> <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"Премногу обиди за внесување лозинка"</string> <string name="device_ownership_relinquished" msgid="4080886992183195724">"Уред откажан од администраторот за лична употреба"</string> - <!-- no translation found for private_space_deleted_by_admin (1484365588862066939) --> - <skip /> - <!-- no translation found for private_space_deleted_by_admin_details (7007781735201818689) --> - <skip /> + <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"„Приватниот простор“ е отстранет"</string> + <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"Вашата организација не дозволува „Приватен простор“ на управуваниов уред."</string> <string name="network_logging_notification_title" msgid="554983187553845004">"Некој управува со уредот"</string> <string name="network_logging_notification_text" msgid="1327373071132562512">"Вашата организација управува со уредов и можно е да го следи сообраќајот на мрежата. Допрете за детали."</string> <string name="location_changed_notification_title" msgid="3620158742816699316">"Апликациите може да пристапуваат до вашата локација"</string> @@ -2198,14 +2196,13 @@ <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Навигациско копче за налево"</string> <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Навигациско копче за надесно"</string> <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Навигациско копче за средина"</string> - <string name="accessibility_freeform_caption" msgid="8377519323496290122">"Насловна лента на <xliff:g id="APP_NAME">%1$s</xliff:g>."</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> е ставен во корпата ОГРАНИЧЕНИ"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"испрати слика"</string> <string name="conversation_title_fallback_one_to_one" msgid="1980753619726908614">"Разговор"</string> <string name="conversation_title_fallback_group_chat" msgid="456073374993104303">"Групен разговор"</string> <string name="unread_convo_overflow" msgid="920517615597353833">"<xliff:g id="MAX_UNREAD_COUNT">%1$d</xliff:g>+"</string> - <string name="resolver_personal_tab" msgid="2051260504014442073">"Лични"</string> + <string name="resolver_personal_tab" msgid="2051260504014442073">"Лично"</string> <string name="resolver_work_tab" msgid="2690019516263167035">"За работа"</string> <string name="resolver_personal_tab_accessibility" msgid="5739524949153091224">"Личен приказ"</string> <string name="resolver_work_tab_accessibility" msgid="4753168230363802734">"Работен приказ"</string> diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml index 97ce202718ac..005d7cb5da4f 100644 --- a/core/res/res/values-ml/strings.xml +++ b/core/res/res/values-ml/strings.xml @@ -201,10 +201,8 @@ <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"ഈ ഉപകരണത്തിൽ തുടർന്നങ്ങോട്ട് നിങ്ങളുടെ ഔദ്യോഗിക പ്രൊഫൈൽ ലഭ്യമല്ല"</string> <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"വളരെയധികം പാസ്വേഡ് ശ്രമങ്ങൾ"</string> <string name="device_ownership_relinquished" msgid="4080886992183195724">"വ്യക്തിപരമായ ഉപയോഗത്തിനായി, ഉപകരണത്തിന്റെ ഔദ്യോഗിക ഉപയോഗം അഡ്മിൻ അവസാനിപ്പിച്ചു"</string> - <!-- no translation found for private_space_deleted_by_admin (1484365588862066939) --> - <skip /> - <!-- no translation found for private_space_deleted_by_admin_details (7007781735201818689) --> - <skip /> + <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"സ്വകാര്യ സ്പേസ് നീക്കം ചെയ്തു"</string> + <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"മാനേജ് ചെയ്യപ്പെടുന്ന ഈ ഉപകരണത്തിൽ നിങ്ങളുടെ സ്ഥാപനം സ്വകാര്യ സ്പേസുകൾ അനുവദിക്കുന്നില്ല."</string> <string name="network_logging_notification_title" msgid="554983187553845004">"ഉപകരണം മാനേജുചെയ്യുന്നുണ്ട്"</string> <string name="network_logging_notification_text" msgid="1327373071132562512">"നിങ്ങളുടെ സ്ഥാപനമാണ് ഈ ഉപകരണം മാനേജുചെയ്യുന്നത്, നെറ്റ്വർക്ക് ട്രാഫിക്ക് നിരീക്ഷിക്കുകയും ചെയ്തേക്കാം, വിശദാംശങ്ങൾ അറിയാൻ ടാപ്പുചെയ്യുക."</string> <string name="location_changed_notification_title" msgid="3620158742816699316">"ആപ്പുകൾക്ക് നിങ്ങളുടെ ലൊക്കേഷൻ ആക്സസ് ചെയ്യാനാകും"</string> @@ -2198,7 +2196,6 @@ <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Dpad ലെഫ്റ്റ്"</string> <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Dpad റൈറ്റ്"</string> <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Dpad സെന്റർ"</string> - <string name="accessibility_freeform_caption" msgid="8377519323496290122">"<xliff:g id="APP_NAME">%1$s</xliff:g> എന്നതിന്റെ അടിക്കുറിപ്പ് ബാർ."</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> നിയന്ത്രിത ബക്കറ്റിലേക്ക് നീക്കി"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"ചിത്രം അയച്ചു"</string> diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml index 2d630acef2a7..ddbeb35cdf31 100644 --- a/core/res/res/values-mn/strings.xml +++ b/core/res/res/values-mn/strings.xml @@ -2196,7 +2196,6 @@ <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Dpad зүүн"</string> <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Dpad баруун"</string> <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Dpad гол"</string> - <string name="accessibility_freeform_caption" msgid="8377519323496290122">"<xliff:g id="APP_NAME">%1$s</xliff:g>-н гарчгийн талбар."</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g>-г ХЯЗГААРЛАСАН сагс руу орууллаа"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"зураг илгээсэн"</string> diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml index a6e3e6637977..77a58aff3157 100644 --- a/core/res/res/values-mr/strings.xml +++ b/core/res/res/values-mr/strings.xml @@ -201,10 +201,8 @@ <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"तुमचे कार्य प्रोफाइल आता या डिव्हाइसवर उपलब्ध नाही"</string> <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"बर्याचदा पासवर्ड टाकण्याचा प्रयत्न केला"</string> <string name="device_ownership_relinquished" msgid="4080886992183195724">"वैयक्तिक वापरासाठी ॲडमिनने नियंत्रण सोडलेले डिव्हाइस"</string> - <!-- no translation found for private_space_deleted_by_admin (1484365588862066939) --> - <skip /> - <!-- no translation found for private_space_deleted_by_admin_details (7007781735201818689) --> - <skip /> + <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"खाजगी स्पेस काढून टाकली आहे"</string> + <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"तुमची संस्था या व्यवस्थापित केलेल्या डिव्हाइसवर खाजगी स्पेसना अनुमती देत नाही."</string> <string name="network_logging_notification_title" msgid="554983187553845004">"डिव्हाइस व्यवस्थापित केले आहे"</string> <string name="network_logging_notification_text" msgid="1327373071132562512">"तुमची संस्था हे डिव्हाइस व्यवस्थापित करते आणि नेटवर्क रहदारीचे निरीक्षण करू शकते. तपशीलांसाठी टॅप करा."</string> <string name="location_changed_notification_title" msgid="3620158742816699316">"ॲप्स तुमचे स्थान अॅक्सेस करू शकतात"</string> @@ -2198,7 +2196,6 @@ <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Dpad डावीकडील"</string> <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Dpad चे उजवीकडील"</string> <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Dpad चे मधले"</string> - <string name="accessibility_freeform_caption" msgid="8377519323496290122">"<xliff:g id="APP_NAME">%1$s</xliff:g> चा शीर्षक बार."</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> हे प्रतिबंधित बादलीमध्ये ठेवण्यात आले आहे"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"इमेज पाठवली आहे"</string> diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml index 4e4189fd7b2b..96691f60b151 100644 --- a/core/res/res/values-ms/strings.xml +++ b/core/res/res/values-ms/strings.xml @@ -201,10 +201,8 @@ <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"Profil kerja anda tidak lagi tersedia pada peranti ini"</string> <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"Terlalu banyak percubaan kata laluan"</string> <string name="device_ownership_relinquished" msgid="4080886992183195724">"Pentadbir melepaskan peranti untuk kegunaan peribadi"</string> - <!-- no translation found for private_space_deleted_by_admin (1484365588862066939) --> - <skip /> - <!-- no translation found for private_space_deleted_by_admin_details (7007781735201818689) --> - <skip /> + <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"Ruang privasi dialih keluar"</string> + <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"Organisasi anda tidak membenarkan ruang privasi pada peranti terurus ini."</string> <string name="network_logging_notification_title" msgid="554983187553845004">"Peranti ini diurus"</string> <string name="network_logging_notification_text" msgid="1327373071132562512">"Organisasi anda mengurus peranti ini dan mungkin memantau trafik rangkaian. Ketik untuk mendapatkan butiran."</string> <string name="location_changed_notification_title" msgid="3620158742816699316">"Apl boleh mengakses lokasi anda"</string> @@ -2198,7 +2196,6 @@ <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Dpad Kiri"</string> <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Dpad Kanan"</string> <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Dpad Tengah"</string> - <string name="accessibility_freeform_caption" msgid="8377519323496290122">"Bar kapsyen <xliff:g id="APP_NAME">%1$s</xliff:g>."</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> telah diletakkan dalam baldi TERHAD"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"menghantar imej"</string> @@ -2407,7 +2404,7 @@ <string name="profile_label_test" msgid="9168641926186071947">"Ujian"</string> <string name="profile_label_communal" msgid="8743921499944800427">"Umum"</string> <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Profil kerja"</string> - <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Ruang privasi"</string> + <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Ruang persendirian"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klon"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Umum"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Kandungan pemberitahuan yang sensitif disembunyikan"</string> diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml index fa449ca59a6f..c1b782236a65 100644 --- a/core/res/res/values-my/strings.xml +++ b/core/res/res/values-my/strings.xml @@ -2196,7 +2196,6 @@ <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Dpad ဘယ်"</string> <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Dpad ညာ"</string> <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Dpad အလယ်"</string> - <string name="accessibility_freeform_caption" msgid="8377519323496290122">"<xliff:g id="APP_NAME">%1$s</xliff:g>၏ ခေါင်းစီး ဘား။"</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> ကို တားမြစ်ထားသော သိမ်းဆည်းမှုအတွင်းသို့ ထည့်ပြီးပါပြီ"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>-"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"ပုံပို့ထားသည်"</string> diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml index a07639bddaff..23f675e73f78 100644 --- a/core/res/res/values-nb/strings.xml +++ b/core/res/res/values-nb/strings.xml @@ -201,10 +201,8 @@ <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"Jobbprofilen din er ikke lenger tilgjengelig på denne enheten"</string> <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"For mange passordforsøk"</string> <string name="device_ownership_relinquished" msgid="4080886992183195724">"Administratoren overførte enheten til personlig bruk"</string> - <!-- no translation found for private_space_deleted_by_admin (1484365588862066939) --> - <skip /> - <!-- no translation found for private_space_deleted_by_admin_details (7007781735201818689) --> - <skip /> + <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"Det private området er fjernet"</string> + <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"Organisasjonen din tillater ikke private områder på denne administrerte enheten."</string> <string name="network_logging_notification_title" msgid="554983187553845004">"Enheten administreres"</string> <string name="network_logging_notification_text" msgid="1327373071132562512">"Organisasjonen din kontrollerer denne enheten og kan overvåke nettverkstrafikk. Trykk for å få mer informasjon."</string> <string name="location_changed_notification_title" msgid="3620158742816699316">"Apper har tilgang til posisjonen din"</string> @@ -2198,7 +2196,6 @@ <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Venstre på styrepilene"</string> <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Høyre på styrepilene"</string> <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Midt på styrepilene"</string> - <string name="accessibility_freeform_caption" msgid="8377519323496290122">"Tekstingsfelt i <xliff:g id="APP_NAME">%1$s</xliff:g>."</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> er blitt plassert i TILGANGSBEGRENSET-toppmappen"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"har sendt et bilde"</string> diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml index 284b3886c51f..99dd0e4c2c9f 100644 --- a/core/res/res/values-ne/strings.xml +++ b/core/res/res/values-ne/strings.xml @@ -2196,7 +2196,6 @@ <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Dpad को बायाँको बटन"</string> <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Dpad को दायाँको बटन"</string> <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Dpad को बिचको बटन"</string> - <string name="accessibility_freeform_caption" msgid="8377519323496290122">"<xliff:g id="APP_NAME">%1$s</xliff:g> को क्याप्सन बार।"</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> लाई प्रतिबन्धित बाल्टीमा राखियो"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"छवि पठाइयो"</string> diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml index 0c55e6bfa77d..0b2c507b644f 100644 --- a/core/res/res/values-nl/strings.xml +++ b/core/res/res/values-nl/strings.xml @@ -2196,7 +2196,6 @@ <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"D-pad links"</string> <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"D-pad rechts"</string> <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"D-pad midden"</string> - <string name="accessibility_freeform_caption" msgid="8377519323496290122">"Ondertitelingsbalk van <xliff:g id="APP_NAME">%1$s</xliff:g>."</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> is in de bucket RESTRICTED geplaatst"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"heeft een afbeelding gestuurd"</string> diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml index d38d824cd190..f3cd70e83db1 100644 --- a/core/res/res/values-or/strings.xml +++ b/core/res/res/values-or/strings.xml @@ -201,10 +201,8 @@ <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"ଏହି ଡିଭାଇସରେ ଆପଣଙ୍କ ୱର୍କ ପ୍ରୋଫାଇଲ୍ ଆଉ ଉପଲବ୍ଧ ନାହିଁ"</string> <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"ବହୁତ ଥର ଭୁଲ ପାସ୍ୱର୍ଡ ଲେଖିଛନ୍ତି"</string> <string name="device_ownership_relinquished" msgid="4080886992183195724">"ବ୍ୟକ୍ତିଗତ ବ୍ୟବହାର ପାଇଁ ଆଡ୍ମିନ୍ ଡିଭାଇସ୍କୁ ଅଲଗା କରିଛନ୍ତି"</string> - <!-- no translation found for private_space_deleted_by_admin (1484365588862066939) --> - <skip /> - <!-- no translation found for private_space_deleted_by_admin_details (7007781735201818689) --> - <skip /> + <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"ପ୍ରାଇଭେଟ ସ୍ପେସ କାଢ଼ି ଦିଆଯାଇଛି"</string> + <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"ଆପଣଙ୍କ ସଂସ୍ଥା ଏହି ପରିଚାଳିତ ଡିଭାଇସରେ ପ୍ରାଇଭେଟ ସ୍ପେସକୁ ଅନୁମତି ଦିଏ ନାହିଁ।"</string> <string name="network_logging_notification_title" msgid="554983187553845004">"ଡିଭାଇସକୁ ପରିଚାଳନା କରାଯାଉଛି"</string> <string name="network_logging_notification_text" msgid="1327373071132562512">"ଆପଣଙ୍କ ସଂସ୍ଥା ଏହି ଡିଭାଇସକୁ ପରିଚାଳନା କରନ୍ତି ଏବଂ ନେଟୱର୍କ ଟ୍ରାଫିକ୍ ନୀରିକ୍ଷଣ କରନ୍ତି। ବିବରଣୀ ପାଇଁ ଟାପ୍ କରନ୍ତୁ।"</string> <string name="location_changed_notification_title" msgid="3620158742816699316">"ଆପଗୁଡ଼ିକ ଆପଣଙ୍କ ଲୋକେସନକୁ ଆକ୍ସେସ୍ କରିପାରିବ"</string> @@ -2198,7 +2196,6 @@ <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Dpad ବାମ"</string> <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Dpad ଡାହାଣ"</string> <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Dpad କେନ୍ଦ୍ର"</string> - <string name="accessibility_freeform_caption" msgid="8377519323496290122">"<xliff:g id="APP_NAME">%1$s</xliff:g>ର କ୍ୟାପ୍ସନ୍ ବାର୍।"</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g>କୁ ପ୍ରତିବନ୍ଧିତ ବକେଟରେ ରଖାଯାଇଛି"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"ଏକ ଛବି ପଠାଯାଇଛି"</string> diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml index 61e184609419..60d8737405da 100644 --- a/core/res/res/values-pa/strings.xml +++ b/core/res/res/values-pa/strings.xml @@ -201,10 +201,8 @@ <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"ਤੁਹਾਡਾ ਕਾਰਜ ਪ੍ਰੋਫਾਈਲ ਹੁਣ ਇਸ ਡੀਵਾਈਸ \'ਤੇ ਉਪਲਬਧ ਨਹੀਂ ਹੈ"</string> <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"ਕਈ ਵਾਰ ਗਲਤ ਪਾਸਵਰਡ ਦਾਖਲ ਕੀਤਾ ਗਿਆ"</string> <string name="device_ownership_relinquished" msgid="4080886992183195724">"ਪ੍ਰਸ਼ਾਸਕ ਨੇ ਨਿੱਜੀ ਵਰਤੋਂ ਲਈ ਡੀਵਾਈਸ ਤਿਆਗਿਆ"</string> - <!-- no translation found for private_space_deleted_by_admin (1484365588862066939) --> - <skip /> - <!-- no translation found for private_space_deleted_by_admin_details (7007781735201818689) --> - <skip /> + <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"ਪ੍ਰਾਈਵੇਟ ਸਪੇਸ ਨੂੰ ਹਟਾਇਆ ਗਿਆ"</string> + <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"ਤੁਹਾਡੀ ਸੰਸਥਾ ਇਸ ਪ੍ਰਬੰਧਿਤ ਕੀਤੇ ਡੀਵਾਈਸ \'ਤੇ ਪ੍ਰਾਈਵੇਟ ਸਪੇਸਾਂ ਦੀ ਆਗਿਆ ਨਹੀਂ ਦਿੰਦੀ।"</string> <string name="network_logging_notification_title" msgid="554983187553845004">"ਡੀਵਾਈਸ ਪ੍ਰਬੰਧਨ ਅਧੀਨ ਹੈ"</string> <string name="network_logging_notification_text" msgid="1327373071132562512">"ਤੁਹਾਡਾ ਸੰਗਠਨ ਇਸ ਡੀਵਾਈਸ ਦਾ ਪ੍ਰਬੰਧਨ ਕਰਦਾ ਹੈ ਅਤੇ ਨੈੱਟਵਰਕ ਟਰੈਫਿਕ ਦੀ ਨਿਗਰਾਨੀ ਕਰ ਸਕਦਾ ਹੈ। ਵੇਰਵਿਆਂ ਲਈ ਟੈਪ ਕਰੋ।"</string> <string name="location_changed_notification_title" msgid="3620158742816699316">"ਐਪਾਂ ਤੁਹਾਡੇ ਟਿਕਾਣੇ ਤੱਕ ਪਹੁੰਚ ਕਰ ਸਕਦੀਆਂ ਹਨ"</string> @@ -2198,7 +2196,6 @@ <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Dpad ਦਾ ਖੱਬੇ ਪਾਸੇ ਵਾਲਾ ਬਟਨ"</string> <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Dpad ਦਾ ਸੱਜੇ ਪਾਸੇ ਵਾਲਾ ਬਟਨ"</string> <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Dpad ਦਾ ਵਿਚਕਾਰਲਾ ਬਟਨ"</string> - <string name="accessibility_freeform_caption" msgid="8377519323496290122">"<xliff:g id="APP_NAME">%1$s</xliff:g> ਦੀ ਸੁਰਖੀ ਪੱਟੀ।"</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> ਨੂੰ ਪ੍ਰਤਿਬੰਧਿਤ ਖਾਨੇ ਵਿੱਚ ਪਾਇਆ ਗਿਆ ਹੈ"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"ਚਿੱਤਰ ਭੇਜਿਆ ਗਿਆ"</string> diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml index fc098743c9d0..ec5fee76d73d 100644 --- a/core/res/res/values-pl/strings.xml +++ b/core/res/res/values-pl/strings.xml @@ -203,10 +203,8 @@ <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"Twój profil służbowy nie jest już dostępny na tym urządzeniu"</string> <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"Zbyt wiele prób podania hasła"</string> <string name="device_ownership_relinquished" msgid="4080886992183195724">"Administrator odstąpił urządzenie do użytku osobistego"</string> - <!-- no translation found for private_space_deleted_by_admin (1484365588862066939) --> - <skip /> - <!-- no translation found for private_space_deleted_by_admin_details (7007781735201818689) --> - <skip /> + <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"Przestrzeń prywatna została usunięta"</string> + <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"Twoja organizacja nie zezwala na przestrzenie prywatne na tym urządzeniu zarządzanym."</string> <string name="network_logging_notification_title" msgid="554983187553845004">"Urządzenie jest zarządzane"</string> <string name="network_logging_notification_text" msgid="1327373071132562512">"Twoja organizacja zarządza tym urządzeniem i może monitorować ruch w sieci. Kliknij, by dowiedzieć się więcej."</string> <string name="location_changed_notification_title" msgid="3620158742816699316">"Aplikacje mogą mieć dostęp do Twojej lokalizacji"</string> @@ -2200,7 +2198,6 @@ <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Dpad – w lewo"</string> <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Dpad – w prawo"</string> <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Dpad – środek"</string> - <string name="accessibility_freeform_caption" msgid="8377519323496290122">"Pasek napisów w aplikacji <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"Umieszczono pakiet <xliff:g id="PACKAGE_NAME">%1$s</xliff:g> w zasobniku danych RESTRICTED"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"wysłano obraz"</string> diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml index 2f666a64812c..7067a89b2621 100644 --- a/core/res/res/values-pt-rBR/strings.xml +++ b/core/res/res/values-pt-rBR/strings.xml @@ -2197,7 +2197,6 @@ <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Botão direcional: para a esquerda"</string> <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Botão direcional: para a direita"</string> <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Botão direcional: centro"</string> - <string name="accessibility_freeform_caption" msgid="8377519323496290122">"Barra de legendas do app <xliff:g id="APP_NAME">%1$s</xliff:g>."</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> foi colocado no intervalo \"RESTRITO\""</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"enviou uma imagem"</string> diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml index 9c9fb8d0999c..6632f85b30fd 100644 --- a/core/res/res/values-pt-rPT/strings.xml +++ b/core/res/res/values-pt-rPT/strings.xml @@ -2197,7 +2197,6 @@ <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Teclado direcional: para a esquerda"</string> <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Teclado direcional: para a direita"</string> <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Teclado direcional: centrar"</string> - <string name="accessibility_freeform_caption" msgid="8377519323496290122">"Barra de legendas da app <xliff:g id="APP_NAME">%1$s</xliff:g>."</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> foi colocado no contentor RESTRITO."</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"enviou uma imagem"</string> diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml index 2f666a64812c..7067a89b2621 100644 --- a/core/res/res/values-pt/strings.xml +++ b/core/res/res/values-pt/strings.xml @@ -2197,7 +2197,6 @@ <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Botão direcional: para a esquerda"</string> <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Botão direcional: para a direita"</string> <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Botão direcional: centro"</string> - <string name="accessibility_freeform_caption" msgid="8377519323496290122">"Barra de legendas do app <xliff:g id="APP_NAME">%1$s</xliff:g>."</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> foi colocado no intervalo \"RESTRITO\""</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"enviou uma imagem"</string> diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml index 76d69d2e64d9..bf82cd8cb5b7 100644 --- a/core/res/res/values-ro/strings.xml +++ b/core/res/res/values-ro/strings.xml @@ -202,10 +202,8 @@ <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"Profilul de serviciu nu mai este disponibil pe acest dispozitiv"</string> <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"Prea multe încercări de introducere a parolei"</string> <string name="device_ownership_relinquished" msgid="4080886992183195724">"Administratorul a retras dispozitivul pentru uz personal"</string> - <!-- no translation found for private_space_deleted_by_admin (1484365588862066939) --> - <skip /> - <!-- no translation found for private_space_deleted_by_admin_details (7007781735201818689) --> - <skip /> + <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"Spațiul privat a fost eliminat"</string> + <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"Organizația ta nu permite spațiile private pe acest dispozitiv gestionat."</string> <string name="network_logging_notification_title" msgid="554983187553845004">"Dispozitivul este gestionat"</string> <string name="network_logging_notification_text" msgid="1327373071132562512">"Organizația ta gestionează acest dispozitiv și poate monitoriza traficul în rețea. Atinge pentru mai multe detalii."</string> <string name="location_changed_notification_title" msgid="3620158742816699316">"Aplicațiile îți pot accesa locația"</string> @@ -2199,7 +2197,6 @@ <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Dpad stânga"</string> <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Dpad dreapta"</string> <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Dpad centru"</string> - <string name="accessibility_freeform_caption" msgid="8377519323496290122">"Bară cu legenda pentru <xliff:g id="APP_NAME">%1$s</xliff:g>."</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> a fost adăugat la grupul RESTRICȚIONATE"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"a trimis o imagine"</string> diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml index b21a13c5b359..c1900a86487a 100644 --- a/core/res/res/values-ru/strings.xml +++ b/core/res/res/values-ru/strings.xml @@ -203,10 +203,8 @@ <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"Ваш рабочий профиль больше не доступен на этом устройстве"</string> <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"Слишком много попыток ввести пароль."</string> <string name="device_ownership_relinquished" msgid="4080886992183195724">"Администратор освободил устройство для личного использования"</string> - <!-- no translation found for private_space_deleted_by_admin (1484365588862066939) --> - <skip /> - <!-- no translation found for private_space_deleted_by_admin_details (7007781735201818689) --> - <skip /> + <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"Частное пространство удалено"</string> + <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"Ваша организация запрещает использовать частное пространство на этом управляемом устройстве."</string> <string name="network_logging_notification_title" msgid="554983187553845004">"Это управляемое устройство"</string> <string name="network_logging_notification_text" msgid="1327373071132562512">"Ваша организация управляет этим устройством и может отслеживать сетевой трафик. Подробнее…"</string> <string name="location_changed_notification_title" msgid="3620158742816699316">"У приложений есть доступ к вашим геоданным"</string> @@ -2200,14 +2198,13 @@ <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"D-pad – влево"</string> <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"D-pad – вправо"</string> <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"D-pad – по центру"</string> - <string name="accessibility_freeform_caption" msgid="8377519323496290122">"Строка субтитров в приложении \"<xliff:g id="APP_NAME">%1$s</xliff:g>\"."</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"Приложение \"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g>\" помещено в категорию с ограниченным доступом."</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"Отправлено изображение"</string> <string name="conversation_title_fallback_one_to_one" msgid="1980753619726908614">"Чат"</string> <string name="conversation_title_fallback_group_chat" msgid="456073374993104303">"Групповой чат"</string> <string name="unread_convo_overflow" msgid="920517615597353833">"<xliff:g id="MAX_UNREAD_COUNT">%1$d</xliff:g>+"</string> - <string name="resolver_personal_tab" msgid="2051260504014442073">"Личное"</string> + <string name="resolver_personal_tab" msgid="2051260504014442073">"Личный"</string> <string name="resolver_work_tab" msgid="2690019516263167035">"Рабочее"</string> <string name="resolver_personal_tab_accessibility" msgid="5739524949153091224">"Просмотр личных данных"</string> <string name="resolver_work_tab_accessibility" msgid="4753168230363802734">"Просмотр рабочих данных"</string> diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml index 11e8d0e5109c..f216ce2da9fe 100644 --- a/core/res/res/values-si/strings.xml +++ b/core/res/res/values-si/strings.xml @@ -201,10 +201,8 @@ <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"ඔබේ කාර්යාල පැතිකඩ මෙම උපාංගය මත තවදුරටත් ලබා ගැනීමට නොහැකිය"</string> <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"මුරපද උත්සාහ කිරීම් ඉතා වැඩි ගණනකි"</string> <string name="device_ownership_relinquished" msgid="4080886992183195724">"පරිපාලක පුද්ගලික භාවිතය සඳහා උපාංගය අත්හැර දමන ලදී"</string> - <!-- no translation found for private_space_deleted_by_admin (1484365588862066939) --> - <skip /> - <!-- no translation found for private_space_deleted_by_admin_details (7007781735201818689) --> - <skip /> + <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"පුද්ගලික ඉඩ ඉවත් කරන ලදි"</string> + <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"ඔබේ සංවිධානය මෙම කළමනා කෙරෙන උපාංගය මත පුද්ගලික ඉඩවලට ඉඩ නොදෙයි."</string> <string name="network_logging_notification_title" msgid="554983187553845004">"උපාංගය කළමනාකරණය කෙරේ"</string> <string name="network_logging_notification_text" msgid="1327373071132562512">"ඔබගේ ආයතනය මෙම උපාංගය කළමනාකරණය කරන අතර එය ජාල තදබදය නිරීක්ෂණය කළ හැක. විස්තර සඳහා තට්ටු කරන්න."</string> <string name="location_changed_notification_title" msgid="3620158742816699316">"යෙදුම්වලට ඔබේ ස්ථානයට ප්රවේශ විය හැකිය"</string> @@ -2198,7 +2196,6 @@ <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Dpad වම"</string> <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Dpad දකුණ"</string> <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Dpad මැද"</string> - <string name="accessibility_freeform_caption" msgid="8377519323496290122">"<xliff:g id="APP_NAME">%1$s</xliff:g> හි සිරස්තල තීරුව."</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> අවහිර කළ බාල්දියට දමා ඇත"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"රූපයක් එව්වා"</string> diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml index 39fee349b781..9c2a87537a4b 100644 --- a/core/res/res/values-sk/strings.xml +++ b/core/res/res/values-sk/strings.xml @@ -203,10 +203,8 @@ <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"Váš pracovný profil už v tomto zariadení nie je k dispozícii"</string> <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"Príliš veľa pokusov o zadanie hesla"</string> <string name="device_ownership_relinquished" msgid="4080886992183195724">"Správca uvoľnil toto zariadenie na osobné používanie"</string> - <!-- no translation found for private_space_deleted_by_admin (1484365588862066939) --> - <skip /> - <!-- no translation found for private_space_deleted_by_admin_details (7007781735201818689) --> - <skip /> + <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"Súkromný priestor bol odstránený"</string> + <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"Vaša organizácia nepovoľuje súkromné priestory v tomto spravovanom zariadení."</string> <string name="network_logging_notification_title" msgid="554983187553845004">"Zariadenie je spravované"</string> <string name="network_logging_notification_text" msgid="1327373071132562512">"Vaša organizácia spravuje toto zariadenie a môže sledovať sieťovú premávku. Klepnutím zobrazíte podrobnosti."</string> <string name="location_changed_notification_title" msgid="3620158742816699316">"Aplikácie majú prístup k vašej polohe"</string> @@ -2200,7 +2198,6 @@ <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Stlačiť tlačidlo doľava krížového ovládača"</string> <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Stlačiť tlačidlo doprava krížového ovládača"</string> <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Stlačiť stredné tlačidlo krížového ovládača"</string> - <string name="accessibility_freeform_caption" msgid="8377519323496290122">"Popis aplikácie <xliff:g id="APP_NAME">%1$s</xliff:g>."</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"Balík <xliff:g id="PACKAGE_NAME">%1$s</xliff:g> bol vložený do kontajnera OBMEDZENÉ"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"odoslal(a) obrázok"</string> diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml index ee37cb8879da..b3530e716b2f 100644 --- a/core/res/res/values-sl/strings.xml +++ b/core/res/res/values-sl/strings.xml @@ -2198,7 +2198,6 @@ <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Smerni gumb levo"</string> <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Smerni gumb desno"</string> <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Smerni gumb sredina"</string> - <string name="accessibility_freeform_caption" msgid="8377519323496290122">"Vrstica s podnapisi aplikacije <xliff:g id="APP_NAME">%1$s</xliff:g>."</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"Paket <xliff:g id="PACKAGE_NAME">%1$s</xliff:g> je bil dodan v segment OMEJENO"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"je poslal(-a) sliko"</string> diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml index 1d097155889a..5f9a6425e208 100644 --- a/core/res/res/values-sq/strings.xml +++ b/core/res/res/values-sq/strings.xml @@ -2198,7 +2198,6 @@ <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Majtas në bllokun e drejtimit"</string> <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Djathtas në bllokun e drejtimit"</string> <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Qendra e bllokut të drejtimit"</string> - <string name="accessibility_freeform_caption" msgid="8377519323496290122">"Shiriti i nëntitullit të <xliff:g id="APP_NAME">%1$s</xliff:g>."</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> është vendosur në grupin E KUFIZUAR"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"dërgoi një imazh"</string> @@ -2399,7 +2398,7 @@ <string name="keyboard_layout_notification_more_than_three_selected_message" msgid="1581834181578206937">"Struktura e tastierës u caktua në: <xliff:g id="LAYOUT_1">%1$s</xliff:g>, <xliff:g id="LAYOUT_2">%2$s</xliff:g>, <xliff:g id="LAYOUT_3">%3$s</xliff:g>… Trokit për ta ndryshuar."</string> <string name="keyboard_layout_notification_multiple_selected_title" msgid="5242444914367024499">"Tastierat fizike u konfiguruan"</string> <string name="keyboard_layout_notification_multiple_selected_message" msgid="6576533454124419202">"Trokit për të parë tastierat"</string> - <string name="profile_label_private" msgid="6463418670715290696">"Privat"</string> + <string name="profile_label_private" msgid="6463418670715290696">"Private"</string> <string name="profile_label_clone" msgid="769106052210954285">"Klon"</string> <string name="profile_label_work" msgid="3495359133038584618">"Puna"</string> <string name="profile_label_work_2" msgid="4691533661598632135">"Puna 2"</string> diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml index a610344f6cb0..301fe244bbf1 100644 --- a/core/res/res/values-sr/strings.xml +++ b/core/res/res/values-sr/strings.xml @@ -202,10 +202,8 @@ <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"Пословни профил више није доступан на овом уређају"</string> <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"Превише покушаја уноса лозинке"</string> <string name="device_ownership_relinquished" msgid="4080886992183195724">"Администратор је уступио уређај за личну употребу"</string> - <!-- no translation found for private_space_deleted_by_admin (1484365588862066939) --> - <skip /> - <!-- no translation found for private_space_deleted_by_admin_details (7007781735201818689) --> - <skip /> + <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"Приватан простор је уклоњен"</string> + <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"Организација не дозвољава приватне просторе на овом управљаном уређају."</string> <string name="network_logging_notification_title" msgid="554983187553845004">"Уређајем се управља"</string> <string name="network_logging_notification_text" msgid="1327373071132562512">"Организација управља овим уређајем и може да надгледа мрежни саобраћај. Додирните за детаље."</string> <string name="location_changed_notification_title" msgid="3620158742816699316">"Апликације могу да приступају вашој локацији"</string> @@ -2199,7 +2197,6 @@ <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"налево на D-pad-у"</string> <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"надесно на D-pad-у"</string> <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"центар на D-pad-у"</string> - <string name="accessibility_freeform_caption" msgid="8377519323496290122">"Трака са насловима апликације <xliff:g id="APP_NAME">%1$s</xliff:g>."</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"Пакет <xliff:g id="PACKAGE_NAME">%1$s</xliff:g> је додат у сегмент ОГРАНИЧЕНО"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"је послао/ла слику"</string> diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml index 9cc174c9ef63..b3c2063a6df7 100644 --- a/core/res/res/values-sv/strings.xml +++ b/core/res/res/values-sv/strings.xml @@ -201,10 +201,8 @@ <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"Jobbprofilen är inte längre tillgänglig på enheten"</string> <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"För många försök med lösenord"</string> <string name="device_ownership_relinquished" msgid="4080886992183195724">"Administratören tillåter inte längre privat bruk av enheten"</string> - <!-- no translation found for private_space_deleted_by_admin (1484365588862066939) --> - <skip /> - <!-- no translation found for private_space_deleted_by_admin_details (7007781735201818689) --> - <skip /> + <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"Privat område har tagits bort"</string> + <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"Din organisation tillåter inte privata områden på den här hanterade enheten."</string> <string name="network_logging_notification_title" msgid="554983187553845004">"Enheten hanteras"</string> <string name="network_logging_notification_text" msgid="1327373071132562512">"Organisationen hanterar den här enheten och kan övervaka nätverkstrafiken. Tryck om du vill veta mer."</string> <string name="location_changed_notification_title" msgid="3620158742816699316">"Appar har åtkomst till din plats"</string> @@ -2198,14 +2196,13 @@ <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Styrkors, vänster"</string> <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Styrkors, höger"</string> <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Styrkors, mitten"</string> - <string name="accessibility_freeform_caption" msgid="8377519323496290122">"Textningsfält för <xliff:g id="APP_NAME">%1$s</xliff:g>."</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> har placerats i hinken RESTRICTED"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"har skickat en bild"</string> <string name="conversation_title_fallback_one_to_one" msgid="1980753619726908614">"Konversation"</string> <string name="conversation_title_fallback_group_chat" msgid="456073374993104303">"Gruppkonversation"</string> <string name="unread_convo_overflow" msgid="920517615597353833">"<xliff:g id="MAX_UNREAD_COUNT">%1$d</xliff:g>+"</string> - <string name="resolver_personal_tab" msgid="2051260504014442073">"Privat"</string> + <string name="resolver_personal_tab" msgid="2051260504014442073">"Personlig"</string> <string name="resolver_work_tab" msgid="2690019516263167035">"Jobb"</string> <string name="resolver_personal_tab_accessibility" msgid="5739524949153091224">"Personlig vy"</string> <string name="resolver_work_tab_accessibility" msgid="4753168230363802734">"Jobbvy"</string> diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml index d1bd775e44cb..f15796e5241b 100644 --- a/core/res/res/values-sw/strings.xml +++ b/core/res/res/values-sw/strings.xml @@ -201,10 +201,8 @@ <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"Wasifu wako wa kazini haupatikani tena kwenye kifaa hiki"</string> <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"Umejaribu kuweka nenosiri mara nyingi mno"</string> <string name="device_ownership_relinquished" msgid="4080886992183195724">"Msimamizi aliacha kutumia kifaa kwa matumizi ya binafsi"</string> - <!-- no translation found for private_space_deleted_by_admin (1484365588862066939) --> - <skip /> - <!-- no translation found for private_space_deleted_by_admin_details (7007781735201818689) --> - <skip /> + <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"Sehemu ya faragha imeondolewa"</string> + <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"Shirika lako haliruhusu sehemu za faragha kwenye kifaa hiki kinachodhibitiwa."</string> <string name="network_logging_notification_title" msgid="554983187553845004">"Kifaa kinadhibitiwa"</string> <string name="network_logging_notification_text" msgid="1327373071132562512">"Shirika lako linadhibiti kifaa hiki na huenda likafuatilia shughuli kwenye mtandao. Gusa ili upate maelezo zaidi."</string> <string name="location_changed_notification_title" msgid="3620158742816699316">"Programu zinaweza kutambua mahali ulipo"</string> @@ -2198,7 +2196,6 @@ <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Kitufe cha kushoto cha Dpad"</string> <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Kitufe cha kulia cha Dpad"</string> <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Kitufe cha katikati cha Dpad"</string> - <string name="accessibility_freeform_caption" msgid="8377519323496290122">"Upau wa manukuu wa <xliff:g id="APP_NAME">%1$s</xliff:g>."</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> kimewekwa katika kikundi KILICHODHIBITIWA"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"alituma picha"</string> diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml index 947903373da9..f615965b721b 100644 --- a/core/res/res/values-ta/strings.xml +++ b/core/res/res/values-ta/strings.xml @@ -201,10 +201,8 @@ <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"இந்தச் சாதனத்தில் இனி பணிக் கணக்கு கிடைக்காது"</string> <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"கடவுச்சொல்லை அதிக முறை தவறாக முயற்சித்துவிட்டீர்கள்"</string> <string name="device_ownership_relinquished" msgid="4080886992183195724">"நிர்வாகியால் தனிப்பட்ட உபயோகத்திற்காக ஒதுக்கப்பட்ட சாதனம்"</string> - <!-- no translation found for private_space_deleted_by_admin (1484365588862066939) --> - <skip /> - <!-- no translation found for private_space_deleted_by_admin_details (7007781735201818689) --> - <skip /> + <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"ரகசிய இடம் அகற்றப்பட்டது"</string> + <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"இந்த நிர்வகிக்கப்படும் சாதனத்தில் ரகசிய இடங்களை உங்கள் நிறுவனம் அனுமதிப்பதில்லை."</string> <string name="network_logging_notification_title" msgid="554983187553845004">"சாதனம் நிர்வகிக்கப்படுகிறது"</string> <string name="network_logging_notification_text" msgid="1327373071132562512">"உங்கள் நிறுவனம் இந்தச் சாதனத்தை நிர்வகிக்கும், அத்துடன் அது நெட்வொர்க் ட்ராஃபிக்கைக் கண்காணிக்கலாம். விவரங்களுக்கு, தட்டவும்."</string> <string name="location_changed_notification_title" msgid="3620158742816699316">"ஆப்ஸ் உங்கள் இருப்பிடத்தை அணுக முடியும்"</string> @@ -2198,7 +2196,6 @@ <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"இடது திசை காட்டும் பட்டன்"</string> <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"வலது திசை காட்டும் பட்டன்"</string> <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"மையப் பகுதியைக் காட்டும் பட்டன்"</string> - <string name="accessibility_freeform_caption" msgid="8377519323496290122">"<xliff:g id="APP_NAME">%1$s</xliff:g> ஆப்ஸின் தலைப்புப் பட்டி."</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> என்பதை வரம்பிடப்பட்ட பக்கெட்திற்குள் சேர்க்கப்பட்டது"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"படம் அனுப்பப்பட்டது"</string> diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml index 05b23d5064f0..a84f6eb13d7e 100644 --- a/core/res/res/values-te/strings.xml +++ b/core/res/res/values-te/strings.xml @@ -201,10 +201,8 @@ <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"ఈ పరికరంలో మీ కార్యాలయ ప్రొఫైల్ ఇప్పుడు అందుబాటులో లేదు"</string> <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"చాలా ఎక్కువ పాస్వర్డ్ ప్రయత్నాలు చేశారు"</string> <string name="device_ownership_relinquished" msgid="4080886992183195724">"వ్యక్తిగత వినియోగం కోసం నిర్వాహకులు పరికరాన్ని తీసి వేశారు"</string> - <!-- no translation found for private_space_deleted_by_admin (1484365588862066939) --> - <skip /> - <!-- no translation found for private_space_deleted_by_admin_details (7007781735201818689) --> - <skip /> + <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"ప్రైవేట్ స్పేస్ తీసివేయబడింది"</string> + <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"మీ సంస్థ ఈ మేనేజ్ చేసే పరికరంలో ప్రైవేట్ స్పేస్లను అనుమతించదు."</string> <string name="network_logging_notification_title" msgid="554983187553845004">"పరికరం నిర్వహించబడింది"</string> <string name="network_logging_notification_text" msgid="1327373071132562512">"మీ సంస్థ ఈ పరికరాన్ని నిర్వహిస్తుంది మరియు నెట్వర్క్ ట్రాఫిక్ని పర్యవేక్షించవచ్చు. వివరాల కోసం నొక్కండి."</string> <string name="location_changed_notification_title" msgid="3620158742816699316">"యాప్లు మీ లొకేషన్ను యాక్సెస్ చేయగలవు"</string> @@ -2198,7 +2196,6 @@ <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Dpad ఎడమవైపున"</string> <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Dpad కుడివైపున"</string> <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"DPad మధ్యన"</string> - <string name="accessibility_freeform_caption" msgid="8377519323496290122">"<xliff:g id="APP_NAME">%1$s</xliff:g> క్యాప్షన్ బార్."</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> పరిమితం చేయబడిన బకెట్లో ఉంచబడింది"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"ఇమేజ్ను పంపారు"</string> diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml index aa9d77d1e66f..e34b526ba940 100644 --- a/core/res/res/values-th/strings.xml +++ b/core/res/res/values-th/strings.xml @@ -201,10 +201,8 @@ <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"โปรไฟล์งานของคุณไม่สามารถใช้ในอุปกรณ์นี้อีกต่อไป"</string> <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"พยายามป้อนรหัสผ่านหลายครั้งเกินไป"</string> <string name="device_ownership_relinquished" msgid="4080886992183195724">"ผู้ดูแลระบบปล่อยอุปกรณ์ให้คุณใช้งานส่วนตัว"</string> - <!-- no translation found for private_space_deleted_by_admin (1484365588862066939) --> - <skip /> - <!-- no translation found for private_space_deleted_by_admin_details (7007781735201818689) --> - <skip /> + <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"นำพื้นที่ส่วนตัวออกแล้ว"</string> + <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"องค์กรของคุณไม่อนุญาตให้มีพื้นที่ส่วนตัวในอุปกรณ์ที่มีการจัดการเครื่องนี้"</string> <string name="network_logging_notification_title" msgid="554983187553845004">"อุปกรณ์มีการจัดการ"</string> <string name="network_logging_notification_text" msgid="1327373071132562512">"องค์กรของคุณจัดการอุปกรณ์นี้และอาจตรวจสอบการจราจรของข้อมูลในเครือข่าย แตะเพื่อดูรายละเอียด"</string> <string name="location_changed_notification_title" msgid="3620158742816699316">"แอปจะเข้าถึงตำแหน่งของคุณได้"</string> @@ -2198,7 +2196,6 @@ <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Dpad ซ้าย"</string> <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Dpad ขวา"</string> <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Dpad กึ่งกลาง"</string> - <string name="accessibility_freeform_caption" msgid="8377519323496290122">"แถบคำบรรยาย <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"ใส่ <xliff:g id="PACKAGE_NAME">%1$s</xliff:g> ในที่เก็บข้อมูลที่ถูกจำกัดแล้ว"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"ส่งรูปภาพ"</string> diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml index 6b4be2977e79..4288460e6e97 100644 --- a/core/res/res/values-tl/strings.xml +++ b/core/res/res/values-tl/strings.xml @@ -2196,7 +2196,6 @@ <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Dpad Left"</string> <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Dpad Right"</string> <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Dpad Center"</string> - <string name="accessibility_freeform_caption" msgid="8377519323496290122">"Caption bar ng <xliff:g id="APP_NAME">%1$s</xliff:g>."</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"Inilagay ang <xliff:g id="PACKAGE_NAME">%1$s</xliff:g> sa PINAGHIHIGPITANG bucket"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"nagpadala ng larawan"</string> diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml index 96780a0de553..906ccbc2ddc7 100644 --- a/core/res/res/values-tr/strings.xml +++ b/core/res/res/values-tr/strings.xml @@ -201,10 +201,8 @@ <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"İş profiliniz arık bu cihazda kullanılamıyor"</string> <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"Çok fazla şifre denemesi yapıldı"</string> <string name="device_ownership_relinquished" msgid="4080886992183195724">"Yönetici, cihazı kişisel kullanım için serbest bıraktı"</string> - <!-- no translation found for private_space_deleted_by_admin (1484365588862066939) --> - <skip /> - <!-- no translation found for private_space_deleted_by_admin_details (7007781735201818689) --> - <skip /> + <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"Özel alan kaldırıldı"</string> + <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"Kuruluşunuz bu yönetilen cihazda özel alan kullanılmasına izin vermiyor."</string> <string name="network_logging_notification_title" msgid="554983187553845004">"Cihaz yönetiliyor"</string> <string name="network_logging_notification_text" msgid="1327373071132562512">"Kuruluşunuz bu cihazı yönetmekte olup ağ trafiğini izleyebilir. Ayrıntılar için dokunun."</string> <string name="location_changed_notification_title" msgid="3620158742816699316">"Uygulamalar konumunuza erişebilir"</string> @@ -2198,7 +2196,6 @@ <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Dpad Sol"</string> <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Dpad Sağ"</string> <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Dpad Orta"</string> - <string name="accessibility_freeform_caption" msgid="8377519323496290122">"<xliff:g id="APP_NAME">%1$s</xliff:g> uygulamasının başlık çubuğu."</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> KISITLANMIŞ gruba yerleştirildi"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"bir resim gönderildi"</string> @@ -2399,7 +2396,7 @@ <string name="keyboard_layout_notification_more_than_three_selected_message" msgid="1581834181578206937">"Klavye düzeni <xliff:g id="LAYOUT_1">%1$s</xliff:g>, <xliff:g id="LAYOUT_2">%2$s</xliff:g>, <xliff:g id="LAYOUT_3">%3$s</xliff:g> olarak ayarlandı… Değiştirmek için dokunun."</string> <string name="keyboard_layout_notification_multiple_selected_title" msgid="5242444914367024499">"Fiziksel klavyeler yapılandırıldı"</string> <string name="keyboard_layout_notification_multiple_selected_message" msgid="6576533454124419202">"Klavyeleri görüntülemek için dokunun"</string> - <string name="profile_label_private" msgid="6463418670715290696">"Gizli"</string> + <string name="profile_label_private" msgid="6463418670715290696">"Özel"</string> <string name="profile_label_clone" msgid="769106052210954285">"Klon"</string> <string name="profile_label_work" msgid="3495359133038584618">"İş"</string> <string name="profile_label_work_2" msgid="4691533661598632135">"İş 2"</string> diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml index a64c9904089a..cc9df2e09efc 100644 --- a/core/res/res/values-uk/strings.xml +++ b/core/res/res/values-uk/strings.xml @@ -203,10 +203,8 @@ <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"Робочий профіль більше не доступний на цьому пристрої"</string> <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"Забагато спроб ввести пароль"</string> <string name="device_ownership_relinquished" msgid="4080886992183195724">"Адміністратор не дозволив використовувати пристрій для особистих потреб"</string> - <!-- no translation found for private_space_deleted_by_admin (1484365588862066939) --> - <skip /> - <!-- no translation found for private_space_deleted_by_admin_details (7007781735201818689) --> - <skip /> + <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"Приватний простір видалено"</string> + <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"Ваша організація не дозволяє мати приватні простори на цьому керованому пристрої."</string> <string name="network_logging_notification_title" msgid="554983187553845004">"Пристрій контролюється"</string> <string name="network_logging_notification_text" msgid="1327373071132562512">"Адміністратор вашої організації контролює цей пристрій і відстежує мережевий трафік. Торкніться, щоб дізнатися більше."</string> <string name="location_changed_notification_title" msgid="3620158742816699316">"Додаток має доступ до геоданих"</string> @@ -2200,7 +2198,6 @@ <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Кнопка \"вліво\" панелі керування"</string> <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Кнопка \"вправо\" панелі керування"</string> <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Центральна кнопка панелі керування"</string> - <string name="accessibility_freeform_caption" msgid="8377519323496290122">"Смуга із субтитрами для додатка <xliff:g id="APP_NAME">%1$s</xliff:g>."</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"Пакет \"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g>\" додано в сегмент з обмеженнями"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"надіслано зображення"</string> diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml index 0436022cd1b3..c0be641c84c7 100644 --- a/core/res/res/values-ur/strings.xml +++ b/core/res/res/values-ur/strings.xml @@ -201,10 +201,8 @@ <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"آپ کا دفتری پروفائل اس آلہ پر مزید دستیاب نہیں ہے"</string> <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"پاس ورڈ کی بہت ساری کوششیں"</string> <string name="device_ownership_relinquished" msgid="4080886992183195724">"منتظم نے ذاتی استعمال کے لیے آلہ کو دستبردار کیا ہے"</string> - <!-- no translation found for private_space_deleted_by_admin (1484365588862066939) --> - <skip /> - <!-- no translation found for private_space_deleted_by_admin_details (7007781735201818689) --> - <skip /> + <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"پرائیویٹ اسپیس کو ہٹا دیا گیا"</string> + <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"آپ کی تنظیم اس زیر انتظام آلے پر پرائیویٹ اسپیسز کو اجازت نہیں دیتی ہے۔"</string> <string name="network_logging_notification_title" msgid="554983187553845004">"آلہ زیر انتظام ہے"</string> <string name="network_logging_notification_text" msgid="1327373071132562512">"آپ کی تنظیم اس آلے کا نظم کرتی ہے اور وہ نیٹ ورک ٹریفک کی نگرانی کر سکتی ہے۔ تفاصیل کیلئے تھپتھپائیں۔"</string> <string name="location_changed_notification_title" msgid="3620158742816699316">"ایپس آپ کے مقام تک رسائی حاصل کر سکتی ہیں"</string> @@ -2198,7 +2196,6 @@ <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Dpad بائیں کریں"</string> <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Dpad دائیں کریں"</string> <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Dpad سینٹر"</string> - <string name="accessibility_freeform_caption" msgid="8377519323496290122">"<xliff:g id="APP_NAME">%1$s</xliff:g> کی کیپشن بار۔"</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> کو پابند کردہ بکٹ میں رکھ دیا گیا ہے"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"ایک تصویر بھیجی"</string> diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml index e5a45da93e83..517176b33a02 100644 --- a/core/res/res/values-uz/strings.xml +++ b/core/res/res/values-uz/strings.xml @@ -2196,7 +2196,6 @@ <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Dpad – chapga"</string> <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Dpad – oʻngga"</string> <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Dpad – markazga"</string> - <string name="accessibility_freeform_caption" msgid="8377519323496290122">"<xliff:g id="APP_NAME">%1$s</xliff:g> taglavhalar paneli."</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> cheklangan turkumga joylandi"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"rasm yuborildi"</string> diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml index 6047ea7dee23..e6db9dd4c680 100644 --- a/core/res/res/values-vi/strings.xml +++ b/core/res/res/values-vi/strings.xml @@ -2196,7 +2196,6 @@ <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Chuyển sang trái bằng bàn phím di chuyển"</string> <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Chuyển sang phải bằng bàn phím di chuyển"</string> <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Căn giữa bằng bàn phím di chuyển"</string> - <string name="accessibility_freeform_caption" msgid="8377519323496290122">"Thanh phụ đề của <xliff:g id="APP_NAME">%1$s</xliff:g>."</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"Đã đưa <xliff:g id="PACKAGE_NAME">%1$s</xliff:g> vào bộ chứa BỊ HẠN CHẾ"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"đã gửi hình ảnh"</string> diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml index 2503bf60f51b..4b0494004dd7 100644 --- a/core/res/res/values-zh-rCN/strings.xml +++ b/core/res/res/values-zh-rCN/strings.xml @@ -201,10 +201,8 @@ <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"您的工作资料已不在此设备上"</string> <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"密码尝试次数过多"</string> <string name="device_ownership_relinquished" msgid="4080886992183195724">"管理员已将该设备开放给个人使用"</string> - <!-- no translation found for private_space_deleted_by_admin (1484365588862066939) --> - <skip /> - <!-- no translation found for private_space_deleted_by_admin_details (7007781735201818689) --> - <skip /> + <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"私密空间已移除"</string> + <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"贵组织不允许在此受管设备上使用私密空间。"</string> <string name="network_logging_notification_title" msgid="554983187553845004">"设备为受管理设备"</string> <string name="network_logging_notification_text" msgid="1327373071132562512">"贵单位会管理该设备,且可能会监控网络流量。点按即可了解详情。"</string> <string name="location_changed_notification_title" msgid="3620158742816699316">"应用可以访问您的位置信息"</string> @@ -2198,7 +2196,6 @@ <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"向左方向键"</string> <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"向右方向键"</string> <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"方向键中心"</string> - <string name="accessibility_freeform_caption" msgid="8377519323496290122">"<xliff:g id="APP_NAME">%1$s</xliff:g>的标题栏。"</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> 已被放入受限存储分区"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"发送了一张图片"</string> diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml index 71131460e656..1aa9c72c8cb9 100644 --- a/core/res/res/values-zh-rHK/strings.xml +++ b/core/res/res/values-zh-rHK/strings.xml @@ -2196,7 +2196,6 @@ <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"十字鍵向左鍵"</string> <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"十字鍵向右鍵"</string> <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"十字鍵中心鍵"</string> - <string name="accessibility_freeform_caption" msgid="8377519323496290122">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」的說明列。"</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> 已納入受限制的儲存區"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"已傳送圖片"</string> diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml index 94a5d11e7710..1c83ee6d7465 100644 --- a/core/res/res/values-zh-rTW/strings.xml +++ b/core/res/res/values-zh-rTW/strings.xml @@ -2196,7 +2196,6 @@ <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Dpad 向左移"</string> <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Dpad 向右移"</string> <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Dpad 置中"</string> - <string name="accessibility_freeform_caption" msgid="8377519323496290122">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」的說明文字列。"</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"已將「<xliff:g id="PACKAGE_NAME">%1$s</xliff:g>」移入受限制的值區"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"傳送了一張圖片"</string> diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml index bbf844e6de95..23754425047b 100644 --- a/core/res/res/values-zu/strings.xml +++ b/core/res/res/values-zu/strings.xml @@ -201,10 +201,8 @@ <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"Iphrofayela yakho yomsebenzi ayisatholakali kule divayisi"</string> <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"Imizamo yamaphasiwedi eminingi kakhulu"</string> <string name="device_ownership_relinquished" msgid="4080886992183195724">"Umphathi udedela idivayisi ngokusetshenziswa komuntu siqu"</string> - <!-- no translation found for private_space_deleted_by_admin (1484365588862066939) --> - <skip /> - <!-- no translation found for private_space_deleted_by_admin_details (7007781735201818689) --> - <skip /> + <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"Indawo engasese isusiwe"</string> + <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"Inhlangano yakho ayivumeli izindawo zangasese kule divayisi ephethwe."</string> <string name="network_logging_notification_title" msgid="554983187553845004">"Idivayisi iphethwe"</string> <string name="network_logging_notification_text" msgid="1327373071132562512">"Inhlangano yakho iphethe le divayisi futhi kungenzeka ingaqaphi ithrafikhi yenethiwekhi. Thephela imininingwane."</string> <string name="location_changed_notification_title" msgid="3620158742816699316">"Izinhlelo zokusebenza zingakwazi ukufinyelela endaweni yakho"</string> @@ -2198,7 +2196,6 @@ <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Ngakwesokunxele se-Dpad"</string> <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Ngakwesokudla se-Dpad"</string> <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Isikhungo se-Dpad"</string> - <string name="accessibility_freeform_caption" msgid="8377519323496290122">"Ibha yamazwibela we-<xliff:g id="APP_NAME">%1$s</xliff:g>."</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"I-<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> ifakwe kubhakede LOKUKHAWULELWE"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"uthumele isithombe"</string> diff --git a/core/res/res/values/arrays.xml b/core/res/res/values/arrays.xml index 97e753e2bdeb..575573cb0ffb 100644 --- a/core/res/res/values/arrays.xml +++ b/core/res/res/values/arrays.xml @@ -136,11 +136,6 @@ <item>@color/search_url_text_material_light</item> </array> - <array name="preloaded_freeform_multi_window_drawables"> - <item>@drawable/decor_maximize_button_dark</item> - <item>@drawable/decor_maximize_button_light</item> - </array> - <!-- Used in LocalePicker --> <string-array translatable="false" name="special_locale_codes"> <!-- http://b/17150708 - ensure that the list of languages says "Arabic" diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 1112e6587961..0706b32dfbb5 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -4200,13 +4200,6 @@ automatically try to pair with it when the device exits tablet mode. --> <string translatable="false" name="config_packagedKeyboardName"></string> - <!-- The device supports freeform window management. Windows have title bars and can be moved - and resized. If you set this to true, you also need to add - PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT feature to your device specification. - The duplication is necessary, because this information is used before the features are - available to the system.--> - <bool name="config_freeformWindowManagement">false</bool> - <!-- If set, this will force all windows to draw the status bar background, including the apps that have not requested doing so (via the WindowManager.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS flag). --> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index a956a43b0441..87141c790f0c 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -5991,8 +5991,6 @@ <string name="accessibility_system_action_dpad_right_label">Dpad Right</string> <!-- Label for Dpad center action [CHAR LIMIT=NONE] --> <string name="accessibility_system_action_dpad_center_label">Dpad Center</string> - <!-- Accessibility description of caption view --> - <string name="accessibility_freeform_caption">Caption bar of <xliff:g id="app_name">%1$s</xliff:g>.</string> <!-- Text to tell the user that a package has been forced by themselves in the RESTRICTED bucket. [CHAR LIMIT=NONE] --> <string name="as_app_forced_to_restricted_bucket"> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 0d2fd1ca8bf6..bb73934450c9 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -402,7 +402,6 @@ <java-symbol type="bool" name="config_supportMicNearUltrasound" /> <java-symbol type="bool" name="config_supportSpeakerNearUltrasound" /> <java-symbol type="bool" name="config_supportAudioSourceUnprocessed" /> - <java-symbol type="bool" name="config_freeformWindowManagement" /> <java-symbol type="bool" name="config_supportsBubble" /> <java-symbol type="bool" name="config_supportsMultiWindow" /> <java-symbol type="bool" name="config_supportsSplitScreenMultiWindow" /> @@ -1266,7 +1265,6 @@ <java-symbol type="array" name="networkAttributes" /> <java-symbol type="array" name="preloaded_color_state_lists" /> <java-symbol type="array" name="preloaded_drawables" /> - <java-symbol type="array" name="preloaded_freeform_multi_window_drawables" /> <java-symbol type="array" name="sim_colors" /> <java-symbol type="array" name="special_locale_codes" /> <java-symbol type="array" name="special_locale_names" /> @@ -2470,16 +2468,6 @@ <!-- From Phone --> <java-symbol type="bool" name="config_built_in_sip_phone" /> - <java-symbol type="id" name="maximize_window" /> - <java-symbol type="id" name="close_window" /> - <java-symbol type="layout" name="decor_caption" /> - <java-symbol type="drawable" name="decor_caption_title_focused" /> - <java-symbol type="drawable" name="decor_close_button_dark" /> - <java-symbol type="drawable" name="decor_close_button_light" /> - <java-symbol type="drawable" name="decor_maximize_button_dark" /> - <java-symbol type="drawable" name="decor_maximize_button_light" /> - <java-symbol type="color" name="decor_button_dark_color" /> - <java-symbol type="color" name="decor_button_light_color" /> <java-symbol type="array" name="unloggable_phone_numbers" /> <!-- From TelephonyProvider --> @@ -4470,8 +4458,6 @@ <java-symbol type="string" name="accessibility_system_action_dpad_right_label" /> <java-symbol type="string" name="accessibility_system_action_dpad_center_label" /> - <java-symbol type="string" name="accessibility_freeform_caption" /> - <!-- For Wide Color Gamut --> <java-symbol type="bool" name="config_enableWcgMode" /> diff --git a/core/tests/coretests/OWNERS b/core/tests/coretests/OWNERS index b7e008b196ff..b669e3bc4f30 100644 --- a/core/tests/coretests/OWNERS +++ b/core/tests/coretests/OWNERS @@ -3,3 +3,4 @@ include platform/frameworks/base:/services/core/java/com/android/server/am/OWNER per-file BinderTest.java = file:platform/frameworks/native:/libs/binder/OWNERS per-file ParcelTest.java = file:platform/frameworks/native:/libs/binder/OWNERS per-file SurfaceControlRegistryTests.java = file:/services/core/java/com/android/server/wm/OWNERS +per-file VintfObjectTest.java = file:platform/system/libvintf:/OWNERS diff --git a/core/tests/coretests/src/android/app/activity/ActivityManagerTest.java b/core/tests/coretests/src/android/app/activity/ActivityManagerTest.java index 89c2b3cecfef..b972882e68e6 100644 --- a/core/tests/coretests/src/android/app/activity/ActivityManagerTest.java +++ b/core/tests/coretests/src/android/app/activity/ActivityManagerTest.java @@ -128,7 +128,8 @@ public class ActivityManagerTest extends AndroidTestCase { 0x222222, // colorBackground 0x333333, // statusBarColor 0x444444, // navigationBarColor - 0, // statusBarAppearance + 0x555555, // systemBarsAppeareance + 0x666666, // topOpaqueSystemBarsAppeareance true, // ensureStatusBarContrastWhenTransparent true, // ensureNavigationBarContrastWhenTransparent RESIZE_MODE_RESIZEABLE, // resizeMode @@ -153,7 +154,8 @@ public class ActivityManagerTest extends AndroidTestCase { 0x222222, // colorBackground 0x333333, // statusBarColor 0x444444, // navigationBarColor - 0, // statusBarAppearance + 0x555555, // systemBarsAppeareance + 0x666666, // topOpaqueSystemBarsAppeareance false, // ensureStatusBarContrastWhenTransparent false, // ensureNavigationBarContrastWhenTransparent RESIZE_MODE_UNRESIZEABLE, // resizeMode @@ -169,7 +171,8 @@ public class ActivityManagerTest extends AndroidTestCase { 0x2222222, // colorBackground 0x3333332, // statusBarColor 0x4444442, // navigationBarColor - 0, // statusBarAppearance + 0x5555552, // systemBarsAppeareance + 0x6666662, // topOpaqueSystemBarsAppeareance true, // ensureStatusBarContrastWhenTransparent true, // ensureNavigationBarContrastWhenTransparent RESIZE_MODE_RESIZEABLE, // resizeMode @@ -200,7 +203,8 @@ public class ActivityManagerTest extends AndroidTestCase { 0x222222, // colorBackground 0x333333, // statusBarColor 0x444444, // navigationBarColor - 0, // statusBarAppearance + 0x555555, // systemBarsAppeareance + 0x666666, // topOpaqueSystemBarsAppeareance false, // ensureStatusBarContrastWhenTransparent false, // ensureNavigationBarContrastWhenTransparent RESIZE_MODE_UNRESIZEABLE, // resizeMode @@ -223,7 +227,8 @@ public class ActivityManagerTest extends AndroidTestCase { 0x222222, // colorBackground 0x333333, // statusBarColor 0x444444, // navigationBarColor - 0, // statusBarAppearance + 0x555555, // systemBarsAppeareance + 0x666666, // topOpaqueSystemBarsAppeareance false, // ensureStatusBarContrastWhenTransparent false, // ensureNavigationBarContrastWhenTransparent RESIZE_MODE_UNRESIZEABLE, // resizeMode @@ -256,6 +261,8 @@ public class ActivityManagerTest extends AndroidTestCase { assertEquals(td1.getStatusBarColor(), td2.getStatusBarColor()); assertEquals(td1.getNavigationBarColor(), td2.getNavigationBarColor()); assertEquals(td1.getSystemBarsAppearance(), td2.getSystemBarsAppearance()); + assertEquals(td1.getTopOpaqueSystemBarsAppearance(), + td2.getTopOpaqueSystemBarsAppearance()); assertEquals(td1.getResizeMode(), td2.getResizeMode()); assertEquals(td1.getMinWidth(), td2.getMinWidth()); assertEquals(td1.getMinHeight(), td2.getMinHeight()); diff --git a/core/tests/coretests/src/android/view/ViewRootImplTest.java b/core/tests/coretests/src/android/view/ViewRootImplTest.java index a7f817665f23..94e187aed698 100644 --- a/core/tests/coretests/src/android/view/ViewRootImplTest.java +++ b/core/tests/coretests/src/android/view/ViewRootImplTest.java @@ -1287,6 +1287,31 @@ public class ViewRootImplTest { } @Test + @RequiresFlagsEnabled(FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY) + public void votePreferredFrameRate_velocityVotedAfterOnDraw() throws Throwable { + mView = new View(sContext); + double delta = 0.1; + float pixelsPerSecond = 1000_000; + float expectedFrameRate = 120; + attachViewToWindow(mView); + sInstrumentation.waitForIdleSync(); + ViewRootImpl viewRoot = mView.getViewRootImpl(); + waitForFrameRateCategoryToSettle(mView); + + sInstrumentation.runOnMainSync(() -> { + mView.setFrameContentVelocity(pixelsPerSecond); + mView.invalidate(); + assertEquals(0, viewRoot.getPreferredFrameRate(), delta); + assertEquals(0, viewRoot.getLastPreferredFrameRate(), delta); + runAfterDraw(() -> { + assertEquals(expectedFrameRate, viewRoot.getPreferredFrameRate(), delta); + assertEquals(expectedFrameRate, viewRoot.getLastPreferredFrameRate(), delta); + }); + }); + waitForAfterDraw(); + } + + @Test public void forceInvertOffDarkThemeOff_forceDarkModeDisabled() { mSetFlagsRule.enableFlags(FLAG_FORCE_INVERT_COLOR); ShellIdentityUtils.invokeWithShellPermissions(() -> { diff --git a/data/etc/core.protolog.pb b/data/etc/core.protolog.pb Binary files differindex ddb706e93ffc..a105ba756d51 100644 --- a/data/etc/core.protolog.pb +++ b/data/etc/core.protolog.pb diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json index 80f143c63333..db68f9528151 100644 --- a/data/etc/services.core.protolog.json +++ b/data/etc/services.core.protolog.json @@ -2035,6 +2035,24 @@ "group": "WM_DEBUG_WINDOW_TRANSITIONS", "at": "com\/android\/server\/wm\/PhysicalDisplaySwitchTransitionLauncher.java" }, + "-1640401313436844534": { + "message": "Resetting frozen recents task list reason=app touch win=%s x=%d y=%d insetFrame=%s", + "level": "INFO", + "group": "WM_DEBUG_TASKS", + "at": "com\/android\/server\/wm\/RecentTasks.java" + }, + "-8803811426486764449": { + "message": "Setting frozen recents task list", + "level": "INFO", + "group": "WM_DEBUG_TASKS", + "at": "com\/android\/server\/wm\/RecentTasks.java" + }, + "4040735335719974079": { + "message": "Resetting frozen recents task list reason=timeout", + "level": "INFO", + "group": "WM_DEBUG_TASKS", + "at": "com\/android\/server\/wm\/RecentTasks.java" + }, "3308140128142966415": { "message": "remove RecentTask %s when finishing user %d", "level": "INFO", diff --git a/ktfmt_includes.txt b/ktfmt_includes.txt deleted file mode 100644 index 0ac6265ecf27..000000000000 --- a/ktfmt_includes.txt +++ /dev/null @@ -1,740 +0,0 @@ -+services/permission -+packages/SystemUI --packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt --packages/SystemUI/animation/src/com/android/systemui/animation/ViewHierarchyAnimator.kt --packages/SystemUI/checks/src/com/android/internal/systemui/lint/BroadcastSentViaContextDetector.kt --packages/SystemUI/checks/src/com/android/internal/systemui/lint/RegisterReceiverViaContextDetector.kt --packages/SystemUI/checks/src/com/android/internal/systemui/lint/SoftwareBitmapDetector.kt --packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt --packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/View.kt --packages/SystemUI/shared/src/com/android/systemui/flags/Flag.kt --packages/SystemUI/shared/src/com/android/systemui/flags/FlagListenable.kt --packages/SystemUI/shared/src/com/android/systemui/flags/FlagManager.kt --packages/SystemUI/shared/src/com/android/systemui/flags/FlagSerializer.kt --packages/SystemUI/shared/src/com/android/systemui/flags/FlagSettingsHelper.kt --packages/SystemUI/shared/src/com/android/systemui/shared/animation/UnfoldMoveFromCenterAnimator.kt --packages/SystemUI/shared/src/com/android/systemui/shared/regionsampling/RegionDarkness.kt --packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButtonPositionCalculator.kt --packages/SystemUI/shared/src/com/android/systemui/shared/system/UncaughtExceptionPreHandlerManager.kt --packages/SystemUI/shared/src/com/android/systemui/shared/system/smartspace/SmartspaceState.kt --packages/SystemUI/shared/src/com/android/systemui/unfold/system/ActivityManagerActivityTypeProvider.kt --packages/SystemUI/shared/src/com/android/systemui/unfold/system/DeviceStateManagerFoldProvider.kt --packages/SystemUI/shared/src/com/android/systemui/unfold/system/SystemUnfoldSharedModule.kt --packages/SystemUI/src-debug/com/android/systemui/flags/FlagsModule.kt --packages/SystemUI/src-release/com/android/systemui/flags/FlagsModule.kt --packages/SystemUI/src/com/android/keyguard/ActiveUnlockConfig.kt --packages/SystemUI/src/com/android/keyguard/BouncerPanelExpansionCalculator.kt --packages/SystemUI/src/com/android/keyguard/ClockEventController.kt --packages/SystemUI/src/com/android/keyguard/KeyguardBiometricLockoutLogger.kt --packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt --packages/SystemUI/src/com/android/keyguard/KeyguardUserSwitcherAnchor.kt --packages/SystemUI/src/com/android/keyguard/clock/ClockPalette.kt --packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt --packages/SystemUI/src/com/android/keyguard/mediator/ScreenOnCoordinator.kt --packages/SystemUI/src/com/android/systemui/BootCompleteCache.kt --packages/SystemUI/src/com/android/systemui/BootCompleteCacheImpl.kt --packages/SystemUI/src/com/android/systemui/CameraAvailabilityListener.kt --packages/SystemUI/src/com/android/systemui/ChooserSelector.kt --packages/SystemUI/src/com/android/systemui/DarkReceiverImpl.kt --packages/SystemUI/src/com/android/systemui/DisplayCutoutBaseView.kt --packages/SystemUI/src/com/android/systemui/DualToneHandler.kt --packages/SystemUI/src/com/android/systemui/FaceScanningOverlay.kt --packages/SystemUI/src/com/android/systemui/ScreenDecorHwcLayer.kt --packages/SystemUI/src/com/android/systemui/SystemUIAppComponentFactoryBase.kt --packages/SystemUI/src/com/android/systemui/SystemUIInitializerFactory.kt --packages/SystemUI/src/com/android/systemui/SystemUIInitializerImpl.kt --packages/SystemUI/src/com/android/systemui/assist/AssistLogger.kt --packages/SystemUI/src/com/android/systemui/assist/AssistantInvocationEvent.kt --packages/SystemUI/src/com/android/systemui/assist/AssistantSessionEvent.kt --packages/SystemUI/src/com/android/systemui/backup/BackupHelper.kt --packages/SystemUI/src/com/android/systemui/biometrics/AlternateUdfpsTouchProvider.kt --packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceIconController.kt --packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceView.kt --packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintAndFaceIconController.kt --packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintAndFaceView.kt --packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconController.kt --packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintView.kt --packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt --packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt --packages/SystemUI/src/com/android/systemui/biometrics/BiometricDisplayListener.kt --packages/SystemUI/src/com/android/systemui/biometrics/DwellRippleShader.kt --packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.kt --packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpView.kt --packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpViewController.kt --packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt --packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDrawable.kt --packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpDrawable.kt --packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmEmptyView.kt --packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmEmptyViewController.kt --packages/SystemUI/src/com/android/systemui/biometrics/UdfpsHapticsSimulator.kt --packages/SystemUI/src/com/android/systemui/biometrics/UdfpsShell.kt --packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.kt --packages/SystemUI/src/com/android/systemui/biometrics/Utils.kt --packages/SystemUI/src/com/android/systemui/biometrics/dagger/BiometricsModule.kt --packages/SystemUI/src/com/android/systemui/broadcast/ActionReceiver.kt --packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt --packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcherStartable.kt --packages/SystemUI/src/com/android/systemui/broadcast/BroadcastSender.kt --packages/SystemUI/src/com/android/systemui/broadcast/PendingRemovalStore.kt --packages/SystemUI/src/com/android/systemui/broadcast/UserBroadcastDispatcher.kt --packages/SystemUI/src/com/android/systemui/broadcast/logging/BroadcastDispatcherLogger.kt --packages/SystemUI/src/com/android/systemui/camera/CameraGestureHelper.kt --packages/SystemUI/src/com/android/systemui/charging/WiredChargingRippleController.kt --packages/SystemUI/src/com/android/systemui/controls/ControlStatus.kt --packages/SystemUI/src/com/android/systemui/controls/ControlsMetricsLogger.kt --packages/SystemUI/src/com/android/systemui/controls/ControlsMetricsLoggerImpl.kt --packages/SystemUI/src/com/android/systemui/controls/ControlsServiceInfo.kt --packages/SystemUI/src/com/android/systemui/controls/CustomIconCache.kt --packages/SystemUI/src/com/android/systemui/controls/TooltipManager.kt --packages/SystemUI/src/com/android/systemui/controls/controller/ControlInfo.kt --packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingController.kt --packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt --packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt --packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt --packages/SystemUI/src/com/android/systemui/controls/controller/ControlsFavoritePersistenceWrapper.kt --packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt --packages/SystemUI/src/com/android/systemui/controls/controller/ControlsTileResourceConfiguration.kt --packages/SystemUI/src/com/android/systemui/controls/controller/ControlsTileResourceConfigurationImpl.kt --packages/SystemUI/src/com/android/systemui/controls/controller/ServiceWrapper.kt --packages/SystemUI/src/com/android/systemui/controls/controller/StatefulControlSubscriber.kt --packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsComponent.kt --packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsFeatureEnabled.kt --packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsModule.kt --packages/SystemUI/src/com/android/systemui/controls/management/AllModel.kt --packages/SystemUI/src/com/android/systemui/controls/management/AppAdapter.kt --packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt --packages/SystemUI/src/com/android/systemui/controls/management/ControlsAnimations.kt --packages/SystemUI/src/com/android/systemui/controls/management/ControlsEditingActivity.kt --packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt --packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingController.kt --packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingControllerImpl.kt --packages/SystemUI/src/com/android/systemui/controls/management/ControlsModel.kt --packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt --packages/SystemUI/src/com/android/systemui/controls/management/ControlsRequestDialog.kt --packages/SystemUI/src/com/android/systemui/controls/management/ControlsRequestReceiver.kt --packages/SystemUI/src/com/android/systemui/controls/management/FavoritesModel.kt --packages/SystemUI/src/com/android/systemui/controls/management/ManagementPageIndicator.kt --packages/SystemUI/src/com/android/systemui/controls/management/StructureAdapter.kt --packages/SystemUI/src/com/android/systemui/controls/start/ControlsStartable.kt --packages/SystemUI/src/com/android/systemui/controls/ui/Behavior.kt --packages/SystemUI/src/com/android/systemui/controls/ui/ChallengeDialogs.kt --packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinator.kt --packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt --packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt --packages/SystemUI/src/com/android/systemui/controls/ui/ControlWithState.kt --packages/SystemUI/src/com/android/systemui/controls/ui/ControlsActivity.kt --packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt --packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt --packages/SystemUI/src/com/android/systemui/controls/ui/CornerDrawable.kt --packages/SystemUI/src/com/android/systemui/controls/ui/DetailDialog.kt --packages/SystemUI/src/com/android/systemui/controls/ui/RenderInfo.kt --packages/SystemUI/src/com/android/systemui/controls/ui/StatusBehavior.kt --packages/SystemUI/src/com/android/systemui/controls/ui/TemperatureControlBehavior.kt --packages/SystemUI/src/com/android/systemui/controls/ui/ThumbnailBehavior.kt --packages/SystemUI/src/com/android/systemui/controls/ui/ToggleBehavior.kt --packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt --packages/SystemUI/src/com/android/systemui/controls/ui/TouchBehavior.kt --packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt --packages/SystemUI/src/com/android/systemui/decor/CutoutDecorProviderFactory.kt --packages/SystemUI/src/com/android/systemui/decor/CutoutDecorProviderImpl.kt --packages/SystemUI/src/com/android/systemui/decor/DecorProvider.kt --packages/SystemUI/src/com/android/systemui/decor/DecorProviderFactory.kt --packages/SystemUI/src/com/android/systemui/decor/FaceScanningProviderFactory.kt --packages/SystemUI/src/com/android/systemui/decor/OverlayWindow.kt --packages/SystemUI/src/com/android/systemui/decor/PrivacyDotDecorProviderFactory.kt --packages/SystemUI/src/com/android/systemui/decor/RoundedCornerDecorProviderFactory.kt --packages/SystemUI/src/com/android/systemui/decor/RoundedCornerDecorProviderImpl.kt --packages/SystemUI/src/com/android/systemui/decor/RoundedCornerResDelegate.kt --packages/SystemUI/src/com/android/systemui/demomode/DemoModeAvailabilityTracker.kt --packages/SystemUI/src/com/android/systemui/doze/DozeLogger.kt --packages/SystemUI/src/com/android/systemui/doze/util/BurnInHelper.kt --packages/SystemUI/src/com/android/systemui/dreams/smartspace/DreamSmartspaceController.kt --packages/SystemUI/src/com/android/systemui/dump/DumpHandler.kt --packages/SystemUI/src/com/android/systemui/dump/DumpManager.kt --packages/SystemUI/src/com/android/systemui/dump/DumpsysTableLogger.kt --packages/SystemUI/src/com/android/systemui/dump/LogBufferEulogizer.kt --packages/SystemUI/src/com/android/systemui/dump/LogBufferFreezer.kt --packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.kt --packages/SystemUI/src/com/android/systemui/flags/Flags.kt --packages/SystemUI/src/com/android/systemui/flags/ServerFlagReader.kt --packages/SystemUI/src/com/android/systemui/flags/SystemPropertiesHelper.kt --packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt --packages/SystemUI/src/com/android/systemui/keyguard/LifecycleScreenStatusProvider.kt --packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceConfig.kt --packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceCoreStartable.kt --packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractor.kt --packages/SystemUI/src/com/android/systemui/log/LogBufferFactory.kt --packages/SystemUI/src/com/android/systemui/media/MediaProjectionCaptureTarget.kt --packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialogFactory.kt --packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt --packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogReceiver.kt --packages/SystemUI/src/com/android/systemui/media/muteawait/MediaMuteAwaitConnectionCli.kt --packages/SystemUI/src/com/android/systemui/media/muteawait/MediaMuteAwaitConnectionManager.kt --packages/SystemUI/src/com/android/systemui/media/muteawait/MediaMuteAwaitConnectionManagerFactory.kt --packages/SystemUI/src/com/android/systemui/media/muteawait/MediaMuteAwaitLogger.kt --packages/SystemUI/src/com/android/systemui/media/nearby/NearbyMediaDevicesLogger.kt --packages/SystemUI/src/com/android/systemui/media/nearby/NearbyMediaDevicesManager.kt --packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt --packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttFlags.kt --packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/ChipStateReceiver.kt --packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt --packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/ReceiverChipRippleView.kt --packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt --packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderUiEventLogger.kt --packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorResultHandler.kt --packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionRecentsViewController.kt --packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionTaskView.kt --packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/RecentTasksAdapter.kt --packages/SystemUI/src/com/android/systemui/mediaprojection/devicepolicy/ScreenCaptureDevicePolicyResolver.kt --packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanel.kt --packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt --packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgePanelParams.kt --packages/SystemUI/src/com/android/systemui/notetask/NoteTaskEventLogger.kt --packages/SystemUI/src/com/android/systemui/power/BatteryStateSnapshot.kt --packages/SystemUI/src/com/android/systemui/privacy/AppOpsPrivacyItemMonitor.kt --packages/SystemUI/src/com/android/systemui/privacy/MediaProjectionPrivacyItemMonitor.kt --packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt --packages/SystemUI/src/com/android/systemui/privacy/PrivacyChipBuilder.kt --packages/SystemUI/src/com/android/systemui/privacy/PrivacyChipEvent.kt --packages/SystemUI/src/com/android/systemui/privacy/PrivacyConfig.kt --packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialog.kt --packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogController.kt --packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogEvent.kt --packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt --packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt --packages/SystemUI/src/com/android/systemui/privacy/logging/PrivacyLogger.kt --packages/SystemUI/src/com/android/systemui/qs/AutoAddTracker.kt --packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt --packages/SystemUI/src/com/android/systemui/qs/HeaderPrivacyIconsController.kt --packages/SystemUI/src/com/android/systemui/qs/QSEvents.kt --packages/SystemUI/src/com/android/systemui/qs/QSExpansionPathInterpolator.kt --packages/SystemUI/src/com/android/systemui/qs/QSFragmentDisableFlagsLogger.kt --packages/SystemUI/src/com/android/systemui/qs/QSSquishinessController.kt --packages/SystemUI/src/com/android/systemui/qs/QSUtils.kt --packages/SystemUI/src/com/android/systemui/qs/SideLabelTileLayout.kt --packages/SystemUI/src/com/android/systemui/qs/VisibilityChangedDispatcher.kt --packages/SystemUI/src/com/android/systemui/qs/carrier/CellSignalState.kt --packages/SystemUI/src/com/android/systemui/qs/customize/CustomizeTileView.kt --packages/SystemUI/src/com/android/systemui/qs/external/CustomTileStatePersister.kt --packages/SystemUI/src/com/android/systemui/qs/external/QSExternalModule.kt --packages/SystemUI/src/com/android/systemui/qs/external/TileRequestDialog.kt --packages/SystemUI/src/com/android/systemui/qs/external/TileRequestDialogEventLogger.kt --packages/SystemUI/src/com/android/systemui/qs/external/TileServiceRequestController.kt --packages/SystemUI/src/com/android/systemui/qs/tileimpl/HeightOverrideable.kt --packages/SystemUI/src/com/android/systemui/qs/tileimpl/IgnorableChildLinearLayout.kt --packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt --packages/SystemUI/src/com/android/systemui/qs/tiles/AlarmTile.kt --packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt --packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogFactory.kt --packages/SystemUI/src/com/android/systemui/qs/user/UserSwitchDialogController.kt --packages/SystemUI/src/com/android/systemui/screenshot/ImageCaptureImpl.kt --packages/SystemUI/src/com/android/systemui/screenshot/RequestProcessor.kt --packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotPolicy.kt --packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotPolicyImpl.kt --packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotProxyService.kt --packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseDialog.kt --packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt --packages/SystemUI/src/com/android/systemui/settings/UserContentResolverProvider.kt --packages/SystemUI/src/com/android/systemui/settings/UserContextProvider.kt --packages/SystemUI/src/com/android/systemui/settings/UserFileManager.kt --packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt --packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt --packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessMirrorHandler.kt --packages/SystemUI/src/com/android/systemui/settings/brightness/MirroredBrightnessController.kt --packages/SystemUI/src/com/android/systemui/shade/CombinedShadeHeadersConstraintManager.kt --packages/SystemUI/src/com/android/systemui/shade/CombinedShadeHeadersConstraintManagerImpl.kt --packages/SystemUI/src/com/android/systemui/shade/NPVCDownEventState.kt --packages/SystemUI/src/com/android/systemui/shade/NotificationPanelUnfoldAnimationController.kt --packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt --packages/SystemUI/src/com/android/systemui/shade/PulsingGestureListener.kt --packages/SystemUI/src/com/android/systemui/shade/ShadeHeightLogger.kt --packages/SystemUI/src/com/android/systemui/shade/ShadeLogger.kt --packages/SystemUI/src/com/android/systemui/shade/ShadeWindowLogger.kt --packages/SystemUI/src/com/android/systemui/shade/transition/ScrimShadeTransitionController.kt --packages/SystemUI/src/com/android/systemui/shade/transition/ShadeTransitionController.kt --packages/SystemUI/src/com/android/systemui/smartspace/SmartspacePrecondition.kt --packages/SystemUI/src/com/android/systemui/smartspace/SmartspaceTargetFilter.kt --packages/SystemUI/src/com/android/systemui/smartspace/dagger/SmartspaceModule.kt --packages/SystemUI/src/com/android/systemui/smartspace/dagger/SmartspaceViewComponent.kt --packages/SystemUI/src/com/android/systemui/smartspace/filters/LockscreenAndDreamTargetFilter.kt --packages/SystemUI/src/com/android/systemui/smartspace/preconditions/LockscreenPrecondition.kt --packages/SystemUI/src/com/android/systemui/statusbar/AbstractLockscreenShadeTransitionController.kt --packages/SystemUI/src/com/android/systemui/statusbar/ActionClickLogger.kt --packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt --packages/SystemUI/src/com/android/systemui/statusbar/LockScreenShadeOverScroller.kt --packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeKeyguardTransitionController.kt --packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeScrimTransitionController.kt --packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt --packages/SystemUI/src/com/android/systemui/statusbar/MediaArtworkProcessor.kt --packages/SystemUI/src/com/android/systemui/statusbar/NotificationClickNotifier.kt --packages/SystemUI/src/com/android/systemui/statusbar/NotificationInteractionTracker.kt --packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt --packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt --packages/SystemUI/src/com/android/systemui/statusbar/SingleShadeLockScreenOverScroller.kt --packages/SystemUI/src/com/android/systemui/statusbar/SplitShadeLockScreenOverScroller.kt --packages/SystemUI/src/com/android/systemui/statusbar/commandline/CommandRegistry.kt --packages/SystemUI/src/com/android/systemui/statusbar/connectivity/AccessPointController.kt --packages/SystemUI/src/com/android/systemui/statusbar/connectivity/ConnectivityState.kt --packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalControllerFactory.kt --packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileState.kt --packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileStatusTrackerFactory.kt --packages/SystemUI/src/com/android/systemui/statusbar/connectivity/SignalCallback.kt --packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiState.kt --packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiStatusTrackerFactory.kt --packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializer.kt --packages/SystemUI/src/com/android/systemui/statusbar/dagger/StartCentralSurfacesModule.kt --packages/SystemUI/src/com/android/systemui/statusbar/disableflags/DisableFlagsLogger.kt --packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt --packages/SystemUI/src/com/android/systemui/statusbar/events/StatusBarEventsModule.kt --packages/SystemUI/src/com/android/systemui/statusbar/events/StatusEvent.kt --packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventChipAnimationController.kt --packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventCoordinator.kt --packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt --packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImpl.kt --packages/SystemUI/src/com/android/systemui/statusbar/gesture/GesturePointerEventDetector.kt --packages/SystemUI/src/com/android/systemui/statusbar/gesture/GenericGestureDetector.kt --packages/SystemUI/src/com/android/systemui/statusbar/gesture/SwipeUpGestureHandler.kt --packages/SystemUI/src/com/android/systemui/statusbar/gesture/SwipeUpGestureLogger.kt --packages/SystemUI/src/com/android/systemui/statusbar/gesture/TapGestureDetector.kt --packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt --packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt --packages/SystemUI/src/com/android/systemui/statusbar/notification/FeedbackIcon.kt --packages/SystemUI/src/com/android/systemui/statusbar/notification/LaunchAnimationParameters.kt --packages/SystemUI/src/com/android/systemui/statusbar/notification/NotifPipelineFlags.kt --packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClickerLogger.kt --packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManager.kt --packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.kt --packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt --packages/SystemUI/src/com/android/systemui/statusbar/notification/ViewGroupFadeHelper.kt --packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListAttachState.kt --packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifLiveDataStoreImpl.kt --packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.kt --packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipelineChoreographer.kt --packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/PipelineDumper.kt --packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/SuppressedAttachState.kt --packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/TargetSdkResolver.kt --packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescerLogger.kt --packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinator.kt --packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/DataStoreCoordinator.kt --packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/DebugModeCoordinator.kt --packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/GroupCountCoordinator.kt --packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/GutsCoordinator.kt --packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/GutsCoordinatorLogger.kt --packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt --packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorLogger.kt --packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt --packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorLogger.kt --packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RemoteInputCoordinator.kt --packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinator.kt --packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinator.kt --packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ShadeEventCoordinator.kt --packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ShadeEventCoordinatorLogger.kt --packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SmartspaceDedupingCoordinator.kt --packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinator.kt --packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt --packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/dagger/CoordinatorsModule.kt --packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/BindEventManager.kt --packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/BindEventManagerImpl.kt --packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifInflater.kt --packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustment.kt --packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProvider.kt --packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/NotifSection.kt --packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt --packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifStabilityManager.kt --packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionInconsistencyTracker.kt --packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionLogger.kt --packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifEvent.kt --packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/SelfTrackingLifetimeExtender.kt --packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/DebugModeFilterProvider.kt --packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/NotificationVisibilityProviderImpl.kt --packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/SectionHeaderVisibilityProvider.kt --packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/SectionStyleProvider.kt --packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/MediaContainerController.kt --packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeController.kt --packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilder.kt --packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilderLogger.kt --packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifGroupController.kt --packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifGutsViewListener.kt --packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifGutsViewManager.kt --packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifRowController.kt --packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifShadeEventSource.kt --packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifStackController.kt --packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifViewBarn.kt --packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifViewController.kt --packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifViewRenderer.kt --packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotificationVisibilityProvider.kt --packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/RenderExtensions.kt --packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/RenderStageManager.kt --packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/RootNodeController.kt --packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/SectionHeaderController.kt --packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDiffer.kt --packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDifferLogger.kt --packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt --packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationSectionHeadersModule.kt --packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconBuilder.kt --packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt --packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt --packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerStub.kt --packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/HeadsUpViewBinderLogger.kt --packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProvider.kt --packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptLogger.kt --packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationMemoryViewWalker.kt --packages/SystemUI/src/com/android/systemui/statusbar/notification/people/NotificationPersonExtractor.kt --packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt --packages/SystemUI/src/com/android/systemui/statusbar/notification/people/ViewPipeline.kt --packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt --packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorListView.kt --packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifBindPipelineLogger.kt --packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindStageLogger.kt --packages/SystemUI/src/com/android/systemui/statusbar/notification/row/dagger/RemoteInputViewModule.kt --packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCallTemplateViewWrapper.kt --packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt --packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaContainerView.kt --packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationPriorityBucket.kt --packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsLogger.kt --packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt --packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLogger.kt --packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt --packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateLogger.kt --packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerImpl.kt --packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaViewController.kt --packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt --packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt --packages/SystemUI/src/com/android/systemui/statusbar/phone/LSShadeTransitionLogger.kt --packages/SystemUI/src/com/android/systemui/statusbar/phone/LetterboxAppearanceCalculator.kt --packages/SystemUI/src/com/android/systemui/statusbar/phone/LetterboxBackgroundProvider.kt --packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt --packages/SystemUI/src/com/android/systemui/statusbar/phone/ScreenOffAnimationController.kt --packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt --packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHideIconsForBouncerManager.kt --packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarLaunchAnimatorController.kt --packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarLocationPublisher.kt --packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarMoveFromCenterAnimationController.kt --packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterLogger.kt --packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemBarAttributesListener.kt --packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt --packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentLogger.kt --packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/StatusBarIconBlocklist.kt --packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/StatusBarSystemEventAnimator.kt --packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallChronometer.kt --packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt --packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallFlags.kt --packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallLogger.kt --packages/SystemUI/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherContainer.kt --packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryStateNotifier.kt --packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceControlsController.kt --packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceControlsControllerImpl.kt --packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.kt --packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManagerLogger.kt --packages/SystemUI/src/com/android/systemui/statusbar/policy/InflatedSmartReplyState.kt --packages/SystemUI/src/com/android/systemui/statusbar/policy/InflatedSmartReplyViewHolder.kt --packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputQuickSettingsDisabler.kt --packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputViewController.kt --packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyStateInflater.kt --packages/SystemUI/src/com/android/systemui/statusbar/policy/VariableDateView.kt --packages/SystemUI/src/com/android/systemui/statusbar/policy/VariableDateViewController.kt --packages/SystemUI/src/com/android/systemui/statusbar/policy/WalletController.kt --packages/SystemUI/src/com/android/systemui/statusbar/policy/WalletControllerImpl.kt --packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/RemoteInput.kt --packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/SmartRepliesInflationModule.kt --packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowModule.kt --packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowStateController.kt --packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayController.kt --packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewInfo.kt --packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarRootView.kt --packages/SystemUI/src/com/android/systemui/toast/ToastDefaultAnimation.kt --packages/SystemUI/src/com/android/systemui/toast/ToastLogger.kt --packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt --packages/SystemUI/src/com/android/systemui/unfold/FoldStateLogger.kt --packages/SystemUI/src/com/android/systemui/unfold/SysUIUnfoldModule.kt --packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt --packages/SystemUI/src/com/android/systemui/unfold/UnfoldProgressProvider.kt --packages/SystemUI/src/com/android/systemui/user/UserSwitcherPopupMenu.kt --packages/SystemUI/src/com/android/systemui/user/UserSwitcherRootView.kt --packages/SystemUI/src/com/android/systemui/util/AsyncActivityLauncher.kt --packages/SystemUI/src/com/android/systemui/util/ColorUtil.kt --packages/SystemUI/src/com/android/systemui/util/ConvenienceExtensions.kt --packages/SystemUI/src/com/android/systemui/util/DelayableMarqueeTextView.kt --packages/SystemUI/src/com/android/systemui/util/DumpUtils.kt --packages/SystemUI/src/com/android/systemui/util/InitializationChecker.kt --packages/SystemUI/src/com/android/systemui/util/LargeScreenUtils.kt --packages/SystemUI/src/com/android/systemui/util/ListenerSet.kt --packages/SystemUI/src/com/android/systemui/util/NeverExactlyLinearLayout.kt --packages/SystemUI/src/com/android/systemui/util/NoRemeasureMotionLayout.kt --packages/SystemUI/src/com/android/systemui/util/PluralMessageFormater.kt --packages/SystemUI/src/com/android/systemui/util/RingerModeTracker.kt --packages/SystemUI/src/com/android/systemui/util/RingerModeTrackerImpl.kt --packages/SystemUI/src/com/android/systemui/util/RoundedCornerProgressDrawable.kt --packages/SystemUI/src/com/android/systemui/util/SafeMarqueeTextView.kt --packages/SystemUI/src/com/android/systemui/util/SparseArrayUtils.kt --packages/SystemUI/src/com/android/systemui/util/TraceUtils.kt --packages/SystemUI/src/com/android/systemui/util/UserAwareController.kt --packages/SystemUI/src/com/android/systemui/util/WallpaperController.kt --packages/SystemUI/src/com/android/systemui/util/animation/AnimationUtil.kt --packages/SystemUI/src/com/android/systemui/util/animation/MeasurementInput.kt --packages/SystemUI/src/com/android/systemui/util/animation/TransitionLayout.kt --packages/SystemUI/src/com/android/systemui/util/animation/TransitionLayoutController.kt --packages/SystemUI/src/com/android/systemui/util/animation/UniqueObjectHostView.kt --packages/SystemUI/src/com/android/systemui/util/concurrency/Execution.kt --packages/SystemUI/src/com/android/systemui/util/concurrency/PendingTasksContainer.kt --packages/SystemUI/src/com/android/systemui/util/drawable/DrawableSize.kt --packages/SystemUI/src/com/android/systemui/util/kotlin/CoroutinesModule.kt --packages/SystemUI/src/com/android/systemui/util/kotlin/Flow.kt --packages/SystemUI/src/com/android/systemui/util/kotlin/IpcSerializer.kt --packages/SystemUI/src/com/android/systemui/util/kotlin/nullability.kt --packages/SystemUI/src/com/android/systemui/util/recycler/HorizontalSpacerItemDecoration.kt --packages/SystemUI/src/com/android/systemui/util/view/ViewUtil.kt --packages/SystemUI/src/com/android/systemui/util/wrapper/RotationPolicyWrapper.kt --packages/SystemUI/src/com/android/systemui/volume/VolumePanelDialogReceiver.kt --packages/SystemUI/src/com/android/systemui/volume/VolumePanelFactory.kt --packages/SystemUI/tests/src/com/android/keyguard/ActiveUnlockConfigTest.kt --packages/SystemUI/tests/src/com/android/keyguard/BouncerPanelExpansionCalculatorTest.kt --packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt --packages/SystemUI/tests/src/com/android/keyguard/KeyguardBiometricLockoutLoggerTest.kt --packages/SystemUI/tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt --packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt --packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewTest.kt --packages/SystemUI/tests/src/com/android/keyguard/KeyguardUserSwitcherAnchorTest.kt --packages/SystemUI/tests/src/com/android/keyguard/clock/ClockPaletteTest.kt --packages/SystemUI/tests/src/com/android/keyguard/clock/ViewPreviewerTest.kt --packages/SystemUI/tests/src/com/android/keyguard/mediator/ScreenOnCoordinatorTest.kt --packages/SystemUI/tests/src/com/android/systemui/BootCompleteCacheTest.kt --packages/SystemUI/tests/src/com/android/systemui/ChooserSelectorTest.kt --packages/SystemUI/tests/src/com/android/systemui/DisplayCutoutBaseViewTest.kt --packages/SystemUI/tests/src/com/android/systemui/InstanceIdSequenceFake.kt --packages/SystemUI/tests/src/com/android/systemui/ScreenDecorHwcLayerTest.kt --packages/SystemUI/tests/src/com/android/systemui/animation/DialogTransitionAnimatorTest.kt --packages/SystemUI/tests/src/com/android/systemui/animation/FontInterpolatorTest.kt --packages/SystemUI/tests/src/com/android/systemui/animation/GhostedViewTransitionAnimatorControllerTest.kt --packages/SystemUI/tests/src/com/android/systemui/animation/TextAnimatorTest.kt --packages/SystemUI/tests/src/com/android/systemui/animation/TextInterpolatorTest.kt --packages/SystemUI/tests/src/com/android/systemui/animation/ViewHierarchyAnimatorTest.kt --packages/SystemUI/tests/src/com/android/systemui/battery/BatteryMeterViewTest.kt --packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFingerprintAndFaceViewTest.kt --packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFingerprintViewTest.kt --packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt --packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt --packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt --packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsViewTest.kt --packages/SystemUI/tests/src/com/android/systemui/broadcast/ActionReceiverTest.kt --packages/SystemUI/tests/src/com/android/systemui/broadcast/BroadcastDispatcherTest.kt --packages/SystemUI/tests/src/com/android/systemui/broadcast/BroadcastSenderTest.kt --packages/SystemUI/tests/src/com/android/systemui/broadcast/PendingRemovalStoreTest.kt --packages/SystemUI/tests/src/com/android/systemui/broadcast/UserBroadcastDispatcherTest.kt --packages/SystemUI/tests/src/com/android/systemui/camera/CameraGestureHelperTest.kt --packages/SystemUI/tests/src/com/android/systemui/camera/CameraIntentsTest.kt --packages/SystemUI/tests/src/com/android/systemui/charging/WiredChargingRippleControllerTest.kt --packages/SystemUI/tests/src/com/android/systemui/controls/CustomIconCacheTest.kt --packages/SystemUI/tests/src/com/android/systemui/controls/controller/AuxiliaryPersistenceWrapperTest.kt --packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlActionCoordinatorImplTest.kt --packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsBindingControllerImplTest.kt --packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt --packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsFavoritePersistenceWrapperTest.kt --packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManagerTest.kt --packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsTileResourceConfigurationImplTest.kt --packages/SystemUI/tests/src/com/android/systemui/controls/controller/ServiceWrapperTest.kt --packages/SystemUI/tests/src/com/android/systemui/controls/controller/StatefulControlSubscriberTest.kt --packages/SystemUI/tests/src/com/android/systemui/controls/dagger/ControlsComponentTest.kt --packages/SystemUI/tests/src/com/android/systemui/controls/management/AllModelTest.kt --packages/SystemUI/tests/src/com/android/systemui/controls/management/AppAdapterTest.kt --packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsListingControllerImplTest.kt --packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsRequestDialogTest.kt --packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsRequestReceiverTest.kt --packages/SystemUI/tests/src/com/android/systemui/controls/management/FavoritesModelTest.kt --packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlViewHolderTest.kt --packages/SystemUI/tests/src/com/android/systemui/controls/ui/DetailDialogTest.kt --packages/SystemUI/tests/src/com/android/systemui/decor/CutoutDecorProviderFactoryTest.kt --packages/SystemUI/tests/src/com/android/systemui/decor/OverlayWindowTest.kt --packages/SystemUI/tests/src/com/android/systemui/decor/PrivacyDotDecorProviderFactoryTest.kt --packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerDecorProviderFactoryTest.kt --packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerResDelegateTest.kt --packages/SystemUI/tests/src/com/android/systemui/dump/DumpHandlerTest.kt --packages/SystemUI/tests/src/com/android/systemui/dump/DumpsysTableLoggerTest.kt --packages/SystemUI/tests/src/com/android/systemui/dump/LogBufferFreezerTest.kt --packages/SystemUI/tests/src/com/android/systemui/dump/LogBufferHelper.kt --packages/SystemUI/tests/src/com/android/systemui/dump/LogEulogizerTest.kt --packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsDebugTest.kt --packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsReleaseTest.kt --packages/SystemUI/tests/src/com/android/systemui/flags/FlagManagerTest.kt --packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt --packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceConfigTest.kt --packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceCoreStartableTest.kt --packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardBouncerRepositoryTest.kt --packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt --packages/SystemUI/tests/src/com/android/systemui/lifecycle/InstantTaskExecutorRule.kt --packages/SystemUI/tests/src/com/android/systemui/media/muteawait/MediaMuteAwaitConnectionManagerTest.kt --packages/SystemUI/tests/src/com/android/systemui/media/nearby/NearbyMediaDevicesManagerTest.kt --packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelperTest.kt --packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt --packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderUiEventLoggerTest.kt --packages/SystemUI/tests/src/com/android/systemui/navigationbar/TaskbarDelegateTest.kt --packages/SystemUI/tests/src/com/android/systemui/navigationbar/gestural/FloatingRotationButtonPositionCalculatorTest.kt --packages/SystemUI/tests/src/com/android/systemui/privacy/AppOpsPrivacyItemMonitorTest.kt --packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyChipBuilderTest.kt --packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyConfigFlagsTest.kt --packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogControllerTest.kt --packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogTest.kt --packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt --packages/SystemUI/tests/src/com/android/systemui/qs/HeaderPrivacyIconsControllerTest.kt --packages/SystemUI/tests/src/com/android/systemui/qs/QSContainerImplTest.kt --packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentDisableFlagsLoggerTest.kt --packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.kt --packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelSwitchToParentTest.kt --packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt --packages/SystemUI/tests/src/com/android/systemui/qs/QSSquishinessControllerTest.kt --packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt --packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelTest.kt --packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt --packages/SystemUI/tests/src/com/android/systemui/qs/SettingObserverTest.kt --packages/SystemUI/tests/src/com/android/systemui/qs/carrier/CellSignalStateTest.kt --packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileStatePersisterTest.kt --packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt --packages/SystemUI/tests/src/com/android/systemui/qs/external/TileRequestDialogEventLoggerTest.kt --packages/SystemUI/tests/src/com/android/systemui/qs/external/TileRequestDialogTest.kt --packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServiceRequestControllerTest.kt --packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSFactoryImplTest.kt --packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileViewImplTest.kt --packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/ResourceIconTest.kt --packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/TilesStatesTextTest.kt --packages/SystemUI/tests/src/com/android/systemui/qs/tiles/AirplaneModeTileTest.kt --packages/SystemUI/tests/src/com/android/systemui/qs/tiles/AlarmTileTest.kt --packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BatterySaverTileTest.kt --packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BluetoothTileTest.kt --packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CameraToggleTileTest.kt --packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt --packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DndTileTest.kt --packages/SystemUI/tests/src/com/android/systemui/qs/tiles/LocationTileTest.kt --packages/SystemUI/tests/src/com/android/systemui/qs/tiles/MicrophoneToggleTileTest.kt --packages/SystemUI/tests/src/com/android/systemui/qs/tiles/NightDisplayTileTest.kt --packages/SystemUI/tests/src/com/android/systemui/qs/user/UserSwitchDialogControllerTest.kt --packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordDialogTest.kt --packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotPolicyImplTest.kt --packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt --packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessSliderControllerTest.kt --packages/SystemUI/tests/src/com/android/systemui/shade/CombinedShadeHeaderConstraintsTest.kt --packages/SystemUI/tests/src/com/android/systemui/shade/NotificationQSContainerControllerTest.kt --packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt --packages/SystemUI/tests/src/com/android/systemui/shade/PulsingGestureListenerTest.kt --packages/SystemUI/tests/src/com/android/systemui/shade/transition/ScrimShadeTransitionControllerTest.kt --packages/SystemUI/tests/src/com/android/systemui/shade/transition/ShadeTransitionControllerTest.kt --packages/SystemUI/tests/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimatorTest.kt --packages/SystemUI/tests/src/com/android/systemui/shared/animation/UnfoldMoveFromCenterAnimatorTest.kt --packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt --packages/SystemUI/tests/src/com/android/systemui/shared/navigationbar/RegionSamplingHelperTest.kt --packages/SystemUI/tests/src/com/android/systemui/shared/rotation/RotationButtonControllerTest.kt --packages/SystemUI/tests/src/com/android/systemui/smartspace/DreamSmartspaceControllerTest.kt --packages/SystemUI/tests/src/com/android/systemui/smartspace/LockscreenAndDreamTargetFilterTest.kt --packages/SystemUI/tests/src/com/android/systemui/smartspace/LockscreenPreconditionTest.kt --packages/SystemUI/tests/src/com/android/systemui/statusbar/BlurUtilsTest.kt --packages/SystemUI/tests/src/com/android/systemui/statusbar/DragDownHelperTest.kt --packages/SystemUI/tests/src/com/android/systemui/statusbar/LSShadeTransitionLoggerTest.kt --packages/SystemUI/tests/src/com/android/systemui/statusbar/LightRevealScrimTest.kt --packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt --packages/SystemUI/tests/src/com/android/systemui/statusbar/MediaArtworkProcessorTest.kt --packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt --packages/SystemUI/tests/src/com/android/systemui/statusbar/SingleShadeLockScreenOverScrollerTest.kt --packages/SystemUI/tests/src/com/android/systemui/statusbar/SplitShadeLockScreenOverScrollerTest.kt --packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt --packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateEventTest.kt --packages/SystemUI/tests/src/com/android/systemui/statusbar/VibratorHelperTest.kt --packages/SystemUI/tests/src/com/android/systemui/statusbar/commandline/CommandRegistryTest.kt --packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/AccessPointControllerImplTest.kt --packages/SystemUI/tests/src/com/android/systemui/statusbar/disableflags/DisableFlagsLoggerTest.kt --packages/SystemUI/tests/src/com/android/systemui/statusbar/disableflags/DisableStateTrackerTest.kt --packages/SystemUI/tests/src/com/android/systemui/statusbar/gesture/GenericGestureDetectorTest.kt --packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt --packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorControllerTest.kt --packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManagerTest.kt --packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifLiveDataImplTest.kt --packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifLiveDataStoreImplTest.kt --packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifPipelineChoreographerTest.kt --packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/TargetSdkResolverTest.kt --packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinatorTest.kt --packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/DataStoreCoordinatorTest.kt --packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/GroupCountCoordinatorTest.kt --packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt --packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.kt --packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RemoteInputCoordinatorTest.kt --packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinatorTest.kt --packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinatorTest.kt --packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ShadeEventCoordinatorTest.kt --packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/SmartspaceDedupingCoordinatorTest.kt --packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinatorTest.kt --packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinatorTest.kt --packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProviderTest.kt --packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionInconsistencyTrackerTest.kt --packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/notifcollection/SelfTrackingLifetimeExtenderTest.kt --packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/provider/VisualStabilityProviderTest.kt --packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilderTest.kt --packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/RenderStageManagerTest.kt --packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/IconManagerTest.kt --packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewTest.kt --packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogControllerTest.kt --packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsTest.kt --packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/MediaContainerViewTest.kt --packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt --packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt --packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt --packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ConfigurationControllerImplTest.kt --packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/FoldStateListenerTest.kt --packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LetterboxAppearanceCalculatorTest.kt --packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LetterboxBackgroundProviderTest.kt --packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconContainerTest.kt --packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt --packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt --packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProviderTest.kt --packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarMoveFromCenterAnimationControllerTest.kt --packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SystemBarAttributesListenerTest.kt --packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationControllerTest.kt --packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentLoggerTest.kt --packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallChronometerTest.kt --packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt --packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallLoggerTest.kt --packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryStateNotifierTest.kt --packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ClockTest.kt --packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceControlsControllerImplTest.kt --packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImplTest.kt --packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/FlashlightControllerImplTest.kt --packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchControllerTest.kt --packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherAdapterTest.kt --packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputQuickSettingsDisablerTest.kt --packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SafetyControllerTest.kt --packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/VariableDateViewControllerTest.kt --packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/WalletControllerImplTest.kt --packages/SystemUI/tests/src/com/android/systemui/statusbar/window/StatusBarWindowStateControllerTest.kt --packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayControllerTest.kt --packages/SystemUI/tests/src/com/android/systemui/unfold/FoldStateLoggingProviderTest.kt --packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldLatencyTrackerTest.kt --packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldTransitionWallpaperControllerTest.kt --packages/SystemUI/tests/src/com/android/systemui/unfold/config/ResourceUnfoldTransitionConfigTest.kt --packages/SystemUI/tests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt --packages/SystemUI/tests/src/com/android/systemui/unfold/util/FoldableTestUtils.kt --packages/SystemUI/tests/src/com/android/systemui/unfold/util/ScaleAwareUnfoldProgressProviderTest.kt --packages/SystemUI/tests/src/com/android/systemui/unfold/util/TestFoldStateProvider.kt --packages/SystemUI/tests/src/com/android/systemui/usb/UsbPermissionActivityTest.kt --packages/SystemUI/tests/src/com/android/systemui/user/UserCreatorTest.kt --packages/SystemUI/tests/src/com/android/systemui/util/FakeSharedPreferencesTest.kt --packages/SystemUI/tests/src/com/android/systemui/util/FloatingContentCoordinatorTest.kt --packages/SystemUI/tests/src/com/android/systemui/util/ListenerSetTest.kt --packages/SystemUI/tests/src/com/android/systemui/util/RingerModeLiveDataTest.kt --packages/SystemUI/tests/src/com/android/systemui/util/WallpaperControllerTest.kt --packages/SystemUI/tests/src/com/android/systemui/util/animation/AnimationUtilTest.kt --packages/SystemUI/tests/src/com/android/systemui/util/drawable/DrawableSizeTest.kt --packages/SystemUI/tests/src/com/android/systemui/util/kotlin/FlowUtilTests.kt --packages/SystemUI/tests/src/com/android/systemui/util/kotlin/IpcSerializerTest.kt --packages/SystemUI/tests/src/com/android/systemui/util/kotlin/SuspendUtilTests.kt --packages/SystemUI/tests/src/com/android/systemui/util/view/ViewUtilTest.kt --packages/SystemUI/tests/utils/src/com/android/systemui/flags/FakeFeatureFlags.kt --packages/SystemUI/tests/utils/src/com/android/systemui/util/FakeSharedPreferences.kt --packages/SystemUI/tests/utils/src/com/android/systemui/util/mockito/KotlinMockitoHelpers.kt --packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedModule.kt --packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt --packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldTransitionProgressProvider.kt --packages/SystemUI/unfold/src/com/android/systemui/unfold/compat/ScreenSizeFoldProvider.kt --packages/SystemUI/unfold/src/com/android/systemui/unfold/compat/SizeScreenStatusProvider.kt --packages/SystemUI/unfold/src/com/android/systemui/unfold/config/ResourceUnfoldTransitionConfig.kt --packages/SystemUI/unfold/src/com/android/systemui/unfold/dagger/UnfoldMain.kt --packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt --packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt --packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/FoldStateProvider.kt --packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/screen/ScreenStatusProvider.kt diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/DividerPresenter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/DividerPresenter.java index 6b957114c00b..774b2129497d 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/DividerPresenter.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/DividerPresenter.java @@ -200,6 +200,10 @@ class DividerPresenter implements View.OnTouchListener { } // At this point, a divider is required. + final TaskFragmentContainer primaryContainer = + topSplitContainer.getPrimaryContainer(); + final TaskFragmentContainer secondaryContainer = + topSplitContainer.getSecondaryContainer(); // Create the decor surface if one is not available yet. final SurfaceControl decorSurface = parentInfo.getDecorSurface(); @@ -207,41 +211,44 @@ class DividerPresenter implements View.OnTouchListener { // Clean up when the decor surface is currently unavailable. removeDivider(); // Request to create the decor surface - createOrMoveDecorSurfaceLocked(wct, topSplitContainer.getPrimaryContainer()); + createOrMoveDecorSurfaceLocked(wct, primaryContainer); return; } // Update the decor surface owner if needed. boolean isDraggableExpandType = SplitAttributesHelper.isDraggableExpandType(splitAttributes); - final TaskFragmentContainer decorSurfaceOwnerContainer = isDraggableExpandType - ? topSplitContainer.getSecondaryContainer() - : topSplitContainer.getPrimaryContainer(); + final TaskFragmentContainer decorSurfaceOwnerContainer = + isDraggableExpandType ? secondaryContainer : primaryContainer; if (!Objects.equals( mDecorSurfaceOwner, decorSurfaceOwnerContainer.getTaskFragmentToken())) { createOrMoveDecorSurfaceLocked(wct, decorSurfaceOwnerContainer); } - final boolean isVerticalSplit = isVerticalSplit(topSplitContainer); - final boolean isReversedLayout = isReversedLayout( - topSplitContainer.getCurrentSplitAttributes(), - parentInfo.getConfiguration()); + + final Configuration parentConfiguration = parentInfo.getConfiguration(); + final Rect taskBounds = parentConfiguration.windowConfiguration.getBounds(); + final boolean isVerticalSplit = isVerticalSplit(splitAttributes); + final boolean isReversedLayout = isReversedLayout(splitAttributes, parentConfiguration); + final int dividerWidthPx = getDividerWidthPx(dividerAttributes); updateProperties( new Properties( - parentInfo.getConfiguration(), + parentConfiguration, dividerAttributes, decorSurface, getInitialDividerPosition( - topSplitContainer, isVerticalSplit, isReversedLayout), + primaryContainer, secondaryContainer, taskBounds, + dividerWidthPx, isDraggableExpandType, isVerticalSplit, + isReversedLayout), isVerticalSplit, isReversedLayout, parentInfo.getDisplayId(), isDraggableExpandType, - getContainerBackgroundColor(topSplitContainer.getPrimaryContainer(), - DEFAULT_PRIMARY_VEIL_COLOR), - getContainerBackgroundColor(topSplitContainer.getSecondaryContainer(), - DEFAULT_SECONDARY_VEIL_COLOR) + getContainerBackgroundColor( + primaryContainer, DEFAULT_PRIMARY_VEIL_COLOR), + getContainerBackgroundColor( + secondaryContainer, DEFAULT_SECONDARY_VEIL_COLOR) )); } } @@ -338,32 +345,31 @@ class DividerPresenter implements View.OnTouchListener { @VisibleForTesting static int getInitialDividerPosition( - @NonNull SplitContainer splitContainer, + @NonNull TaskFragmentContainer primaryContainer, + @NonNull TaskFragmentContainer secondaryContainer, + @NonNull Rect taskBounds, + int dividerWidthPx, + boolean isDraggableExpandType, boolean isVerticalSplit, boolean isReversedLayout) { - final Rect primaryBounds = - splitContainer.getPrimaryContainer().getLastRequestedBounds(); - final Rect secondaryBounds = - splitContainer.getSecondaryContainer().getLastRequestedBounds(); - final SplitAttributes splitAttributes = splitContainer.getCurrentSplitAttributes(); - - if (SplitAttributesHelper.isDraggableExpandType(splitAttributes)) { - // If the container is fully expanded by dragging the divider, we display the divider - // on the edge. - final int dividerWidth = getDividerWidthPx(splitAttributes.getDividerAttributes()); + if (isDraggableExpandType) { + // If the secondary container is fully expanded by dragging the divider, we display the + // divider on the edge. final int fullyExpandedPosition = isVerticalSplit - ? primaryBounds.right - dividerWidth - : primaryBounds.bottom - dividerWidth; + ? taskBounds.width() - dividerWidthPx + : taskBounds.height() - dividerWidthPx; return isReversedLayout ? fullyExpandedPosition : 0; } else { + final Rect primaryBounds = primaryContainer.getLastRequestedBounds(); + final Rect secondaryBounds = secondaryContainer.getLastRequestedBounds(); return isVerticalSplit ? Math.min(primaryBounds.right, secondaryBounds.right) : Math.min(primaryBounds.bottom, secondaryBounds.bottom); } } - private static boolean isVerticalSplit(@NonNull SplitContainer splitContainer) { - final int layoutDirection = splitContainer.getCurrentSplitAttributes().getLayoutDirection(); + private static boolean isVerticalSplit(@NonNull SplitAttributes splitAttributes) { + final int layoutDirection = splitAttributes.getLayoutDirection(); switch (layoutDirection) { case SplitAttributes.LayoutDirection.LEFT_TO_RIGHT: case SplitAttributes.LayoutDirection.RIGHT_TO_LEFT: @@ -510,7 +516,7 @@ class DividerPresenter implements View.OnTouchListener { if (mProperties != null && mRenderer != null) { final Rect taskBounds = mProperties.mConfiguration.windowConfiguration.getBounds(); mDividerPosition = calculateDividerPosition( - event, taskBounds, mRenderer.mDividerWidthPx, + event, taskBounds, mProperties.mDividerWidthPx, mProperties.mDividerAttributes, mProperties.mIsVerticalSplit, calculateMinPosition(), calculateMaxPosition()); mRenderer.setDividerPosition(mDividerPosition); @@ -676,8 +682,8 @@ class DividerPresenter implements View.OnTouchListener { final int minPosition = calculateMinPosition(); final int maxPosition = calculateMaxPosition(); final int fullyExpandedPosition = mProperties.mIsVerticalSplit - ? taskBounds.right - mRenderer.mDividerWidthPx - : taskBounds.bottom - mRenderer.mDividerWidthPx; + ? taskBounds.width() - mProperties.mDividerWidthPx + : taskBounds.height() - mProperties.mDividerWidthPx; if (isDraggingToFullscreenAllowed(mProperties.mDividerAttributes)) { final float displayDensity = getDisplayDensity(); @@ -782,7 +788,7 @@ class DividerPresenter implements View.OnTouchListener { private int calculateMinPosition() { return calculateMinPosition( mProperties.mConfiguration.windowConfiguration.getBounds(), - mRenderer.mDividerWidthPx, mProperties.mDividerAttributes, + mProperties.mDividerWidthPx, mProperties.mDividerAttributes, mProperties.mIsVerticalSplit, mProperties.mIsReversedLayout); } @@ -790,7 +796,7 @@ class DividerPresenter implements View.OnTouchListener { private int calculateMaxPosition() { return calculateMaxPosition( mProperties.mConfiguration.windowConfiguration.getBounds(), - mRenderer.mDividerWidthPx, mProperties.mDividerAttributes, + mProperties.mDividerWidthPx, mProperties.mDividerAttributes, mProperties.mIsVerticalSplit, mProperties.mIsReversedLayout); } @@ -828,13 +834,12 @@ class DividerPresenter implements View.OnTouchListener { * Returns the new split ratio of the {@link SplitContainer} based on the current divider * position. */ - float calculateNewSplitRatio(@NonNull SplitContainer topSplitContainer) { + float calculateNewSplitRatio() { synchronized (mLock) { return calculateNewSplitRatio( - topSplitContainer, mDividerPosition, mProperties.mConfiguration.windowConfiguration.getBounds(), - mRenderer.mDividerWidthPx, + mProperties.mDividerWidthPx, mProperties.mIsVerticalSplit, mProperties.mIsReversedLayout, calculateMinPosition(), @@ -846,21 +851,20 @@ class DividerPresenter implements View.OnTouchListener { private static boolean isDraggingToFullscreenAllowed( @NonNull DividerAttributes dividerAttributes) { // TODO(b/293654166) Use DividerAttributes.isDraggingToFullscreenAllowed when extension is - // updated. - return true; + // updated to v7. + return false; } /** * Returns the new split ratio of the {@link SplitContainer} based on the current divider * position. * - * @param topSplitContainer the {@link SplitContainer} for which to compute the split ratio. * @param dividerPosition the divider position. See {@link #mDividerPosition}. * @param taskBounds the task bounds * @param dividerWidthPx the width of the divider in pixels. * @param isVerticalSplit if {@code true}, the split is a vertical split. If {@code false}, the * split is a horizontal split. See - * {@link #isVerticalSplit(SplitContainer)}. + * {@link #isVerticalSplit(SplitAttributes)}. * @param isReversedLayout if {@code true}, the split layout is reversed, i.e. right-to-left or * bottom-to-top. If {@code false}, the split is not reversed, i.e. * left-to-right or top-to-bottom. See @@ -871,7 +875,6 @@ class DividerPresenter implements View.OnTouchListener { */ @VisibleForTesting static float calculateNewSplitRatio( - @NonNull SplitContainer topSplitContainer, int dividerPosition, @NonNull Rect taskBounds, int dividerWidthPx, @@ -896,8 +899,6 @@ class DividerPresenter implements View.OnTouchListener { dividerPosition = Math.clamp(dividerPosition, minPosition, maxPosition); } - final TaskFragmentContainer primaryContainer = topSplitContainer.getPrimaryContainer(); - final Rect origPrimaryBounds = primaryContainer.getLastRequestedBounds(); final int usableSize = isVerticalSplit ? taskBounds.width() - dividerWidthPx : taskBounds.height() - dividerWidthPx; @@ -905,13 +906,13 @@ class DividerPresenter implements View.OnTouchListener { final float newRatio; if (isVerticalSplit) { final int newPrimaryWidth = isReversedLayout - ? (origPrimaryBounds.right - (dividerPosition + dividerWidthPx)) - : (dividerPosition - origPrimaryBounds.left); + ? taskBounds.width() - (dividerPosition + dividerWidthPx) + : dividerPosition; newRatio = 1.0f * newPrimaryWidth / usableSize; } else { final int newPrimaryHeight = isReversedLayout - ? (origPrimaryBounds.bottom - (dividerPosition + dividerWidthPx)) - : (dividerPosition - origPrimaryBounds.top); + ? taskBounds.height() - (dividerPosition + dividerWidthPx) + : dividerPosition; newRatio = 1.0f * newPrimaryHeight / usableSize; } return newRatio; @@ -966,6 +967,7 @@ class DividerPresenter implements View.OnTouchListener { private final boolean mIsDraggableExpandType; private final Color mPrimaryVeilColor; private final Color mSecondaryVeilColor; + private final int mDividerWidthPx; @VisibleForTesting Properties( @@ -989,6 +991,7 @@ class DividerPresenter implements View.OnTouchListener { mIsDraggableExpandType = isDraggableExpandType; mPrimaryVeilColor = primaryVeilColor; mSecondaryVeilColor = secondaryVeilColor; + mDividerWidthPx = getDividerWidthPx(dividerAttributes); } /** @@ -1055,7 +1058,6 @@ class DividerPresenter implements View.OnTouchListener { private final View.OnTouchListener mListener; @NonNull private Properties mProperties; - private int mDividerWidthPx; private int mHandleWidthPx; @Nullable private SurfaceControl mPrimaryVeil; @@ -1095,7 +1097,6 @@ class DividerPresenter implements View.OnTouchListener { /** Updates the divider when initializing or when properties are changed */ @VisibleForTesting void update() { - mDividerWidthPx = getDividerWidthPx(mProperties.mDividerAttributes); mDividerPosition = mProperties.mInitialDividerPosition; mWindowlessWindowManager.setConfiguration(mProperties.mConfiguration); @@ -1161,15 +1162,17 @@ class DividerPresenter implements View.OnTouchListener { // When the divider drag handle width is larger than the divider width, the position // of the divider surface is adjusted so that it is large enough to host both the // divider line and the divider drag handle. - mDividerSurfaceWidthPx = Math.max(mDividerWidthPx, mHandleWidthPx); + mDividerSurfaceWidthPx = Math.max(mProperties.mDividerWidthPx, mHandleWidthPx); + dividerSurfacePosition = mProperties.mIsReversedLayout + ? mDividerPosition + : mDividerPosition + mProperties.mDividerWidthPx - mDividerSurfaceWidthPx; dividerSurfacePosition = - mProperties.mIsReversedLayout - ? mDividerPosition - : mDividerPosition + mDividerWidthPx - mDividerSurfaceWidthPx; - dividerSurfacePosition = Math.clamp(dividerSurfacePosition, 0, - mProperties.mIsVerticalSplit ? taskBounds.width() : taskBounds.height()); + Math.clamp(dividerSurfacePosition, 0, + mProperties.mIsVerticalSplit + ? taskBounds.width() - mDividerSurfaceWidthPx + : taskBounds.height() - mDividerSurfaceWidthPx); } else { - mDividerSurfaceWidthPx = mDividerWidthPx; + mDividerSurfaceWidthPx = mProperties.mDividerWidthPx; dividerSurfacePosition = mDividerPosition; } @@ -1182,16 +1185,9 @@ class DividerPresenter implements View.OnTouchListener { } // Update divider line position in the surface - if (!mProperties.mIsReversedLayout) { - final int offset = mDividerPosition - dividerSurfacePosition; - mDividerLine.setX(mProperties.mIsVerticalSplit ? offset : 0); - mDividerLine.setY(mProperties.mIsVerticalSplit ? 0 : offset); - } else { - // For reversed layout, the divider line is always at the start of the divider - // surface. - mDividerLine.setX(0); - mDividerLine.setY(0); - } + final int offset = mDividerPosition - dividerSurfacePosition; + mDividerLine.setX(mProperties.mIsVerticalSplit ? offset : 0); + mDividerLine.setY(mProperties.mIsVerticalSplit ? 0 : offset); if (mIsDragging) { updateVeils(t); @@ -1241,8 +1237,10 @@ class DividerPresenter implements View.OnTouchListener { final Rect taskBounds = mProperties.mConfiguration.windowConfiguration.getBounds(); mDividerLine.setLayoutParams( mProperties.mIsVerticalSplit - ? new FrameLayout.LayoutParams(mDividerWidthPx, taskBounds.height()) - : new FrameLayout.LayoutParams(taskBounds.width(), mDividerWidthPx) + ? new FrameLayout.LayoutParams( + mProperties.mDividerWidthPx, taskBounds.height()) + : new FrameLayout.LayoutParams( + taskBounds.width(), mProperties.mDividerWidthPx) ); if (mProperties.mDividerAttributes.getDividerType() == DividerAttributes.DIVIDER_TYPE_DRAGGABLE) { @@ -1352,13 +1350,14 @@ class DividerPresenter implements View.OnTouchListener { Rect secondaryBounds; if (mProperties.mIsVerticalSplit) { final Rect boundsLeft = new Rect(0, 0, mDividerPosition, taskBounds.height()); - final Rect boundsRight = new Rect(mDividerPosition + mDividerWidthPx, 0, + final Rect boundsRight = new Rect(mDividerPosition + mProperties.mDividerWidthPx, 0, taskBounds.width(), taskBounds.height()); primaryBounds = mProperties.mIsReversedLayout ? boundsRight : boundsLeft; secondaryBounds = mProperties.mIsReversedLayout ? boundsLeft : boundsRight; } else { final Rect boundsTop = new Rect(0, 0, taskBounds.width(), mDividerPosition); - final Rect boundsBottom = new Rect(0, mDividerPosition + mDividerWidthPx, + final Rect boundsBottom = new Rect( + 0, mDividerPosition + mProperties.mDividerWidthPx, taskBounds.width(), taskBounds.height()); primaryBounds = mProperties.mIsReversedLayout ? boundsBottom : boundsTop; secondaryBounds = mProperties.mIsReversedLayout ? boundsTop : boundsBottom; diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java index c708da97d908..ee00c4cd67eb 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java @@ -510,7 +510,7 @@ class TaskContainer { return; } final TaskFragmentContainer primaryContainer = topSplitContainer.getPrimaryContainer(); - final float newRatio = dividerPresenter.calculateNewSplitRatio(topSplitContainer); + final float newRatio = dividerPresenter.calculateNewSplitRatio(); // If the primary container is fully expanded, we should finish all the associated // secondary containers. diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/DividerPresenterTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/DividerPresenterTest.java index 20626c79714e..4515187f231e 100644 --- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/DividerPresenterTest.java +++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/DividerPresenterTest.java @@ -144,6 +144,7 @@ public class DividerPresenterTest { new SplitAttributes.Builder() .setDividerAttributes(DEFAULT_DIVIDER_ATTRIBUTES) .build()); + final Rect mockTaskBounds = new Rect(0, 0, 2000, 1000); final TaskFragmentContainer mockPrimaryContainer = createMockTaskFragmentContainer( mPrimaryContainerToken, new Rect(0, 0, 950, 1000)); @@ -158,7 +159,9 @@ public class DividerPresenterTest { DEFAULT_DIVIDER_ATTRIBUTES, mSurfaceControl, getInitialDividerPosition( - mSplitContainer, true /* isVerticalSplit */, false /* isReversedLayout */), + mockPrimaryContainer, mockSecondaryContainer, mockTaskBounds, + 50 /* divideWidthPx */, false /* isDraggableExpandType */, + true /* isVerticalSplit */, false /* isReversedLayout */), true /* isVerticalSplit */, false /* isReversedLayout */, Display.DEFAULT_DISPLAY, @@ -502,7 +505,6 @@ public class DividerPresenterTest { assertEquals( 0.3f, // Primary is 300px after dragging. DividerPresenter.calculateNewSplitRatio( - mSplitContainer, dividerPosition, taskBounds, dividerWidthPx, @@ -518,7 +520,6 @@ public class DividerPresenterTest { assertEquals( DividerPresenter.RATIO_EXPANDED_SECONDARY, DividerPresenter.calculateNewSplitRatio( - mSplitContainer, dividerPosition, taskBounds, dividerWidthPx, @@ -535,7 +536,6 @@ public class DividerPresenterTest { assertEquals( 0.2f, // Adjusted to the minPosition 200 DividerPresenter.calculateNewSplitRatio( - mSplitContainer, dividerPosition, taskBounds, dividerWidthPx, @@ -569,7 +569,6 @@ public class DividerPresenterTest { // After dragging, secondary is [0, 0, 2000, 300]. Primary is [0, 400, 2000, 1100]. 0.7f, DividerPresenter.calculateNewSplitRatio( - mSplitContainer, dividerPosition, taskBounds, dividerWidthPx, @@ -587,7 +586,6 @@ public class DividerPresenterTest { // The primary (bottom) container is expanded DividerPresenter.RATIO_EXPANDED_PRIMARY, DividerPresenter.calculateNewSplitRatio( - mSplitContainer, dividerPosition, taskBounds, dividerWidthPx, @@ -605,7 +603,6 @@ public class DividerPresenterTest { // Adjusted to minPosition 200, so the primary (bottom) container is 800. 0.8f, DividerPresenter.calculateNewSplitRatio( - mSplitContainer, dividerPosition, taskBounds, dividerWidthPx, diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/TransitionUtil.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/TransitionUtil.java index 6ca6517abbb0..dc022b4afd3b 100644 --- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/TransitionUtil.java +++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/TransitionUtil.java @@ -69,7 +69,7 @@ public class TransitionUtil { /** Returns {@code true} if the transition is opening or closing mode. */ public static boolean isOpenOrCloseMode(@TransitionInfo.TransitionMode int mode) { - return isOpeningMode(mode) || mode == TRANSIT_CLOSE || mode == TRANSIT_TO_BACK; + return isOpeningMode(mode) || isClosingMode(mode); } /** Returns {@code true} if the transition is opening mode. */ @@ -77,6 +77,11 @@ public class TransitionUtil { return mode == TRANSIT_OPEN || mode == TRANSIT_TO_FRONT; } + /** Returns {@code true} if the transition is closing mode. */ + public static boolean isClosingMode(@TransitionInfo.TransitionMode int mode) { + return mode == TRANSIT_CLOSE || mode == TRANSIT_TO_BACK; + } + /** Returns {@code true} if the transition has a display change. */ public static boolean hasDisplayChange(@NonNull TransitionInfo info) { for (int i = info.getChanges().size() - 1; i >= 0; --i) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java index 6fcea1fe5560..b52b0d8dee74 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java @@ -489,6 +489,14 @@ public class PipTransition extends PipTransitionController { // activity windowing mode, and set the task bounds to the final bounds wct.setActivityWindowingMode(taskInfo.token, WINDOWING_MODE_UNDEFINED); wct.setBounds(taskInfo.token, destinationBounds); + // If the animation is only used to apply destination bounds immediately and + // invisibly, then reshow it until the pip is drawn with the bounds. + final PipAnimationController.PipTransitionAnimator<?> animator = + mPipAnimationController.getCurrentAnimator(); + if (animator != null && animator.getEndValue().equals(0f)) { + tx.addTransactionCommittedListener(mTransitions.getMainExecutor(), + () -> fadeExistingPip(true /* show */)); + } } else { wct.setBounds(taskInfo.token, null /* bounds */); } @@ -1026,6 +1034,7 @@ public class PipTransition extends PipTransitionController { } startTransaction.apply(); + int animationDuration = mEnterExitAnimationDuration; PipAnimationController.PipTransitionAnimator animator; if (enterAnimationType == ANIM_TYPE_BOUNDS) { animator = mPipAnimationController.getAnimator(taskInfo, leash, currentBounds, @@ -1057,8 +1066,17 @@ public class PipTransition extends PipTransitionController { } } } else if (enterAnimationType == ANIM_TYPE_ALPHA) { + // In case augmentRequest() is unable to apply the entering bounds (e.g. the request + // info only contains display change), keep the animation invisible (alpha 0) and + // duration 0 to apply the destination bounds. The actual fade-in animation will be + // done in onFinishResize() after the bounds are applied. + final boolean fadeInAfterOnFinishResize = rotationDelta != Surface.ROTATION_0 + && mFixedRotationState == FIXED_ROTATION_CALLBACK; animator = mPipAnimationController.getAnimator(taskInfo, leash, destinationBounds, - 0f, 1f); + 0f, fadeInAfterOnFinishResize ? 0f : 1f); + if (fadeInAfterOnFinishResize) { + animationDuration = 0; + } mSurfaceTransactionHelper .crop(finishTransaction, leash, destinationBounds) .round(finishTransaction, leash, true /* applyCornerRadius */); @@ -1068,7 +1086,7 @@ public class PipTransition extends PipTransitionController { mPipOrganizer.setContentOverlay(animator.getContentOverlayLeash(), currentBounds); animator.setTransitionDirection(TRANSITION_DIRECTION_TO_PIP) .setPipAnimationCallback(mPipAnimationCallback) - .setDuration(mEnterExitAnimationDuration); + .setDuration(animationDuration); if (rotationDelta != Surface.ROTATION_0 && mFixedRotationState == FIXED_ROTATION_TRANSITION) { // For fixed rotation, the animation destination bounds is in old rotation coordinates. diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java index 9ce22094d56b..e196254628d0 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java @@ -167,7 +167,7 @@ class ScreenRotationAnimation { t.show(mScreenshotLayer); if (!isCustomRotate()) { mStartLuma = TransitionAnimation.getBorderLuma(hardwareBuffer, - screenshotBuffer.getColorSpace()); + screenshotBuffer.getColorSpace(), mSurfaceControl); } hardwareBuffer.close(); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java index e85cb6400000..95e0d79c212e 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java @@ -255,6 +255,7 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel { private final WindowContainerToken mTaskToken; private final DragPositioningCallback mDragPositioningCallback; private final DragDetector mDragDetector; + private final int mDisplayId; private int mDragPointerId = -1; private boolean mIsDragging; @@ -266,6 +267,7 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel { mTaskToken = taskInfo.token; mDragPositioningCallback = dragPositioningCallback; mDragDetector = new DragDetector(this); + mDisplayId = taskInfo.displayId; } @Override @@ -274,7 +276,7 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel { if (id == R.id.close_window) { mTaskOperations.closeTask(mTaskToken); } else if (id == R.id.back_button) { - mTaskOperations.injectBackKey(); + mTaskOperations.injectBackKey(mDisplayId); } else if (id == R.id.minimize_window) { mTaskOperations.minimizeTask(mTaskToken); } else if (id == R.id.maximize_window) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java index 74b091fc4964..6901e75019bc 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java @@ -386,6 +386,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { private final DragPositioningCallback mDragPositioningCallback; private final DragDetector mDragDetector; private final GestureDetector mGestureDetector; + private final int mDisplayId; /** * Whether to pilfer the next motion event to send cancellations to the windows below. @@ -407,6 +408,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { mDragPositioningCallback = dragPositioningCallback; mDragDetector = new DragDetector(this); mGestureDetector = new GestureDetector(mContext, this); + mDisplayId = taskInfo.displayId; mCloseMaximizeWindowRunnable = () -> { final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.get(mTaskId); if (decoration == null) return; @@ -432,7 +434,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { mTaskOperations.closeTask(mTaskToken, wct); } } else if (id == R.id.back_button) { - mTaskOperations.injectBackKey(); + mTaskOperations.injectBackKey(mDisplayId); } else if (id == R.id.caption_handle || id == R.id.open_menu_button) { if (!decoration.isHandleMenuActive()) { moveTaskToFront(decoration.mTaskInfo); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/ResizeVeil.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/ResizeVeil.java deleted file mode 100644 index 93e2a21c6b02..000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/ResizeVeil.java +++ /dev/null @@ -1,451 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.wm.shell.windowdecor; - -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; -import android.animation.ValueAnimator; -import android.annotation.ColorRes; -import android.annotation.NonNull; -import android.app.ActivityManager.RunningTaskInfo; -import android.content.Context; -import android.content.res.Configuration; -import android.graphics.Bitmap; -import android.graphics.Color; -import android.graphics.PixelFormat; -import android.graphics.PointF; -import android.graphics.Rect; -import android.os.Trace; -import android.view.Display; -import android.view.LayoutInflater; -import android.view.SurfaceControl; -import android.view.SurfaceControlViewHost; -import android.view.SurfaceSession; -import android.view.View; -import android.view.WindowManager; -import android.view.WindowlessWindowManager; -import android.widget.ImageView; -import android.window.TaskConstants; - -import com.android.wm.shell.R; -import com.android.wm.shell.common.DisplayController; - -import java.util.function.Supplier; - -/** - * Creates and updates a veil that covers task contents on resize. - */ -public class ResizeVeil { - private static final String TAG = "ResizeVeil"; - private static final int RESIZE_ALPHA_DURATION = 100; - - private static final int VEIL_CONTAINER_LAYER = TaskConstants.TASK_CHILD_LAYER_RESIZE_VEIL; - /** The background is a child of the veil container layer and goes at the bottom. */ - private static final int VEIL_BACKGROUND_LAYER = 0; - /** The icon is a child of the veil container layer and goes in front of the background. */ - private static final int VEIL_ICON_LAYER = 1; - - private final Context mContext; - private final DisplayController mDisplayController; - private final Supplier<SurfaceControl.Transaction> mSurfaceControlTransactionSupplier; - private final SurfaceControlBuilderFactory mSurfaceControlBuilderFactory; - private final WindowDecoration.SurfaceControlViewHostFactory mSurfaceControlViewHostFactory; - private final SurfaceSession mSurfaceSession = new SurfaceSession(); - private final Bitmap mAppIcon; - private ImageView mIconView; - private int mIconSize; - private SurfaceControl mParentSurface; - - /** A container surface to host the veil background and icon child surfaces. */ - private SurfaceControl mVeilSurface; - /** A color surface for the veil background. */ - private SurfaceControl mBackgroundSurface; - /** A surface that hosts a windowless window with the app icon. */ - private SurfaceControl mIconSurface; - - private final RunningTaskInfo mTaskInfo; - private SurfaceControlViewHost mViewHost; - private Display mDisplay; - private ValueAnimator mVeilAnimator; - - private boolean mIsShowing = false; - - private final DisplayController.OnDisplaysChangedListener mOnDisplaysChangedListener = - new DisplayController.OnDisplaysChangedListener() { - @Override - public void onDisplayAdded(int displayId) { - if (mTaskInfo.displayId != displayId) { - return; - } - mDisplayController.removeDisplayWindowListener(this); - setupResizeVeil(); - } - }; - - public ResizeVeil(Context context, - @NonNull DisplayController displayController, - Bitmap appIcon, RunningTaskInfo taskInfo, - SurfaceControl taskSurface, - Supplier<SurfaceControl.Transaction> surfaceControlTransactionSupplier) { - this(context, - displayController, - appIcon, - taskInfo, - taskSurface, - surfaceControlTransactionSupplier, - new SurfaceControlBuilderFactory() {}, - new WindowDecoration.SurfaceControlViewHostFactory() {}); - } - - public ResizeVeil(Context context, - @NonNull DisplayController displayController, - Bitmap appIcon, RunningTaskInfo taskInfo, - SurfaceControl taskSurface, - Supplier<SurfaceControl.Transaction> surfaceControlTransactionSupplier, - SurfaceControlBuilderFactory surfaceControlBuilderFactory, - WindowDecoration.SurfaceControlViewHostFactory surfaceControlViewHostFactory) { - mContext = context; - mDisplayController = displayController; - mAppIcon = appIcon; - mSurfaceControlTransactionSupplier = surfaceControlTransactionSupplier; - mTaskInfo = taskInfo; - mParentSurface = taskSurface; - mSurfaceControlBuilderFactory = surfaceControlBuilderFactory; - mSurfaceControlViewHostFactory = surfaceControlViewHostFactory; - setupResizeVeil(); - } - /** - * Create the veil in its default invisible state. - */ - private void setupResizeVeil() { - if (!obtainDisplayOrRegisterListener()) { - // Display may not be available yet, skip this until then. - return; - } - Trace.beginSection("ResizeVeil#setupResizeVeil"); - mVeilSurface = mSurfaceControlBuilderFactory - .create("Resize veil of Task=" + mTaskInfo.taskId) - .setContainerLayer() - .setHidden(true) - .setParent(mParentSurface) - .setCallsite("ResizeVeil#setupResizeVeil") - .build(); - mBackgroundSurface = mSurfaceControlBuilderFactory - .create("Resize veil background of Task=" + mTaskInfo.taskId, mSurfaceSession) - .setColorLayer() - .setHidden(true) - .setParent(mVeilSurface) - .setCallsite("ResizeVeil#setupResizeVeil") - .build(); - mIconSurface = mSurfaceControlBuilderFactory - .create("Resize veil icon of Task=" + mTaskInfo.taskId) - .setContainerLayer() - .setHidden(true) - .setParent(mVeilSurface) - .setCallsite("ResizeVeil#setupResizeVeil") - .build(); - - mIconSize = mContext.getResources() - .getDimensionPixelSize(R.dimen.desktop_mode_resize_veil_icon_size); - final View root = LayoutInflater.from(mContext) - .inflate(R.layout.desktop_mode_resize_veil, null /* root */); - mIconView = root.findViewById(R.id.veil_application_icon); - mIconView.setImageBitmap(mAppIcon); - - final WindowManager.LayoutParams lp = - new WindowManager.LayoutParams( - mIconSize, - mIconSize, - WindowManager.LayoutParams.TYPE_APPLICATION, - WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, - PixelFormat.TRANSPARENT); - lp.setTitle("Resize veil icon window of Task=" + mTaskInfo.taskId); - lp.setTrustedOverlay(); - - final WindowlessWindowManager wwm = new WindowlessWindowManager(mTaskInfo.configuration, - mIconSurface, null /* hostInputToken */); - - mViewHost = mSurfaceControlViewHostFactory.create(mContext, mDisplay, wwm, "ResizeVeil"); - mViewHost.setView(root, lp); - Trace.endSection(); - } - - private boolean obtainDisplayOrRegisterListener() { - mDisplay = mDisplayController.getDisplay(mTaskInfo.displayId); - if (mDisplay == null) { - mDisplayController.addDisplayWindowListener(mOnDisplaysChangedListener); - return false; - } - return true; - } - - /** - * Shows the veil surface/view. - * - * @param t the transaction to apply in sync with the veil draw - * @param parentSurface the surface that the veil should be a child of - * @param taskBounds the bounds of the task that owns the veil - * @param fadeIn if true, the veil will fade-in with an animation, if false, it will be shown - * immediately - */ - public void showVeil(SurfaceControl.Transaction t, SurfaceControl parentSurface, - Rect taskBounds, boolean fadeIn) { - if (!isReady() || isVisible()) { - t.apply(); - return; - } - mIsShowing = true; - - // Parent surface can change, ensure it is up to date. - if (!parentSurface.equals(mParentSurface)) { - t.reparent(mVeilSurface, parentSurface); - mParentSurface = parentSurface; - } - - t.show(mVeilSurface); - t.setLayer(mVeilSurface, VEIL_CONTAINER_LAYER); - t.setLayer(mIconSurface, VEIL_ICON_LAYER); - t.setLayer(mBackgroundSurface, VEIL_BACKGROUND_LAYER); - t.setColor(mBackgroundSurface, - Color.valueOf(mContext.getColor(getBackgroundColorId())).getComponents()); - - relayout(taskBounds, t); - if (fadeIn) { - cancelAnimation(); - final SurfaceControl.Transaction veilAnimT = mSurfaceControlTransactionSupplier.get(); - mVeilAnimator = new ValueAnimator(); - mVeilAnimator.setFloatValues(0f, 1f); - mVeilAnimator.setDuration(RESIZE_ALPHA_DURATION); - mVeilAnimator.addUpdateListener(animation -> { - veilAnimT.setAlpha(mBackgroundSurface, mVeilAnimator.getAnimatedFraction()); - veilAnimT.apply(); - }); - mVeilAnimator.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationStart(Animator animation) { - veilAnimT.show(mBackgroundSurface) - .setAlpha(mBackgroundSurface, 0) - .apply(); - } - - @Override - public void onAnimationEnd(Animator animation) { - veilAnimT.setAlpha(mBackgroundSurface, 1).apply(); - } - }); - - final SurfaceControl.Transaction iconAnimT = mSurfaceControlTransactionSupplier.get(); - final ValueAnimator iconAnimator = new ValueAnimator(); - iconAnimator.setFloatValues(0f, 1f); - iconAnimator.setDuration(RESIZE_ALPHA_DURATION); - iconAnimator.addUpdateListener(animation -> { - iconAnimT.setAlpha(mIconSurface, animation.getAnimatedFraction()); - iconAnimT.apply(); - }); - iconAnimator.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationStart(Animator animation) { - iconAnimT.show(mIconSurface) - .setAlpha(mIconSurface, 0) - .apply(); - } - - @Override - public void onAnimationEnd(Animator animation) { - iconAnimT.setAlpha(mIconSurface, 1).apply(); - } - }); - // Let the animators show it with the correct alpha value once the animation starts. - t.hide(mIconSurface); - t.hide(mBackgroundSurface); - t.apply(); - - mVeilAnimator.start(); - iconAnimator.start(); - } else { - // Show the veil immediately. - t.show(mIconSurface); - t.show(mBackgroundSurface); - t.setAlpha(mIconSurface, 1); - t.setAlpha(mBackgroundSurface, 1); - t.apply(); - } - } - - /** - * Animate veil's alpha to 1, fading it in. - */ - public void showVeil(SurfaceControl parentSurface, Rect taskBounds) { - if (!isReady() || isVisible()) { - return; - } - SurfaceControl.Transaction t = mSurfaceControlTransactionSupplier.get(); - showVeil(t, parentSurface, taskBounds, true /* fadeIn */); - } - - /** - * Update veil bounds to match bounds changes. - * @param newBounds bounds to update veil to. - */ - private void relayout(Rect newBounds, SurfaceControl.Transaction t) { - t.setWindowCrop(mVeilSurface, newBounds.width(), newBounds.height()); - final PointF iconPosition = calculateAppIconPosition(newBounds); - t.setPosition(mIconSurface, iconPosition.x, iconPosition.y); - t.setPosition(mParentSurface, newBounds.left, newBounds.top); - t.setWindowCrop(mParentSurface, newBounds.width(), newBounds.height()); - } - - /** - * Calls relayout to update task and veil bounds. - * @param newBounds bounds to update veil to. - */ - public void updateResizeVeil(Rect newBounds) { - if (!isVisible()) { - return; - } - SurfaceControl.Transaction t = mSurfaceControlTransactionSupplier.get(); - updateResizeVeil(t, newBounds); - } - - /** - * Calls relayout to update task and veil bounds. - * Finishes veil fade in if animation is currently running; this is to prevent empty space - * being visible behind the transparent veil during a fast resize. - * - * @param t a transaction to be applied in sync with the veil draw. - * @param newBounds bounds to update veil to. - */ - public void updateResizeVeil(SurfaceControl.Transaction t, Rect newBounds) { - if (!isVisible()) { - t.apply(); - return; - } - if (mVeilAnimator != null && mVeilAnimator.isStarted()) { - mVeilAnimator.removeAllUpdateListeners(); - mVeilAnimator.end(); - } - relayout(newBounds, t); - t.apply(); - } - - /** - * Animate veil's alpha to 0, fading it out. - */ - public void hideVeil() { - if (!isVisible()) { - return; - } - cancelAnimation(); - mVeilAnimator = new ValueAnimator(); - mVeilAnimator.setFloatValues(1, 0); - mVeilAnimator.setDuration(RESIZE_ALPHA_DURATION); - mVeilAnimator.addUpdateListener(animation -> { - SurfaceControl.Transaction t = mSurfaceControlTransactionSupplier.get(); - t.setAlpha(mBackgroundSurface, 1 - mVeilAnimator.getAnimatedFraction()); - t.setAlpha(mIconSurface, 1 - mVeilAnimator.getAnimatedFraction()); - t.apply(); - }); - mVeilAnimator.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - SurfaceControl.Transaction t = mSurfaceControlTransactionSupplier.get(); - t.hide(mBackgroundSurface); - t.hide(mIconSurface); - t.apply(); - } - }); - mVeilAnimator.start(); - mIsShowing = false; - } - - @ColorRes - private int getBackgroundColorId() { - Configuration configuration = mContext.getResources().getConfiguration(); - if ((configuration.uiMode & Configuration.UI_MODE_NIGHT_MASK) - == Configuration.UI_MODE_NIGHT_YES) { - return R.color.desktop_mode_resize_veil_dark; - } else { - return R.color.desktop_mode_resize_veil_light; - } - } - - private PointF calculateAppIconPosition(Rect parentBounds) { - return new PointF((float) parentBounds.width() / 2 - (float) mIconSize / 2, - (float) parentBounds.height() / 2 - (float) mIconSize / 2); - } - - private void cancelAnimation() { - if (mVeilAnimator != null) { - mVeilAnimator.removeAllUpdateListeners(); - mVeilAnimator.cancel(); - } - } - - /** - * Whether the resize veil is currently visible. - * - * Note: when animating a {@link ResizeVeil#hideVeil()}, the veil is considered visible as soon - * as the animation starts. - */ - private boolean isVisible() { - return mIsShowing; - } - - /** Whether the resize veil is ready to be shown. */ - private boolean isReady() { - return mViewHost != null; - } - - /** - * Dispose of veil when it is no longer needed, likely on close of its container decor. - */ - void dispose() { - cancelAnimation(); - mIsShowing = false; - mVeilAnimator = null; - - if (mViewHost != null) { - mViewHost.release(); - mViewHost = null; - } - final SurfaceControl.Transaction t = mSurfaceControlTransactionSupplier.get(); - if (mBackgroundSurface != null) { - t.remove(mBackgroundSurface); - mBackgroundSurface = null; - } - if (mIconSurface != null) { - t.remove(mIconSurface); - mIconSurface = null; - } - if (mVeilSurface != null) { - t.remove(mVeilSurface); - mVeilSurface = null; - } - t.apply(); - mDisplayController.removeDisplayWindowListener(mOnDisplaysChangedListener); - } - - interface SurfaceControlBuilderFactory { - default SurfaceControl.Builder create(@NonNull String name) { - return new SurfaceControl.Builder().setName(name); - } - default SurfaceControl.Builder create(@NonNull String name, - @NonNull SurfaceSession surfaceSession) { - return new SurfaceControl.Builder(surfaceSession).setName(name); - } - } -} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/ResizeVeil.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/ResizeVeil.kt new file mode 100644 index 000000000000..4f2d945e49f9 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/ResizeVeil.kt @@ -0,0 +1,417 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.wm.shell.windowdecor + +import android.animation.Animator +import android.animation.AnimatorListenerAdapter +import android.animation.ValueAnimator +import android.app.ActivityManager.RunningTaskInfo +import android.content.Context +import android.content.res.Configuration +import android.graphics.Bitmap +import android.graphics.Color +import android.graphics.PixelFormat +import android.graphics.PointF +import android.graphics.Rect +import android.os.Trace +import android.view.Display +import android.view.LayoutInflater +import android.view.SurfaceControl +import android.view.SurfaceControlViewHost +import android.view.SurfaceSession +import android.view.WindowManager +import android.view.WindowlessWindowManager +import android.widget.ImageView +import android.window.TaskConstants +import com.android.wm.shell.R +import com.android.wm.shell.common.DisplayController +import com.android.wm.shell.common.DisplayController.OnDisplaysChangedListener +import com.android.wm.shell.windowdecor.WindowDecoration.SurfaceControlViewHostFactory +import java.util.function.Supplier + +/** + * Creates and updates a veil that covers task contents on resize. + */ +class ResizeVeil @JvmOverloads constructor( + private val context: Context, + private val displayController: DisplayController, + private val appIcon: Bitmap, + private val taskInfo: RunningTaskInfo, + private var parentSurface: SurfaceControl, + private val surfaceControlTransactionSupplier: Supplier<SurfaceControl.Transaction>, + private val surfaceControlBuilderFactory: SurfaceControlBuilderFactory = + object : SurfaceControlBuilderFactory {}, + private val surfaceControlViewHostFactory: SurfaceControlViewHostFactory = + object : SurfaceControlViewHostFactory {} +) { + private val surfaceSession = SurfaceSession() + private lateinit var iconView: ImageView + private var iconSize = 0 + + /** A container surface to host the veil background and icon child surfaces. */ + private var veilSurface: SurfaceControl? = null + /** A color surface for the veil background. */ + private var backgroundSurface: SurfaceControl? = null + /** A surface that hosts a windowless window with the app icon. */ + private var iconSurface: SurfaceControl? = null + private var viewHost: SurfaceControlViewHost? = null + private var display: Display? = null + private var veilAnimator: ValueAnimator? = null + + /** + * Whether the resize veil is currently visible. + * + * Note: when animating a [ResizeVeil.hideVeil], the veil is considered visible as soon + * as the animation starts. + */ + private var isVisible = false + + private val onDisplaysChangedListener: OnDisplaysChangedListener = + object : OnDisplaysChangedListener { + override fun onDisplayAdded(displayId: Int) { + if (taskInfo.displayId != displayId) { + return + } + displayController.removeDisplayWindowListener(this) + setupResizeVeil() + } + } + + private val backgroundColorId: Int + get() { + val configuration = context.resources.configuration + return if (configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK + == Configuration.UI_MODE_NIGHT_YES) { + R.color.desktop_mode_resize_veil_dark + } else { + R.color.desktop_mode_resize_veil_light + } + } + + /** + * Whether the resize veil is ready to be shown. + */ + private val isReady: Boolean + get() = viewHost != null + + init { + setupResizeVeil() + } + + /** + * Create the veil in its default invisible state. + */ + private fun setupResizeVeil() { + if (!obtainDisplayOrRegisterListener()) { + // Display may not be available yet, skip this until then. + return + } + Trace.beginSection("ResizeVeil#setupResizeVeil") + veilSurface = surfaceControlBuilderFactory + .create("Resize veil of Task=" + taskInfo.taskId) + .setContainerLayer() + .setHidden(true) + .setParent(parentSurface) + .setCallsite("ResizeVeil#setupResizeVeil") + .build() + backgroundSurface = surfaceControlBuilderFactory + .create("Resize veil background of Task=" + taskInfo.taskId, surfaceSession) + .setColorLayer() + .setHidden(true) + .setParent(veilSurface) + .setCallsite("ResizeVeil#setupResizeVeil") + .build() + iconSurface = surfaceControlBuilderFactory + .create("Resize veil icon of Task=" + taskInfo.taskId) + .setContainerLayer() + .setHidden(true) + .setParent(veilSurface) + .setCallsite("ResizeVeil#setupResizeVeil") + .build() + iconSize = context.resources + .getDimensionPixelSize(R.dimen.desktop_mode_resize_veil_icon_size) + val root = LayoutInflater.from(context) + .inflate(R.layout.desktop_mode_resize_veil, null /* root */) + iconView = root.requireViewById(R.id.veil_application_icon) + iconView.setImageBitmap(appIcon) + val lp = WindowManager.LayoutParams( + iconSize, + iconSize, + WindowManager.LayoutParams.TYPE_APPLICATION, + WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, + PixelFormat.TRANSPARENT) + lp.title = "Resize veil icon window of Task=" + taskInfo.taskId + lp.setTrustedOverlay() + val wwm = WindowlessWindowManager(taskInfo.configuration, + iconSurface, null /* hostInputToken */) + viewHost = surfaceControlViewHostFactory.create(context, display, wwm, "ResizeVeil") + viewHost?.setView(root, lp) + Trace.endSection() + } + + private fun obtainDisplayOrRegisterListener(): Boolean { + display = displayController.getDisplay(taskInfo.displayId) + if (display == null) { + displayController.addDisplayWindowListener(onDisplaysChangedListener) + return false + } + return true + } + + /** + * Shows the veil surface/view. + * + * @param t the transaction to apply in sync with the veil draw + * @param parent the surface that the veil should be a child of + * @param taskBounds the bounds of the task that owns the veil + * @param fadeIn if true, the veil will fade-in with an animation, if false, it will be shown + * immediately + */ + fun showVeil( + t: SurfaceControl.Transaction, + parent: SurfaceControl, + taskBounds: Rect, + fadeIn: Boolean + ) { + if (!isReady || isVisible) { + t.apply() + return + } + isVisible = true + val background = backgroundSurface + val icon = iconSurface + val veil = veilSurface + if (background == null || icon == null || veil == null) return + + // Parent surface can change, ensure it is up to date. + if (parent != parentSurface) { + t.reparent(veil, parent) + parentSurface = parent + } + + + t.show(veil) + .setLayer(veil, VEIL_CONTAINER_LAYER) + .setLayer(icon, VEIL_ICON_LAYER) + .setLayer(background, VEIL_BACKGROUND_LAYER) + .setColor(background, + Color.valueOf(context.getColor(backgroundColorId)).components) + relayout(taskBounds, t) + if (fadeIn) { + cancelAnimation() + val veilAnimT = surfaceControlTransactionSupplier.get() + val iconAnimT = surfaceControlTransactionSupplier.get() + veilAnimator = ValueAnimator.ofFloat(0f, 1f).apply { + duration = RESIZE_ALPHA_DURATION + addUpdateListener { + veilAnimT.setAlpha(background, animatedValue as Float) + .apply() + } + addListener(object : AnimatorListenerAdapter() { + override fun onAnimationStart(animation: Animator) { + veilAnimT.show(background) + .setAlpha(background, 0f) + .apply() + } + + override fun onAnimationEnd(animation: Animator) { + veilAnimT.setAlpha(background, 1f).apply() + } + }) + } + val iconAnimator = ValueAnimator.ofFloat(0f, 1f).apply { + duration = RESIZE_ALPHA_DURATION + addUpdateListener { + iconAnimT.setAlpha(icon, animatedValue as Float) + .apply() + } + addListener(object : AnimatorListenerAdapter() { + override fun onAnimationStart(animation: Animator) { + iconAnimT.show(icon) + .setAlpha(icon, 0f) + .apply() + } + + override fun onAnimationEnd(animation: Animator) { + iconAnimT.setAlpha(icon, 1f).apply() + } + }) + } + + // Let the animators show it with the correct alpha value once the animation starts. + t.hide(icon) + .hide(background) + .apply() + veilAnimator?.start() + iconAnimator.start() + } else { + // Show the veil immediately. + t.show(icon) + .show(background) + .setAlpha(icon, 1f) + .setAlpha(background, 1f) + .apply() + } + } + + /** + * Animate veil's alpha to 1, fading it in. + */ + fun showVeil(parentSurface: SurfaceControl, taskBounds: Rect) { + if (!isReady || isVisible) { + return + } + val t = surfaceControlTransactionSupplier.get() + showVeil(t, parentSurface, taskBounds, true /* fadeIn */) + } + + /** + * Update veil bounds to match bounds changes. + * @param newBounds bounds to update veil to. + */ + private fun relayout(newBounds: Rect, t: SurfaceControl.Transaction) { + val iconPosition = calculateAppIconPosition(newBounds) + val veil = veilSurface + val icon = iconSurface + if (veil == null || icon == null) return + t.setWindowCrop(veil, newBounds.width(), newBounds.height()) + .setPosition(icon, iconPosition.x, iconPosition.y) + .setPosition(parentSurface, newBounds.left.toFloat(), newBounds.top.toFloat()) + .setWindowCrop(parentSurface, newBounds.width(), newBounds.height()) + } + + /** + * Calls relayout to update task and veil bounds. + * @param newBounds bounds to update veil to. + */ + fun updateResizeVeil(newBounds: Rect) { + if (!isVisible) { + return + } + val t = surfaceControlTransactionSupplier.get() + updateResizeVeil(t, newBounds) + } + + /** + * Calls relayout to update task and veil bounds. + * Finishes veil fade in if animation is currently running; this is to prevent empty space + * being visible behind the transparent veil during a fast resize. + * + * @param t a transaction to be applied in sync with the veil draw. + * @param newBounds bounds to update veil to. + */ + fun updateResizeVeil(t: SurfaceControl.Transaction, newBounds: Rect) { + if (!isVisible) { + t.apply() + return + } + veilAnimator?.let { animator -> + if (animator.isStarted) { + animator.removeAllUpdateListeners() + animator.end() + } + } + relayout(newBounds, t) + t.apply() + } + + /** + * Animate veil's alpha to 0, fading it out. + */ + fun hideVeil() { + if (!isVisible) { + return + } + cancelAnimation() + val background = backgroundSurface + val icon = iconSurface + if (background == null || icon == null) return + + veilAnimator = ValueAnimator.ofFloat(1f, 0f).apply { + duration = RESIZE_ALPHA_DURATION + addUpdateListener { + surfaceControlTransactionSupplier.get() + .setAlpha(background, animatedValue as Float) + .setAlpha(icon, animatedValue as Float) + .apply() + } + addListener(object : AnimatorListenerAdapter() { + override fun onAnimationEnd(animation: Animator) { + surfaceControlTransactionSupplier.get() + .hide(background) + .hide(icon) + .apply() + } + }) + } + veilAnimator?.start() + isVisible = false + } + + private fun calculateAppIconPosition(parentBounds: Rect): PointF { + return PointF(parentBounds.width().toFloat() / 2 - iconSize.toFloat() / 2, + parentBounds.height().toFloat() / 2 - iconSize.toFloat() / 2) + } + + private fun cancelAnimation() { + veilAnimator?.removeAllUpdateListeners() + veilAnimator?.cancel() + } + + /** + * Dispose of veil when it is no longer needed, likely on close of its container decor. + */ + fun dispose() { + cancelAnimation() + veilAnimator = null + isVisible = false + + viewHost?.release() + viewHost = null + + val t: SurfaceControl.Transaction = surfaceControlTransactionSupplier.get() + backgroundSurface?.let { background -> t.remove(background) } + backgroundSurface = null + iconSurface?.let { icon -> t.remove(icon) } + iconSurface = null + veilSurface?.let { veil -> t.remove(veil) } + veilSurface = null + t.apply() + displayController.removeDisplayWindowListener(onDisplaysChangedListener) + } + + interface SurfaceControlBuilderFactory { + fun create(name: String): SurfaceControl.Builder { + return SurfaceControl.Builder().setName(name) + } + + fun create(name: String, surfaceSession: SurfaceSession): SurfaceControl.Builder { + return SurfaceControl.Builder(surfaceSession).setName(name) + } + } + + companion object { + private const val TAG = "ResizeVeil" + private const val RESIZE_ALPHA_DURATION = 100L + private const val VEIL_CONTAINER_LAYER = TaskConstants.TASK_CHILD_LAYER_RESIZE_VEIL + + /** The background is a child of the veil container layer and goes at the bottom. */ + private const val VEIL_BACKGROUND_LAYER = 0 + + /** The icon is a child of the veil container layer and goes in front of the background. */ + private const val VEIL_ICON_LAYER = 1 + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskOperations.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskOperations.java index 53d4e2701849..ad238c35dd83 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskOperations.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskOperations.java @@ -52,19 +52,19 @@ class TaskOperations { mSyncQueue = syncQueue; } - void injectBackKey() { - sendBackEvent(KeyEvent.ACTION_DOWN); - sendBackEvent(KeyEvent.ACTION_UP); + void injectBackKey(int displayId) { + sendBackEvent(KeyEvent.ACTION_DOWN, displayId); + sendBackEvent(KeyEvent.ACTION_UP, displayId); } - private void sendBackEvent(int action) { + private void sendBackEvent(int action, int displayId) { final long when = SystemClock.uptimeMillis(); final KeyEvent ev = new KeyEvent(when, when, action, KeyEvent.KEYCODE_BACK, 0 /* repeat */, 0 /* metaState */, KeyCharacterMap.VIRTUAL_KEYBOARD, 0 /* scancode */, KeyEvent.FLAG_FROM_SYSTEM | KeyEvent.FLAG_VIRTUAL_HARD_KEY, InputDevice.SOURCE_KEYBOARD); - ev.setDisplayId(mContext.getDisplay().getDisplayId()); + ev.setDisplayId(displayId); if (!mContext.getSystemService(InputManager.class) .injectInputEvent(ev, InputManager.INJECT_INPUT_EVENT_MODE_ASYNC)) { Log.e(TAG, "Inject input event fail"); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java index 0dc512835d65..2ae3cb9ef3c0 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java @@ -51,6 +51,7 @@ import android.window.TaskConstants; import android.window.WindowContainerToken; import android.window.WindowContainerTransaction; +import com.android.internal.annotations.VisibleForTesting; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.shared.DesktopModeStatus; @@ -687,7 +688,8 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> } } - interface SurfaceControlViewHostFactory { + @VisibleForTesting + public interface SurfaceControlViewHostFactory { default SurfaceControlViewHost create(Context c, Display d, WindowlessWindowManager wmm) { return new SurfaceControlViewHost(c, d, wmm, "WindowDecoration"); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/extension/TaskInfo.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/extension/TaskInfo.kt index ec204714c341..7ade9876d28a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/extension/TaskInfo.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/extension/TaskInfo.kt @@ -23,13 +23,13 @@ import android.view.WindowInsetsController.APPEARANCE_TRANSPARENT_CAPTION_BAR_BA val TaskInfo.isTransparentCaptionBarAppearance: Boolean get() { - val appearance = taskDescription?.systemBarsAppearance ?: 0 + val appearance = taskDescription?.topOpaqueSystemBarsAppearance ?: 0 return (appearance and APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND) != 0 } val TaskInfo.isLightCaptionBarAppearance: Boolean get() { - val appearance = taskDescription?.systemBarsAppearance ?: 0 + val appearance = taskDescription?.topOpaqueSystemBarsAppearance ?: 0 return (appearance and APPEARANCE_LIGHT_CAPTION_BARS) != 0 } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeUiEventLoggerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeUiEventLoggerTest.kt index 285e5b6a04a5..51b291c0b7a4 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeUiEventLoggerTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeUiEventLoggerTest.kt @@ -39,7 +39,7 @@ import org.junit.runner.RunWith class DesktopModeUiEventLoggerTest : ShellTestCase() { private lateinit var uiEventLoggerFake: UiEventLoggerFake private lateinit var logger: DesktopModeUiEventLogger - private val instanceIdSequence = InstanceIdSequence(10) + private val instanceIdSequence = InstanceIdSequence(/* instanceIdMax */ 1 shl 20) @Before diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt index aa2cee79fcfc..9c1dc22bcef2 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt @@ -26,6 +26,7 @@ import android.content.Context import android.graphics.Rect import android.hardware.display.DisplayManager import android.hardware.display.VirtualDisplay +import android.hardware.input.InputManager import android.os.Handler import android.platform.test.annotations.EnableFlags import android.platform.test.annotations.RequiresFlagsEnabled @@ -42,8 +43,10 @@ import android.view.InputChannel import android.view.InputMonitor import android.view.InsetsSource import android.view.InsetsState +import android.view.KeyEvent import android.view.SurfaceControl import android.view.SurfaceView +import android.view.View import android.view.WindowInsets.Type.navigationBars import android.view.WindowInsets.Type.statusBars import androidx.test.filters.SmallTest @@ -51,6 +54,7 @@ import com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession import com.android.dx.mockito.inline.extended.StaticMockitoSession import com.android.window.flags.Flags +import com.android.wm.shell.R import com.android.wm.shell.RootTaskDisplayAreaOrganizer import com.android.wm.shell.ShellTaskOrganizer import com.android.wm.shell.ShellTestCase @@ -61,6 +65,7 @@ import com.android.wm.shell.common.DisplayLayout import com.android.wm.shell.common.ShellExecutor import com.android.wm.shell.common.SyncTransactionQueue import com.android.wm.shell.desktopmode.DesktopTasksController +import com.android.wm.shell.freeform.FreeformTaskTransitionStarter import com.android.wm.shell.shared.DesktopModeStatus import com.android.wm.shell.sysui.KeyguardChangeListener import com.android.wm.shell.sysui.ShellCommandHandler @@ -70,6 +75,7 @@ import com.android.wm.shell.transition.Transitions import com.android.wm.shell.windowdecor.DesktopModeWindowDecorViewModel.DesktopModeOnInsetsChangedListener import java.util.Optional import java.util.function.Supplier +import org.junit.Assert.assertEquals import org.junit.Before import org.junit.Rule import org.junit.Test @@ -279,6 +285,41 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() { } @Test + fun testBackEventHasRightDisplayId() { + val secondaryDisplay = createVirtualDisplay() ?: return + val secondaryDisplayId = secondaryDisplay.display.displayId + val task = createTask( + displayId = secondaryDisplayId, + windowingMode = WINDOWING_MODE_FREEFORM + ) + val windowDecor = setUpMockDecorationForTask(task) + + onTaskOpening(task) + val onClickListenerCaptor = argumentCaptor<View.OnClickListener>() + verify(windowDecor).setCaptionListeners( + onClickListenerCaptor.capture(), any(), any(), any()) + + val onClickListener = onClickListenerCaptor.firstValue + val view = mock(View::class.java) + whenever(view.id).thenReturn(R.id.back_button) + + val inputManager = mock(InputManager::class.java) + mContext.addMockSystemService(InputManager::class.java, inputManager) + + val freeformTaskTransitionStarter = mock(FreeformTaskTransitionStarter::class.java) + desktopModeWindowDecorViewModel + .setFreeformTaskTransitionStarter(freeformTaskTransitionStarter) + + onClickListener.onClick(view) + + val eventCaptor = argumentCaptor<KeyEvent>() + verify(inputManager, times(2)).injectInputEvent(eventCaptor.capture(), anyInt()) + + assertEquals(secondaryDisplayId, eventCaptor.firstValue.displayId) + assertEquals(secondaryDisplayId, eventCaptor.secondValue.displayId) + } + + @Test fun testCaptionIsNotCreatedWhenKeyguardIsVisible() { val task = createTask(windowingMode = WINDOWING_MODE_FULLSCREEN, focused = true) val keyguardListenerCaptor = argumentCaptor<KeyguardChangeListener>() diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java index 3ca9b57e03fd..a731e5394bdf 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java @@ -228,7 +228,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { public void updateRelayoutParams_freeformAndTransparentAppearance_allowsInputFallthrough() { final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true); taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM); - taskInfo.taskDescription.setSystemBarsAppearance( + taskInfo.taskDescription.setTopOpaqueSystemBarsAppearance( APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND); final RelayoutParams relayoutParams = new RelayoutParams(); @@ -246,7 +246,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { public void updateRelayoutParams_freeformButOpaqueAppearance_disallowsInputFallthrough() { final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true); taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM); - taskInfo.taskDescription.setSystemBarsAppearance(0); + taskInfo.taskDescription.setTopOpaqueSystemBarsAppearance(0); final RelayoutParams relayoutParams = new RelayoutParams(); DesktopModeWindowDecoration.updateRelayoutParams( diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/ResizeVeilTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/ResizeVeilTest.kt index 87425915fbf7..5da57c50e6c1 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/ResizeVeilTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/ResizeVeilTest.kt @@ -33,6 +33,8 @@ import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentCaptor +import org.mockito.ArgumentMatchers.anyFloat +import org.mockito.ArgumentMatchers.anyInt import org.mockito.Mock import org.mockito.Spy import org.mockito.kotlin.any @@ -102,6 +104,14 @@ class ResizeVeilTest : ShellTestCase() { .create("Resize veil icon of Task=" + taskInfo.taskId)) .thenReturn(spyIconSurfaceBuilder) doReturn(mockIconSurface).whenever(spyIconSurfaceBuilder).build() + + doReturn(mockTransaction).whenever(mockTransaction).setLayer(any(), anyInt()) + doReturn(mockTransaction).whenever(mockTransaction).setAlpha(any(), anyFloat()) + doReturn(mockTransaction).whenever(mockTransaction).show(any()) + doReturn(mockTransaction).whenever(mockTransaction).hide(any()) + doReturn(mockTransaction).whenever(mockTransaction) + .setPosition(any(), anyFloat(), anyFloat()) + doReturn(mockTransaction).whenever(mockTransaction).setWindowCrop(any(), anyInt(), anyInt()) } @Test @@ -139,52 +149,48 @@ class ResizeVeilTest : ShellTestCase() { @Test fun showVeil() { val veil = createResizeVeil() - val tx = mock<SurfaceControl.Transaction>() - veil.showVeil(tx, mock(), Rect(0, 0, 100, 100), false /* fadeIn */) + veil.showVeil(mockTransaction, mock(), Rect(0, 0, 100, 100), false /* fadeIn */) - verify(tx).show(mockResizeVeilSurface) - verify(tx).show(mockBackgroundSurface) - verify(tx).show(mockIconSurface) - verify(tx).apply() + verify(mockTransaction).show(mockResizeVeilSurface) + verify(mockTransaction).show(mockBackgroundSurface) + verify(mockTransaction).show(mockIconSurface) + verify(mockTransaction).apply() } @Test fun showVeil_displayUnavailable_doesNotShow() { val veil = createResizeVeil(withDisplayAvailable = false) - val tx = mock<SurfaceControl.Transaction>() - veil.showVeil(tx, mock(), Rect(0, 0, 100, 100), false /* fadeIn */) + veil.showVeil(mockTransaction, mock(), Rect(0, 0, 100, 100), false /* fadeIn */) - verify(tx, never()).show(mockResizeVeilSurface) - verify(tx, never()).show(mockBackgroundSurface) - verify(tx, never()).show(mockIconSurface) - verify(tx).apply() + verify(mockTransaction, never()).show(mockResizeVeilSurface) + verify(mockTransaction, never()).show(mockBackgroundSurface) + verify(mockTransaction, never()).show(mockIconSurface) + verify(mockTransaction).apply() } @Test fun showVeil_alreadyVisible_doesNotShowAgain() { val veil = createResizeVeil() - val tx = mock<SurfaceControl.Transaction>() - veil.showVeil(tx, mock(), Rect(0, 0, 100, 100), false /* fadeIn */) - veil.showVeil(tx, mock(), Rect(0, 0, 100, 100), false /* fadeIn */) + veil.showVeil(mockTransaction, mock(), Rect(0, 0, 100, 100), false /* fadeIn */) + veil.showVeil(mockTransaction, mock(), Rect(0, 0, 100, 100), false /* fadeIn */) - verify(tx, times(1)).show(mockResizeVeilSurface) - verify(tx, times(1)).show(mockBackgroundSurface) - verify(tx, times(1)).show(mockIconSurface) - verify(tx, times(2)).apply() + verify(mockTransaction, times(1)).show(mockResizeVeilSurface) + verify(mockTransaction, times(1)).show(mockBackgroundSurface) + verify(mockTransaction, times(1)).show(mockIconSurface) + verify(mockTransaction, times(2)).apply() } @Test fun showVeil_reparentsVeilToNewParent() { val veil = createResizeVeil(parent = mock()) - val tx = mock<SurfaceControl.Transaction>() val newParent = mock<SurfaceControl>() - veil.showVeil(tx, newParent, Rect(0, 0, 100, 100), false /* fadeIn */) + veil.showVeil(mockTransaction, newParent, Rect(0, 0, 100, 100), false /* fadeIn */) - verify(tx).reparent(mockResizeVeilSurface, newParent) + verify(mockTransaction).reparent(mockResizeVeilSurface, newParent) } @Test diff --git a/libs/input/MouseCursorController.cpp b/libs/input/MouseCursorController.cpp index f1ee3256dbee..eecc741a3bbb 100644 --- a/libs/input/MouseCursorController.cpp +++ b/libs/input/MouseCursorController.cpp @@ -165,6 +165,15 @@ void MouseCursorController::setStylusHoverMode(bool stylusHoverMode) { } } +void MouseCursorController::setSkipScreenshot(bool skip) { + std::scoped_lock lock(mLock); + if (mLocked.skipScreenshot == skip) { + return; + } + mLocked.skipScreenshot = skip; + updatePointerLocked(); +} + void MouseCursorController::reloadPointerResources(bool getAdditionalMouseResources) { std::scoped_lock lock(mLock); @@ -352,6 +361,7 @@ void MouseCursorController::updatePointerLocked() REQUIRES(mLock) { mLocked.pointerSprite->setLayer(Sprite::BASE_LAYER_POINTER); mLocked.pointerSprite->setPosition(mLocked.pointerX, mLocked.pointerY); mLocked.pointerSprite->setDisplayId(mLocked.viewport.displayId); + mLocked.pointerSprite->setSkipScreenshot(mLocked.skipScreenshot); if (mLocked.pointerAlpha > 0) { mLocked.pointerSprite->setAlpha(mLocked.pointerAlpha); diff --git a/libs/input/MouseCursorController.h b/libs/input/MouseCursorController.h index dc7e8ca16c8a..78f6413ff111 100644 --- a/libs/input/MouseCursorController.h +++ b/libs/input/MouseCursorController.h @@ -53,6 +53,9 @@ public: void setDisplayViewport(const DisplayViewport& viewport, bool getAdditionalMouseResources); void setStylusHoverMode(bool stylusHoverMode); + // Set/Unset flag to hide the mouse cursor on the mirrored display + void setSkipScreenshot(bool skip); + void updatePointerIcon(PointerIconStyle iconId); void setCustomPointerIcon(const SpriteIcon& icon); void reloadPointerResources(bool getAdditionalMouseResources); @@ -94,6 +97,7 @@ private: PointerIconStyle requestedPointerType; PointerIconStyle resolvedPointerType; + bool skipScreenshot{false}; bool animating{false}; } mLocked GUARDED_BY(mLock); diff --git a/libs/input/PointerController.cpp b/libs/input/PointerController.cpp index cca1b07c3118..11b27a214984 100644 --- a/libs/input/PointerController.cpp +++ b/libs/input/PointerController.cpp @@ -286,13 +286,16 @@ void PointerController::setCustomPointerIcon(const SpriteIcon& icon) { mCursorController.setCustomPointerIcon(icon); } -void PointerController::setSkipScreenshot(ui::LogicalDisplayId displayId, bool skip) { +void PointerController::setSkipScreenshotFlagForDisplay(ui::LogicalDisplayId displayId) { std::scoped_lock lock(getLock()); - if (skip) { - mLocked.displaysToSkipScreenshot.insert(displayId); - } else { - mLocked.displaysToSkipScreenshot.erase(displayId); - } + mLocked.displaysToSkipScreenshot.insert(displayId); + mCursorController.setSkipScreenshot(true); +} + +void PointerController::clearSkipScreenshotFlags() { + std::scoped_lock lock(getLock()); + mLocked.displaysToSkipScreenshot.clear(); + mCursorController.setSkipScreenshot(false); } void PointerController::doInactivityTimeout() { diff --git a/libs/input/PointerController.h b/libs/input/PointerController.h index c6430f7f36ff..4d1e1d733cc1 100644 --- a/libs/input/PointerController.h +++ b/libs/input/PointerController.h @@ -66,7 +66,8 @@ public: void clearSpots() override; void updatePointerIcon(PointerIconStyle iconId) override; void setCustomPointerIcon(const SpriteIcon& icon) override; - void setSkipScreenshot(ui::LogicalDisplayId displayId, bool skip) override; + void setSkipScreenshotFlagForDisplay(ui::LogicalDisplayId displayId) override; + void clearSkipScreenshotFlags() override; virtual void setInactivityTimeout(InactivityTimeout inactivityTimeout); void doInactivityTimeout(); diff --git a/libs/input/tests/PointerController_test.cpp b/libs/input/tests/PointerController_test.cpp index 2dcb1f1d1650..cbef68e2eb8f 100644 --- a/libs/input/tests/PointerController_test.cpp +++ b/libs/input/tests/PointerController_test.cpp @@ -183,12 +183,16 @@ private: MyLooper() : Looper(false) {} ~MyLooper() = default; }; - sp<MyLooper> mLooper; std::thread mThread; + +protected: + sp<MyLooper> mLooper; }; -PointerControllerTest::PointerControllerTest() : mPointerSprite(new NiceMock<MockSprite>), - mLooper(new MyLooper), mThread(&PointerControllerTest::loopThread, this) { +PointerControllerTest::PointerControllerTest() + : mPointerSprite(new NiceMock<MockSprite>), + mThread(&PointerControllerTest::loopThread, this), + mLooper(new MyLooper) { mSpriteController.reset(new NiceMock<MockSpriteController>(mLooper)); mPolicy = new MockPointerControllerPolicyInterface(); @@ -339,7 +343,7 @@ TEST_F(PointerControllerTest, updatesSkipScreenshotFlagForTouchSpots) { testing::Mock::VerifyAndClearExpectations(testSpotSprite.get()); // Marking the display to skip screenshot should update sprite as well - mPointerController->setSkipScreenshot(ui::LogicalDisplayId::DEFAULT, true); + mPointerController->setSkipScreenshotFlagForDisplay(ui::LogicalDisplayId::DEFAULT); EXPECT_CALL(*testSpotSprite, setSkipScreenshot).With(testing::Args<0>(true)); // Update spots to sync state with sprite @@ -348,13 +352,53 @@ TEST_F(PointerControllerTest, updatesSkipScreenshotFlagForTouchSpots) { testing::Mock::VerifyAndClearExpectations(testSpotSprite.get()); // Reset flag and verify again - mPointerController->setSkipScreenshot(ui::LogicalDisplayId::DEFAULT, false); + mPointerController->clearSkipScreenshotFlags(); EXPECT_CALL(*testSpotSprite, setSkipScreenshot).With(testing::Args<0>(false)); mPointerController->setSpots(&testSpotCoords, testIdToIndex.cbegin(), testIdBits, ui::LogicalDisplayId::DEFAULT); testing::Mock::VerifyAndClearExpectations(testSpotSprite.get()); } +class PointerControllerSkipScreenshotFlagTest + : public PointerControllerTest, + public testing::WithParamInterface<PointerControllerInterface::ControllerType> {}; + +TEST_P(PointerControllerSkipScreenshotFlagTest, updatesSkipScreenshotFlag) { + sp<MockSprite> testPointerSprite(new NiceMock<MockSprite>); + EXPECT_CALL(*mSpriteController, createSprite).WillOnce(Return(testPointerSprite)); + + // Create a pointer controller + mPointerController = + PointerController::create(mPolicy, mLooper, *mSpriteController, GetParam()); + ensureDisplayViewportIsSet(ui::LogicalDisplayId::DEFAULT); + + // By default skip screenshot flag is not set for the sprite + EXPECT_CALL(*testPointerSprite, setSkipScreenshot).With(testing::Args<0>(false)); + + // Update pointer to sync state with sprite + mPointerController->setPosition(100, 100); + testing::Mock::VerifyAndClearExpectations(testPointerSprite.get()); + + // Marking the controller to skip screenshot should update pointer sprite + mPointerController->setSkipScreenshotFlagForDisplay(ui::LogicalDisplayId::DEFAULT); + EXPECT_CALL(*testPointerSprite, setSkipScreenshot).With(testing::Args<0>(true)); + + // Update pointer to sync state with sprite + mPointerController->move(10, 10); + testing::Mock::VerifyAndClearExpectations(testPointerSprite.get()); + + // Reset flag and verify again + mPointerController->clearSkipScreenshotFlags(); + EXPECT_CALL(*testPointerSprite, setSkipScreenshot).With(testing::Args<0>(false)); + mPointerController->move(10, 10); + testing::Mock::VerifyAndClearExpectations(testPointerSprite.get()); +} + +INSTANTIATE_TEST_SUITE_P(PointerControllerSkipScreenshotFlagTest, + PointerControllerSkipScreenshotFlagTest, + testing::Values(PointerControllerInterface::ControllerType::MOUSE, + PointerControllerInterface::ControllerType::STYLUS)); + class PointerControllerWindowInfoListenerTest : public Test {}; TEST_F(PointerControllerWindowInfoListenerTest, diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java index 679e8a1b95e6..5672cd54e369 100644 --- a/media/java/android/media/MediaRouter2.java +++ b/media/java/android/media/MediaRouter2.java @@ -681,8 +681,19 @@ public final class MediaRouter2 { /** * Registers a callback to discover routes and to receive events when they change. * + * <p>Clients can register multiple callbacks, as long as the {@link RouteCallback} instances + * are different. Each callback can provide a unique {@link RouteDiscoveryPreference preference} + * and will only receive updates related to that set preference. + * * <p>If the specified callback is already registered, its registration will be updated for the * given {@link Executor executor} and {@link RouteDiscoveryPreference discovery preference}. + * + * <p>{@link #getInstance(Context) Local routers} must register a route callback to register in + * the system and start receiving updates. Otherwise, all operations will be no-ops. + * + * <p>Any discovery preference passed by a {@link #getInstance(Context, String) proxy router} + * will be ignored and will receive route updates based on the preference set by its matching + * local router. */ public void registerRouteCallback( @NonNull @CallbackExecutor Executor executor, diff --git a/media/java/android/media/session/MediaController.java b/media/java/android/media/session/MediaController.java index 70462effaa54..442ccdcddb2b 100644 --- a/media/java/android/media/session/MediaController.java +++ b/media/java/android/media/session/MediaController.java @@ -141,10 +141,9 @@ public final class MediaController { } try { return mSessionBinder.sendMediaButton(mContext.getPackageName(), keyEvent); - } catch (RemoteException e) { - // System is dead. =( + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); } - return false; } /** @@ -155,9 +154,8 @@ public final class MediaController { public @Nullable PlaybackState getPlaybackState() { try { return mSessionBinder.getPlaybackState(); - } catch (RemoteException e) { - Log.wtf(TAG, "Error calling getPlaybackState.", e); - return null; + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); } } @@ -169,9 +167,8 @@ public final class MediaController { public @Nullable MediaMetadata getMetadata() { try { return mSessionBinder.getMetadata(); - } catch (RemoteException e) { - Log.wtf(TAG, "Error calling getMetadata.", e); - return null; + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); } } @@ -185,10 +182,9 @@ public final class MediaController { try { ParceledListSlice list = mSessionBinder.getQueue(); return list == null ? null : list.getList(); - } catch (RemoteException e) { - Log.wtf(TAG, "Error calling getQueue.", e); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); } - return null; } /** @@ -197,10 +193,9 @@ public final class MediaController { public @Nullable CharSequence getQueueTitle() { try { return mSessionBinder.getQueueTitle(); - } catch (RemoteException e) { - Log.wtf(TAG, "Error calling getQueueTitle", e); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); } - return null; } /** @@ -209,10 +204,9 @@ public final class MediaController { public @Nullable Bundle getExtras() { try { return mSessionBinder.getExtras(); - } catch (RemoteException e) { - Log.wtf(TAG, "Error calling getExtras", e); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); } - return null; } /** @@ -232,9 +226,8 @@ public final class MediaController { public int getRatingType() { try { return mSessionBinder.getRatingType(); - } catch (RemoteException e) { - Log.wtf(TAG, "Error calling getRatingType.", e); - return Rating.RATING_NONE; + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); } } @@ -246,10 +239,9 @@ public final class MediaController { public long getFlags() { try { return mSessionBinder.getFlags(); - } catch (RemoteException e) { - Log.wtf(TAG, "Error calling getFlags.", e); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); } - return 0; } /** Returns the current playback info for this session. */ @@ -271,10 +263,9 @@ public final class MediaController { public @Nullable PendingIntent getSessionActivity() { try { return mSessionBinder.getLaunchPendingIntent(); - } catch (RemoteException e) { - Log.wtf(TAG, "Error calling getPendingIntent.", e); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); } - return null; } /** @@ -304,8 +295,8 @@ public final class MediaController { // AppOpsManager usages. mSessionBinder.setVolumeTo(mContext.getPackageName(), mContext.getOpPackageName(), value, flags); - } catch (RemoteException e) { - Log.wtf(TAG, "Error calling setVolumeTo.", e); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); } } @@ -329,8 +320,8 @@ public final class MediaController { // AppOpsManager usages. mSessionBinder.adjustVolume(mContext.getPackageName(), mContext.getOpPackageName(), direction, flags); - } catch (RemoteException e) { - Log.wtf(TAG, "Error calling adjustVolumeBy.", e); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); } } @@ -395,8 +386,8 @@ public final class MediaController { } try { mSessionBinder.sendCommand(mContext.getPackageName(), command, args, cb); - } catch (RemoteException e) { - Log.d(TAG, "Dead object in sendCommand.", e); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); } } @@ -409,8 +400,8 @@ public final class MediaController { if (mPackageName == null) { try { mPackageName = mSessionBinder.getPackageName(); - } catch (RemoteException e) { - Log.d(TAG, "Dead object in getPackageName.", e); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); } } return mPackageName; @@ -430,8 +421,8 @@ public final class MediaController { // Get info from the connected session. try { mSessionInfo = mSessionBinder.getSessionInfo(); - } catch (RemoteException e) { - Log.d(TAG, "Dead object in getSessionInfo.", e); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); } if (mSessionInfo == null) { @@ -454,8 +445,8 @@ public final class MediaController { if (mTag == null) { try { mTag = mSessionBinder.getTag(); - } catch (RemoteException e) { - Log.d(TAG, "Dead object in getTag.", e); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); } } return mTag; @@ -485,8 +476,8 @@ public final class MediaController { try { mSessionBinder.registerCallback(mContext.getPackageName(), mCbStub); mCbRegistered = true; - } catch (RemoteException e) { - Log.e(TAG, "Dead object in registerCallback", e); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); } } } @@ -504,8 +495,8 @@ public final class MediaController { if (mCbRegistered && mCallbacks.size() == 0) { try { mSessionBinder.unregisterCallback(mCbStub); - } catch (RemoteException e) { - Log.e(TAG, "Dead object in removeCallbackLocked"); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); } mCbRegistered = false; } @@ -641,8 +632,8 @@ public final class MediaController { public void prepare() { try { mSessionBinder.prepare(mContext.getPackageName()); - } catch (RemoteException e) { - Log.wtf(TAG, "Error calling prepare.", e); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); } } @@ -665,8 +656,8 @@ public final class MediaController { } try { mSessionBinder.prepareFromMediaId(mContext.getPackageName(), mediaId, extras); - } catch (RemoteException e) { - Log.wtf(TAG, "Error calling prepare(" + mediaId + ").", e); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); } } @@ -691,8 +682,8 @@ public final class MediaController { } try { mSessionBinder.prepareFromSearch(mContext.getPackageName(), query, extras); - } catch (RemoteException e) { - Log.wtf(TAG, "Error calling prepare(" + query + ").", e); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); } } @@ -715,8 +706,8 @@ public final class MediaController { } try { mSessionBinder.prepareFromUri(mContext.getPackageName(), uri, extras); - } catch (RemoteException e) { - Log.wtf(TAG, "Error calling prepare(" + uri + ").", e); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); } } @@ -726,8 +717,8 @@ public final class MediaController { public void play() { try { mSessionBinder.play(mContext.getPackageName()); - } catch (RemoteException e) { - Log.wtf(TAG, "Error calling play.", e); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); } } @@ -745,8 +736,8 @@ public final class MediaController { } try { mSessionBinder.playFromMediaId(mContext.getPackageName(), mediaId, extras); - } catch (RemoteException e) { - Log.wtf(TAG, "Error calling play(" + mediaId + ").", e); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); } } @@ -767,8 +758,8 @@ public final class MediaController { } try { mSessionBinder.playFromSearch(mContext.getPackageName(), query, extras); - } catch (RemoteException e) { - Log.wtf(TAG, "Error calling play(" + query + ").", e); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); } } @@ -786,8 +777,8 @@ public final class MediaController { } try { mSessionBinder.playFromUri(mContext.getPackageName(), uri, extras); - } catch (RemoteException e) { - Log.wtf(TAG, "Error calling play(" + uri + ").", e); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); } } @@ -798,8 +789,8 @@ public final class MediaController { public void skipToQueueItem(long id) { try { mSessionBinder.skipToQueueItem(mContext.getPackageName(), id); - } catch (RemoteException e) { - Log.wtf(TAG, "Error calling skipToItem(" + id + ").", e); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); } } @@ -810,8 +801,8 @@ public final class MediaController { public void pause() { try { mSessionBinder.pause(mContext.getPackageName()); - } catch (RemoteException e) { - Log.wtf(TAG, "Error calling pause.", e); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); } } @@ -822,8 +813,8 @@ public final class MediaController { public void stop() { try { mSessionBinder.stop(mContext.getPackageName()); - } catch (RemoteException e) { - Log.wtf(TAG, "Error calling stop.", e); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); } } @@ -835,8 +826,8 @@ public final class MediaController { public void seekTo(long pos) { try { mSessionBinder.seekTo(mContext.getPackageName(), pos); - } catch (RemoteException e) { - Log.wtf(TAG, "Error calling seekTo.", e); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); } } @@ -847,8 +838,8 @@ public final class MediaController { public void fastForward() { try { mSessionBinder.fastForward(mContext.getPackageName()); - } catch (RemoteException e) { - Log.wtf(TAG, "Error calling fastForward.", e); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); } } @@ -858,8 +849,8 @@ public final class MediaController { public void skipToNext() { try { mSessionBinder.next(mContext.getPackageName()); - } catch (RemoteException e) { - Log.wtf(TAG, "Error calling next.", e); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); } } @@ -870,8 +861,8 @@ public final class MediaController { public void rewind() { try { mSessionBinder.rewind(mContext.getPackageName()); - } catch (RemoteException e) { - Log.wtf(TAG, "Error calling rewind.", e); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); } } @@ -881,8 +872,8 @@ public final class MediaController { public void skipToPrevious() { try { mSessionBinder.previous(mContext.getPackageName()); - } catch (RemoteException e) { - Log.wtf(TAG, "Error calling previous.", e); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); } } @@ -896,8 +887,8 @@ public final class MediaController { public void setRating(Rating rating) { try { mSessionBinder.rate(mContext.getPackageName(), rating); - } catch (RemoteException e) { - Log.wtf(TAG, "Error calling rate.", e); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); } } @@ -914,8 +905,8 @@ public final class MediaController { } try { mSessionBinder.setPlaybackSpeed(mContext.getPackageName(), speed); - } catch (RemoteException e) { - Log.wtf(TAG, "Error calling setPlaybackSpeed.", e); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); } } @@ -949,8 +940,8 @@ public final class MediaController { } try { mSessionBinder.sendCustomAction(mContext.getPackageName(), action, args); - } catch (RemoteException e) { - Log.d(TAG, "Dead object in sendCustomAction.", e); + } catch (RemoteException ex) { + throw ex.rethrowFromSystemServer(); } } } diff --git a/media/tests/MediaRouter/Android.bp b/media/tests/MediaRouter/Android.bp index 61b18c88e734..d21cb9319885 100644 --- a/media/tests/MediaRouter/Android.bp +++ b/media/tests/MediaRouter/Android.bp @@ -9,6 +9,7 @@ package { android_test { name: "mediaroutertest", + team: "trendy_team_android_media_solutions", srcs: ["**/*.java"], diff --git a/packages/InputDevices/res/values-ar/strings.xml b/packages/InputDevices/res/values-ar/strings.xml index c5be20840384..fe8f59c45d30 100644 --- a/packages/InputDevices/res/values-ar/strings.xml +++ b/packages/InputDevices/res/values-ar/strings.xml @@ -52,8 +52,6 @@ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"الجورجية"</string> <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"التايلاندية (Kedmanee)"</string> <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"التايلاندية (Pattachote)"</string> - <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) --> - <skip /> - <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) --> - <skip /> + <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"الصربية (اللاتينية)"</string> + <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"لغة الجبل الأسود (اللاتينية)"</string> </resources> diff --git a/packages/InputDevices/res/values-be/strings.xml b/packages/InputDevices/res/values-be/strings.xml index 1b6491aec773..697ab632aef9 100644 --- a/packages/InputDevices/res/values-be/strings.xml +++ b/packages/InputDevices/res/values-be/strings.xml @@ -52,8 +52,6 @@ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Грузінская"</string> <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"Тайская (Kedmanee)"</string> <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Тайская (Патачотэ)"</string> - <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) --> - <skip /> - <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) --> - <skip /> + <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"Сербская (лацініца)"</string> + <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"Чарнагорская (лацініца)"</string> </resources> diff --git a/packages/InputDevices/res/values-el/strings.xml b/packages/InputDevices/res/values-el/strings.xml index 78677b3093e3..fb34edd476eb 100644 --- a/packages/InputDevices/res/values-el/strings.xml +++ b/packages/InputDevices/res/values-el/strings.xml @@ -52,8 +52,6 @@ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Γεωργιανά"</string> <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"Ταϊλανδικά (Kedmanee)"</string> <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Ταϊλανδικά (Pattachote)"</string> - <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) --> - <skip /> - <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) --> - <skip /> + <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"Σερβικά (Λατινικά)"</string> + <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"Μαυροβουνιακά (Λατινικά)"</string> </resources> diff --git a/packages/InputDevices/res/values-es/strings.xml b/packages/InputDevices/res/values-es/strings.xml index 5c40ca824d83..39905de11fc0 100644 --- a/packages/InputDevices/res/values-es/strings.xml +++ b/packages/InputDevices/res/values-es/strings.xml @@ -52,8 +52,6 @@ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Georgiano"</string> <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"Tailandés (Kedmanee)"</string> <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Tailandés (Pattachote)"</string> - <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) --> - <skip /> - <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) --> - <skip /> + <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"Serbio (latino)"</string> + <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"Montenegrino (latino)"</string> </resources> diff --git a/packages/InputDevices/res/values-et/strings.xml b/packages/InputDevices/res/values-et/strings.xml index 48eb369b2949..f2d434070fb7 100644 --- a/packages/InputDevices/res/values-et/strings.xml +++ b/packages/InputDevices/res/values-et/strings.xml @@ -52,8 +52,6 @@ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"gruusia"</string> <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"tai (Kedmanee)"</string> <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Tai (Pattachote)"</string> - <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) --> - <skip /> - <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) --> - <skip /> + <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"Serbia (ladina)"</string> + <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"Montenegro (ladina)"</string> </resources> diff --git a/packages/InputDevices/res/values-in/strings.xml b/packages/InputDevices/res/values-in/strings.xml index 5ed73dd89d57..d4f024ab3fa2 100644 --- a/packages/InputDevices/res/values-in/strings.xml +++ b/packages/InputDevices/res/values-in/strings.xml @@ -52,8 +52,6 @@ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Georgia"</string> <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"Thai (Kedmanee)"</string> <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Thai (Pattachote)"</string> - <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) --> - <skip /> - <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) --> - <skip /> + <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"Serbia (Latin)"</string> + <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"Montenegro (Latin)"</string> </resources> diff --git a/packages/InputDevices/res/values-is/strings.xml b/packages/InputDevices/res/values-is/strings.xml index 12bdf3d444cb..680c4e303af7 100644 --- a/packages/InputDevices/res/values-is/strings.xml +++ b/packages/InputDevices/res/values-is/strings.xml @@ -52,8 +52,6 @@ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"georgíska"</string> <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"Taílenskt (Kedmanee)"</string> <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Taílenskt (Pattachote)"</string> - <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) --> - <skip /> - <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) --> - <skip /> + <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"Serbneska (latneskt)"</string> + <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"Svartfellska (latneskt)"</string> </resources> diff --git a/packages/InputDevices/res/values-km/strings.xml b/packages/InputDevices/res/values-km/strings.xml index abf55515f7b7..b8571ec0a8f2 100644 --- a/packages/InputDevices/res/values-km/strings.xml +++ b/packages/InputDevices/res/values-km/strings.xml @@ -52,8 +52,6 @@ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"ហ្សកហ្ស៊ី"</string> <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"ថៃ (Kedmanee)"</string> <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"ថៃ (ប៉ាតាឈោត)"</string> - <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) --> - <skip /> - <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) --> - <skip /> + <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"ស៊ែប៊ី (ឡាតាំង)"</string> + <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"ម៉ុងតេណេហ្គ្រោ (ឡាតាំង)"</string> </resources> diff --git a/packages/InputDevices/res/values-lt/strings.xml b/packages/InputDevices/res/values-lt/strings.xml index ac2a689ec5e1..b9a3e209e578 100644 --- a/packages/InputDevices/res/values-lt/strings.xml +++ b/packages/InputDevices/res/values-lt/strings.xml @@ -52,8 +52,6 @@ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Gruzinų"</string> <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"Tajų („Kedmanee“)"</string> <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Tajų („Pattachote“)"</string> - <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) --> - <skip /> - <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) --> - <skip /> + <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"Serbų (lotynų rašmenys)"</string> + <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"Juodkalniečių (lotynų rašmenys)"</string> </resources> diff --git a/packages/InputDevices/res/values-mn/strings.xml b/packages/InputDevices/res/values-mn/strings.xml index 51f1a1405509..2490d81923e6 100644 --- a/packages/InputDevices/res/values-mn/strings.xml +++ b/packages/InputDevices/res/values-mn/strings.xml @@ -52,8 +52,6 @@ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Гүрж"</string> <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"Тай (кедмани)"</string> <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Тай (паттачоте)"</string> - <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) --> - <skip /> - <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) --> - <skip /> + <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"Серби (латин)"</string> + <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"Монтенегро (латин)"</string> </resources> diff --git a/packages/InputDevices/res/values-my/strings.xml b/packages/InputDevices/res/values-my/strings.xml index 009a6c64fbd6..051024024049 100644 --- a/packages/InputDevices/res/values-my/strings.xml +++ b/packages/InputDevices/res/values-my/strings.xml @@ -52,8 +52,6 @@ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"ဂျော်ဂျီယာ"</string> <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"ထိုင်း (ကတ်မနီး)"</string> <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"ထိုင်း (ပတ်တာချုတ်)"</string> - <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) --> - <skip /> - <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) --> - <skip /> + <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"ဆားဘီးယား (လက်တင်)"</string> + <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"မွန်တီနီဂရင်း (လက်တင်)"</string> </resources> diff --git a/packages/InputDevices/res/values-nl/strings.xml b/packages/InputDevices/res/values-nl/strings.xml index 7fe2153e18c3..1893704ea19f 100644 --- a/packages/InputDevices/res/values-nl/strings.xml +++ b/packages/InputDevices/res/values-nl/strings.xml @@ -52,8 +52,6 @@ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Georgisch"</string> <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"Thai (Kedmanee)"</string> <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Thai (Pattachote)"</string> - <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) --> - <skip /> - <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) --> - <skip /> + <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"Servisch (Latijns)"</string> + <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"Montenegrijns (Latijns)"</string> </resources> diff --git a/packages/InputDevices/res/values-pl/strings.xml b/packages/InputDevices/res/values-pl/strings.xml index c44cab5c82be..b76c0fec702f 100644 --- a/packages/InputDevices/res/values-pl/strings.xml +++ b/packages/InputDevices/res/values-pl/strings.xml @@ -52,8 +52,6 @@ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"gruziński"</string> <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"tajski (Kedmanee)"</string> <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"tajski (Pattachote)"</string> - <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) --> - <skip /> - <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) --> - <skip /> + <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"serbski (alfabet łaciński)"</string> + <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"czarnogórski (alfabet łaciński)"</string> </resources> diff --git a/packages/InputDevices/res/values-uz/strings.xml b/packages/InputDevices/res/values-uz/strings.xml index 7717909fdc5d..0e80d71ba56a 100644 --- a/packages/InputDevices/res/values-uz/strings.xml +++ b/packages/InputDevices/res/values-uz/strings.xml @@ -52,8 +52,6 @@ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Gruzin"</string> <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"Tay (Kedmanee)"</string> <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Tay (Pattachote)"</string> - <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) --> - <skip /> - <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) --> - <skip /> + <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"Serb (lotin)"</string> + <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"Chernogor (lotin)"</string> </resources> diff --git a/packages/InputDevices/res/values-vi/strings.xml b/packages/InputDevices/res/values-vi/strings.xml index 1e3d7e4979d1..5094a29d04c0 100644 --- a/packages/InputDevices/res/values-vi/strings.xml +++ b/packages/InputDevices/res/values-vi/strings.xml @@ -52,8 +52,6 @@ <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Tiếng Georgia"</string> <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"Tiếng Thái (Kedmanee)"</string> <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Tiếng Thái (Pattachote)"</string> - <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) --> - <skip /> - <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) --> - <skip /> + <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"Tiếng Serbia (Latinh)"</string> + <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"Tiếng Montenegro (Latinh)"</string> </resources> diff --git a/packages/PrintSpooler/res/values-kk/strings.xml b/packages/PrintSpooler/res/values-kk/strings.xml index 939e1b43d2bc..1755c7a313e4 100644 --- a/packages/PrintSpooler/res/values-kk/strings.xml +++ b/packages/PrintSpooler/res/values-kk/strings.xml @@ -74,7 +74,7 @@ <string name="enabled_services_title" msgid="7036986099096582296">"Қосылған қызметтер"</string> <string name="recommended_services_title" msgid="3799434882937956924">"Ұсынылған қызметтер"</string> <string name="disabled_services_title" msgid="7313253167968363211">"Өшірілген қызметтер"</string> - <string name="all_services_title" msgid="5578662754874906455">"Барлық қызметтер"</string> + <string name="all_services_title" msgid="5578662754874906455">"Барлық қызмет"</string> <plurals name="print_services_recommendation_subtitle" formatted="false" msgid="5678487708807185138"> <item quantity="other"><xliff:g id="COUNT_1">%1$s</xliff:g> принтерді табу үшін орнатыңыз</item> <item quantity="one"><xliff:g id="COUNT_0">%1$s</xliff:g> принтерді табу үшін орнатыңыз</item> diff --git a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/BackupRestoreStorageManager.kt b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/BackupRestoreStorageManager.kt index 82423473e682..b4a91726ac1d 100644 --- a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/BackupRestoreStorageManager.kt +++ b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/BackupRestoreStorageManager.kt @@ -138,7 +138,7 @@ class BackupRestoreStorageManager private constructor(private val application: A private fun notifyBackupManager(key: Any?, reason: Int) { val name = storage.name // prefer not triggering backup immediately after restore - if (reason == ChangeReason.RESTORE) { + if (reason == DataChangeReason.RESTORE) { Log.d( LOG_TAG, "Notify BackupManager dataChanged ignored for restore: storage=$name key=$key" @@ -161,8 +161,8 @@ class BackupRestoreStorageManager private constructor(private val application: A fun notifyRestoreFinished() { when (storage) { - is KeyedObservable<*> -> storage.notifyChange(ChangeReason.RESTORE) - is Observable -> storage.notifyChange(ChangeReason.RESTORE) + is KeyedObservable<*> -> storage.notifyChange(DataChangeReason.RESTORE) + is Observable -> storage.notifyChange(DataChangeReason.RESTORE) } } } diff --git a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/DataChangeReason.kt b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/DataChangeReason.kt new file mode 100644 index 000000000000..145fabea52af --- /dev/null +++ b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/DataChangeReason.kt @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settingslib.datastore + +import androidx.annotation.IntDef + +/** The reason of data change. */ +@IntDef( + DataChangeReason.UNKNOWN, + DataChangeReason.UPDATE, + DataChangeReason.DELETE, + DataChangeReason.RESTORE, + DataChangeReason.SYNC_ACROSS_PROFILES, +) +@Retention(AnnotationRetention.SOURCE) +annotation class DataChangeReason { + companion object { + /** Unknown reason of the change. */ + const val UNKNOWN = 0 + /** Data is updated. */ + const val UPDATE = 1 + /** Data is deleted. */ + const val DELETE = 2 + /** Data is restored from backup/restore framework. */ + const val RESTORE = 3 + /** Data is synced from another profile (e.g. personal profile to work profile). */ + const val SYNC_ACROSS_PROFILES = 4 + } +} diff --git a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyedObserver.kt b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyedObserver.kt index 3ed4d46459cf..ede7c63d00b4 100644 --- a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyedObserver.kt +++ b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyedObserver.kt @@ -37,7 +37,7 @@ fun interface KeyedObserver<in K> { * @param reason the reason of change * @see KeyedObservable.addObserver */ - fun onKeyChanged(key: K, @ChangeReason reason: Int) + fun onKeyChanged(key: K, reason: Int) } /** @@ -89,7 +89,7 @@ interface KeyedObservable<K> { * * @param reason reason of the change */ - fun notifyChange(@ChangeReason reason: Int) + fun notifyChange(reason: Int) /** * Notifies observers that a change occurs on given key. @@ -99,7 +99,7 @@ interface KeyedObservable<K> { * @param key key of the change * @param reason reason of the change */ - fun notifyChange(key: K, @ChangeReason reason: Int) + fun notifyChange(key: K, reason: Int) } /** A thread safe implementation of [KeyedObservable]. */ @@ -141,7 +141,7 @@ class KeyedDataObservable<K> : KeyedObservable<K> { } } - override fun notifyChange(@ChangeReason reason: Int) { + override fun notifyChange(reason: Int) { // make a copy to avoid potential ConcurrentModificationException val observers = synchronized(observers) { observers.entries.toTypedArray() } val keyedObservers = synchronized(keyedObservers) { keyedObservers.copy() } @@ -165,7 +165,7 @@ class KeyedDataObservable<K> : KeyedObservable<K> { return result } - override fun notifyChange(key: K, @ChangeReason reason: Int) { + override fun notifyChange(key: K, reason: Int) { // make a copy to avoid potential ConcurrentModificationException val observers = synchronized(observers) { observers.entries.toTypedArray() } val keyedObservers = diff --git a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/Observer.kt b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/Observer.kt index 6d0ca6690c9f..98d0f6e3f9a1 100644 --- a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/Observer.kt +++ b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/Observer.kt @@ -18,34 +18,9 @@ package com.android.settingslib.datastore import androidx.annotation.AnyThread import androidx.annotation.GuardedBy -import androidx.annotation.IntDef import java.util.WeakHashMap import java.util.concurrent.Executor -/** The reason of a change. */ -@IntDef( - ChangeReason.UNKNOWN, - ChangeReason.UPDATE, - ChangeReason.DELETE, - ChangeReason.RESTORE, - ChangeReason.SYNC_ACROSS_PROFILES, -) -@Retention(AnnotationRetention.SOURCE) -annotation class ChangeReason { - companion object { - /** Unknown reason of the change. */ - const val UNKNOWN = 0 - /** Data is updated. */ - const val UPDATE = 1 - /** Data is deleted. */ - const val DELETE = 2 - /** Data is restored from backup/restore framework. */ - const val RESTORE = 3 - /** Data is synced from another profile (e.g. personal profile to work profile). */ - const val SYNC_ACROSS_PROFILES = 4 - } -} - /** * Callback to be informed of changes in [Observable] object. * @@ -60,7 +35,7 @@ fun interface Observer { * @param reason the reason of change * @see [Observable.addObserver] for the notices. */ - fun onChanged(@ChangeReason reason: Int) + fun onChanged(reason: Int) } /** An observable object allows to observe change with [Observer]. */ @@ -90,7 +65,7 @@ interface Observable { * * @param reason reason of the change */ - fun notifyChange(@ChangeReason reason: Int) + fun notifyChange(reason: Int) } /** A thread safe implementation of [Observable]. */ @@ -110,7 +85,7 @@ class DataObservable : Observable { synchronized(observers) { observers.remove(observer) } } - override fun notifyChange(@ChangeReason reason: Int) { + override fun notifyChange(reason: Int) { // make a copy to avoid potential ConcurrentModificationException val entries = synchronized(observers) { observers.entries.toTypedArray() } for (entry in entries) { diff --git a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SharedPreferencesStorage.kt b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SharedPreferencesStorage.kt index 9f9c0d839744..20a95d7efc4b 100644 --- a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SharedPreferencesStorage.kt +++ b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SharedPreferencesStorage.kt @@ -83,10 +83,10 @@ constructor( private val sharedPreferencesListener = SharedPreferences.OnSharedPreferenceChangeListener { _, key -> if (key != null) { - notifyChange(key, ChangeReason.UPDATE) + notifyChange(key, DataChangeReason.UPDATE) } else { // On Android >= R, SharedPreferences.Editor.clear() will trigger this case - notifyChange(ChangeReason.DELETE) + notifyChange(DataChangeReason.DELETE) } } diff --git a/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/BackupRestoreStorageManagerTest.kt b/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/BackupRestoreStorageManagerTest.kt index d8f502854402..19c574a843ca 100644 --- a/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/BackupRestoreStorageManagerTest.kt +++ b/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/BackupRestoreStorageManagerTest.kt @@ -157,9 +157,9 @@ class BackupRestoreStorageManagerTest { manager.onRestoreFinished() - verify(keyedObserver).onKeyChanged("key", ChangeReason.RESTORE) - verify(anyKeyObserver).onKeyChanged(null, ChangeReason.RESTORE) - verify(observer).onChanged(ChangeReason.RESTORE) + verify(keyedObserver).onKeyChanged("key", DataChangeReason.RESTORE) + verify(anyKeyObserver).onKeyChanged(null, DataChangeReason.RESTORE) + verify(observer).onChanged(DataChangeReason.RESTORE) if (isRobolectric()) { Shadows.shadowOf(BackupManager(application)).apply { assertThat(isDataChanged).isFalse() @@ -186,8 +186,8 @@ class BackupRestoreStorageManagerTest { assertThat(dataChangedCount).isEqualTo(0) } - fileStorage.notifyChange(ChangeReason.UPDATE) - verify(observer).onChanged(ChangeReason.UPDATE) + fileStorage.notifyChange(DataChangeReason.UPDATE) + verify(observer).onChanged(DataChangeReason.UPDATE) verify(keyedObserver, never()).onKeyChanged(any(), any()) verify(anyKeyObserver, never()).onKeyChanged(any(), any()) reset(observer) @@ -196,10 +196,10 @@ class BackupRestoreStorageManagerTest { assertThat(dataChangedCount).isEqualTo(1) } - keyedStorage.notifyChange("key", ChangeReason.DELETE) + keyedStorage.notifyChange("key", DataChangeReason.DELETE) verify(observer, never()).onChanged(any()) - verify(keyedObserver).onKeyChanged("key", ChangeReason.DELETE) - verify(anyKeyObserver).onKeyChanged("key", ChangeReason.DELETE) + verify(keyedObserver).onKeyChanged("key", DataChangeReason.DELETE) + verify(anyKeyObserver).onKeyChanged("key", DataChangeReason.DELETE) backupManager?.apply { assertThat(isDataChanged).isTrue() assertThat(dataChangedCount).isEqualTo(2) @@ -207,11 +207,11 @@ class BackupRestoreStorageManagerTest { reset(keyedObserver) // backup manager is not notified for restore event - fileStorage.notifyChange(ChangeReason.RESTORE) - keyedStorage.notifyChange("key", ChangeReason.RESTORE) - verify(observer).onChanged(ChangeReason.RESTORE) - verify(keyedObserver).onKeyChanged("key", ChangeReason.RESTORE) - verify(anyKeyObserver).onKeyChanged("key", ChangeReason.RESTORE) + fileStorage.notifyChange(DataChangeReason.RESTORE) + keyedStorage.notifyChange("key", DataChangeReason.RESTORE) + verify(observer).onChanged(DataChangeReason.RESTORE) + verify(keyedObserver).onKeyChanged("key", DataChangeReason.RESTORE) + verify(anyKeyObserver).onKeyChanged("key", DataChangeReason.RESTORE) backupManager?.apply { assertThat(isDataChanged).isTrue() assertThat(dataChangedCount).isEqualTo(2) diff --git a/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/KeyedObserverTest.kt b/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/KeyedObserverTest.kt index 8638b2f20b52..0fdecb034f83 100644 --- a/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/KeyedObserverTest.kt +++ b/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/KeyedObserverTest.kt @@ -77,7 +77,7 @@ class KeyedObserverTest { var observer: KeyedObserver<Any?>? = KeyedObserver { _, _ -> counter.incrementAndGet() } keyedObservable.addObserver(observer!!, executor1) - keyedObservable.notifyChange(ChangeReason.UPDATE) + keyedObservable.notifyChange(DataChangeReason.UPDATE) assertThat(counter.get()).isEqualTo(1) // trigger GC, the observer callback should not be invoked @@ -85,7 +85,7 @@ class KeyedObserverTest { System.gc() System.runFinalization() - keyedObservable.notifyChange(ChangeReason.UPDATE) + keyedObservable.notifyChange(DataChangeReason.UPDATE) assertThat(counter.get()).isEqualTo(1) } @@ -95,7 +95,7 @@ class KeyedObserverTest { var keyObserver: KeyedObserver<Any>? = KeyedObserver { _, _ -> counter.incrementAndGet() } keyedObservable.addObserver(key1, keyObserver!!, executor1) - keyedObservable.notifyChange(key1, ChangeReason.UPDATE) + keyedObservable.notifyChange(key1, DataChangeReason.UPDATE) assertThat(counter.get()).isEqualTo(1) // trigger GC, the observer callback should not be invoked @@ -103,7 +103,7 @@ class KeyedObserverTest { System.gc() System.runFinalization() - keyedObservable.notifyChange(key1, ChangeReason.UPDATE) + keyedObservable.notifyChange(key1, DataChangeReason.UPDATE) assertThat(counter.get()).isEqualTo(1) } @@ -112,16 +112,16 @@ class KeyedObserverTest { keyedObservable.addObserver(observer1, executor1) keyedObservable.addObserver(observer2, executor2) - keyedObservable.notifyChange(ChangeReason.UPDATE) - verify(observer1).onKeyChanged(null, ChangeReason.UPDATE) - verify(observer2).onKeyChanged(null, ChangeReason.UPDATE) + keyedObservable.notifyChange(DataChangeReason.UPDATE) + verify(observer1).onKeyChanged(null, DataChangeReason.UPDATE) + verify(observer2).onKeyChanged(null, DataChangeReason.UPDATE) reset(observer1, observer2) keyedObservable.removeObserver(observer2) - keyedObservable.notifyChange(ChangeReason.DELETE) - verify(observer1).onKeyChanged(null, ChangeReason.DELETE) - verify(observer2, never()).onKeyChanged(null, ChangeReason.DELETE) + keyedObservable.notifyChange(DataChangeReason.DELETE) + verify(observer1).onKeyChanged(null, DataChangeReason.DELETE) + verify(observer2, never()).onKeyChanged(null, DataChangeReason.DELETE) } @Test @@ -129,16 +129,16 @@ class KeyedObserverTest { keyedObservable.addObserver(key1, keyedObserver1, executor1) keyedObservable.addObserver(key2, keyedObserver2, executor2) - keyedObservable.notifyChange(key1, ChangeReason.UPDATE) - verify(keyedObserver1).onKeyChanged(key1, ChangeReason.UPDATE) - verify(keyedObserver2, never()).onKeyChanged(key2, ChangeReason.UPDATE) + keyedObservable.notifyChange(key1, DataChangeReason.UPDATE) + verify(keyedObserver1).onKeyChanged(key1, DataChangeReason.UPDATE) + verify(keyedObserver2, never()).onKeyChanged(key2, DataChangeReason.UPDATE) reset(keyedObserver1, keyedObserver2) keyedObservable.removeObserver(key1, keyedObserver1) - keyedObservable.notifyChange(key1, ChangeReason.DELETE) - verify(keyedObserver1, never()).onKeyChanged(key1, ChangeReason.DELETE) - verify(keyedObserver2, never()).onKeyChanged(key2, ChangeReason.DELETE) + keyedObservable.notifyChange(key1, DataChangeReason.DELETE) + verify(keyedObserver1, never()).onKeyChanged(key1, DataChangeReason.DELETE) + verify(keyedObserver2, never()).onKeyChanged(key2, DataChangeReason.DELETE) } @Test @@ -147,24 +147,24 @@ class KeyedObserverTest { keyedObservable.addObserver(key1, keyedObserver1, executor1) keyedObservable.addObserver(key2, keyedObserver2, executor1) - keyedObservable.notifyChange(ChangeReason.UPDATE) - verify(observer1).onKeyChanged(null, ChangeReason.UPDATE) - verify(keyedObserver1).onKeyChanged(key1, ChangeReason.UPDATE) - verify(keyedObserver2).onKeyChanged(key2, ChangeReason.UPDATE) + keyedObservable.notifyChange(DataChangeReason.UPDATE) + verify(observer1).onKeyChanged(null, DataChangeReason.UPDATE) + verify(keyedObserver1).onKeyChanged(key1, DataChangeReason.UPDATE) + verify(keyedObserver2).onKeyChanged(key2, DataChangeReason.UPDATE) reset(observer1, keyedObserver1, keyedObserver2) - keyedObservable.notifyChange(key1, ChangeReason.UPDATE) + keyedObservable.notifyChange(key1, DataChangeReason.UPDATE) - verify(observer1).onKeyChanged(key1, ChangeReason.UPDATE) - verify(keyedObserver1).onKeyChanged(key1, ChangeReason.UPDATE) - verify(keyedObserver2, never()).onKeyChanged(key1, ChangeReason.UPDATE) + verify(observer1).onKeyChanged(key1, DataChangeReason.UPDATE) + verify(keyedObserver1).onKeyChanged(key1, DataChangeReason.UPDATE) + verify(keyedObserver2, never()).onKeyChanged(key1, DataChangeReason.UPDATE) reset(observer1, keyedObserver1, keyedObserver2) - keyedObservable.notifyChange(key2, ChangeReason.UPDATE) + keyedObservable.notifyChange(key2, DataChangeReason.UPDATE) - verify(observer1).onKeyChanged(key2, ChangeReason.UPDATE) - verify(keyedObserver1, never()).onKeyChanged(key2, ChangeReason.UPDATE) - verify(keyedObserver2).onKeyChanged(key2, ChangeReason.UPDATE) + verify(observer1).onKeyChanged(key2, DataChangeReason.UPDATE) + verify(keyedObserver1, never()).onKeyChanged(key2, DataChangeReason.UPDATE) + verify(keyedObserver2).onKeyChanged(key2, DataChangeReason.UPDATE) } @Test @@ -176,7 +176,7 @@ class KeyedObserverTest { keyedObservable.addObserver(observer, executor1) - keyedObservable.notifyChange(ChangeReason.UPDATE) + keyedObservable.notifyChange(DataChangeReason.UPDATE) keyedObservable.removeObserver(observer) } @@ -189,7 +189,7 @@ class KeyedObserverTest { keyedObservable.addObserver(key1, keyObserver, executor1) - keyedObservable.notifyChange(key1, ChangeReason.UPDATE) + keyedObservable.notifyChange(key1, DataChangeReason.UPDATE) keyedObservable.removeObserver(key1, keyObserver) } } diff --git a/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/ObserverTest.kt b/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/ObserverTest.kt index 173c2b1d4b81..5d0303c06d41 100644 --- a/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/ObserverTest.kt +++ b/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/ObserverTest.kt @@ -58,7 +58,7 @@ class ObserverTest { var observer: Observer? = Observer { counter.incrementAndGet() } observable.addObserver(observer!!, executor1) - observable.notifyChange(ChangeReason.UPDATE) + observable.notifyChange(DataChangeReason.UPDATE) assertThat(counter.get()).isEqualTo(1) // trigger GC, the observer callback should not be invoked @@ -66,7 +66,7 @@ class ObserverTest { System.gc() System.runFinalization() - observable.notifyChange(ChangeReason.UPDATE) + observable.notifyChange(DataChangeReason.UPDATE) assertThat(counter.get()).isEqualTo(1) } @@ -75,17 +75,17 @@ class ObserverTest { observable.addObserver(observer1, executor1) observable.addObserver(observer2, executor2) - observable.notifyChange(ChangeReason.DELETE) + observable.notifyChange(DataChangeReason.DELETE) - verify(observer1).onChanged(ChangeReason.DELETE) - verify(observer2).onChanged(ChangeReason.DELETE) + verify(observer1).onChanged(DataChangeReason.DELETE) + verify(observer2).onChanged(DataChangeReason.DELETE) reset(observer1, observer2) observable.removeObserver(observer2) - observable.notifyChange(ChangeReason.UPDATE) - verify(observer1).onChanged(ChangeReason.UPDATE) - verify(observer2, never()).onChanged(ChangeReason.UPDATE) + observable.notifyChange(DataChangeReason.UPDATE) + verify(observer1).onChanged(DataChangeReason.UPDATE) + verify(observer2, never()).onChanged(DataChangeReason.UPDATE) } @Test @@ -93,7 +93,7 @@ class ObserverTest { // ConcurrentModificationException is raised if it is not implemented correctly val observer = Observer { observable.addObserver(observer1, executor1) } observable.addObserver(observer, executor1) - observable.notifyChange(ChangeReason.UPDATE) + observable.notifyChange(DataChangeReason.UPDATE) observable.removeObserver(observer) } } diff --git a/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/SharedPreferencesStorageTest.kt b/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/SharedPreferencesStorageTest.kt index fec7d758b893..a135d77e6d25 100644 --- a/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/SharedPreferencesStorageTest.kt +++ b/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/SharedPreferencesStorageTest.kt @@ -80,13 +80,13 @@ class SharedPreferencesStorageTest { storage.addObserver("key", keyedObserver, executor) storage.sharedPreferences.edit().putString("key", "string").applySync() - verify(observer).onKeyChanged("key", ChangeReason.UPDATE) - verify(keyedObserver).onKeyChanged("key", ChangeReason.UPDATE) + verify(observer).onKeyChanged("key", DataChangeReason.UPDATE) + verify(keyedObserver).onKeyChanged("key", DataChangeReason.UPDATE) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { storage.sharedPreferences.edit().clear().applySync() - verify(observer).onKeyChanged(null, ChangeReason.DELETE) - verify(keyedObserver).onKeyChanged("key", ChangeReason.DELETE) + verify(observer).onKeyChanged(null, DataChangeReason.DELETE) + verify(keyedObserver).onKeyChanged("key", DataChangeReason.DELETE) } } diff --git a/packages/SettingsLib/ProfileSelector/res/values-cs/strings.xml b/packages/SettingsLib/ProfileSelector/res/values-cs/strings.xml index d50fc9a7da88..3e5f52696916 100644 --- a/packages/SettingsLib/ProfileSelector/res/values-cs/strings.xml +++ b/packages/SettingsLib/ProfileSelector/res/values-cs/strings.xml @@ -18,6 +18,6 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="settingslib_category_personal" msgid="1142302328104700620">"Osobní"</string> - <string name="settingslib_category_work" msgid="4867750733682444676">"Prácovní"</string> + <string name="settingslib_category_work" msgid="4867750733682444676">"Pracovní"</string> <string name="settingslib_category_private" msgid="5039276873477591386">"Soukromé"</string> </resources> diff --git a/packages/SettingsLib/ProfileSelector/res/values-el/strings.xml b/packages/SettingsLib/ProfileSelector/res/values-el/strings.xml index 628388d696b8..b1bc69ce7a41 100644 --- a/packages/SettingsLib/ProfileSelector/res/values-el/strings.xml +++ b/packages/SettingsLib/ProfileSelector/res/values-el/strings.xml @@ -19,5 +19,5 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="settingslib_category_personal" msgid="1142302328104700620">"Προσωπικά"</string> <string name="settingslib_category_work" msgid="4867750733682444676">"Εργασία"</string> - <string name="settingslib_category_private" msgid="5039276873477591386">"Ιδιωτικό"</string> + <string name="settingslib_category_private" msgid="5039276873477591386">"Ιδιωτικός"</string> </resources> diff --git a/packages/SettingsLib/ProfileSelector/res/values-is/strings.xml b/packages/SettingsLib/ProfileSelector/res/values-is/strings.xml index 75668e8c55ff..b7aa61bf8566 100644 --- a/packages/SettingsLib/ProfileSelector/res/values-is/strings.xml +++ b/packages/SettingsLib/ProfileSelector/res/values-is/strings.xml @@ -19,5 +19,5 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="settingslib_category_personal" msgid="1142302328104700620">"Persónulegt"</string> <string name="settingslib_category_work" msgid="4867750733682444676">"Vinna"</string> - <string name="settingslib_category_private" msgid="5039276873477591386">"Lokað"</string> + <string name="settingslib_category_private" msgid="5039276873477591386">"Laynirými"</string> </resources> diff --git a/packages/SettingsLib/ProfileSelector/res/values-ja/strings.xml b/packages/SettingsLib/ProfileSelector/res/values-ja/strings.xml index 21419e65e408..c9faa3c353c5 100644 --- a/packages/SettingsLib/ProfileSelector/res/values-ja/strings.xml +++ b/packages/SettingsLib/ProfileSelector/res/values-ja/strings.xml @@ -19,5 +19,5 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="settingslib_category_personal" msgid="1142302328104700620">"個人用"</string> <string name="settingslib_category_work" msgid="4867750733682444676">"仕事用"</string> - <string name="settingslib_category_private" msgid="5039276873477591386">"非公開"</string> + <string name="settingslib_category_private" msgid="5039276873477591386">"プライベート"</string> </resources> diff --git a/packages/SettingsLib/ProfileSelector/res/values-mk/strings.xml b/packages/SettingsLib/ProfileSelector/res/values-mk/strings.xml index 07cf9c71e5f3..e8c2bf563c40 100644 --- a/packages/SettingsLib/ProfileSelector/res/values-mk/strings.xml +++ b/packages/SettingsLib/ProfileSelector/res/values-mk/strings.xml @@ -19,5 +19,5 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="settingslib_category_personal" msgid="1142302328104700620">"Лични"</string> <string name="settingslib_category_work" msgid="4867750733682444676">"Работа"</string> - <string name="settingslib_category_private" msgid="5039276873477591386">"Приватен"</string> + <string name="settingslib_category_private" msgid="5039276873477591386">"Приватно"</string> </resources> diff --git a/packages/SettingsLib/ProfileSelector/res/values-pa/strings.xml b/packages/SettingsLib/ProfileSelector/res/values-pa/strings.xml index e1e68c758a15..1e4abc1e2025 100644 --- a/packages/SettingsLib/ProfileSelector/res/values-pa/strings.xml +++ b/packages/SettingsLib/ProfileSelector/res/values-pa/strings.xml @@ -18,6 +18,6 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="settingslib_category_personal" msgid="1142302328104700620">"ਨਿੱਜੀ"</string> - <string name="settingslib_category_work" msgid="4867750733682444676">"ਕਾਰਜ"</string> + <string name="settingslib_category_work" msgid="4867750733682444676">"ਕੰਮ ਸੰਬੰਧੀ"</string> <string name="settingslib_category_private" msgid="5039276873477591386">"ਪ੍ਰਾਈਵੇਟ"</string> </resources> diff --git a/packages/SettingsLib/ProfileSelector/res/values-ru/strings.xml b/packages/SettingsLib/ProfileSelector/res/values-ru/strings.xml index ee4212f82ba4..e0b1471c0859 100644 --- a/packages/SettingsLib/ProfileSelector/res/values-ru/strings.xml +++ b/packages/SettingsLib/ProfileSelector/res/values-ru/strings.xml @@ -19,5 +19,5 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="settingslib_category_personal" msgid="1142302328104700620">"Личный профиль"</string> <string name="settingslib_category_work" msgid="4867750733682444676">"Рабочий профиль"</string> - <string name="settingslib_category_private" msgid="5039276873477591386">"Личное"</string> + <string name="settingslib_category_private" msgid="5039276873477591386">"Частный профиль"</string> </resources> diff --git a/packages/SettingsLib/ProfileSelector/res/values-tr/strings.xml b/packages/SettingsLib/ProfileSelector/res/values-tr/strings.xml index 59f21c8c8ba6..e4e2bdcc44f0 100644 --- a/packages/SettingsLib/ProfileSelector/res/values-tr/strings.xml +++ b/packages/SettingsLib/ProfileSelector/res/values-tr/strings.xml @@ -19,5 +19,5 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="settingslib_category_personal" msgid="1142302328104700620">"Kişisel"</string> <string name="settingslib_category_work" msgid="4867750733682444676">"İş"</string> - <string name="settingslib_category_private" msgid="5039276873477591386">"Gizli"</string> + <string name="settingslib_category_private" msgid="5039276873477591386">"Özel"</string> </resources> diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/LifecycleEffect.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/LifecycleEffect.kt index e91fa65401a4..e9f9689cb319 100644 --- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/LifecycleEffect.kt +++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/LifecycleEffect.kt @@ -18,9 +18,9 @@ package com.android.settingslib.spa.framework.compose import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect -import androidx.compose.ui.platform.LocalLifecycleOwner import androidx.lifecycle.Lifecycle import androidx.lifecycle.LifecycleEventObserver +import androidx.lifecycle.compose.LocalLifecycleOwner @Composable fun LifecycleEffect( diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/OnBackEffect.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/OnBackEffect.kt index 3991f26e1b0c..0b1c92d78a57 100644 --- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/OnBackEffect.kt +++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/OnBackEffect.kt @@ -24,7 +24,7 @@ import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.remember import androidx.compose.runtime.rememberUpdatedState -import androidx.compose.ui.platform.LocalLifecycleOwner +import androidx.lifecycle.compose.LocalLifecycleOwner /** * An effect for detecting presses of the system back button, and the back event will not be diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/slice/presenter/Demo.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/slice/presenter/Demo.kt index ee24a09d4395..007f47bd3c82 100644 --- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/slice/presenter/Demo.kt +++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/slice/presenter/Demo.kt @@ -21,8 +21,8 @@ import androidx.compose.material3.HorizontalDivider import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.platform.LocalLifecycleOwner import androidx.compose.ui.viewinterop.AndroidView +import androidx.lifecycle.compose.LocalLifecycleOwner import androidx.slice.widget.SliceLiveData import androidx.slice.widget.SliceView diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/dialog/SettingsAlertDialog.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/dialog/SettingsAlertDialog.kt index de080e3d8ef4..022ddedd1062 100644 --- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/dialog/SettingsAlertDialog.kt +++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/dialog/SettingsAlertDialog.kt @@ -38,6 +38,7 @@ import androidx.compose.ui.window.DialogProperties data class AlertDialogButton( val text: String, + val enabled: Boolean = true, val onClick: () -> Unit = {}, ) @@ -114,6 +115,7 @@ private fun AlertDialogPresenter.Button(button: AlertDialogButton) { close() button.onClick() }, + enabled = button.enabled, ) { Text(button.text) } diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/DropdownTextBox.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/DropdownTextBox.kt index b471e50be275..bdbe62c07425 100644 --- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/DropdownTextBox.kt +++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/DropdownTextBox.kt @@ -22,6 +22,7 @@ import androidx.compose.foundation.layout.width import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExposedDropdownMenuBox import androidx.compose.material3.ExposedDropdownMenuDefaults +import androidx.compose.material3.MenuAnchorType import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Text import androidx.compose.runtime.Composable @@ -65,7 +66,7 @@ internal fun DropdownTextBox( OutlinedTextField( // The `menuAnchor` modifier must be passed to the text field for correctness. modifier = Modifier - .menuAnchor() + .menuAnchor(MenuAnchorType.PrimaryNotEditable) .fillMaxWidth(), value = text, onValueChange = { }, diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/compose/LifecycleEffectTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/compose/LifecycleEffectTest.kt index fe7baff43101..8b0efff591f2 100644 --- a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/compose/LifecycleEffectTest.kt +++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/compose/LifecycleEffectTest.kt @@ -18,9 +18,9 @@ package com.android.settingslib.spa.framework.compose import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.LaunchedEffect -import androidx.compose.ui.platform.LocalLifecycleOwner import androidx.compose.ui.test.junit4.createComposeRule import androidx.lifecycle.Lifecycle +import androidx.lifecycle.compose.LocalLifecycleOwner import androidx.lifecycle.testing.TestLifecycleOwner import androidx.test.ext.junit.runners.AndroidJUnit4 import com.google.common.truth.Truth.assertThat diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/dialog/SettingsAlertDialogTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/dialog/SettingsAlertDialogTest.kt index 9468f95a094e..20ea397d7222 100644 --- a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/dialog/SettingsAlertDialogTest.kt +++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/dialog/SettingsAlertDialogTest.kt @@ -20,6 +20,8 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.ui.test.assertIsDisplayed +import androidx.compose.ui.test.assertIsEnabled +import androidx.compose.ui.test.assertIsNotEnabled import androidx.compose.ui.test.junit4.createComposeRule import androidx.compose.ui.test.performClick import androidx.test.ext.junit.runners.AndroidJUnit4 @@ -67,7 +69,18 @@ class SettingsAlertDialogTest { rememberAlertDialogPresenter(confirmButton = AlertDialogButton(CONFIRM_TEXT)) } - composeTestRule.onDialogText(CONFIRM_TEXT).assertIsDisplayed() + composeTestRule.onDialogText(CONFIRM_TEXT).assertIsDisplayed().assertIsEnabled() + } + + @Test + fun confirmButton_disabled() { + setAndOpenDialog { + rememberAlertDialogPresenter( + confirmButton = AlertDialogButton(text = CONFIRM_TEXT, enabled = false) + ) + } + + composeTestRule.onDialogText(CONFIRM_TEXT).assertIsDisplayed().assertIsNotEnabled() } @Test @@ -90,7 +103,18 @@ class SettingsAlertDialogTest { rememberAlertDialogPresenter(dismissButton = AlertDialogButton(DISMISS_TEXT)) } - composeTestRule.onDialogText(DISMISS_TEXT).assertIsDisplayed() + composeTestRule.onDialogText(DISMISS_TEXT).assertIsDisplayed().assertIsEnabled() + } + + @Test + fun dismissButton_disabled() { + setAndOpenDialog { + rememberAlertDialogPresenter( + dismissButton = AlertDialogButton(text = DISMISS_TEXT, enabled = false) + ) + } + + composeTestRule.onDialogText(DISMISS_TEXT).assertIsDisplayed().assertIsNotEnabled() } @Test diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/scaffold/RestrictedMenuItem.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/scaffold/RestrictedMenuItem.kt index 977615b55a6a..f95cfc3191c8 100644 --- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/scaffold/RestrictedMenuItem.kt +++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/scaffold/RestrictedMenuItem.kt @@ -30,22 +30,24 @@ import com.android.settingslib.spaprivileged.model.enterprise.rememberRestricted @Composable fun MoreOptionsScope.RestrictedMenuItem( text: String, + enabled: Boolean = true, restrictions: Restrictions, onClick: () -> Unit, ) { - RestrictedMenuItemImpl(text, restrictions, onClick, ::RestrictionsProviderImpl) + RestrictedMenuItemImpl(text, enabled, restrictions, onClick, ::RestrictionsProviderImpl) } @VisibleForTesting @Composable internal fun MoreOptionsScope.RestrictedMenuItemImpl( text: String, + enabled: Boolean = true, restrictions: Restrictions, onClick: () -> Unit, restrictionsProviderFactory: RestrictionsProviderFactory, ) { val restrictedMode = restrictionsProviderFactory.rememberRestrictedMode(restrictions).value - MenuItem(text = text, enabled = restrictedMode !== BaseUserRestricted) { + MenuItem(text = text, enabled = enabled && restrictedMode !== BaseUserRestricted) { when (restrictedMode) { is BlockedByAdmin -> restrictedMode.sendShowAdminSupportDetailsIntent() is BlockedByEcm -> restrictedMode.showRestrictedSettingsDetails() diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/scaffold/RestrictedMenuItemTest.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/scaffold/RestrictedMenuItemTest.kt index 556adc750763..4068bceb1475 100644 --- a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/scaffold/RestrictedMenuItemTest.kt +++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/scaffold/RestrictedMenuItemTest.kt @@ -49,6 +49,15 @@ class RestrictedMenuItemTest { private var menuItemOnClickIsCalled = false @Test + fun whenDisabled() { + val restrictions = Restrictions(userId = USER_ID, keys = emptyList()) + + setContent(restrictions, enabled = false) + + composeTestRule.onNodeWithText(TEXT).assertIsDisplayed().assertIsNotEnabled() + } + + @Test fun whenRestrictionsKeysIsEmpty_enabled() { val restrictions = Restrictions(userId = USER_ID, keys = emptyList()) @@ -153,13 +162,14 @@ class RestrictedMenuItemTest { assertThat(menuItemOnClickIsCalled).isFalse() } - private fun setContent(restrictions: Restrictions) { + private fun setContent(restrictions: Restrictions, enabled: Boolean = true) { val fakeMoreOptionsScope = object : MoreOptionsScope() { override fun dismiss() {} } composeTestRule.setContent { fakeMoreOptionsScope.RestrictedMenuItemImpl( text = TEXT, + enabled = enabled, restrictions = restrictions, onClick = { menuItemOnClickIsCalled = true }, restrictionsProviderFactory = { _, _ -> fakeRestrictionsProvider }, diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml index 3f39d4d0e962..d02164704884 100644 --- a/packages/SettingsLib/res/values-as/strings.xml +++ b/packages/SettingsLib/res/values-as/strings.xml @@ -237,7 +237,7 @@ <string name="choose_profile" msgid="343803890897657450">"প্ৰ’ফাইল বাছনি কৰক"</string> <string name="category_personal" msgid="6236798763159385225">"ব্যক্তিগত"</string> <string name="category_work" msgid="4014193632325996115">"কৰ্মস্থান-সম্পৰ্কীয়"</string> - <string name="category_private" msgid="4244892185452788977">"গোপনীয়"</string> + <string name="category_private" msgid="4244892185452788977">"প্ৰাইভেট"</string> <string name="category_clone" msgid="1554511758987195974">"ক্ল’ন"</string> <string name="development_settings_title" msgid="140296922921597393">"বিকাশকৰ্তাৰ বিকল্পসমূহ"</string> <string name="development_settings_enable" msgid="4285094651288242183">"বিকাশকৰ্তা বিষয়ক বিকল্পসমূহ সক্ষম কৰক"</string> diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml index b9961b9d0d14..1533a1961cc1 100644 --- a/packages/SettingsLib/res/values-bn/strings.xml +++ b/packages/SettingsLib/res/values-bn/strings.xml @@ -237,7 +237,7 @@ <string name="choose_profile" msgid="343803890897657450">"প্রোফাইল বেছে নিন"</string> <string name="category_personal" msgid="6236798763159385225">"ব্যক্তিগত"</string> <string name="category_work" msgid="4014193632325996115">"অফিস"</string> - <string name="category_private" msgid="4244892185452788977">"ব্যক্তিগত"</string> + <string name="category_private" msgid="4244892185452788977">"প্রাইভেট"</string> <string name="category_clone" msgid="1554511758987195974">"ক্লোন"</string> <string name="development_settings_title" msgid="140296922921597393">"ডেভেলপার বিকল্প"</string> <string name="development_settings_enable" msgid="4285094651288242183">"ডেভেলপার বিকল্প সক্ষম করুন"</string> diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml index 240b0f0802f0..194c616b8ca5 100644 --- a/packages/SettingsLib/res/values-cs/strings.xml +++ b/packages/SettingsLib/res/values-cs/strings.xml @@ -237,7 +237,7 @@ <string name="choose_profile" msgid="343803890897657450">"Vyberte profil"</string> <string name="category_personal" msgid="6236798763159385225">"Osobní"</string> <string name="category_work" msgid="4014193632325996115">"Pracovní"</string> - <string name="category_private" msgid="4244892185452788977">"Soukromé"</string> + <string name="category_private" msgid="4244892185452788977">"Soukromý"</string> <string name="category_clone" msgid="1554511758987195974">"Klon"</string> <string name="development_settings_title" msgid="140296922921597393">"Pro vývojáře"</string> <string name="development_settings_enable" msgid="4285094651288242183">"Aktivovat možnosti pro vývojáře"</string> diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml index f549ae2bc197..94d4297d7f57 100644 --- a/packages/SettingsLib/res/values-el/strings.xml +++ b/packages/SettingsLib/res/values-el/strings.xml @@ -235,9 +235,9 @@ <item msgid="6946761421234586000">"400%"</item> </string-array> <string name="choose_profile" msgid="343803890897657450">"Επιλογή προφίλ"</string> - <string name="category_personal" msgid="6236798763159385225">"Προσωπικό"</string> + <string name="category_personal" msgid="6236798763159385225">"Προσωπικός"</string> <string name="category_work" msgid="4014193632325996115">"Εργασίας"</string> - <string name="category_private" msgid="4244892185452788977">"Ιδιωτικό"</string> + <string name="category_private" msgid="4244892185452788977">"Ιδιωτικός"</string> <string name="category_clone" msgid="1554511758987195974">"Κλωνοποίηση"</string> <string name="development_settings_title" msgid="140296922921597393">"Επιλογές για προγραμματιστές"</string> <string name="development_settings_enable" msgid="4285094651288242183">"Ενεργοποίηση επιλογών για προγραμματιστές"</string> diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml index 8f832162be3a..f66499fba432 100644 --- a/packages/SettingsLib/res/values-hi/strings.xml +++ b/packages/SettingsLib/res/values-hi/strings.xml @@ -708,7 +708,7 @@ <string name="physical_keyboard_title" msgid="4811935435315835220">"फ़िज़िकल कीबोर्ड"</string> <string name="keyboard_layout_dialog_title" msgid="3927180147005616290">"कीबोर्ड का लेआउट चुनें"</string> <string name="keyboard_layout_default_label" msgid="1997292217218546957">"डिफ़ॉल्ट"</string> - <string name="turn_screen_on_title" msgid="2662312432042116026">"इन ऐप के पास स्क्रीन को चालू करने का कंट्रोल है"</string> + <string name="turn_screen_on_title" msgid="2662312432042116026">"इस ऐप के पास स्क्रीन को चालू करने का कंट्रोल है"</string> <string name="allow_turn_screen_on" msgid="6194845766392742639">"स्क्रीन चालू करने की अनुमति दें"</string> <string name="allow_turn_screen_on_description" msgid="43834403291575164">"ऐप्लिकेशन को स्क्रीन चालू करने की अनुमति दें. ऐसा करने पर, ऐप्लिकेशन आपकी अनुमति लिए बिना भी, जब चाहे स्क्रीन चालू कर सकता है."</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="5392738488989777074">"<xliff:g id="APP_NAME">%1$s</xliff:g> पर ब्रॉडकास्ट करना रोकें?"</string> diff --git a/packages/SettingsLib/res/values-is/strings.xml b/packages/SettingsLib/res/values-is/strings.xml index 66be184efe8b..47cdcca2bbe4 100644 --- a/packages/SettingsLib/res/values-is/strings.xml +++ b/packages/SettingsLib/res/values-is/strings.xml @@ -235,7 +235,7 @@ <item msgid="6946761421234586000">"400%"</item> </string-array> <string name="choose_profile" msgid="343803890897657450">"Veldu snið"</string> - <string name="category_personal" msgid="6236798763159385225">"Persónulegt"</string> + <string name="category_personal" msgid="6236798763159385225">"Einkasnið"</string> <string name="category_work" msgid="4014193632325996115">"Vinna"</string> <string name="category_private" msgid="4244892185452788977">"Lokað"</string> <string name="category_clone" msgid="1554511758987195974">"Afrit"</string> diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml index 630dd649d3f7..b2456d3f9b53 100644 --- a/packages/SettingsLib/res/values-mk/strings.xml +++ b/packages/SettingsLib/res/values-mk/strings.xml @@ -235,9 +235,9 @@ <item msgid="6946761421234586000">"400 %"</item> </string-array> <string name="choose_profile" msgid="343803890897657450">"Изберете профил"</string> - <string name="category_personal" msgid="6236798763159385225">"Личен"</string> + <string name="category_personal" msgid="6236798763159385225">"Лично"</string> <string name="category_work" msgid="4014193632325996115">"Работа"</string> - <string name="category_private" msgid="4244892185452788977">"Приватен"</string> + <string name="category_private" msgid="4244892185452788977">"Приватно"</string> <string name="category_clone" msgid="1554511758987195974">"Клон"</string> <string name="development_settings_title" msgid="140296922921597393">"Програмерски опции"</string> <string name="development_settings_enable" msgid="4285094651288242183">"Овозможете ги програмерските опции"</string> diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml index 04c8a4080c5a..c4677beb6e11 100644 --- a/packages/SettingsLib/res/values-sv/strings.xml +++ b/packages/SettingsLib/res/values-sv/strings.xml @@ -235,7 +235,7 @@ <item msgid="6946761421234586000">"400 %"</item> </string-array> <string name="choose_profile" msgid="343803890897657450">"Välj profil"</string> - <string name="category_personal" msgid="6236798763159385225">"Privat"</string> + <string name="category_personal" msgid="6236798763159385225">"Personlig"</string> <string name="category_work" msgid="4014193632325996115">"Jobb"</string> <string name="category_private" msgid="4244892185452788977">"Privat"</string> <string name="category_clone" msgid="1554511758987195974">"Klon"</string> diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml index afda7cbd5ade..014ba26e2f3b 100644 --- a/packages/SettingsLib/res/values-th/strings.xml +++ b/packages/SettingsLib/res/values-th/strings.xml @@ -235,7 +235,7 @@ <item msgid="6946761421234586000">"400%"</item> </string-array> <string name="choose_profile" msgid="343803890897657450">"เลือกโปรไฟล์"</string> - <string name="category_personal" msgid="6236798763159385225">"ส่วนตัว"</string> + <string name="category_personal" msgid="6236798763159385225">"ส่วนบุคคล"</string> <string name="category_work" msgid="4014193632325996115">"งาน"</string> <string name="category_private" msgid="4244892185452788977">"ส่วนตัว"</string> <string name="category_clone" msgid="1554511758987195974">"โคลน"</string> diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml index c79db5fc2933..6aea659292c3 100644 --- a/packages/SettingsLib/res/values-tr/strings.xml +++ b/packages/SettingsLib/res/values-tr/strings.xml @@ -237,7 +237,7 @@ <string name="choose_profile" msgid="343803890897657450">"Profil seçin"</string> <string name="category_personal" msgid="6236798763159385225">"Kişisel"</string> <string name="category_work" msgid="4014193632325996115">"İş"</string> - <string name="category_private" msgid="4244892185452788977">"Gizli"</string> + <string name="category_private" msgid="4244892185452788977">"Özel"</string> <string name="category_clone" msgid="1554511758987195974">"Klon"</string> <string name="development_settings_title" msgid="140296922921597393">"Geliştirici seçenekleri"</string> <string name="development_settings_enable" msgid="4285094651288242183">"Geliştirici seçeneklerini etkinleştir"</string> diff --git a/packages/SettingsLib/res/values/dimens.xml b/packages/SettingsLib/res/values/dimens.xml index ab049042b5f9..470cdeea149b 100644 --- a/packages/SettingsLib/res/values/dimens.xml +++ b/packages/SettingsLib/res/values/dimens.xml @@ -32,7 +32,7 @@ <!-- Usage graph dimens --> <dimen name="usage_graph_margin_top_bottom">9dp</dimen> - <dimen name="usage_graph_labels_width">56dp</dimen> + <dimen name="usage_graph_labels_width">60dp</dimen> <dimen name="usage_graph_divider_size">1dp</dimen> diff --git a/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/LocalMediaRepository.kt b/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/LocalMediaRepository.kt index 869fb7f4043c..70811951c9b7 100644 --- a/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/LocalMediaRepository.kt +++ b/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/LocalMediaRepository.kt @@ -81,7 +81,7 @@ class LocalMediaRepositoryImpl( localMediaManager.unregisterCallback(callback) } } - .shareIn(coroutineScope, SharingStarted.WhileSubscribed(), replay = 0) + .shareIn(coroutineScope, SharingStarted.Eagerly, replay = 0) override val currentConnectedDevice: StateFlow<MediaDevice?> = merge(devicesChanges, mediaDevicesUpdates) @@ -89,8 +89,8 @@ class LocalMediaRepositoryImpl( .onStart { emit(localMediaManager.currentConnectedDevice) } .stateIn( coroutineScope, - SharingStarted.WhileSubscribed(), - localMediaManager.currentConnectedDevice + SharingStarted.Eagerly, + localMediaManager.currentConnectedDevice, ) private sealed interface DevicesUpdate { diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java index 04922d6e2c99..c8992c344b2d 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java @@ -64,6 +64,7 @@ import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; +import java.io.InputStream; import java.io.IOException; import java.io.PrintWriter; import java.nio.file.Files; @@ -85,13 +86,13 @@ import java.util.concurrent.CountDownLatch; // FOR ACONFIGD TEST MISSION AND ROLLOUT import java.io.DataInputStream; import java.io.DataOutputStream; -import android.net.LocalSocketAddress; -import android.net.LocalSocket; import android.util.proto.ProtoInputStream; import android.aconfigd.Aconfigd.StorageRequestMessage; import android.aconfigd.Aconfigd.StorageRequestMessages; import android.aconfigd.Aconfigd.StorageReturnMessage; import android.aconfigd.Aconfigd.StorageReturnMessages; +import android.aconfigd.AconfigdClientSocket; +import android.aconfigd.AconfigdFlagInfo; import android.aconfigd.AconfigdJavaUtils; import static com.android.aconfig_new_storage.Flags.enableAconfigStorageDaemon; /** @@ -265,6 +266,10 @@ final class SettingsState { @NonNull private Map<String, Map<String, String>> mNamespaceDefaults; + // TOBO(b/312444587): remove the comparison logic after Test Mission 2. + @NonNull + private Map<String, AconfigdFlagInfo> mAconfigDefaultFlags; + public static final int SETTINGS_TYPE_GLOBAL = 0; public static final int SETTINGS_TYPE_SYSTEM = 1; public static final int SETTINGS_TYPE_SECURE = 2; @@ -334,8 +339,13 @@ final class SettingsState { + settingTypeToString(getTypeFromKey(key)) + "]"; } - public SettingsState(Context context, Object lock, File file, int key, - int maxBytesPerAppPackage, Looper looper) { + public SettingsState( + Context context, + Object lock, + File file, + int key, + int maxBytesPerAppPackage, + Looper looper) { // It is important that we use the same lock as the settings provider // to ensure multiple mutations on this state are atomically persisted // as the async persistence should be blocked while we make changes. @@ -353,12 +363,15 @@ final class SettingsState { mPackageToMemoryUsage = null; } - mHistoricalOperations = Build.IS_DEBUGGABLE - ? new ArrayList<>(HISTORICAL_OPERATION_COUNT) : null; + mHistoricalOperations = + Build.IS_DEBUGGABLE ? new ArrayList<>(HISTORICAL_OPERATION_COUNT) : null; mNamespaceDefaults = new HashMap<>(); + mAconfigDefaultFlags = new HashMap<>(); ProtoOutputStream requests = null; + Map<String, AconfigdFlagInfo> aconfigFlagMap = new HashMap<>(); + synchronized (mLock) { readStateSyncLocked(); @@ -375,39 +388,114 @@ final class SettingsState { } } + if (enableAconfigStorageDaemon()) { + if (isConfigSettingsKey(mKey)) { + aconfigFlagMap = getAllAconfigFlagsFromSettings(); + } + } + if (isConfigSettingsKey(mKey)) { - requests = handleBulkSyncToNewStorage(); + requests = handleBulkSyncToNewStorage(aconfigFlagMap); } } - if (requests != null) { - LocalSocket client = new LocalSocket(); - try{ - client.connect(new LocalSocketAddress( - "aconfigd", LocalSocketAddress.Namespace.RESERVED)); - Slog.d(LOG_TAG, "connected to aconfigd socket"); - } catch (IOException ioe) { - Slog.e(LOG_TAG, "failed to connect to aconfigd socket", ioe); - return; + if (enableAconfigStorageDaemon()) { + if (isConfigSettingsKey(mKey)){ + AconfigdClientSocket localSocket = AconfigdJavaUtils.getAconfigdClientSocket(); + if (requests != null) { + InputStream res = localSocket.send(requests.getBytes()); + if (res == null) { + Slog.w(LOG_TAG, "Bulk sync request to acongid failed."); + } + } + // TOBO(b/312444587): remove the comparison logic after Test Mission 2. + if (mSettings.get("aconfigd_marker/bulk_synced").value.equals("true") + && requests == null) { + Map<String, AconfigdFlagInfo> aconfigdFlagMap = + AconfigdJavaUtils.listFlagsValueInNewStorage(localSocket); + compareFlagValueInNewStorage( + aconfigFlagMap, + mAconfigDefaultFlags, + aconfigdFlagMap); + } } - AconfigdJavaUtils.sendAconfigdRequests(client, requests); } } - // TODO(b/341764371): migrate aconfig flag push to GMS core - public static class FlagOverrideToSync { - public String packageName; - public String flagName; - public String flagValue; - public boolean isLocal; + // TOBO(b/312444587): remove the comparison logic after Test Mission 2. + public int compareFlagValueInNewStorage( + Map<String, AconfigdFlagInfo> settingFlagMap, + Map<String, AconfigdFlagInfo> defaultFlagMap, + Map<String, AconfigdFlagInfo> aconfigdFlagMap) { + + // Get all defaults from the default map. The mSettings may not contain + // all flags, since it only contains updated flags. + int diffNum = 0; + for (Map.Entry<String, AconfigdFlagInfo> entry : defaultFlagMap.entrySet()) { + String key = entry.getKey(); + AconfigdFlagInfo flag = entry.getValue(); + if (settingFlagMap.containsKey(key)) { + flag.merge(settingFlagMap.get(key)); + } + + AconfigdFlagInfo aconfigdFlag = aconfigdFlagMap.get(key); + if (aconfigdFlag == null) { + Slog.w(LOG_TAG, String.format("Flag %s is missing from aconfigd", key)); + diffNum++; + continue; + } + String diff = flag.dumpDiff(aconfigdFlag); + if (!diff.isEmpty()) { + Slog.w( + LOG_TAG, + String.format( + "Flag %s is different in Settings and aconfig: %s", key, diff)); + diffNum++; + } + } + + for (String key : aconfigdFlagMap.keySet()) { + if (defaultFlagMap.containsKey(key)) continue; + Slog.w(LOG_TAG, String.format("Flag %s is missing from Settings", key)); + diffNum++; + } + + if (diffNum == 0) { + Slog.i(LOG_TAG, "Settings and new storage have same flags."); + } + return diffNum; + } + + @GuardedBy("mLock") + public Map<String, AconfigdFlagInfo> getAllAconfigFlagsFromSettings() { + Map<String, AconfigdFlagInfo> ret = new HashMap<>(); + int numSettings = mSettings.size(); + int num_requests = 0; + for (int i = 0; i < numSettings; i++) { + String name = mSettings.keyAt(i); + Setting setting = mSettings.valueAt(i); + AconfigdFlagInfo flag = + getFlagOverrideToSync(name, setting.getValue()); + if (flag == null) { + continue; + } + String fullFlagName = flag.getFullFlagName(); + AconfigdFlagInfo prev = ret.putIfAbsent(fullFlagName,flag); + if (prev != null) { + prev.merge(flag); + } + ++num_requests; + } + Slog.i(LOG_TAG, num_requests + " flag override requests created"); + return ret; } // TODO(b/341764371): migrate aconfig flag push to GMS core @VisibleForTesting @GuardedBy("mLock") - public FlagOverrideToSync getFlagOverrideToSync(String name, String value) { + public AconfigdFlagInfo getFlagOverrideToSync(String name, String value) { int slashIdx = name.indexOf("/"); - if (slashIdx <= 0 || slashIdx >= name.length()-1) { + if (slashIdx <= 0 || slashIdx >= name.length() - 1) { Slog.e(LOG_TAG, "invalid flag name " + name); return null; } @@ -430,8 +518,9 @@ final class SettingsState { } String aconfigName = namespace + "/" + fullFlagName; - boolean isAconfig = mNamespaceDefaults.containsKey(namespace) - && mNamespaceDefaults.get(namespace).containsKey(aconfigName); + boolean isAconfig = + mNamespaceDefaults.containsKey(namespace) + && mNamespaceDefaults.get(namespace).containsKey(aconfigName); if (!isAconfig) { return null; } @@ -443,25 +532,30 @@ final class SettingsState { return null; } - FlagOverrideToSync flag = new FlagOverrideToSync(); - flag.packageName = fullFlagName.substring(0, dotIdx); - flag.flagName = fullFlagName.substring(dotIdx + 1); - flag.isLocal = isLocal; - flag.flagValue = value; - return flag; + AconfigdFlagInfo.Builder builder = AconfigdFlagInfo.newBuilder() + .setPackageName(fullFlagName.substring(0, dotIdx)) + .setFlagName(fullFlagName.substring(dotIdx + 1)) + .setDefaultFlagValue(mNamespaceDefaults.get(namespace).get(aconfigName)); + + if (isLocal) { + builder.setHasLocalOverride(isLocal).setBootFlagValue(value).setLocalFlagValue(value); + } else { + builder.setHasServerOverride(true).setServerFlagValue(value).setBootFlagValue(value); + } + return builder.build(); } // TODO(b/341764371): migrate aconfig flag push to GMS core @VisibleForTesting @GuardedBy("mLock") - public ProtoOutputStream handleBulkSyncToNewStorage() { + public ProtoOutputStream handleBulkSyncToNewStorage( + Map<String, AconfigdFlagInfo> aconfigFlagMap) { // get marker or add marker if it does not exist final String bulkSyncMarkerName = new String("aconfigd_marker/bulk_synced"); Setting markerSetting = mSettings.get(bulkSyncMarkerName); if (markerSetting == null) { - markerSetting = new Setting( - bulkSyncMarkerName, "false", false, "aconfig", "aconfig"); + markerSetting = new Setting(bulkSyncMarkerName, "false", false, "aconfig", "aconfig"); mSettings.put(bulkSyncMarkerName, markerSetting); } @@ -479,24 +573,19 @@ final class SettingsState { AconfigdJavaUtils.writeResetStorageRequest(requests); // loop over all settings and add flag override requests - final int numSettings = mSettings.size(); - int num_requests = 0; - for (int i = 0; i < numSettings; i++) { - String name = mSettings.keyAt(i); - Setting setting = mSettings.valueAt(i); - FlagOverrideToSync flag = - getFlagOverrideToSync(name, setting.getValue()); - if (flag == null) { - continue; - } - ++num_requests; + for (AconfigdFlagInfo flag : aconfigFlagMap.values()) { + String value = + flag.getHasLocalOverride() + ? flag.getLocalFlagValue() + : flag.getServerFlagValue(); AconfigdJavaUtils.writeFlagOverrideRequest( - requests, flag.packageName, flag.flagName, flag.flagValue, - flag.isLocal); + requests, + flag.getPackageName(), + flag.getFlagName(), + value, + flag.getHasLocalOverride()); } - Slog.i(LOG_TAG, num_requests + " flag override requests created"); - // mark sync has been done markerSetting.value = "true"; scheduleWriteIfNeededLocked(); @@ -513,14 +602,14 @@ final class SettingsState { return null; } } - } @GuardedBy("mLock") private void loadAconfigDefaultValuesLocked(List<String> filePaths) { for (String fileName : filePaths) { try (FileInputStream inputStream = new FileInputStream(fileName)) { - loadAconfigDefaultValues(inputStream.readAllBytes(), mNamespaceDefaults); + loadAconfigDefaultValues( + inputStream.readAllBytes(), mNamespaceDefaults, mAconfigDefaultFlags); } catch (IOException e) { Slog.e(LOG_TAG, "failed to read protobuf", e); } @@ -566,21 +655,30 @@ final class SettingsState { @VisibleForTesting @GuardedBy("mLock") - public static void loadAconfigDefaultValues(byte[] fileContents, - @NonNull Map<String, Map<String, String>> defaultMap) { + public static void loadAconfigDefaultValues( + byte[] fileContents, + @NonNull Map<String, Map<String, String>> defaultMap, + @NonNull Map<String, AconfigdFlagInfo> flagInfoDefault) { try { - parsed_flags parsedFlags = - parsed_flags.parseFrom(fileContents); + parsed_flags parsedFlags = parsed_flags.parseFrom(fileContents); for (parsed_flag flag : parsedFlags.getParsedFlagList()) { if (!defaultMap.containsKey(flag.getNamespace())) { Map<String, String> defaults = new HashMap<>(); defaultMap.put(flag.getNamespace(), defaults); } - String flagName = flag.getNamespace() - + "/" + flag.getPackage() + "." + flag.getName(); - String flagValue = flag.getState() == flag_state.ENABLED - ? "true" : "false"; + String fullFlagName = flag.getPackage() + "." + flag.getName(); + String flagName = flag.getNamespace() + "/" + fullFlagName; + String flagValue = flag.getState() == flag_state.ENABLED ? "true" : "false"; defaultMap.get(flag.getNamespace()).put(flagName, flagValue); + if (!flagInfoDefault.containsKey(fullFlagName)) { + flagInfoDefault.put( + fullFlagName, + AconfigdFlagInfo.newBuilder() + .setPackageName(flag.getPackage()) + .setFlagName(flag.getName()) + .setDefaultFlagValue(flagValue) + .build()); + } } } catch (IOException e) { Slog.e(LOG_TAG, "failed to parse protobuf", e); @@ -1646,7 +1744,6 @@ final class SettingsState { } } } - mSettings.put(name, new Setting(name, value, defaultValue, packageName, tag, fromSystem, id, isPreservedInRestore)); diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsProviderMultiUsersTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsProviderMultiUsersTest.java index ca1e4c10d339..e4898daf3cbf 100644 --- a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsProviderMultiUsersTest.java +++ b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsProviderMultiUsersTest.java @@ -27,11 +27,11 @@ import android.support.test.uiautomator.UiDevice; import androidx.test.platform.app.InstrumentationRegistry; +import com.android.bedstead.enterprise.annotations.EnsureHasNoWorkProfile; +import com.android.bedstead.enterprise.annotations.EnsureHasWorkProfile; import com.android.bedstead.harrier.BedsteadJUnit4; import com.android.bedstead.harrier.DeviceState; -import com.android.bedstead.harrier.annotations.EnsureHasNoWorkProfile; import com.android.bedstead.harrier.annotations.EnsureHasSecondaryUser; -import com.android.bedstead.harrier.annotations.EnsureHasWorkProfile; import com.android.bedstead.harrier.annotations.RequireFeature; import com.android.bedstead.harrier.annotations.RequireRunOnInitialUser; import com.android.bedstead.harrier.annotations.RequireRunOnPrimaryUser; diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java index 244c8c4d99bc..256b999ca6c5 100644 --- a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java +++ b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java @@ -24,13 +24,13 @@ import static junit.framework.Assert.fail; import android.aconfig.Aconfig; import android.aconfig.Aconfig.parsed_flag; import android.aconfig.Aconfig.parsed_flags; +import android.aconfigd.AconfigdFlagInfo; import android.os.Looper; import android.platform.test.annotations.RequiresFlagsEnabled; import android.platform.test.flag.junit.CheckFlagsRule; import android.platform.test.flag.junit.DeviceFlagsValueProvider; import android.util.Xml; import android.util.proto.ProtoOutputStream; -import com.android.providers.settings.SettingsState.FlagOverrideToSync; import androidx.test.InstrumentationRegistry; import androidx.test.runner.AndroidJUnit4; @@ -145,16 +145,32 @@ public class SettingsStateTest { .setState(Aconfig.flag_state.ENABLED) .setPermission(Aconfig.flag_permission.READ_WRITE)) .build(); + + AconfigdFlagInfo flag1 = AconfigdFlagInfo.newBuilder() + .setPackageName("com.android.flags") + .setFlagName("flag1") + .setDefaultFlagValue("false") + .build(); + AconfigdFlagInfo flag2 = AconfigdFlagInfo.newBuilder() + .setPackageName("com.android.flags") + .setFlagName("flag2") + .setDefaultFlagValue("true") + .build(); + Map<String, AconfigdFlagInfo> flagInfoDefault = new HashMap<>(); synchronized (lock) { Map<String, Map<String, String>> defaults = new HashMap<>(); - settingsState.loadAconfigDefaultValues(flags.toByteArray(), defaults); + settingsState.loadAconfigDefaultValues( + flags.toByteArray(), defaults, flagInfoDefault); Map<String, String> namespaceDefaults = defaults.get("test_namespace"); assertEquals(2, namespaceDefaults.keySet().size()); assertEquals("false", namespaceDefaults.get("test_namespace/com.android.flags.flag1")); assertEquals("true", namespaceDefaults.get("test_namespace/com.android.flags.flag2")); } + + assertEquals(flag1, flagInfoDefault.get(flag1.getFullFlagName())); + assertEquals(flag2, flagInfoDefault.get(flag2.getFullFlagName())); } @Test @@ -165,6 +181,8 @@ public class SettingsStateTest { InstrumentationRegistry.getContext(), lock, mSettingsFile, configKey, SettingsState.MAX_BYTES_PER_APP_PACKAGE_UNLIMITED, Looper.getMainLooper()); + Map<String, AconfigdFlagInfo> flagInfoDefault = new HashMap<>(); + parsed_flags flags = parsed_flags .newBuilder() .addParsedFlag(parsed_flag @@ -177,7 +195,8 @@ public class SettingsStateTest { synchronized (lock) { Map<String, Map<String, String>> defaults = new HashMap<>(); - settingsState.loadAconfigDefaultValues(flags.toByteArray(), defaults); + settingsState.loadAconfigDefaultValues( + flags.toByteArray(), defaults, flagInfoDefault); Map<String, String> namespaceDefaults = defaults.get("test_namespace"); assertEquals(null, namespaceDefaults); @@ -204,10 +223,12 @@ public class SettingsStateTest { .setState(Aconfig.flag_state.DISABLED) .setPermission(Aconfig.flag_permission.READ_WRITE)) .build(); + Map<String, AconfigdFlagInfo> flagInfoDefault = new HashMap<>(); synchronized (lock) { Map<String, Map<String, String>> defaults = new HashMap<>(); - settingsState.loadAconfigDefaultValues(flags.toByteArray(), defaults); + settingsState.loadAconfigDefaultValues( + flags.toByteArray(), defaults, flagInfoDefault); settingsState.addAconfigDefaultValuesFromMap(defaults); settingsState.insertSettingLocked("test_namespace/com.android.flags.flag5", @@ -238,8 +259,10 @@ public class SettingsStateTest { @Test public void testInvalidAconfigProtoDoesNotCrash() { Map<String, Map<String, String>> defaults = new HashMap<>(); + Map<String, AconfigdFlagInfo> flagInfoDefault = new HashMap<>(); SettingsState settingsState = getSettingStateObject(); - settingsState.loadAconfigDefaultValues("invalid protobuf".getBytes(), defaults); + settingsState.loadAconfigDefaultValues( + "invalid protobuf".getBytes(), defaults, flagInfoDefault); } @Test @@ -759,6 +782,8 @@ public class SettingsStateTest { Map<String, String> keyValues = Map.of("test_namespace/com.android.flags.flag3", "true"); + Map<String, AconfigdFlagInfo> flagInfoDefault = new HashMap<>(); + parsed_flags flags = parsed_flags .newBuilder() .addParsedFlag(parsed_flag @@ -774,7 +799,8 @@ public class SettingsStateTest { synchronized (mLock) { settingsState.loadAconfigDefaultValues( - flags.toByteArray(), settingsState.getAconfigDefaultValues()); + flags.toByteArray(), + settingsState.getAconfigDefaultValues(), flagInfoDefault); List<String> updates = settingsState.setSettingsLocked("test_namespace/", keyValues, packageName); assertEquals(1, updates.size()); @@ -840,10 +866,13 @@ public class SettingsStateTest { .setState(Aconfig.flag_state.DISABLED) .setPermission(Aconfig.flag_permission.READ_WRITE)) .build(); + Map<String, AconfigdFlagInfo> flagInfoDefault = new HashMap<>(); synchronized (mLock) { settingsState.loadAconfigDefaultValues( - flags.toByteArray(), settingsState.getAconfigDefaultValues()); + flags.toByteArray(), + settingsState.getAconfigDefaultValues(), + flagInfoDefault); List<String> updates = settingsState.setSettingsLocked("test_namespace/", keyValues, packageName); assertEquals(3, updates.size()); @@ -973,10 +1002,12 @@ public class SettingsStateTest { .setState(Aconfig.flag_state.DISABLED) .setPermission(Aconfig.flag_permission.READ_WRITE)) .build(); + Map<String, AconfigdFlagInfo> flagInfoDefault = new HashMap<>(); synchronized (lock) { Map<String, Map<String, String>> defaults = new HashMap<>(); - settingsState.loadAconfigDefaultValues(flags.toByteArray(), defaults); + settingsState.loadAconfigDefaultValues( + flags.toByteArray(), defaults, flagInfoDefault); Map<String, String> namespaceDefaults = defaults.get("test_namespace"); assertEquals(1, namespaceDefaults.keySet().size()); settingsState.addAconfigDefaultValuesFromMap(defaults); @@ -991,22 +1022,28 @@ public class SettingsStateTest { "some_namespace/some_flag", "false") == null); // server override - FlagOverrideToSync flag = settingsState.getFlagOverrideToSync( + AconfigdFlagInfo flag = settingsState.getFlagOverrideToSync( "test_namespace/com.android.flags.flag1", "false"); assertTrue(flag != null); - assertEquals(flag.packageName, "com.android.flags"); - assertEquals(flag.flagName, "flag1"); - assertEquals(flag.flagValue, "false"); - assertEquals(flag.isLocal, false); + assertEquals(flag.getPackageName(), "com.android.flags"); + assertEquals(flag.getFlagName(), "flag1"); + assertEquals("false", flag.getBootFlagValue()); + assertEquals("false", flag.getServerFlagValue()); + assertFalse(flag.getHasLocalOverride()); + assertNull(flag.getLocalFlagValue()); + assertEquals("false", flag.getDefaultFlagValue()); // local override flag = settingsState.getFlagOverrideToSync( "device_config_overrides/test_namespace:com.android.flags.flag1", "false"); assertTrue(flag != null); - assertEquals(flag.packageName, "com.android.flags"); - assertEquals(flag.flagName, "flag1"); - assertEquals(flag.flagValue, "false"); - assertEquals(flag.isLocal, true); + assertEquals(flag.getPackageName(), "com.android.flags"); + assertEquals(flag.getFlagName(), "flag1"); + assertEquals("false", flag.getLocalFlagValue()); + assertEquals("false", flag.getBootFlagValue()); + assertTrue(flag.getHasLocalOverride()); + assertNull(flag.getServerFlagValue()); + assertEquals("false", flag.getDefaultFlagValue()); } @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); @@ -1020,18 +1057,25 @@ public class SettingsStateTest { InstrumentationRegistry.getContext(), lock, mSettingsFile, configKey, SettingsState.MAX_BYTES_PER_APP_PACKAGE_UNLIMITED, Looper.getMainLooper()); + Map<String, AconfigdFlagInfo> flags = new HashMap<>(); + AconfigdFlagInfo flag = AconfigdFlagInfo.newBuilder() + .setPackageName("com.android.flags") + .setFlagName("flag1") + .setBootFlagValue("true").build(); + flags.put("com.android.flags/flag1", flag); + synchronized (lock) { settingsState.insertSettingLocked("aconfigd_marker/bulk_synced", "false", null, false, "aconfig"); // first bulk sync - ProtoOutputStream requests = settingsState.handleBulkSyncToNewStorage(); + ProtoOutputStream requests = settingsState.handleBulkSyncToNewStorage(flags); assertTrue(requests != null); String value = settingsState.getSettingLocked("aconfigd_marker/bulk_synced").getValue(); assertEquals("true", value); // send time should no longer bulk sync - requests = settingsState.handleBulkSyncToNewStorage(); + requests = settingsState.handleBulkSyncToNewStorage(flags); assertTrue(requests == null); value = settingsState.getSettingLocked("aconfigd_marker/bulk_synced").getValue(); assertEquals("true", value); @@ -1047,21 +1091,200 @@ public class SettingsStateTest { InstrumentationRegistry.getContext(), lock, mSettingsFile, configKey, SettingsState.MAX_BYTES_PER_APP_PACKAGE_UNLIMITED, Looper.getMainLooper()); + Map<String, AconfigdFlagInfo> flags = new HashMap<>(); synchronized (lock) { settingsState.insertSettingLocked("aconfigd_marker/bulk_synced", "true", null, false, "aconfig"); // when aconfigd is off, should change the marker to false - ProtoOutputStream requests = settingsState.handleBulkSyncToNewStorage(); + ProtoOutputStream requests = settingsState.handleBulkSyncToNewStorage(flags); assertTrue(requests == null); String value = settingsState.getSettingLocked("aconfigd_marker/bulk_synced").getValue(); assertEquals("false", value); // marker started with false value, after call, it should remain false - requests = settingsState.handleBulkSyncToNewStorage(); + requests = settingsState.handleBulkSyncToNewStorage(flags); assertTrue(requests == null); value = settingsState.getSettingLocked("aconfigd_marker/bulk_synced").getValue(); assertEquals("false", value); } } + + @Test + public void testGetAllAconfigFlagsFromSettings() throws Exception { + final Object lock = new Object(); + final PrintStream os = new PrintStream(new FileOutputStream(mSettingsFile)); + os.print( + "<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>" + + "<settings version=\"120\">" + + " <setting id=\"0\" name=\"test_namespace/com.android.flags.flag1\" " + + "value=\"false\" package=\"com.android.flags\" />" + + " <setting id=\"1\" name=\"device_config_overrides/test_namespace:com.android.flags.flag1\" " + + "value=\"true\" package=\"com.android.flags\" />" + + " <setting id=\"2\" name=\"device_config_overrides/test_namespace:com.android.flags.flag2\" " + + "value=\"true\" package=\"com.android.flags\" />" + + " <setting id=\"3\" name=\"test_namespace/com.android.flags.flag3\" " + + "value=\"true\" package=\"com.android.flags\" />" + + "</settings>"); + os.close(); + + int configKey = SettingsState.makeKey(SettingsState.SETTINGS_TYPE_CONFIG, 0); + + SettingsState settingsState = new SettingsState( + InstrumentationRegistry.getContext(), lock, mSettingsFile, configKey, + SettingsState.MAX_BYTES_PER_APP_PACKAGE_UNLIMITED, Looper.getMainLooper()); + + Map<String, AconfigdFlagInfo> ret; + synchronized (lock) { + ret = settingsState.getAllAconfigFlagsFromSettings(); + } + + assertTrue(ret.isEmpty()); + + parsed_flags flags = + parsed_flags + .newBuilder() + .addParsedFlag( + parsed_flag + .newBuilder() + .setPackage("com.android.flags") + .setName("flag1") + .setNamespace("test_namespace") + .setDescription("test flag") + .addBug("12345678") + .setState(Aconfig.flag_state.DISABLED) + .setPermission(Aconfig.flag_permission.READ_WRITE)) + .addParsedFlag( + parsed_flag + .newBuilder() + .setPackage("com.android.flags") + .setName("flag2") + .setNamespace("test_namespace") + .setDescription("test flag") + .addBug("12345678") + .setState(Aconfig.flag_state.DISABLED) + .setPermission(Aconfig.flag_permission.READ_WRITE)) + .addParsedFlag( + parsed_flag + .newBuilder() + .setPackage("com.android.flags") + .setName("flag3") + .setNamespace("test_namespace") + .setDescription("test flag") + .addBug("12345678") + .setState(Aconfig.flag_state.DISABLED) + .setPermission(Aconfig.flag_permission.READ_WRITE)) + .build(); + + Map<String, Map<String, String>> defaults = new HashMap<>(); + Map<String, AconfigdFlagInfo> flagInfoDefault = new HashMap<>(); + synchronized (lock) { + settingsState.loadAconfigDefaultValues( + flags.toByteArray(), defaults, flagInfoDefault); + settingsState.addAconfigDefaultValuesFromMap(defaults); + ret = settingsState.getAllAconfigFlagsFromSettings(); + } + + AconfigdFlagInfo expectedFlag1 = + AconfigdFlagInfo.newBuilder() + .setPackageName("com.android.flags") + .setFlagName("flag1") + .setServerFlagValue("false") + .setLocalFlagValue("true") + .setDefaultFlagValue("false") + .setBootFlagValue("true") + .setHasServerOverride(true) + .setHasLocalOverride(true) + .setIsReadWrite(false) + .build(); + + AconfigdFlagInfo expectedFlag2 = + AconfigdFlagInfo.newBuilder() + .setPackageName("com.android.flags") + .setFlagName("flag2") + .setLocalFlagValue("true") + .setDefaultFlagValue("false") + .setBootFlagValue("true") + .setHasLocalOverride(true) + .setHasServerOverride(false) + .setIsReadWrite(false) + .build(); + + + AconfigdFlagInfo expectedFlag3 = + AconfigdFlagInfo.newBuilder() + .setPackageName("com.android.flags") + .setFlagName("flag3") + .setServerFlagValue("true") + .setBootFlagValue("true") + .setDefaultFlagValue("false") + .setHasServerOverride(true) + .setIsReadWrite(false) + .build(); + + assertEquals(expectedFlag1, ret.get("com.android.flags.flag1")); + assertEquals(expectedFlag2, ret.get("com.android.flags.flag2")); + assertEquals(expectedFlag3, ret.get("com.android.flags.flag3")); + } + + @Test + public void testCompareFlagValueInNewStorage() { + int configKey = SettingsState.makeKey(SettingsState.SETTINGS_TYPE_CONFIG, 0); + Object lock = new Object(); + SettingsState settingsState = + new SettingsState( + InstrumentationRegistry.getContext(), + lock, + mSettingsFile, + configKey, + SettingsState.MAX_BYTES_PER_APP_PACKAGE_UNLIMITED, + Looper.getMainLooper()); + + AconfigdFlagInfo defaultFlag1 = + AconfigdFlagInfo.newBuilder() + .setPackageName("com.android.flags") + .setFlagName("flag1") + .setDefaultFlagValue("false") + .build(); + + AconfigdFlagInfo settingFlag1 = + AconfigdFlagInfo.newBuilder() + .setPackageName("com.android.flags") + .setFlagName("flag1") + .setServerFlagValue("true") + .setHasServerOverride(true) + .build(); + + AconfigdFlagInfo expectedFlag1 = + AconfigdFlagInfo.newBuilder() + .setPackageName("com.android.flags") + .setFlagName("flag1") + .setBootFlagValue("true") + .setServerFlagValue("true") + .setDefaultFlagValue("false") + .setHasServerOverride(true) + .build(); + + Map<String, AconfigdFlagInfo> settingMap = new HashMap<>(); + Map<String, AconfigdFlagInfo> aconfigdMap = new HashMap<>(); + Map<String, AconfigdFlagInfo> defaultMap = new HashMap<>(); + + defaultMap.put("com.android.flags.flag1", defaultFlag1); + settingMap.put("com.android.flags.flag1", settingFlag1); + aconfigdMap.put("com.android.flags.flag1", expectedFlag1); + + int ret = settingsState.compareFlagValueInNewStorage(settingMap, defaultMap, aconfigdMap); + assertEquals(0, ret); + + AconfigdFlagInfo defaultFlag2 = + AconfigdFlagInfo.newBuilder() + .setPackageName("com.android.flags") + .setFlagName("flag2") + .setDefaultFlagValue("false") + .build(); + defaultMap.put("com.android.flags.flag2", defaultFlag2); + + ret = settingsState.compareFlagValueInNewStorage(settingMap, defaultMap, aconfigdMap); + assertEquals(1, ret); + } } diff --git a/packages/SystemUI/aconfig/accessibility.aconfig b/packages/SystemUI/aconfig/accessibility.aconfig index 55edff6d9518..d201071620e4 100644 --- a/packages/SystemUI/aconfig/accessibility.aconfig +++ b/packages/SystemUI/aconfig/accessibility.aconfig @@ -81,3 +81,13 @@ flag { purpose: PURPOSE_BUGFIX } } + +flag { + name: "hearing_devices_dialog_related_tools" + namespace: "accessibility" + description: "Shows the related tools for hearing devices dialog." + bug: "341648471" + metadata { + purpose: PURPOSE_BUGFIX + } +} diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig index 1df9c88e48ac..dcca8455fe41 100644 --- a/packages/SystemUI/aconfig/systemui.aconfig +++ b/packages/SystemUI/aconfig/systemui.aconfig @@ -177,16 +177,9 @@ flag { } flag { - name: "notification_throttle_hun" - namespace: "systemui" - description: "During notification avalanche, throttle HUNs showing in fast succession." - bug: "307288824" -} - -flag { name: "notification_avalanche_throttle_hun" namespace: "systemui" - description: "(currently unused) During notification avalanche, throttle HUNs showing in fast succession." + description: "During notification avalanche, throttle HUNs showing in fast succession." bug: "307288824" } @@ -1026,3 +1019,13 @@ flag { purpose: PURPOSE_BUGFIX } } + +flag { + name: "notification_media_manager_background_execution" + namespace: "systemui" + description: "Decide whether to execute binder calls in background thread" + bug: "336612071" + metadata { + purpose: PURPOSE_BUGFIX + } +} diff --git a/packages/SystemUI/compose/core/src/com/android/compose/ui/platform/DensityAwareComposeView.kt b/packages/SystemUI/compose/core/src/com/android/compose/ui/platform/DensityAwareComposeView.kt deleted file mode 100644 index dff8753fd880..000000000000 --- a/packages/SystemUI/compose/core/src/com/android/compose/ui/platform/DensityAwareComposeView.kt +++ /dev/null @@ -1,96 +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.compose.ui.platform - -import android.content.Context -import android.content.res.Configuration -import android.util.AttributeSet -import androidx.compose.runtime.Composable -import androidx.compose.runtime.mutableStateOf -import androidx.compose.ui.platform.AbstractComposeView - -/** - * A ComposeView that recreates its composition if the display size or font scale was changed. - * - * TODO(b/317317814): Remove this workaround. - */ -class DensityAwareComposeView(context: Context) : OpenComposeView(context) { - private var lastDensityDpi: Int = -1 - private var lastFontScale: Float = -1f - - override fun onAttachedToWindow() { - super.onAttachedToWindow() - - val configuration = context.resources.configuration - lastDensityDpi = configuration.densityDpi - lastFontScale = configuration.fontScale - } - - override fun dispatchConfigurationChanged(newConfig: Configuration) { - super.dispatchConfigurationChanged(newConfig) - - // If the density or font scale changed, we dispose then recreate the composition. Note that - // we do this here after dispatching the new configuration to children (instead of doing - // this in onConfigurationChanged()) because the new configuration should first be - // dispatched to the AndroidComposeView that holds the current density before we recreate - // the composition. - val densityDpi = newConfig.densityDpi - val fontScale = newConfig.fontScale - if (densityDpi != lastDensityDpi || fontScale != lastFontScale) { - lastDensityDpi = densityDpi - lastFontScale = fontScale - - disposeComposition() - if (isAttachedToWindow) { - createComposition() - } - } - } -} - -/** A fork of [androidx.compose.ui.platform.ComposeView] that is open and can be subclassed. */ -open class OpenComposeView -internal constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) : - AbstractComposeView(context, attrs, defStyleAttr) { - - private val content = mutableStateOf<(@Composable () -> Unit)?>(null) - - @Suppress("RedundantVisibilityModifier") - protected override var shouldCreateCompositionOnAttachedToWindow: Boolean = false - - @Composable - override fun Content() { - content.value?.invoke() - } - - override fun getAccessibilityClassName(): CharSequence { - return javaClass.name - } - - /** - * Set the Jetpack Compose UI content for this view. Initial composition will occur when the - * view becomes attached to a window or when [createComposition] is called, whichever comes - * first. - */ - fun setContent(content: @Composable () -> Unit) { - shouldCreateCompositionOnAttachedToWindow = true - this.content.value = content - if (isAttachedToWindow) { - createComposition() - } - } -} diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt index cd27d5713c2d..9dd3d39cb040 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt @@ -23,6 +23,7 @@ import android.util.SizeF import android.view.View.IMPORTANT_FOR_ACCESSIBILITY_AUTO import android.view.View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS import android.widget.FrameLayout +import androidx.annotation.VisibleForTesting import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.AnimatedVisibilityScope import androidx.compose.animation.ExperimentalAnimationApi @@ -162,7 +163,6 @@ fun CommunalHub( var removeButtonCoordinates: LayoutCoordinates? by remember { mutableStateOf(null) } var toolbarSize: IntSize? by remember { mutableStateOf(null) } var gridCoordinates: LayoutCoordinates? by remember { mutableStateOf(null) } - var isDraggingToRemove by remember { mutableStateOf(false) } val gridState = rememberLazyGridState() val contentListState = rememberContentListState(widgetConfigurator, communalContent, viewModel) val reorderingWidgets by viewModel.reorderingWidgets.collectAsStateWithLifecycle() @@ -250,12 +250,11 @@ fun CommunalHub( contentOffset = contentOffset, setGridCoordinates = { gridCoordinates = it }, updateDragPositionForRemove = { offset -> - isDraggingToRemove = - isPointerWithinCoordinates( - offset = gridCoordinates?.let { it.positionInWindow() + offset }, - containerToCheck = removeButtonCoordinates - ) - isDraggingToRemove + isPointerWithinEnabledRemoveButton( + removeEnabled = removeButtonEnabled, + offset = gridCoordinates?.let { it.positionInWindow() + offset }, + containerToCheck = removeButtonCoordinates + ) }, gridState = gridState, contentListState = contentListState, @@ -640,11 +639,16 @@ private fun AnimatedVisibilityScope.ButtonToEditWidgets( enter = fadeIn( initialAlpha = 0f, - animationSpec = tween(durationMillis = 500, easing = LinearEasing) + animationSpec = tween(durationMillis = 83, easing = LinearEasing) ), exit = fadeOut( - animationSpec = tween(durationMillis = 500, easing = LinearEasing) + animationSpec = + tween( + durationMillis = 83, + delayMillis = 167, + easing = LinearEasing + ) ) ) .background(colors.secondary, RoundedCornerShape(50.dp)), @@ -658,7 +662,7 @@ private fun AnimatedVisibilityScope.ButtonToEditWidgets( animationSpec = tween( durationMillis = 167, - delayMillis = 500, + delayMillis = 83, easing = LinearEasing ) ), @@ -1195,11 +1199,13 @@ private fun beforeContentPadding(paddingValues: PaddingValues): ContentPaddingIn * Check whether the pointer position that the item is being dragged at is within the coordinates of * the remove button in the toolbar. Returns true if the item is removable. */ -private fun isPointerWithinCoordinates( +@VisibleForTesting +fun isPointerWithinEnabledRemoveButton( + removeEnabled: Boolean, offset: Offset?, containerToCheck: LayoutCoordinates? ): Boolean { - if (offset == null || containerToCheck == null) { + if (!removeEnabled || offset == null || containerToCheck == null) { return false } val container = containerToCheck.boundsInWindow() diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt index c26259f3287c..ee3ffce27d51 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt @@ -126,7 +126,8 @@ fun SceneScope.HeadsUpNotificationSpace( " size=${coordinates.size}" + " bounds=$boundsInWindow" } - viewModel.onHeadsUpTopChanged(boundsInWindow.top) + // Note: boundsInWindow doesn't scroll off the screen + stackScrollView.setHeadsUpTop(boundsInWindow.top) } ) { content {} diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlider.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlider.kt index 271eb9601dbd..fbf91b702fb9 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlider.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlider.kt @@ -68,9 +68,7 @@ fun VolumeSlider( state.a11yClickDescription?.let { customActions = listOf( - CustomAccessibilityAction( - it, - ) { + CustomAccessibilityAction(it) { onIconTapped() true } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/brightness/data/repository/ScreenBrightnessDisplayManagerRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/brightness/data/repository/ScreenBrightnessDisplayManagerRepositoryTest.kt index e39ad4f0b405..a676c7db4290 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/brightness/data/repository/ScreenBrightnessDisplayManagerRepositoryTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/brightness/data/repository/ScreenBrightnessDisplayManagerRepositoryTest.kt @@ -25,15 +25,18 @@ import android.view.Display import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase -import com.android.systemui.brightness.data.model.LinearBrightness +import com.android.systemui.brightness.shared.model.LinearBrightness import com.android.systemui.coroutines.collectLastValue import com.android.systemui.kosmos.applicationCoroutineScope import com.android.systemui.kosmos.testDispatcher import com.android.systemui.kosmos.testScope +import com.android.systemui.log.core.FakeLogBuffer +import com.android.systemui.log.table.TableLogBuffer import com.android.systemui.testKosmos import com.android.systemui.util.mockito.argumentCaptor import com.android.systemui.util.mockito.capture import com.android.systemui.util.mockito.eq +import com.android.systemui.util.mockito.mock import com.android.systemui.util.mockito.whenever import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -74,6 +77,8 @@ class ScreenBrightnessDisplayManagerRepositoryTest : SysuiTestCase() { ScreenBrightnessDisplayManagerRepository( displayId, displayManager, + FakeLogBuffer.Factory.create(), + mock<TableLogBuffer>(), kosmos.applicationCoroutineScope, kosmos.testDispatcher, ) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/brightness/domain/interactor/ScreenBrightnessInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/brightness/domain/interactor/ScreenBrightnessInteractorTest.kt index 33c44f8a331e..b6616bf0c8de 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/brightness/domain/interactor/ScreenBrightnessInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/brightness/domain/interactor/ScreenBrightnessInteractorTest.kt @@ -20,13 +20,16 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.settingslib.display.BrightnessUtils import com.android.systemui.SysuiTestCase -import com.android.systemui.brightness.data.model.LinearBrightness import com.android.systemui.brightness.data.repository.fakeScreenBrightnessRepository import com.android.systemui.brightness.data.repository.screenBrightnessRepository -import com.android.systemui.brightness.shared.GammaBrightness +import com.android.systemui.brightness.shared.model.GammaBrightness +import com.android.systemui.brightness.shared.model.LinearBrightness import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.kosmos.applicationCoroutineScope import com.android.systemui.kosmos.testScope +import com.android.systemui.log.table.TableLogBuffer import com.android.systemui.testKosmos +import com.android.systemui.util.mockito.mock import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runCurrent @@ -41,7 +44,14 @@ class ScreenBrightnessInteractorTest : SysuiTestCase() { private val kosmos = testKosmos() - private val underTest = ScreenBrightnessInteractor(kosmos.screenBrightnessRepository) + private val underTest = + with(kosmos) { + ScreenBrightnessInteractor( + screenBrightnessRepository, + applicationCoroutineScope, + mock<TableLogBuffer>() + ) + } @Test fun gammaBrightness() = diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/brightness/ui/viewmodel/BrightnessSliderViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/brightness/ui/viewmodel/BrightnessSliderViewModelTest.kt index 0058ee4a9c4e..8402676dbd6b 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/brightness/ui/viewmodel/BrightnessSliderViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/brightness/ui/viewmodel/BrightnessSliderViewModelTest.kt @@ -20,15 +20,16 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.settingslib.display.BrightnessUtils import com.android.systemui.SysuiTestCase -import com.android.systemui.brightness.data.model.LinearBrightness import com.android.systemui.brightness.data.repository.fakeScreenBrightnessRepository import com.android.systemui.brightness.domain.interactor.brightnessPolicyEnforcementInteractor import com.android.systemui.brightness.domain.interactor.screenBrightnessInteractor -import com.android.systemui.brightness.shared.GammaBrightness +import com.android.systemui.brightness.shared.model.GammaBrightness +import com.android.systemui.brightness.shared.model.LinearBrightness import com.android.systemui.common.shared.model.ContentDescription import com.android.systemui.common.shared.model.Icon import com.android.systemui.common.shared.model.Text import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.kosmos.applicationCoroutineScope import com.android.systemui.kosmos.testScope import com.android.systemui.res.R import com.android.systemui.testKosmos @@ -52,6 +53,7 @@ class BrightnessSliderViewModelTest : SysuiTestCase() { BrightnessSliderViewModel( screenBrightnessInteractor, brightnessPolicyEnforcementInteractor, + applicationCoroutineScope, ) } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalRepositoryImplTest.kt index 45e7d8a43c2a..fd0bf4dae198 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalRepositoryImplTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalRepositoryImplTest.kt @@ -41,6 +41,7 @@ class CommunalRepositoryImplTest : SysuiTestCase() { private val underTest by lazy { CommunalSceneRepositoryImpl( kosmos.applicationCoroutineScope, + kosmos.applicationCoroutineScope, kosmos.sceneDataSource, ) } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/compose/CommunalHubUtilsTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/compose/CommunalHubUtilsTest.kt new file mode 100644 index 000000000000..643063e738da --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/compose/CommunalHubUtilsTest.kt @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.communal.ui.compose + +import android.testing.TestableLooper +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.layout.LayoutCoordinates +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.google.common.truth.Truth.assertThat +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.kotlin.mock + +@RunWith(AndroidJUnit4::class) +@TestableLooper.RunWithLooper(setAsMainLooper = true) +@SmallTest +class CommunalHubUtilsTest : SysuiTestCase() { + @Test + fun isPointerWithinEnabledRemoveButton_ensureDisabledStatePriority() { + assertThat( + isPointerWithinEnabledRemoveButton(false, mock<Offset>(), mock<LayoutCoordinates>()) + ) + .isFalse() + } +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt index a3a49524e15f..ee8a22c17455 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt @@ -397,6 +397,9 @@ class DreamOverlayServiceTest : SysuiTestCase() { verify(mStateController).setOverlayActive(false) verify(mStateController).setLowLightActive(false) verify(mStateController).setEntryAnimationsFinished(false) + + // Verify touch monitor destroyed + verify(mTouchMonitor).destroy() } @Test diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorTest.kt index d630a2f64c5f..6c5001ab9415 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorTest.kt @@ -136,20 +136,20 @@ class FromAodTransitionInteractorTest : SysuiTestCase() { @Test @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR) - fun testTransitionToOccluded_onWakeUp_ifPowerButtonGestureDetected_fromAod_nonDismissableKeyguard() = + fun testTransitionToOccluded_onWakeUp_ifPowerButtonGestureDetected_fromAod_nonDismissibleKeyguard() = testScope.runTest { powerInteractor.onCameraLaunchGestureDetected() powerInteractor.setAwakeForTest() advanceTimeBy(100) // account for debouncing - // We should head back to GONE since we started there. + // We should head to OCCLUDED because keyguard is not dismissible. assertThat(transitionRepository) .startedTransition(from = KeyguardState.AOD, to = KeyguardState.OCCLUDED) } @Test @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR) - fun testTransitionToGone_onWakeUp_ifPowerButtonGestureDetected_fromAod_dismissableKeyguard() = + fun testTransitionToGone_onWakeUp_ifPowerButtonGestureDetected_fromAod_dismissibleKeyguard() = testScope.runTest { kosmos.fakeKeyguardRepository.setKeyguardDismissible(true) powerInteractor.onCameraLaunchGestureDetected() @@ -188,6 +188,7 @@ class FromAodTransitionInteractorTest : SysuiTestCase() { ) // Detect a power gesture and then wake up. + kosmos.fakeKeyguardRepository.setKeyguardDismissible(true) reset(transitionRepository) powerInteractor.onCameraLaunchGestureDetected() powerInteractor.setAwakeForTest() @@ -355,6 +356,7 @@ class FromAodTransitionInteractorTest : SysuiTestCase() { ) // Detect a power gesture and then wake up. + kosmos.fakeKeyguardRepository.setKeyguardDismissible(true) reset(transitionRepository) powerInteractor.onCameraLaunchGestureDetected() powerInteractor.setAwakeForTest() diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorTest.kt index bfc777509c7b..612f2e73e4bb 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorTest.kt @@ -56,13 +56,13 @@ import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.se import com.android.systemui.power.domain.interactor.powerInteractor import com.android.systemui.testKosmos import junit.framework.Assert.assertEquals -import kotlin.test.Test import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.runBlocking import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Before +import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mockito.reset import org.mockito.Mockito.spy @@ -230,6 +230,7 @@ class FromDozingTransitionInteractorTest : SysuiTestCase() { ) // Detect a power gesture and then wake up. + kosmos.fakeKeyguardRepository.setKeyguardDismissible(true) reset(transitionRepository) powerInteractor.onCameraLaunchGestureDetected() powerInteractor.setAwakeForTest() diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt index 78a116737349..5068f6830fa1 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt @@ -350,7 +350,7 @@ class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() { } @Test - fun quickAffordance_updateOncePerShadeExpansion() = + fun quickAffordance_doNotSendUpdatesWhileShadeExpandingAndStillHidden() = testScope.runTest { val shadeExpansion = MutableStateFlow(0f) whenever(shadeInteractor.anyExpansion).thenReturn(shadeExpansion) @@ -365,7 +365,9 @@ class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() { shadeExpansion.value = i / 10f } - assertThat(collectedValue.size).isEqualTo(initialSize + 1) + assertThat(collectedValue[0]) + .isInstanceOf(KeyguardQuickAffordanceModel.Hidden::class.java) + assertThat(collectedValue.size).isEqualTo(initialSize) } @Test diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModelTest.kt index 04c270d07b0a..ad24a711e9b7 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModelTest.kt @@ -28,6 +28,7 @@ import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository import com.android.systemui.keyguard.domain.interactor.BurnInInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardBottomAreaInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory +import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor import com.android.systemui.keyguard.shared.model.BurnInModel import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancePosition import com.android.systemui.kosmos.testScope @@ -104,6 +105,7 @@ class KeyguardIndicationAreaViewModelTest : SysuiTestCase() { burnInInteractor = burnInInteractor, shortcutsCombinedViewModel = shortcutsCombinedViewModel, configurationInteractor = ConfigurationInteractor(FakeConfigurationRepository()), + keyguardTransitionInteractor = kosmos.keyguardTransitionInteractor, ) } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/TileSpecSettingsRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/TileSpecSettingsRepositoryTest.kt index 37d472169ae5..7ebebd7afa91 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/TileSpecSettingsRepositoryTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/TileSpecSettingsRepositoryTest.kt @@ -203,6 +203,21 @@ class TileSpecSettingsRepositoryTest : SysuiTestCase() { .containsExactlyElementsIn(DEFAULT_TILES.toTileSpecs() + startingTiles) } + @Test + fun prependDefault_noChangesWhenInRetail() = + testScope.runTest { + val user = 0 + retailModeRepository.setRetailMode(true) + val startingTiles = "a" + storeTilesForUser(startingTiles, user) + + runCurrent() + underTest.prependDefault(user) + runCurrent() + + assertThat(loadTilesForUser(user)).isEqualTo(startingTiles) + } + private fun TestScope.storeTilesForUser(specs: String, forUser: Int) { secureSettings.putStringForUser(SETTING, specs, forUser) runCurrent() diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/UserTileSpecRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/UserTileSpecRepositoryTest.kt index 58fc10917d44..b12fbc2066a2 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/UserTileSpecRepositoryTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/UserTileSpecRepositoryTest.kt @@ -327,6 +327,32 @@ class UserTileSpecRepositoryTest : SysuiTestCase() { assertThat(loadTiles()).isEqualTo(expected) } + @Test + fun setTilesWithRepeats_onlyDistinctTiles() = + testScope.runTest { + val tilesToSet = "a,b,c,a,d,b".toTileSpecs() + val expected = "a,b,c,d" + + val tiles by collectLastValue(underTest.tiles()) + underTest.setTiles(tilesToSet) + + assertThat(tiles).isEqualTo(expected.toTileSpecs()) + assertThat(loadTiles()).isEqualTo(expected) + } + + @Test + fun prependDefaultTwice_doesntAddMoreTiles() = + testScope.runTest { + val tiles by collectLastValue(underTest.tiles()) + underTest.setTiles(listOf(TileSpec.create("a"))) + + underTest.prependDefault() + val currentTiles = tiles!! + underTest.prependDefault() + + assertThat(tiles).isEqualTo(currentTiles) + } + private fun getDefaultTileSpecs(): List<TileSpec> { return defaultTilesRepository.defaultTiles } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt index 1c73fe2b305d..6ad4b317b94c 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt @@ -49,6 +49,7 @@ import com.android.systemui.qs.pipeline.shared.TileSpec import com.android.systemui.qs.pipeline.shared.logging.QSPipelineLogger import com.android.systemui.qs.tiles.di.NewQSTileFactory import com.android.systemui.qs.toProto +import com.android.systemui.retail.data.repository.FakeRetailModeRepository import com.android.systemui.settings.UserTracker import com.android.systemui.user.data.repository.FakeUserRepository import com.android.systemui.util.mockito.any @@ -85,6 +86,7 @@ class CurrentTilesInteractorImplTest : SysuiTestCase() { private val pipelineFlags = QSPipelineFlagsRepository() private val tileLifecycleManagerFactory = TLMFactory() private val minimumTilesRepository = MinimumTilesFixedRepository() + private val retailModeRepository = FakeRetailModeRepository() @Mock private lateinit var customTileStatePersister: CustomTileStatePersister @@ -118,6 +120,7 @@ class CurrentTilesInteractorImplTest : SysuiTestCase() { installedTilesComponentRepository = installedTilesPackageRepository, userRepository = userRepository, minimumTilesRepository = minimumTilesRepository, + retailModeRepository = retailModeRepository, customTileStatePersister = customTileStatePersister, tileFactory = tileFactory, newQSTileFactory = { newQSTileFactory }, diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/NoLowNumberOfTilesTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/NoLowNumberOfTilesTest.kt index 260189d401d2..e8ad038f8fbc 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/NoLowNumberOfTilesTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/NoLowNumberOfTilesTest.kt @@ -34,6 +34,8 @@ import com.android.systemui.qs.pipeline.data.repository.MinimumTilesFixedReposit import com.android.systemui.qs.pipeline.data.repository.fakeDefaultTilesRepository import com.android.systemui.qs.pipeline.data.repository.fakeMinimumTilesRepository import com.android.systemui.qs.pipeline.data.repository.fakeRestoreRepository +import com.android.systemui.qs.pipeline.data.repository.fakeRetailModeRepository +import com.android.systemui.qs.pipeline.data.repository.fakeTileSpecRepository import com.android.systemui.qs.pipeline.shared.TileSpec import com.android.systemui.qs.qsTileFactory import com.android.systemui.settings.fakeUserTracker @@ -138,6 +140,19 @@ class NoLowNumberOfTilesTest : SysuiTestCase() { } } + @Test + fun inRetailMode_onlyOneTile_noPrependDefault() = + with(kosmos) { + testScope.runTest { + fakeRetailModeRepository.setRetailMode(true) + fakeTileSpecRepository.setTiles(0, listOf(goodTile)) + val tiles by collectLastValue(currentTilesInteractor.currentTiles) + runCurrent() + + assertThat(tiles!!.map { it.spec }).isEqualTo(listOf(goodTile)) + } + } + private fun tileCreator(spec: String): QSTile? { return if (spec.contains("OEM")) { null // We don't know how to create OEM spec tiles diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationMediaManagerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationMediaManagerTest.kt index a67a8ab643fb..fed613153a4e 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationMediaManagerTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationMediaManagerTest.kt @@ -16,6 +16,10 @@ package com.android.systemui.statusbar +import android.media.MediaMetadata +import android.media.session.MediaController +import android.media.session.MediaSession +import android.os.fakeExecutorHandler import android.platform.test.annotations.DisableFlags import android.platform.test.annotations.EnableFlags import android.service.notification.NotificationListenerService @@ -54,6 +58,7 @@ class NotificationMediaManagerTest : SysuiTestCase() { private val notifPipeline = kosmos.notifPipeline private val notifCollection = kosmos.mockNotifCollection private val dumpManager = kosmos.dumpManager + private val handler = kosmos.fakeExecutorHandler private val mediaDataManager = mock<MediaDataManager>() private val backgroundExecutor = FakeExecutor(FakeSystemClock()) @@ -72,7 +77,11 @@ class NotificationMediaManagerTest : SysuiTestCase() { mediaDataManager, dumpManager, backgroundExecutor, + handler, ) + val mediaSession = MediaSession(context, "TEST") + notificationMediaManager.mMediaController = + MediaController(context, mediaSession.sessionToken) verify(mediaDataManager).addListener(listenerCaptor.capture()) } @@ -114,4 +123,32 @@ class NotificationMediaManagerTest : SysuiTestCase() { verify(notifCollection).dismissNotification(notifEntryCaptor.capture(), any()) assertThat(notifEntryCaptor.lastValue.key).isEqualTo(KEY) } + + @Test + @EnableFlags(Flags.FLAG_NOTIFICATION_MEDIA_MANAGER_BACKGROUND_EXECUTION) + fun clearMediaNotification_flagOn_resetMediaMetadata() { + // set up media metadata. + notificationMediaManager.mMediaListener.onMetadataChanged(MediaMetadata.Builder().build()) + backgroundExecutor.runAllReady() + + // clear media notification. + notificationMediaManager.clearCurrentMediaNotification() + backgroundExecutor.runAllReady() + + assertThat(notificationMediaManager.mediaMetadata).isNull() + assertThat(notificationMediaManager.mMediaController).isNull() + } + + @Test + @DisableFlags(Flags.FLAG_NOTIFICATION_MEDIA_MANAGER_BACKGROUND_EXECUTION) + fun clearMediaNotification_flagOff_resetMediaMetadata() { + // set up media metadata. + notificationMediaManager.mMediaListener.onMetadataChanged(MediaMetadata.Builder().build()) + + // clear media notification. + notificationMediaManager.clearCurrentMediaNotification() + + assertThat(notificationMediaManager.mediaMetadata).isNull() + assertThat(notificationMediaManager.mMediaController).isNull() + } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/domain/interactor/AudioOutputInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/domain/interactor/AudioOutputInteractorTest.kt index cec8ccf96b4a..b83b93b8f77e 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/domain/interactor/AudioOutputInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/domain/interactor/AudioOutputInteractorTest.kt @@ -18,33 +18,26 @@ package com.android.systemui.volume.domain.interactor import android.bluetooth.BluetoothDevice import android.graphics.drawable.TestStubDrawable -import android.media.AudioDeviceInfo -import android.media.AudioDevicePort import android.media.AudioManager import android.testing.TestableLooper import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.settingslib.R import com.android.settingslib.bluetooth.CachedBluetoothDevice -import com.android.settingslib.media.BluetoothMediaDevice -import com.android.settingslib.media.MediaDevice -import com.android.settingslib.media.PhoneMediaDevice import com.android.systemui.SysuiTestCase import com.android.systemui.bluetooth.bluetoothAdapter import com.android.systemui.bluetooth.cachedBluetoothDeviceManager import com.android.systemui.coroutines.collectLastValue import com.android.systemui.kosmos.testScope import com.android.systemui.testKosmos -import com.android.systemui.util.mockito.any -import com.android.systemui.util.mockito.eq -import com.android.systemui.util.mockito.mock -import com.android.systemui.util.mockito.whenever +import com.android.systemui.volume.data.repository.TestAudioDevicesFactory import com.android.systemui.volume.data.repository.audioRepository import com.android.systemui.volume.data.repository.audioSharingRepository import com.android.systemui.volume.domain.model.AudioOutputDevice import com.android.systemui.volume.localMediaController import com.android.systemui.volume.localMediaRepository import com.android.systemui.volume.mediaControllerRepository +import com.android.systemui.volume.panel.component.mediaoutput.domain.interactor.TestMediaDevicesFactory import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runCurrent @@ -52,6 +45,10 @@ import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test import org.junit.runner.RunWith +import org.mockito.kotlin.any +import org.mockito.kotlin.eq +import org.mockito.kotlin.mock +import org.mockito.kotlin.whenever @OptIn(ExperimentalCoroutinesApi::class) @RunWith(AndroidJUnit4::class) @@ -84,7 +81,7 @@ class AudioOutputInteractorTest : SysuiTestCase() { testScope.runTest { with(audioRepository) { setMode(AudioManager.MODE_IN_CALL) - setCommunicationDevice(builtInDevice) + setCommunicationDevice(TestAudioDevicesFactory.builtInDevice()) } val device by collectLastValue(underTest.currentAudioDevice) @@ -104,7 +101,7 @@ class AudioOutputInteractorTest : SysuiTestCase() { testScope.runTest { with(audioRepository) { setMode(AudioManager.MODE_IN_CALL) - setCommunicationDevice(wiredDevice) + setCommunicationDevice(TestAudioDevicesFactory.wiredDevice()) } val device by collectLastValue(underTest.currentAudioDevice) @@ -122,17 +119,18 @@ class AudioOutputInteractorTest : SysuiTestCase() { fun inCall_bluetooth_returnsCommunicationDevice() { with(kosmos) { testScope.runTest { + val btDevice = TestAudioDevicesFactory.bluetoothDevice() with(audioRepository) { setMode(AudioManager.MODE_IN_CALL) setCommunicationDevice(btDevice) } val bluetoothDevice: BluetoothDevice = mock { - whenever(address).thenReturn(btDevice.address) + on { address }.thenReturn(btDevice.address) } val cachedBluetoothDevice: CachedBluetoothDevice = mock { - whenever(address).thenReturn(btDevice.address) - whenever(name).thenReturn(btDevice.productName.toString()) - whenever(isHearingAidDevice).thenReturn(true) + on { address }.thenReturn(btDevice.address) + on { name }.thenReturn(btDevice.productName.toString()) + on { isHearingAidDevice }.thenReturn(true) } whenever(bluetoothAdapter.getRemoteDevice(eq(btDevice.address))) .thenReturn(bluetoothDevice) @@ -156,7 +154,9 @@ class AudioOutputInteractorTest : SysuiTestCase() { testScope.runTest { audioRepository.setMode(AudioManager.MODE_NORMAL) mediaControllerRepository.setActiveSessions(listOf(localMediaController)) - localMediaRepository.updateCurrentConnectedDevice(builtInMediaDevice) + localMediaRepository.updateCurrentConnectedDevice( + TestMediaDevicesFactory.builtInMediaDevice() + ) val device by collectLastValue(underTest.currentAudioDevice) @@ -175,7 +175,9 @@ class AudioOutputInteractorTest : SysuiTestCase() { testScope.runTest { audioRepository.setMode(AudioManager.MODE_NORMAL) mediaControllerRepository.setActiveSessions(listOf(localMediaController)) - localMediaRepository.updateCurrentConnectedDevice(wiredMediaDevice) + localMediaRepository.updateCurrentConnectedDevice( + TestMediaDevicesFactory.wiredMediaDevice() + ) val device by collectLastValue(underTest.currentAudioDevice) @@ -194,7 +196,9 @@ class AudioOutputInteractorTest : SysuiTestCase() { testScope.runTest { audioRepository.setMode(AudioManager.MODE_NORMAL) mediaControllerRepository.setActiveSessions(listOf(localMediaController)) - localMediaRepository.updateCurrentConnectedDevice(bluetoothMediaDevice) + localMediaRepository.updateCurrentConnectedDevice( + TestMediaDevicesFactory.bluetoothMediaDevice() + ) val device by collectLastValue(underTest.currentAudioDevice) @@ -208,48 +212,8 @@ class AudioOutputInteractorTest : SysuiTestCase() { } private companion object { + val testIcon = TestStubDrawable() - val builtInDevice = - AudioDeviceInfo( - AudioDevicePort.createForTesting( - AudioDeviceInfo.TYPE_BUILTIN_SPEAKER, - "built_in", - "" - ) - ) - val wiredDevice = - AudioDeviceInfo( - AudioDevicePort.createForTesting(AudioDeviceInfo.TYPE_WIRED_HEADPHONES, "wired", "") - ) - val btDevice = - AudioDeviceInfo( - AudioDevicePort.createForTesting( - AudioDeviceInfo.TYPE_BLE_HEADSET, - "bt", - "test_address" - ) - ) - val builtInMediaDevice: MediaDevice = - mock<PhoneMediaDevice> { - whenever(name).thenReturn("built_in_media") - whenever(icon).thenReturn(testIcon) - } - val wiredMediaDevice: MediaDevice = - mock<PhoneMediaDevice> { - whenever(deviceType) - .thenReturn(MediaDevice.MediaDeviceType.TYPE_3POINT5_MM_AUDIO_DEVICE) - whenever(name).thenReturn("wired_media") - whenever(icon).thenReturn(testIcon) - } - val bluetoothMediaDevice: MediaDevice = - mock<BluetoothMediaDevice> { - whenever(name).thenReturn("bt_media") - whenever(icon).thenReturn(testIcon) - val cachedBluetoothDevice: CachedBluetoothDevice = mock { - whenever(isHearingAidDevice).thenReturn(true) - } - whenever(cachedDevice).thenReturn(cachedBluetoothDevice) - } } @Test diff --git a/packages/SystemUI/res-keyguard/layout/footer_actions.xml b/packages/SystemUI/res-keyguard/layout/footer_actions.xml deleted file mode 100644 index 4a2a1cb9dc6d..000000000000 --- a/packages/SystemUI/res-keyguard/layout/footer_actions.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -** Copyright 2022, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. ---> - -<!-- Action buttons for footer in QS/QQS, containing settings button, power off button etc --> -<LinearLayout - xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="match_parent" - android:layout_height="@dimen/footer_actions_height" - android:elevation="@dimen/qs_panel_elevation" - android:paddingTop="@dimen/qs_footer_actions_top_padding" - android:paddingBottom="@dimen/qs_footer_actions_bottom_padding" - android:background="@drawable/qs_footer_actions_background" - android:gravity="center_vertical|end" - android:layout_gravity="bottom" -/>
\ No newline at end of file diff --git a/packages/SystemUI/res-keyguard/layout/footer_actions_icon_button.xml b/packages/SystemUI/res-keyguard/layout/footer_actions_icon_button.xml deleted file mode 100644 index fad41c822ec0..000000000000 --- a/packages/SystemUI/res-keyguard/layout/footer_actions_icon_button.xml +++ /dev/null @@ -1,28 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - Copyright (C) 2022 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> -<com.android.systemui.statusbar.AlphaOptimizedFrameLayout - xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="@dimen/qs_footer_action_button_size" - android:layout_height="@dimen/qs_footer_action_button_size" - android:visibility="gone"> - <ImageView - android:id="@+id/icon" - android:layout_width="@dimen/qs_footer_icon_size" - android:layout_height="@dimen/qs_footer_icon_size" - android:layout_gravity="center" - android:scaleType="centerInside" /> -</com.android.systemui.statusbar.AlphaOptimizedFrameLayout>
\ No newline at end of file diff --git a/packages/SystemUI/res-keyguard/layout/footer_actions_number_button.xml b/packages/SystemUI/res-keyguard/layout/footer_actions_number_button.xml deleted file mode 100644 index c09607d19bdd..000000000000 --- a/packages/SystemUI/res-keyguard/layout/footer_actions_number_button.xml +++ /dev/null @@ -1,39 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - Copyright (C) 2022 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> -<com.android.systemui.statusbar.AlphaOptimizedFrameLayout - xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="@dimen/qs_footer_action_button_size" - android:layout_height="@dimen/qs_footer_action_button_size" - android:background="@drawable/qs_footer_action_circle" - android:visibility="gone"> - <TextView - android:id="@+id/number" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:textAppearance="@style/TextAppearance.QS.SecurityFooter" - android:layout_gravity="center" - android:textColor="?attr/onShadeInactiveVariant" - android:textSize="18sp"/> - <ImageView - android:id="@+id/new_dot" - android:layout_width="12dp" - android:layout_height="12dp" - android:scaleType="fitCenter" - android:layout_gravity="bottom|end" - android:src="@drawable/fgs_dot" - android:contentDescription="@string/fgs_dot_content_description" /> -</com.android.systemui.statusbar.AlphaOptimizedFrameLayout>
\ No newline at end of file diff --git a/packages/SystemUI/res-keyguard/layout/footer_actions_text_button.xml b/packages/SystemUI/res-keyguard/layout/footer_actions_text_button.xml deleted file mode 100644 index 1c31f1da0681..000000000000 --- a/packages/SystemUI/res-keyguard/layout/footer_actions_text_button.xml +++ /dev/null @@ -1,66 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - Copyright (C) 2022 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> -<com.android.systemui.animation.view.LaunchableLinearLayout - xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="0dp" - android:layout_height="@dimen/qs_security_footer_single_line_height" - android:layout_weight="1" - android:orientation="horizontal" - android:paddingHorizontal="@dimen/qs_footer_padding" - android:gravity="center_vertical" - android:layout_marginEnd="@dimen/qs_footer_action_inset" - android:background="@drawable/qs_security_footer_background" - android:visibility="gone"> - <ImageView - android:id="@+id/icon" - android:layout_width="@dimen/qs_footer_icon_size" - android:layout_height="@dimen/qs_footer_icon_size" - android:gravity="start" - android:layout_marginEnd="12dp" - android:contentDescription="@null" - android:src="@drawable/ic_info_outline" - android:tint="?attr/onSurfaceVariant" /> - - <TextView - android:id="@+id/text" - android:layout_width="0dp" - android:layout_height="wrap_content" - android:layout_weight="1" - android:maxLines="1" - android:ellipsize="end" - android:textAppearance="@style/TextAppearance.QS.SecurityFooter" - android:textColor="?attr/onSurfaceVariant"/> - - <ImageView - android:id="@+id/new_dot" - android:layout_width="12dp" - android:layout_height="12dp" - android:scaleType="fitCenter" - android:src="@drawable/fgs_dot" - android:contentDescription="@string/fgs_dot_content_description" - /> - - <ImageView - android:id="@+id/chevron_icon" - android:layout_width="@dimen/qs_footer_icon_size" - android:layout_height="@dimen/qs_footer_icon_size" - android:layout_marginStart="8dp" - android:contentDescription="@null" - android:src="@*android:drawable/ic_chevron_end" - android:autoMirrored="true" - android:tint="?attr/onSurfaceVariant" /> -</com.android.systemui.animation.view.LaunchableLinearLayout>
\ No newline at end of file diff --git a/packages/SystemUI/res/color/qs_footer_power_button_overlay_color.xml b/packages/SystemUI/res/color/qs_footer_power_button_overlay_color.xml deleted file mode 100644 index a8abd793bd00..000000000000 --- a/packages/SystemUI/res/color/qs_footer_power_button_overlay_color.xml +++ /dev/null @@ -1,22 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - ~ Copyright (C) 2023 The Android Open Source Project - ~ - ~ Licensed under the Apache License, Version 2.0 (the "License"); - ~ you may not use this file except in compliance with the License. - ~ You may obtain a copy of the License at - ~ - ~ http://www.apache.org/licenses/LICENSE-2.0 - ~ - ~ Unless required by applicable law or agreed to in writing, software - ~ distributed under the License is distributed on an "AS IS" BASIS, - ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ~ See the License for the specific language governing permissions and - ~ limitations under the License. - --> - -<selector xmlns:android="http://schemas.android.com/apk/res/android"> - <item android:state_pressed="true" android:color="?attr/onShadeActive" android:alpha="0.12" /> - <item android:state_hovered="true" android:color="?attr/onShadeActive" android:alpha="0.09" /> - <item android:color="@color/transparent" /> -</selector>
\ No newline at end of file diff --git a/packages/SystemUI/res/drawable/fgs_dot.xml b/packages/SystemUI/res/drawable/fgs_dot.xml deleted file mode 100644 index 0881d7c5c2b5..000000000000 --- a/packages/SystemUI/res/drawable/fgs_dot.xml +++ /dev/null @@ -1,23 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -** Copyright 2022, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. ---> -<shape xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" - android:shape="oval" - android:width="12dp" - android:height="12dp"> - <solid android:color="?attr/tertiary" /> -</shape>
\ No newline at end of file diff --git a/packages/SystemUI/res/drawable/qs_footer_action_circle.xml b/packages/SystemUI/res/drawable/qs_footer_action_circle.xml deleted file mode 100644 index 4a5d4af96497..000000000000 --- a/packages/SystemUI/res/drawable/qs_footer_action_circle.xml +++ /dev/null @@ -1,37 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - ~ Copyright (C) 2022 The Android Open Source Project - ~ - ~ Licensed under the Apache License, Version 2.0 (the "License"); - ~ you may not use this file except in compliance with the License. - ~ You may obtain a copy of the License at - ~ - ~ http://www.apache.org/licenses/LICENSE-2.0 - ~ - ~ Unless required by applicable law or agreed to in writing, software - ~ distributed under the License is distributed on an "AS IS" BASIS, - ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ~ See the License for the specific language governing permissions and - ~ limitations under the License. - --> -<inset xmlns:android="http://schemas.android.com/apk/res/android" - android:inset="@dimen/qs_footer_action_inset"> - <ripple - android:color="?android:attr/colorControlHighlight"> - <item android:id="@android:id/mask"> - <!-- We make this shape a rounded rectangle instead of a oval so that it can animate --> - <!-- properly into an app/dialog. --> - <shape android:shape="rectangle"> - <solid android:color="@android:color/white"/> - <corners android:radius="@dimen/qs_footer_action_corner_radius"/> - </shape> - </item> - <item> - <shape android:shape="rectangle"> - <solid android:color="?attr/shadeInactive"/> - <corners android:radius="@dimen/qs_footer_action_corner_radius"/> - </shape> - </item> - - </ripple> -</inset>
\ No newline at end of file diff --git a/packages/SystemUI/res/drawable/qs_footer_action_circle_color.xml b/packages/SystemUI/res/drawable/qs_footer_action_circle_color.xml deleted file mode 100644 index 47a2965bcfac..000000000000 --- a/packages/SystemUI/res/drawable/qs_footer_action_circle_color.xml +++ /dev/null @@ -1,43 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - ~ Copyright (C) 2022 The Android Open Source Project - ~ - ~ Licensed under the Apache License, Version 2.0 (the "License"); - ~ you may not use this file except in compliance with the License. - ~ You may obtain a copy of the License at - ~ - ~ http://www.apache.org/licenses/LICENSE-2.0 - ~ - ~ Unless required by applicable law or agreed to in writing, software - ~ distributed under the License is distributed on an "AS IS" BASIS, - ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ~ See the License for the specific language governing permissions and - ~ limitations under the License. - --> -<inset xmlns:android="http://schemas.android.com/apk/res/android" - android:inset="@dimen/qs_footer_action_inset"> - <ripple - android:color="?android:attr/colorControlHighlight"> - <item android:id="@android:id/mask"> - <!-- We make this shape a rounded rectangle instead of a oval so that it can animate --> - <!-- properly into an app/dialog. --> - <shape android:shape="rectangle"> - <solid android:color="@android:color/white"/> - <corners android:radius="@dimen/qs_footer_action_corner_radius"/> - </shape> - </item> - <item> - <shape android:shape="rectangle"> - <solid android:color="?attr/shadeActive"/> - <corners android:radius="@dimen/qs_footer_action_corner_radius"/> - </shape> - </item> - <item> - <shape android:shape="rectangle"> - <solid android:color="@color/qs_footer_power_button_overlay_color"/> - <corners android:radius="@dimen/qs_footer_action_corner_radius"/> - </shape> - </item> - - </ripple> -</inset>
\ No newline at end of file diff --git a/packages/SystemUI/res/drawable/qs_hearing_devices_related_tools_background.xml b/packages/SystemUI/res/drawable/qs_hearing_devices_related_tools_background.xml new file mode 100644 index 000000000000..627b92b8a779 --- /dev/null +++ b/packages/SystemUI/res/drawable/qs_hearing_devices_related_tools_background.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2021 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<ripple + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" + android:color="?android:attr/colorControlHighlight"> + <item> + <shape android:shape="rectangle"> + <solid android:color="@android:color/transparent"/> + <corners android:radius="@dimen/hearing_devices_preset_spinner_background_radius"/> + <stroke + android:width="1dp" + android:color="?androidprv:attr/textColorTertiary" /> + </shape> + </item> +</ripple> diff --git a/packages/SystemUI/res/drawable/qs_security_footer_background.xml b/packages/SystemUI/res/drawable/qs_security_footer_background.xml deleted file mode 100644 index 0b0055b1f020..000000000000 --- a/packages/SystemUI/res/drawable/qs_security_footer_background.xml +++ /dev/null @@ -1,37 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - ~ Copyright (C) 2021 The Android Open Source Project - ~ - ~ Licensed under the Apache License, Version 2.0 (the "License"); - ~ you may not use this file except in compliance with the License. - ~ You may obtain a copy of the License at - ~ - ~ http://www.apache.org/licenses/LICENSE-2.0 - ~ - ~ Unless required by applicable law or agreed to in writing, software - ~ distributed under the License is distributed on an "AS IS" BASIS, - ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ~ See the License for the specific language governing permissions and - ~ limitations under the License. - --> -<inset xmlns:android="http://schemas.android.com/apk/res/android" - android:insetTop="@dimen/qs_footer_action_inset" - android:insetBottom="@dimen/qs_footer_action_inset" - > - <ripple - android:color="?android:attr/colorControlHighlight"> - <item android:id="@android:id/mask"> - <shape android:shape="rectangle"> - <solid android:color="@android:color/white"/> - <corners android:radius="@dimen/qs_security_footer_corner_radius"/> - </shape> - </item> - <item> - <shape android:shape="rectangle"> - <stroke android:width="1dp" - android:color="?attr/shadeInactive"/> - <corners android:radius="@dimen/qs_security_footer_corner_radius"/> - </shape> - </item> - </ripple> -</inset>
\ No newline at end of file diff --git a/packages/SystemUI/res/drawable/rounded_bg_full_large_radius.xml b/packages/SystemUI/res/drawable/rounded_bg_full_large_radius.xml deleted file mode 100644 index 29a014a713f7..000000000000 --- a/packages/SystemUI/res/drawable/rounded_bg_full_large_radius.xml +++ /dev/null @@ -1,21 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2021 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> -<shape xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" - android:shape="rectangle"> - <solid android:color="?androidprv:attr/colorAccentPrimary" /> - <corners android:radius="40dp" /> -</shape> diff --git a/packages/SystemUI/res/layout-sw600dp/biometric_prompt_constraint_layout.xml b/packages/SystemUI/res/layout-sw600dp/biometric_prompt_constraint_layout.xml deleted file mode 100644 index 8b9eabc5bd93..000000000000 --- a/packages/SystemUI/res/layout-sw600dp/biometric_prompt_constraint_layout.xml +++ /dev/null @@ -1,237 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:app="http://schemas.android.com/apk/res-auto" - xmlns:tools="http://schemas.android.com/tools" - xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" - android:id="@+id/biometric_prompt_constraint_layout" - android:layout_width="match_parent" - android:layout_height="match_parent"> - - <ImageView - android:id="@+id/background" - android:layout_width="0dp" - android:layout_height="0dp" - android:contentDescription="@string/biometric_dialog_empty_space_description" - app:layout_constraintBottom_toBottomOf="parent" - app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toTopOf="parent" /> - - <View - android:id="@+id/panel" - style="@style/AuthCredentialPanelStyle" - android:layout_width="0dp" - android:layout_height="0dp" - app:layout_constraintBottom_toBottomOf="parent" - app:layout_constraintEnd_toStartOf="@+id/rightGuideline" - app:layout_constraintStart_toStartOf="@+id/leftGuideline" - app:layout_constraintTop_toTopOf="@+id/topBarrier" - app:layout_constraintWidth_max="640dp" /> - - <include - android:id="@+id/button_bar" - layout="@layout/biometric_prompt_button_bar" - android:layout_width="0dp" - android:layout_height="wrap_content" - app:layout_constraintBottom_toBottomOf="@id/bottomGuideline" - app:layout_constraintEnd_toEndOf="@id/panel" - app:layout_constraintStart_toStartOf="@id/panel" /> - - <ScrollView - android:id="@+id/scrollView" - android:layout_width="0dp" - android:layout_height="wrap_content" - android:fadeScrollbars="false" - android:fillViewport="true" - android:paddingBottom="32dp" - android:paddingHorizontal="32dp" - android:paddingTop="24dp" - app:layout_constrainedHeight="true" - app:layout_constrainedWidth="true" - app:layout_constraintBottom_toTopOf="@+id/scrollBarrier" - app:layout_constraintEnd_toEndOf="@id/panel" - app:layout_constraintStart_toStartOf="@id/panel" - app:layout_constraintTop_toTopOf="@+id/topGuideline" - app:layout_constraintVertical_bias="1.0"> - - <androidx.constraintlayout.widget.ConstraintLayout - android:id="@+id/innerConstraint" - android:layout_width="match_parent" - android:layout_height="wrap_content"> - - <ImageView - android:id="@+id/logo" - android:layout_width="@dimen/biometric_prompt_logo_size" - android:layout_height="@dimen/biometric_prompt_logo_size" - android:layout_gravity="center" - android:contentDescription="@string/biometric_dialog_logo" - android:scaleType="fitXY" - android:visibility="visible" - app:layout_constraintBottom_toTopOf="@+id/logo_description" - app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toTopOf="parent" /> - - <TextView - android:id="@+id/logo_description" - style="@style/TextAppearance.AuthCredential.LogoDescription" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:paddingTop="16dp" - app:layout_constraintBottom_toTopOf="@+id/title" - app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@+id/logo" /> - - <TextView - android:id="@+id/title" - style="@style/TextAppearance.AuthCredential.Title" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:gravity="@integer/biometric_dialog_text_gravity" - android:paddingTop="16dp" - app:layout_constraintBottom_toTopOf="@+id/subtitle" - app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@+id/logo_description" /> - - <TextView - android:id="@+id/subtitle" - style="@style/TextAppearance.AuthCredential.Subtitle" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:gravity="@integer/biometric_dialog_text_gravity" - android:paddingTop="16dp" - app:layout_constraintBottom_toTopOf="@+id/contentBarrier" - app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@+id/title" /> - - <LinearLayout - android:id="@+id/customized_view_container" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:gravity="center_vertical" - android:orientation="vertical" - android:paddingTop="24dp" - android:visibility="gone" - app:layout_constraintBottom_toBottomOf="parent" - app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@+id/subtitle" /> - - <TextView - android:id="@+id/description" - style="@style/TextAppearance.AuthCredential.Description" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:gravity="@integer/biometric_dialog_text_gravity" - android:paddingTop="16dp" - android:textAlignment="viewStart" - app:layout_constraintBottom_toBottomOf="parent" - app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@+id/subtitle" /> - - <androidx.constraintlayout.widget.Barrier - android:id="@+id/contentBarrier" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - app:barrierAllowsGoneWidgets="false" - app:barrierDirection="top" - app:constraint_referenced_ids="description, customized_view_container" /> - - </androidx.constraintlayout.widget.ConstraintLayout> - </ScrollView> - - <!-- Cancel Button, replaces negative button when biometric is accepted --> - <TextView - android:id="@+id/indicator" - style="@style/TextAppearance.AuthCredential.Indicator" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginTop="24dp" - android:accessibilityLiveRegion="assertive" - android:fadingEdge="horizontal" - android:gravity="center_horizontal" - android:scrollHorizontally="true" - app:layout_constraintBottom_toTopOf="@+id/button_bar" - app:layout_constraintEnd_toEndOf="@+id/panel" - app:layout_constraintStart_toStartOf="@+id/panel" - app:layout_constraintTop_toBottomOf="@+id/biometric_icon" - app:layout_constraintVertical_bias="0.0" /> - - <!-- "Use Credential" Button, replaces if device credential is allowed --> - - <!-- Positive Button --> - <androidx.constraintlayout.widget.Barrier - android:id="@+id/topBarrier" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - app:barrierAllowsGoneWidgets="false" - app:barrierDirection="top" - app:constraint_referenced_ids="scrollView" /> - - <!-- Try Again Button --> - <androidx.constraintlayout.widget.Barrier - android:id="@+id/scrollBarrier" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - app:barrierAllowsGoneWidgets="false" - app:barrierDirection="top" - app:constraint_referenced_ids="biometric_icon, button_bar" /> - - <!-- Guidelines for setting panel border --> - <androidx.constraintlayout.widget.Guideline - android:id="@+id/leftGuideline" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:orientation="vertical" - app:layout_constraintGuide_begin="@dimen/biometric_dialog_border_padding" /> - - <androidx.constraintlayout.widget.Guideline - android:id="@+id/rightGuideline" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:orientation="vertical" - app:layout_constraintGuide_end="@dimen/biometric_dialog_border_padding" /> - - <androidx.constraintlayout.widget.Guideline - android:id="@+id/bottomGuideline" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:orientation="horizontal" - app:layout_constraintGuide_end="40dp" /> - - <androidx.constraintlayout.widget.Guideline - android:id="@+id/topGuideline" - android:layout_width="0dp" - android:layout_height="0dp" - android:orientation="horizontal" - app:layout_constraintGuide_begin="56dp" /> - - <com.android.systemui.biometrics.BiometricPromptLottieViewWrapper - android:id="@+id/biometric_icon" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - app:layout_constraintBottom_toBottomOf="parent" - app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toTopOf="parent" - app:layout_constraintVertical_bias="1.0" - tools:srcCompat="@tools:sample/avatars" /> - - <com.android.systemui.biometrics.BiometricPromptLottieViewWrapper - android:id="@+id/biometric_icon_overlay" - android:layout_width="0dp" - android:layout_height="0dp" - android:layout_gravity="center" - android:contentDescription="@null" - android:scaleType="fitXY" - android:importantForAccessibility="no" - app:layout_constraintBottom_toBottomOf="@+id/biometric_icon" - app:layout_constraintEnd_toEndOf="@+id/biometric_icon" - app:layout_constraintStart_toStartOf="@+id/biometric_icon" - app:layout_constraintTop_toTopOf="@+id/biometric_icon" /> - -</androidx.constraintlayout.widget.ConstraintLayout> diff --git a/packages/SystemUI/res/layout/biometric_prompt_constraint_layout.xml b/packages/SystemUI/res/layout/biometric_prompt_one_pane_layout.xml index 9b5b59fc116f..8d50bfa00fd5 100644 --- a/packages/SystemUI/res/layout/biometric_prompt_constraint_layout.xml +++ b/packages/SystemUI/res/layout/biometric_prompt_one_pane_layout.xml @@ -22,9 +22,11 @@ android:layout_width="0dp" android:layout_height="0dp" app:layout_constraintBottom_toBottomOf="parent" - app:layout_constraintEnd_toEndOf="@id/rightGuideline" + app:layout_constraintEnd_toStartOf="@id/rightGuideline" app:layout_constraintStart_toStartOf="@id/leftGuideline" - app:layout_constraintTop_toTopOf="@id/topBarrier" /> + app:layout_constraintTop_toTopOf="@id/topBarrier" + app:layout_constraintWidth_max="@dimen/biometric_prompt_panel_max_width" /> + <include android:id="@+id/button_bar" @@ -41,8 +43,8 @@ android:layout_height="wrap_content" android:fillViewport="true" android:fadeScrollbars="false" - android:paddingBottom="24dp" - android:paddingHorizontal="24dp" + android:paddingBottom="@dimen/biometric_prompt_top_scroll_view_bottom_padding" + android:paddingHorizontal="@dimen/biometric_prompt_top_scroll_view_horizontal_padding" android:paddingTop="24dp" app:layout_constrainedHeight="true" app:layout_constrainedWidth="true" @@ -76,7 +78,7 @@ style="@style/TextAppearance.AuthCredential.LogoDescription" android:layout_width="match_parent" android:layout_height="wrap_content" - android:paddingTop="8dp" + android:paddingTop="@dimen/biometric_prompt_logo_description_top_padding" app:layout_constraintBottom_toTopOf="@+id/title" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" @@ -180,14 +182,14 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="vertical" - app:layout_constraintGuide_begin="0dp" /> + app:layout_constraintGuide_begin="@dimen/biometric_prompt_one_pane_medium_horizontal_guideline_padding" /> <androidx.constraintlayout.widget.Guideline android:id="@+id/rightGuideline" android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="vertical" - app:layout_constraintGuide_end="0dp" /> + app:layout_constraintGuide_end="@dimen/biometric_prompt_one_pane_medium_horizontal_guideline_padding" /> <androidx.constraintlayout.widget.Guideline android:id="@+id/bottomGuideline" @@ -201,7 +203,7 @@ android:layout_width="0dp" android:layout_height="0dp" android:orientation="horizontal" - app:layout_constraintGuide_begin="119dp" /> + app:layout_constraintGuide_begin="@dimen/biometric_prompt_one_pane_medium_top_guideline_padding" /> <com.android.systemui.biometrics.BiometricPromptLottieViewWrapper android:id="@+id/biometric_icon" diff --git a/packages/SystemUI/res/layout-land/biometric_prompt_constraint_layout.xml b/packages/SystemUI/res/layout/biometric_prompt_two_pane_layout.xml index 01b9f7e2e38a..01b9f7e2e38a 100644 --- a/packages/SystemUI/res/layout-land/biometric_prompt_constraint_layout.xml +++ b/packages/SystemUI/res/layout/biometric_prompt_two_pane_layout.xml diff --git a/packages/SystemUI/res/layout/hearing_devices_tile_dialog.xml b/packages/SystemUI/res/layout/hearing_devices_tile_dialog.xml index 2bf6f80c32d0..4a7bef9f48b9 100644 --- a/packages/SystemUI/res/layout/hearing_devices_tile_dialog.xml +++ b/packages/SystemUI/res/layout/hearing_devices_tile_dialog.xml @@ -36,9 +36,8 @@ style="@style/BluetoothTileDialog.Device" android:layout_width="match_parent" android:layout_height="wrap_content" - android:minHeight="@dimen/hearing_devices_preset_spinner_height" android:layout_marginTop="@dimen/hearing_devices_layout_margin" - android:layout_marginBottom="@dimen/hearing_devices_layout_margin" + android:minHeight="@dimen/hearing_devices_preset_spinner_height" android:gravity="center_vertical" android:background="@drawable/hearing_devices_preset_spinner_background" android:popupBackground="@drawable/hearing_devices_preset_spinner_popup_background" @@ -54,9 +53,10 @@ android:visibility="gone"/> <androidx.constraintlayout.widget.Barrier - android:id="@+id/device_barrier" + android:id="@+id/device_function_barrier" android:layout_width="wrap_content" android:layout_height="wrap_content" + app:barrierAllowsGoneWidgets="false" app:barrierDirection="bottom" app:constraint_referenced_ids="device_list,preset_spinner" /> @@ -71,7 +71,8 @@ android:contentDescription="@string/accessibility_hearing_device_pair_new_device" app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintTop_toBottomOf="@id/device_barrier" + app:layout_constraintTop_toBottomOf="@id/device_function_barrier" + app:layout_constraintBottom_toTopOf="@id/related_tools_scroll" android:drawableStart="@drawable/ic_add" android:drawablePadding="20dp" android:drawableTint="?android:attr/textColorPrimary" @@ -83,4 +84,32 @@ android:maxLines="1" android:ellipsize="end" /> + <androidx.constraintlayout.widget.Barrier + android:id="@+id/device_barrier" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + app:barrierAllowsGoneWidgets="false" + app:barrierDirection="bottom" + app:constraint_referenced_ids="device_function_barrier, pair_new_device_button" /> + + <HorizontalScrollView + android:id="@+id/related_tools_scroll" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginStart="@dimen/bluetooth_dialog_layout_margin" + android:layout_marginEnd="@dimen/bluetooth_dialog_layout_margin" + android:layout_marginTop="@dimen/hearing_devices_layout_margin" + android:scrollbars="none" + android:fillViewport="true" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintTop_toBottomOf="@id/preset_spinner"> + <LinearLayout + android:id="@+id/related_tools_container" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="horizontal"> + </LinearLayout> + </HorizontalScrollView> + </androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/hearing_tool_item.xml b/packages/SystemUI/res/layout/hearing_tool_item.xml new file mode 100644 index 000000000000..84462d08d4a0 --- /dev/null +++ b/packages/SystemUI/res/layout/hearing_tool_item.xml @@ -0,0 +1,53 @@ +<!-- + Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/tool_view" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical" + android:gravity="center" + android:focusable="true" + android:clickable="true" + android:layout_weight="1"> + <FrameLayout + android:id="@+id/icon_frame" + android:layout_width="@dimen/hearing_devices_tool_icon_frame_width" + android:layout_height="@dimen/hearing_devices_tool_icon_frame_height" + android:background="@drawable/qs_hearing_devices_related_tools_background" + android:focusable="false" > + <ImageView + android:id="@+id/tool_icon" + android:layout_width="@dimen/hearing_devices_tool_icon_size" + android:layout_height="@dimen/hearing_devices_tool_icon_size" + android:layout_gravity="center" + android:scaleType="fitCenter" + android:focusable="false" /> + </FrameLayout> + <TextView + android:id="@+id/tool_name" + android:textDirection="locale" + android:textAlignment="center" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="@dimen/hearing_devices_layout_margin" + android:ellipsize="end" + android:maxLines="1" + android:textSize="12sp" + android:textAppearance="@style/TextAppearance.Dialog.Body.Message" + android:focusable="false" /> +</LinearLayout>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/ongoing_activity_chip.xml b/packages/SystemUI/res/layout/ongoing_activity_chip.xml index a33be12a655a..cd5c37d43633 100644 --- a/packages/SystemUI/res/layout/ongoing_activity_chip.xml +++ b/packages/SystemUI/res/layout/ongoing_activity_chip.xml @@ -40,6 +40,7 @@ <ImageView android:src="@*android:drawable/ic_phone" + android:id="@+id/ongoing_activity_chip_icon" android:layout_width="@dimen/ongoing_activity_chip_icon_size" android:layout_height="@dimen/ongoing_activity_chip_icon_size" android:tint="?android:attr/colorPrimary" diff --git a/packages/SystemUI/res/layout/people_space_activity.xml b/packages/SystemUI/res/layout/people_space_activity.xml deleted file mode 100644 index f45cc7c464d5..000000000000 --- a/packages/SystemUI/res/layout/people_space_activity.xml +++ /dev/null @@ -1,23 +0,0 @@ -<!-- - ~ Copyright (C) 2020 The Android Open Source Project - ~ - ~ Licensed under the Apache License, Version 2.0 (the "License"); - ~ you may not use this file except in compliance with the License. - ~ You may obtain a copy of the License at - ~ - ~ http://www.apache.org/licenses/LICENSE-2.0 - ~ - ~ Unless required by applicable law or agreed to in writing, software - ~ distributed under the License is distributed on an "AS IS" BASIS, - ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ~ See the License for the specific language governing permissions and - ~ limitations under the License. - --> -<FrameLayout - xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@+id/container" - android:layout_width="match_parent" - android:layout_height="match_parent"> - <!-- The content of people_space_activity_(no|with)_conversations.xml will be added here at - runtime depending on the number of conversations to show. --> -</FrameLayout> diff --git a/packages/SystemUI/res/layout/people_space_activity_list_divider.xml b/packages/SystemUI/res/layout/people_space_activity_list_divider.xml deleted file mode 100644 index 3b9fb3be3814..000000000000 --- a/packages/SystemUI/res/layout/people_space_activity_list_divider.xml +++ /dev/null @@ -1,21 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - Copyright (C) 2021 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> -<View - xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="match_parent" - android:layout_height="2dp" - android:background="?android:attr/colorBackground" /> diff --git a/packages/SystemUI/res/layout/people_space_activity_no_conversations.xml b/packages/SystemUI/res/layout/people_space_activity_no_conversations.xml deleted file mode 100644 index a97c90c5e8ac..000000000000 --- a/packages/SystemUI/res/layout/people_space_activity_no_conversations.xml +++ /dev/null @@ -1,79 +0,0 @@ -<!-- - ~ Copyright (C) 2021 The Android Open Source Project - ~ - ~ Licensed under the Apache License, Version 2.0 (the "License"); - ~ you may not use this file except in compliance with the License. - ~ You may obtain a copy of the License at - ~ - ~ http://www.apache.org/licenses/LICENSE-2.0 - ~ - ~ Unless required by applicable law or agreed to in writing, software - ~ distributed under the License is distributed on an "AS IS" BASIS, - ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ~ See the License for the specific language governing permissions and - ~ limitations under the License. - --> -<RelativeLayout - xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" - android:id="@+id/top_level_no_conversations" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:padding="24dp" - android:clipToOutline="true"> - <TextView - android:id="@+id/select_conversation_title" - android:gravity="center" - android:text="@string/select_conversation_title" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_centerHorizontal="true" - android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem" - android:textColor="?android:attr/textColorPrimary" - android:textSize="24sp" - android:layout_alignParentTop="true" /> - - <TextView - android:id="@+id/select_conversation" - android:gravity="center" - android:text="@string/no_conversations_text" - android:layout_width="match_parent" - android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem" - android:textColor="?android:attr/textColorPrimary" - android:textSize="16sp" - android:layout_height="wrap_content" - android:layout_gravity="center_horizontal" - android:padding="24dp" - android:layout_marginTop="26dp" - android:layout_below="@id/select_conversation_title"/> - - <Button - style="?android:attr/buttonBarButtonStyle" - android:id="@+id/got_it_button" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_gravity="center" - android:background="@drawable/rounded_bg_full_large_radius" - android:text="@string/got_it" - android:textColor="?androidprv:attr/textColorOnAccent" - android:layout_marginBottom="60dp" - android:layout_alignParentBottom="true" /> - - <LinearLayout - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_above="@id/got_it_button" - android:layout_below="@id/select_conversation" - android:layout_centerInParent="true" - android:clipToOutline="true"> - <LinearLayout - android:id="@+id/widget_initial_layout" - android:layout_width="200dp" - android:layout_height="100dp" - android:layout_gravity="center" - android:background="@drawable/rounded_bg_full_large_radius" - android:layout_above="@id/got_it_button"> - <include layout="@layout/people_space_placeholder_layout" /> - </LinearLayout> - </LinearLayout> -</RelativeLayout>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/people_space_activity_with_conversations.xml b/packages/SystemUI/res/layout/people_space_activity_with_conversations.xml deleted file mode 100644 index 2384963c44db..000000000000 --- a/packages/SystemUI/res/layout/people_space_activity_with_conversations.xml +++ /dev/null @@ -1,115 +0,0 @@ -<!-- - ~ Copyright (C) 2022 The Android Open Source Project - ~ - ~ Licensed under the Apache License, Version 2.0 (the "License"); - ~ you may not use this file except in compliance with the License. - ~ You may obtain a copy of the License at - ~ - ~ http://www.apache.org/licenses/LICENSE-2.0 - ~ - ~ Unless required by applicable law or agreed to in writing, software - ~ distributed under the License is distributed on an "AS IS" BASIS, - ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ~ See the License for the specific language governing permissions and - ~ limitations under the License. - --> -<LinearLayout - xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" - android:id="@+id/top_level_with_conversations" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:orientation="vertical" - android:padding="8dp"> - <TextView - android:id="@+id/select_conversation_title" - android:text="@string/select_conversation_title" - android:gravity="center" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_gravity="center_horizontal" - android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem" - android:textColor="?android:attr/textColorPrimary" - android:textSize="24sp"/> - - <TextView - android:id="@+id/select_conversation" - android:text="@string/select_conversation_text" - android:gravity="center" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_gravity="center_horizontal" - android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem" - android:textColor="?android:attr/textColorPrimary" - android:textSize="16sp" - android:paddingVertical="24dp" - android:paddingHorizontal="48dp"/> - - <androidx.core.widget.NestedScrollView - android:id="@+id/scroll_view" - android:layout_width="match_parent" - android:layout_height="match_parent"> - - <LinearLayout - android:id="@+id/scroll_layout" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginTop="16dp" - android:orientation="vertical"> - - <LinearLayout - android:id="@+id/priority" - android:orientation="vertical" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginBottom="35dp"> - <TextView - android:id="@+id/priority_header" - android:text="@string/priority_conversations" - android:layout_width="wrap_content" - android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Notification.Title" - android:textColor="?androidprv:attr/colorAccentPrimaryVariant" - android:textSize="14sp" - android:paddingStart="16dp" - android:layout_height="wrap_content"/> - - <LinearLayout - android:id="@+id/priority_tiles" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginTop="10dp" - android:orientation="vertical" - android:background="@drawable/rounded_bg_full_large_radius" - android:clipToOutline="true"> - </LinearLayout> - </LinearLayout> - - <LinearLayout - android:id="@+id/recent" - android:orientation="vertical" - android:layout_width="match_parent" - android:layout_height="wrap_content"> - <TextView - android:id="@+id/recent_header" - android:gravity="start" - android:text="@string/recent_conversations" - android:layout_width="wrap_content" - android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Notification.Title" - android:textColor="?androidprv:attr/colorAccentPrimaryVariant" - android:textSize="14sp" - android:paddingStart="16dp" - android:layout_height="wrap_content"/> - - <LinearLayout - android:id="@+id/recent_tiles" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginTop="10dp" - android:orientation="vertical" - android:background="@drawable/rounded_bg_full_large_radius" - android:clipToOutline="true"> - </LinearLayout> - </LinearLayout> - </LinearLayout> - </androidx.core.widget.NestedScrollView> -</LinearLayout>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/people_space_tile_view.xml b/packages/SystemUI/res/layout/people_space_tile_view.xml deleted file mode 100644 index b0599caae6df..000000000000 --- a/packages/SystemUI/res/layout/people_space_tile_view.xml +++ /dev/null @@ -1,60 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?><!-- - ~ Copyright (C) 2020 The Android Open Source Project - ~ - ~ Licensed under the Apache License, Version 2.0 (the "License"); - ~ you may not use this file except in compliance with the License. - ~ You may obtain a copy of the License at - ~ - ~ http://www.apache.org/licenses/LICENSE-2.0 - ~ - ~ Unless required by applicable law or agreed to in writing, software - ~ distributed under the License is distributed on an "AS IS" BASIS, - ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ~ See the License for the specific language governing permissions and - ~ limitations under the License. - --> -<LinearLayout - xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" - android:id="@+id/tile_view" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:orientation="vertical"> - - <LinearLayout - android:orientation="vertical" - android:background="?androidprv:attr/colorSurface" - android:padding="12dp" - android:elevation="4dp" - android:layout_width="match_parent" - android:layout_height="wrap_content"> - - <LinearLayout - android:orientation="horizontal" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:gravity="start"> - - <ImageView - android:id="@+id/tile_view_person_icon" - android:layout_width="@dimen/avatar_size_for_medium" - android:layout_height="@dimen/avatar_size_for_medium" /> - - <LinearLayout - android:orientation="horizontal" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_gravity="center_vertical"> - - <TextView - android:id="@+id/tile_view_name" - android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem" - android:paddingHorizontal="16dp" - android:textSize="22sp" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_gravity="center_vertical"/> - </LinearLayout> - </LinearLayout> - </LinearLayout> -</LinearLayout>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/qs_panel.xml b/packages/SystemUI/res/layout/qs_panel.xml index e3c5a7d03d2e..5f77f61d805b 100644 --- a/packages/SystemUI/res/layout/qs_panel.xml +++ b/packages/SystemUI/res/layout/qs_panel.xml @@ -47,13 +47,12 @@ <include layout="@layout/quick_status_bar_expanded_header" /> - <include - layout="@layout/footer_actions" + <androidx.compose.ui.platform.ComposeView android:id="@+id/qs_footer_actions" android:layout_height="@dimen/footer_actions_height" android:layout_width="match_parent" android:layout_gravity="bottom" - /> + android:elevation="@dimen/qs_panel_elevation" /> <include android:id="@+id/qs_customize" diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml index e19b3feb08ad..63aa97f542ae 100644 --- a/packages/SystemUI/res/values-af/strings.xml +++ b/packages/SystemUI/res/values-af/strings.xml @@ -76,8 +76,7 @@ <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"het \'n prent gestuur"</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"Stoor tans skermkiekie..."</string> <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"Stoor tans skermskoot in werkprofiel …"</string> - <!-- no translation found for screenshot_saving_private_profile (8934706048497093297) --> - <skip /> + <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"Stoor tans skermskoot in privaat"</string> <string name="screenshot_saved_title" msgid="8893267638659083153">"Skermkiekie is gestoor"</string> <string name="screenshot_failed_title" msgid="3259148215671936891">"Kon nie skermkiekie stoor nie"</string> <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"Eksterne skerm"</string> diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml index b257f0fd46bd..58940bb27d95 100644 --- a/packages/SystemUI/res/values-am/strings.xml +++ b/packages/SystemUI/res/values-am/strings.xml @@ -76,8 +76,7 @@ <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"ምስል ተልኳል"</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"ቅጽበታዊ ገፅ ዕይታ በማስቀመጥ ላይ..."</string> <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"ቅጽበታዊ ገፅ እይታን ወደ የስራ መገለጫ በማስቀመጥ ላይ…"</string> - <!-- no translation found for screenshot_saving_private_profile (8934706048497093297) --> - <skip /> + <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"ቅጽበታዊ ገጽ ዕይታን ወደ ግል በማስቀመጥ ላይ"</string> <string name="screenshot_saved_title" msgid="8893267638659083153">"ቅጽበታዊ ገፅ ዕይታ ተቀምጧል"</string> <string name="screenshot_failed_title" msgid="3259148215671936891">"ቅጽበታዊ ገፅ ዕይታን ማስቀመጥ አልተቻለም"</string> <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"ውጫዊ ማሳያ"</string> diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml index d145ee990024..b74b6cc6759e 100644 --- a/packages/SystemUI/res/values-ar/strings.xml +++ b/packages/SystemUI/res/values-ar/strings.xml @@ -76,8 +76,7 @@ <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"أرسَل صورة"</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"جارٍ حفظ لقطة الشاشة..."</string> <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"جارٍ حفظ لقطة الشاشة في ملف العمل…"</string> - <!-- no translation found for screenshot_saving_private_profile (8934706048497093297) --> - <skip /> + <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"يتم حاليًا حفظ لقطة الشاشة في الملف الشخصي الخاص"</string> <string name="screenshot_saved_title" msgid="8893267638659083153">"تم حفظ لقطة الشاشة."</string> <string name="screenshot_failed_title" msgid="3259148215671936891">"تعذّر حفظ لقطة الشاشة"</string> <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"الشاشة الخارجية"</string> diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml index 33c0b7e3b0e7..e25a19e7f128 100644 --- a/packages/SystemUI/res/values-as/strings.xml +++ b/packages/SystemUI/res/values-as/strings.xml @@ -76,8 +76,7 @@ <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"এখন প্ৰতিচ্ছবি পঠিয়াইছে"</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"স্ক্ৰীনশ্বট ছেভ কৰি থকা হৈছে…"</string> <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"কৰ্মস্থানৰ প্ৰ’ফাইলত স্ক্ৰীনশ্বট ছেভ কৰি থকা হৈছে…"</string> - <!-- no translation found for screenshot_saving_private_profile (8934706048497093297) --> - <skip /> + <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"ব্যক্তিগত প্ৰ’ফাইলত স্ক্ৰীনশ্বট ছেভ কৰি থকা হৈছে"</string> <string name="screenshot_saved_title" msgid="8893267638659083153">"স্ক্ৰীনশ্বট ছেভ কৰা হ’ল"</string> <string name="screenshot_failed_title" msgid="3259148215671936891">"স্ক্ৰীনশ্বট ছেভ কৰিব পৰা নগ\'ল"</string> <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"বাহ্যিক ডিছপ্লে’"</string> diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml index 5ff6b1ab9ec1..f7c252ea94e4 100644 --- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml +++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml @@ -76,8 +76,7 @@ <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"je poslao/la sliku"</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"Čuvanje snimka ekrana..."</string> <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"Snimak ekrana se čuva na poslovnom profilu…"</string> - <!-- no translation found for screenshot_saving_private_profile (8934706048497093297) --> - <skip /> + <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"Snimak ekrana se čuva na privatnom profilu"</string> <string name="screenshot_saved_title" msgid="8893267638659083153">"Snimak ekrana je sačuvan"</string> <string name="screenshot_failed_title" msgid="3259148215671936891">"Čuvanje snimka ekrana nije uspelo"</string> <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"Spoljni ekran"</string> diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml index aa34a8827d1a..60084389d9fb 100644 --- a/packages/SystemUI/res/values-be/strings.xml +++ b/packages/SystemUI/res/values-be/strings.xml @@ -76,8 +76,7 @@ <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"адпраўлены відарыс"</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"Захаванне скрыншота..."</string> <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"Захаванне здымка экрана ў працоўны профіль…"</string> - <!-- no translation found for screenshot_saving_private_profile (8934706048497093297) --> - <skip /> + <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"Здымак экрана захоўваецца ў прыватны профіль"</string> <string name="screenshot_saved_title" msgid="8893267638659083153">"Здымак экрана захаваны"</string> <string name="screenshot_failed_title" msgid="3259148215671936891">"Не атрымалася зрабіць здымак экрана"</string> <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"Знешні дысплэй"</string> diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml index 4b305ae92ce8..efaa31f502bd 100644 --- a/packages/SystemUI/res/values-bg/strings.xml +++ b/packages/SystemUI/res/values-bg/strings.xml @@ -76,8 +76,7 @@ <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"изпратено изображение"</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"Екранната снимка се запазва..."</string> <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"Екранната снимка се запазва в служебния профил…"</string> - <!-- no translation found for screenshot_saving_private_profile (8934706048497093297) --> - <skip /> + <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"Екранната снимка се запазва в личния потребителски профил"</string> <string name="screenshot_saved_title" msgid="8893267638659083153">"Екранната снимка е запазена"</string> <string name="screenshot_failed_title" msgid="3259148215671936891">"Не можа да се запази екранна снимка"</string> <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"Външен екран"</string> diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml index 87d5ffeccc3f..6decb6169419 100644 --- a/packages/SystemUI/res/values-bn/strings.xml +++ b/packages/SystemUI/res/values-bn/strings.xml @@ -76,8 +76,7 @@ <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"একটি ছবি পাঠানো হয়েছে"</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"স্ক্রিনশট সেভ করা হচ্ছে..."</string> <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"অফিস প্রোফাইলে স্ক্রিনশট সেভ করা হচ্ছে…"</string> - <!-- no translation found for screenshot_saving_private_profile (8934706048497093297) --> - <skip /> + <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"ব্যক্তিগত প্রোফাইলে স্ক্রিনশট সেভ করা হয়েছে"</string> <string name="screenshot_saved_title" msgid="8893267638659083153">"স্ক্রিনশট সেভ করা হয়েছে"</string> <string name="screenshot_failed_title" msgid="3259148215671936891">"স্ক্রিনশট সেভ করা যায়নি"</string> <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"এক্সটার্নাল ডিসপ্লে"</string> diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml index 2b80eef03fe2..1aea98254a2e 100644 --- a/packages/SystemUI/res/values-bs/strings.xml +++ b/packages/SystemUI/res/values-bs/strings.xml @@ -76,8 +76,7 @@ <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"je poslao/la sliku"</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"Spašavanje snimka ekrana..."</string> <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"Pohranjivanje snimka ekrana na radni profil…"</string> - <!-- no translation found for screenshot_saving_private_profile (8934706048497093297) --> - <skip /> + <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"Pohranjivanje snimka ekrana na privatni profil"</string> <string name="screenshot_saved_title" msgid="8893267638659083153">"Snimak ekrana je sačuvan"</string> <string name="screenshot_failed_title" msgid="3259148215671936891">"Nije moguće sačuvati snimak ekrana"</string> <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"Vanjski ekran"</string> diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml index 66875caf7a46..8229e4b4c7ac 100644 --- a/packages/SystemUI/res/values-ca/strings.xml +++ b/packages/SystemUI/res/values-ca/strings.xml @@ -76,8 +76,7 @@ <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"ha enviat una imatge"</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"S\'està desant la captura de pantalla..."</string> <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"S\'està desant la captura al perfil de treball…"</string> - <!-- no translation found for screenshot_saving_private_profile (8934706048497093297) --> - <skip /> + <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"S\'està desant la captura de pantalla al perfil privat"</string> <string name="screenshot_saved_title" msgid="8893267638659083153">"S\'ha desat la captura de pantalla"</string> <string name="screenshot_failed_title" msgid="3259148215671936891">"No s\'ha pogut desar la captura de pantalla"</string> <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"Pantalla externa"</string> diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml index 706076c3f530..85cef4b65426 100644 --- a/packages/SystemUI/res/values-cs/strings.xml +++ b/packages/SystemUI/res/values-cs/strings.xml @@ -76,8 +76,7 @@ <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"odesílá obrázek"</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"Ukládání snímku obrazovky..."</string> <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"Ukládání snímku obrazovky do pracovního profilu…"</string> - <!-- no translation found for screenshot_saving_private_profile (8934706048497093297) --> - <skip /> + <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"Ukládání snímku obrazovky do soukromého profilu"</string> <string name="screenshot_saved_title" msgid="8893267638659083153">"Snímek obrazovky byl uložen"</string> <string name="screenshot_failed_title" msgid="3259148215671936891">"Snímek obrazovky se nepodařilo uložit"</string> <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"Externí displej"</string> diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml index 8c364473041c..07a07a83e10f 100644 --- a/packages/SystemUI/res/values-de/strings.xml +++ b/packages/SystemUI/res/values-de/strings.xml @@ -76,8 +76,7 @@ <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"Bild gesendet"</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"Screenshot wird gespeichert..."</string> <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"Screenshot wird in Arbeitsprofil gespeichert…"</string> - <!-- no translation found for screenshot_saving_private_profile (8934706048497093297) --> - <skip /> + <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"Screenshot wird im vertraulichen Profil gespeichert"</string> <string name="screenshot_saved_title" msgid="8893267638659083153">"Screenshot gespeichert"</string> <string name="screenshot_failed_title" msgid="3259148215671936891">"Screenshot konnte nicht gespeichert werden"</string> <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"Äußeres Display"</string> diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml index 11c348190d14..a1dd5f920a39 100644 --- a/packages/SystemUI/res/values-el/strings.xml +++ b/packages/SystemUI/res/values-el/strings.xml @@ -76,8 +76,7 @@ <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"έστειλε μια εικόνα"</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"Αποθήκευση στιγμιότυπου οθόνης..."</string> <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"Αποθήκευση στιγμιότ. οθόνης στο προφίλ εργασίας…"</string> - <!-- no translation found for screenshot_saving_private_profile (8934706048497093297) --> - <skip /> + <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"Αποθήκευση στιγμιότυπου οθόνης σε ιδιωτικό"</string> <string name="screenshot_saved_title" msgid="8893267638659083153">"Το στιγμιότυπο οθόνης αποθηκεύτηκε"</string> <string name="screenshot_failed_title" msgid="3259148215671936891">"Μη δυνατή αποθήκευση του στιγμιότυπου οθόνης"</string> <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"Εξωτερική οθόνη"</string> diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml index 6df88b9a74fe..4ac0efe0d853 100644 --- a/packages/SystemUI/res/values-en-rCA/strings.xml +++ b/packages/SystemUI/res/values-en-rCA/strings.xml @@ -372,8 +372,7 @@ <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Pair new device"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Click to pair new device"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"Couldn\'t update preset"</string> - <!-- no translation found for hearing_devices_preset_label (7878267405046232358) --> - <skip /> + <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Preset"</string> <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Unblock device microphone?"</string> <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Unblock device camera?"</string> <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Unblock device camera and microphone?"</string> diff --git a/packages/SystemUI/res/values-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml index 8354cc73888e..67c332eef5f0 100644 --- a/packages/SystemUI/res/values-en-rXC/strings.xml +++ b/packages/SystemUI/res/values-en-rXC/strings.xml @@ -372,8 +372,7 @@ <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Pair new device"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Click to pair new device"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"Couldn\'t update preset"</string> - <!-- no translation found for hearing_devices_preset_label (7878267405046232358) --> - <skip /> + <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Preset"</string> <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Unblock device microphone?"</string> <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Unblock device camera?"</string> <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Unblock device camera and microphone?"</string> diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml index 32c242d48adb..c6d206b8a746 100644 --- a/packages/SystemUI/res/values-es-rUS/strings.xml +++ b/packages/SystemUI/res/values-es-rUS/strings.xml @@ -76,8 +76,7 @@ <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"envió una imagen"</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"Guardando la captura de pantalla..."</string> <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"Guardando cap. de pantalla en perfil de trabajo…"</string> - <!-- no translation found for screenshot_saving_private_profile (8934706048497093297) --> - <skip /> + <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"Guardando captura de pantalla en privado"</string> <string name="screenshot_saved_title" msgid="8893267638659083153">"Se guardó la captura de pantalla"</string> <string name="screenshot_failed_title" msgid="3259148215671936891">"No se pudo guardar la captura de pantalla"</string> <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"Pantalla externa"</string> diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml index 9a5f70ad3e1b..76effd15c665 100644 --- a/packages/SystemUI/res/values-es/strings.xml +++ b/packages/SystemUI/res/values-es/strings.xml @@ -76,8 +76,7 @@ <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"ha enviado una imagen"</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"Guardando captura..."</string> <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"Guardando captura en el perfil de trabajo…"</string> - <!-- no translation found for screenshot_saving_private_profile (8934706048497093297) --> - <skip /> + <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"Guardando captura de pantalla en espacio privado"</string> <string name="screenshot_saved_title" msgid="8893267638659083153">"Se ha guardado la captura de pantalla"</string> <string name="screenshot_failed_title" msgid="3259148215671936891">"No se ha podido guardar la captura de pantalla"</string> <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"Pantalla externa"</string> diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml index 2697eb312d74..f2bae1b1a488 100644 --- a/packages/SystemUI/res/values-fr-rCA/strings.xml +++ b/packages/SystemUI/res/values-fr-rCA/strings.xml @@ -76,8 +76,7 @@ <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"a envoyé une image"</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"Enregistrement capture écran…"</string> <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"Sauv. de la capture dans le profil prof. en cours…"</string> - <!-- no translation found for screenshot_saving_private_profile (8934706048497093297) --> - <skip /> + <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"Enregistrement de la capture d\'écran dans le profil privé en cours…"</string> <string name="screenshot_saved_title" msgid="8893267638659083153">"Capture d\'écran enregistrée"</string> <string name="screenshot_failed_title" msgid="3259148215671936891">"Impossible d\'enregistrer la capture d\'écran"</string> <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"Écran externe"</string> diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml index 945d353e01eb..e216c8453b07 100644 --- a/packages/SystemUI/res/values-fr/strings.xml +++ b/packages/SystemUI/res/values-fr/strings.xml @@ -76,8 +76,7 @@ <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"a envoyé une image"</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"Enregistrement de la capture d\'écran…"</string> <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"Enregistrement de capture d\'écran dans profil pro…"</string> - <!-- no translation found for screenshot_saving_private_profile (8934706048497093297) --> - <skip /> + <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"Enregistrement de la capture d\'écran dans le profil privé"</string> <string name="screenshot_saved_title" msgid="8893267638659083153">"Capture d\'écran enregistrée"</string> <string name="screenshot_failed_title" msgid="3259148215671936891">"Impossible d\'enregistrer la capture d\'écran"</string> <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"Écran externe"</string> diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml index 913d65fba67c..1ccab5d8dd60 100644 --- a/packages/SystemUI/res/values-gl/strings.xml +++ b/packages/SystemUI/res/values-gl/strings.xml @@ -76,8 +76,7 @@ <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"enviou unha imaxe"</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"Gardando captura de pantalla…"</string> <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"Gardando captura de pantalla no perfil de traballo"</string> - <!-- no translation found for screenshot_saving_private_profile (8934706048497093297) --> - <skip /> + <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"Gardando captura de pantalla no perfil privado"</string> <string name="screenshot_saved_title" msgid="8893267638659083153">"Gardouse a captura de pantalla"</string> <string name="screenshot_failed_title" msgid="3259148215671936891">"Non se puido gardar a captura de pantalla"</string> <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"Pantalla externa"</string> diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml index b6b27301d2bf..e096fff98221 100644 --- a/packages/SystemUI/res/values-gu/strings.xml +++ b/packages/SystemUI/res/values-gu/strings.xml @@ -76,8 +76,7 @@ <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"છબી મોકલી"</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"સ્ક્રીનશોટ સાચવી રહ્યું છે…"</string> <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"ઑફિસની પ્રોફાઇલમાં સ્ક્રીનશૉટ સાચવી રહ્યાં છીએ…"</string> - <!-- no translation found for screenshot_saving_private_profile (8934706048497093297) --> - <skip /> + <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"ખાનગી પ્રોફાઇલમાં સ્ક્રીનશૉટ સાચવી રહ્યાં છીએ"</string> <string name="screenshot_saved_title" msgid="8893267638659083153">"સ્ક્રીનશૉટ સાચવ્યો"</string> <string name="screenshot_failed_title" msgid="3259148215671936891">"સ્ક્રીનશૉટ સાચવી શક્યાં નથી"</string> <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"બાહ્ય ડિસ્પ્લે"</string> diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml index 639a3070d01d..d8c7dcd67f18 100644 --- a/packages/SystemUI/res/values-hi/strings.xml +++ b/packages/SystemUI/res/values-hi/strings.xml @@ -76,8 +76,7 @@ <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"एक इमेज भेजी गई"</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"स्क्रीनशॉट सहेजा जा रहा है..."</string> <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"स्क्रीनशॉट, वर्क प्रोफ़ाइल में सेव किया जा रहा है…"</string> - <!-- no translation found for screenshot_saving_private_profile (8934706048497093297) --> - <skip /> + <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"स्क्रीनशॉट को निजी प्रोफ़ाइल में सेव किया जा रहा है"</string> <string name="screenshot_saved_title" msgid="8893267638659083153">"स्क्रीनशॉट सेव किया गया"</string> <string name="screenshot_failed_title" msgid="3259148215671936891">"स्क्रीनशॉट सेव नहीं किया जा सका"</string> <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"बाहरी डिसप्ले"</string> diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml index 6a53ad159da2..19625bd0c887 100644 --- a/packages/SystemUI/res/values-hr/strings.xml +++ b/packages/SystemUI/res/values-hr/strings.xml @@ -76,8 +76,7 @@ <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"šalje sliku"</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"Spremanje snimke zaslona..."</string> <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"Spremanje snimke zaslona na poslovni profil…"</string> - <!-- no translation found for screenshot_saving_private_profile (8934706048497093297) --> - <skip /> + <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"Spremanje snimke zaslona na osobni profil"</string> <string name="screenshot_saved_title" msgid="8893267638659083153">"Snimka zaslona je spremljena"</string> <string name="screenshot_failed_title" msgid="3259148215671936891">"Snimka zaslona nije spremljena"</string> <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"Vanjski prikaz"</string> diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml index d0b87da25de3..70990336e93f 100644 --- a/packages/SystemUI/res/values-hu/strings.xml +++ b/packages/SystemUI/res/values-hu/strings.xml @@ -76,8 +76,7 @@ <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"képet küldött"</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"Képernyőkép mentése..."</string> <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"Képernyőkép mentése a munkaprofilba…"</string> - <!-- no translation found for screenshot_saving_private_profile (8934706048497093297) --> - <skip /> + <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"Képernyőkép mentése a privát területre"</string> <string name="screenshot_saved_title" msgid="8893267638659083153">"A képernyőkép mentése sikerült"</string> <string name="screenshot_failed_title" msgid="3259148215671936891">"Nem sikerült a képernyőkép mentése"</string> <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"Külső kijelző"</string> diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml index 66dd606198da..c539407f4efd 100644 --- a/packages/SystemUI/res/values-hy/strings.xml +++ b/packages/SystemUI/res/values-hy/strings.xml @@ -76,8 +76,7 @@ <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"պատկեր է ուղարկվել"</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"Սքրինշոթը պահվում է..."</string> <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"Սքրինշոթը պահվում է աշխատանքային պրոֆիլում…"</string> - <!-- no translation found for screenshot_saving_private_profile (8934706048497093297) --> - <skip /> + <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"Սքրինշոթը պահվում է մասնավոր պրոֆիլում"</string> <string name="screenshot_saved_title" msgid="8893267638659083153">"Սքրինշոթը պահվեց"</string> <string name="screenshot_failed_title" msgid="3259148215671936891">"Չհաջողվեց պահել սքրինշոթը"</string> <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"Արտաքին էկրան"</string> diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml index a72cae7bd948..5af06e8f6f11 100644 --- a/packages/SystemUI/res/values-in/strings.xml +++ b/packages/SystemUI/res/values-in/strings.xml @@ -76,8 +76,7 @@ <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"mengirim gambar"</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"Menyimpan screenshot..."</string> <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"Menyimpan screenshot ke profil kerja …"</string> - <!-- no translation found for screenshot_saving_private_profile (8934706048497093297) --> - <skip /> + <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"Menyimpan screenshot ke profil privasi"</string> <string name="screenshot_saved_title" msgid="8893267638659083153">"Screenshot disimpan"</string> <string name="screenshot_failed_title" msgid="3259148215671936891">"Tidak dapat menyimpan screenshot"</string> <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"Layar Eksternal"</string> diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml index d19e9a1cd630..71d0c6be20b6 100644 --- a/packages/SystemUI/res/values-iw/strings.xml +++ b/packages/SystemUI/res/values-iw/strings.xml @@ -76,8 +76,7 @@ <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"נשלחה תמונה"</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"המערכת שומרת את צילום המסך..."</string> <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"צילום המסך נשמר בפרופיל העבודה…"</string> - <!-- no translation found for screenshot_saving_private_profile (8934706048497093297) --> - <skip /> + <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"צילום המסך נשמר בפרופיל האישי"</string> <string name="screenshot_saved_title" msgid="8893267638659083153">"צילום המסך נשמר"</string> <string name="screenshot_failed_title" msgid="3259148215671936891">"לא ניתן היה לשמור את צילום המסך"</string> <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"תצוגה במסך חיצוני"</string> diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml index f29dee099327..d6dc93c8887f 100644 --- a/packages/SystemUI/res/values-ja/strings.xml +++ b/packages/SystemUI/res/values-ja/strings.xml @@ -76,8 +76,7 @@ <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"画像を送信しました"</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"スクリーンショットを保存しています..."</string> <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"スクリーンショットを仕事用プロファイルに保存中…"</string> - <!-- no translation found for screenshot_saving_private_profile (8934706048497093297) --> - <skip /> + <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"スクリーンショットをプライベートに保存しています"</string> <string name="screenshot_saved_title" msgid="8893267638659083153">"スクリーンショットを保存しました"</string> <string name="screenshot_failed_title" msgid="3259148215671936891">"スクリーンショット保存エラー"</string> <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"外側ディスプレイ"</string> diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml index e37c16b04689..2670e4542344 100644 --- a/packages/SystemUI/res/values-ka/strings.xml +++ b/packages/SystemUI/res/values-ka/strings.xml @@ -76,8 +76,7 @@ <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"გაიგზავნა სურათი"</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"ეკრანის სურათის შენახვა…"</string> <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"მიმდინარეობს ეკრანის ანაბეჭდის შენახვა სამუშაო პროფილში…"</string> - <!-- no translation found for screenshot_saving_private_profile (8934706048497093297) --> - <skip /> + <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"მიმდინარეობს ეკრანის ანაბეჭდის შენახვა კერძო სივრცეში"</string> <string name="screenshot_saved_title" msgid="8893267638659083153">"ეკრანის ანაბეჭდი შენახულია"</string> <string name="screenshot_failed_title" msgid="3259148215671936891">"ეკრანის ანაბეჭდის შენახვა ვერ მოხერხდა"</string> <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"გარე ეკრანი"</string> diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml index 2618198392ee..c709bbec2b1f 100644 --- a/packages/SystemUI/res/values-kk/strings.xml +++ b/packages/SystemUI/res/values-kk/strings.xml @@ -76,8 +76,7 @@ <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"сурет жіберілді"</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"Скриншотты сақтауда…"</string> <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"Скриншот жұмыс профиліне сақталып жатыр…"</string> - <!-- no translation found for screenshot_saving_private_profile (8934706048497093297) --> - <skip /> + <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"Скриншот жеке профильде сақталып жатыр."</string> <string name="screenshot_saved_title" msgid="8893267638659083153">"Скриншот сақталды"</string> <string name="screenshot_failed_title" msgid="3259148215671936891">"Скриншот сақталмады"</string> <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"Сыртқы дисплей"</string> diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml index 88259f34260a..042e7481370c 100644 --- a/packages/SystemUI/res/values-km/strings.xml +++ b/packages/SystemUI/res/values-km/strings.xml @@ -76,8 +76,7 @@ <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"បានផ្ញើរូបភាព"</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"កំពុងរក្សាទុករូបថតអេក្រង់..."</string> <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"កំពុងរក្សាទុករូបថតអេក្រង់ទៅកម្រងព័ត៌មានការងារ…"</string> - <!-- no translation found for screenshot_saving_private_profile (8934706048497093297) --> - <skip /> + <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"កំពុងរក្សាទុករូបថតអេក្រង់ទៅក្នុងកម្រងព័ត៌មានឯកជន"</string> <string name="screenshot_saved_title" msgid="8893267638659083153">"បានរក្សាទុករូបថតអេក្រង់"</string> <string name="screenshot_failed_title" msgid="3259148215671936891">"មិនអាចរក្សាទុករូបថតអេក្រង់បានទេ"</string> <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"ផ្ទាំងអេក្រង់ខាងក្រៅ"</string> diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml index cd14d0ef6925..8b1ab5f3b7e3 100644 --- a/packages/SystemUI/res/values-kn/strings.xml +++ b/packages/SystemUI/res/values-kn/strings.xml @@ -76,8 +76,7 @@ <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"ಚಿತ್ರವನ್ನು ಕಳುಹಿಸಲಾಗಿದೆ"</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"ಸ್ಕ್ರೀನ್ಶಾಟ್ ಉಳಿಸಲಾಗುತ್ತಿದೆ…"</string> <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"ಉದ್ಯೋಗ ಪ್ರೊಫೈಲ್ಗೆ ಸ್ಕ್ರೀನ್ಶಾಟ್ ಉಳಿಸಲಾಗುತ್ತಿದೆ…"</string> - <!-- no translation found for screenshot_saving_private_profile (8934706048497093297) --> - <skip /> + <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"ಸ್ಕ್ರೀನ್ಶಾಟ್ ಅನ್ನು ಖಾಸಗಿ ಪ್ರೊಫೈಲ್ಗೆ ಸೇವ್ ಮಾಡಲಾಗುತ್ತಿದೆ"</string> <string name="screenshot_saved_title" msgid="8893267638659083153">"ಸ್ಕ್ರೀನ್ಶಾಟ್ ಅನ್ನು ಸೇವ್ ಮಾಡಲಾಗಿದೆ"</string> <string name="screenshot_failed_title" msgid="3259148215671936891">"ಸ್ಕ್ರೀನ್ಶಾಟ್ ಅನ್ನು ಉಳಿಸಲು ಸಾಧ್ಯವಾಗಲಿಲ್ಲ"</string> <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"ಬಾಹ್ಯ ಡಿಸ್ಪ್ಲೇ"</string> diff --git a/packages/SystemUI/res/values-land/dimens.xml b/packages/SystemUI/res/values-land/dimens.xml index 56ebc0668097..aea79e84f7e8 100644 --- a/packages/SystemUI/res/values-land/dimens.xml +++ b/packages/SystemUI/res/values-land/dimens.xml @@ -28,9 +28,7 @@ <!-- In landscape the security footer is actually part of the header, and needs to be as short as the header --> - <dimen name="qs_security_footer_single_line_height">@*android:dimen/quick_qs_offset_height</dimen> <dimen name="qs_footer_padding">14dp</dimen> - <dimen name="qs_security_footer_background_inset">12dp</dimen> <dimen name="volume_tool_tip_top_margin">12dp</dimen> <dimen name="volume_row_slider_height">128dp</dimen> diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml index 0622dc5e5ed2..bec3cc1e61f2 100644 --- a/packages/SystemUI/res/values-lt/strings.xml +++ b/packages/SystemUI/res/values-lt/strings.xml @@ -76,8 +76,7 @@ <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"išsiuntė vaizdą"</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"Išsaugoma ekrano kopija..."</string> <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"Išsaugoma ekrano kopija darbo profilyje…"</string> - <!-- no translation found for screenshot_saving_private_profile (8934706048497093297) --> - <skip /> + <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"Ekrano kopija išsaugoma privačiame profilyje"</string> <string name="screenshot_saved_title" msgid="8893267638659083153">"Ekrano kopija išsaugota"</string> <string name="screenshot_failed_title" msgid="3259148215671936891">"Ekrano kopijos išsaugoti nepavyko"</string> <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"Išorinė pateiktis"</string> diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml index 7b7ba80d3ba5..001f6a027ce4 100644 --- a/packages/SystemUI/res/values-lv/strings.xml +++ b/packages/SystemUI/res/values-lv/strings.xml @@ -76,8 +76,7 @@ <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"nosūtīts attēls"</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"Notiek ekrānuzņēmuma saglabāšana..."</string> <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"Notiek ekrānuzņēmuma saglabāšana darba profilā…"</string> - <!-- no translation found for screenshot_saving_private_profile (8934706048497093297) --> - <skip /> + <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"Ekrānuzņēmums tiek saglabāts privātajā profilā"</string> <string name="screenshot_saved_title" msgid="8893267638659083153">"Ekrānuzņēmums saglabāts"</string> <string name="screenshot_failed_title" msgid="3259148215671936891">"Ekrānuzņēmumu neizdevās saglabāt."</string> <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"Ārējais displejs"</string> diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml index b9571ce2beb5..12bf5689525b 100644 --- a/packages/SystemUI/res/values-mk/strings.xml +++ b/packages/SystemUI/res/values-mk/strings.xml @@ -76,8 +76,7 @@ <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"испрати слика"</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"Сликата на екранот се зачувува..."</string> <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"Се зачувува слика од екранот на вашиот работен профил…"</string> - <!-- no translation found for screenshot_saving_private_profile (8934706048497093297) --> - <skip /> + <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"Сликата од екранот се зачувува во приватниот профил"</string> <string name="screenshot_saved_title" msgid="8893267638659083153">"Сликата од екранот е зачувана"</string> <string name="screenshot_failed_title" msgid="3259148215671936891">"Не може да се зачува слика од екранот"</string> <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"Надворешен екран"</string> diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml index 18fc5b498cee..d165242e49e1 100644 --- a/packages/SystemUI/res/values-ml/strings.xml +++ b/packages/SystemUI/res/values-ml/strings.xml @@ -76,8 +76,7 @@ <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"ചിത്രം അയച്ചു"</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"സ്ക്രീൻഷോട്ട് സംരക്ഷിക്കുന്നു..."</string> <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"ഔദ്യോഗിക പ്രൊഫൈലിലേക്ക് സ്ക്രീൻഷോട്ട് സംരക്ഷിക്കുന്നു…"</string> - <!-- no translation found for screenshot_saving_private_profile (8934706048497093297) --> - <skip /> + <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"സ്ക്രീൻഷോട്ട് സ്വകാര്യമാക്കി സംരക്ഷിക്കുന്നു"</string> <string name="screenshot_saved_title" msgid="8893267638659083153">"സ്ക്രീൻഷോട്ട് സംരക്ഷിച്ചു"</string> <string name="screenshot_failed_title" msgid="3259148215671936891">"സ്ക്രീൻഷോട്ട് സംരക്ഷിക്കാനായില്ല"</string> <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"ബാഹ്യ ഡിസ്പ്ലേ"</string> diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml index 57e58fa0d32f..017a6c8067a5 100644 --- a/packages/SystemUI/res/values-mr/strings.xml +++ b/packages/SystemUI/res/values-mr/strings.xml @@ -76,8 +76,7 @@ <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"इमेज पाठवली आहे"</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"स्क्रीनशॉट सेव्ह करत आहे…"</string> <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"कार्य प्रोफाइलवर स्क्रीनशॉट सेव्ह करत आहे…"</string> - <!-- no translation found for screenshot_saving_private_profile (8934706048497093297) --> - <skip /> + <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"स्क्रीनशॉट खाजगीमध्ये सेव्ह करत आहे"</string> <string name="screenshot_saved_title" msgid="8893267638659083153">"स्क्रीनशॉट सेव्ह केला"</string> <string name="screenshot_failed_title" msgid="3259148215671936891">"स्क्रीनशॉट सेव्ह करू शकलो नाही"</string> <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"बाह्य डिस्प्ले"</string> diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml index 651d4989caef..23e4559d97a0 100644 --- a/packages/SystemUI/res/values-ms/strings.xml +++ b/packages/SystemUI/res/values-ms/strings.xml @@ -76,8 +76,7 @@ <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"menghantar imej"</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"Menyimpan tangkapan skrin..."</string> <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"Menyimpan tangkapan skrin ke profil kerja…"</string> - <!-- no translation found for screenshot_saving_private_profile (8934706048497093297) --> - <skip /> + <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"Menyimpan tangkapan skrin pada profil peribadi"</string> <string name="screenshot_saved_title" msgid="8893267638659083153">"Tangkapan skrin disimpan"</string> <string name="screenshot_failed_title" msgid="3259148215671936891">"Tidak dapat menyimpan tangkapan skrin"</string> <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"Paparan Luaran"</string> diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml index edfbf09ed42b..dbcfaa366656 100644 --- a/packages/SystemUI/res/values-nb/strings.xml +++ b/packages/SystemUI/res/values-nb/strings.xml @@ -76,8 +76,7 @@ <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"har sendt et bilde"</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"Lagrer skjermdumpen …"</string> <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"Lagrer skjermdumpen i jobbprofilen …"</string> - <!-- no translation found for screenshot_saving_private_profile (8934706048497093297) --> - <skip /> + <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"Lagrer skjermdump i den private profilen"</string> <string name="screenshot_saved_title" msgid="8893267638659083153">"Skjermdumpen er lagret"</string> <string name="screenshot_failed_title" msgid="3259148215671936891">"Kunne ikke lagre skjermdump"</string> <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"Ekstern skjerm"</string> diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml index f6650eecb1ef..29a5e7302530 100644 --- a/packages/SystemUI/res/values-or/strings.xml +++ b/packages/SystemUI/res/values-or/strings.xml @@ -76,8 +76,7 @@ <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"ଏକ ଛବି ପଠାଯାଇଛି"</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"ସ୍କ୍ରୀନଶଟ୍ ସେଭ୍ କରାଯାଉଛି…"</string> <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"ୱାର୍କ ପ୍ରୋଫାଇଲରେ ସ୍କ୍ରିନସଟ ସେଭ କରାଯାଉଛି…"</string> - <!-- no translation found for screenshot_saving_private_profile (8934706048497093297) --> - <skip /> + <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"ପ୍ରାଇଭେଟରେ ସ୍କ୍ରିନସଟକୁ ସେଭ କରାଯାଉଛି"</string> <string name="screenshot_saved_title" msgid="8893267638659083153">"ସ୍କ୍ରୀନଶଟ୍ ସେଭ୍ ହୋଇଛି"</string> <string name="screenshot_failed_title" msgid="3259148215671936891">"ସ୍କ୍ରୀନ୍ଶଟ୍ ସେଭ୍ କରିହେବ ନାହିଁ"</string> <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"ଏକ୍ସଟର୍ନଲ ଡିସପ୍ଲେ"</string> diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml index 3886e781ab14..624557597677 100644 --- a/packages/SystemUI/res/values-pa/strings.xml +++ b/packages/SystemUI/res/values-pa/strings.xml @@ -76,8 +76,7 @@ <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"ਚਿੱਤਰ ਭੇਜਿਆ ਗਿਆ"</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"ਸਕ੍ਰੀਨਸ਼ਾਟ ਸੁਰੱਖਿਅਤ ਕਰ ਰਿਹਾ ਹੈ…"</string> <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"ਸਕ੍ਰੀਨਸ਼ਾਟ ਕਾਰਜ ਪ੍ਰੋਫਾਈਲ \'ਤੇ ਰੱਖਿਅਤ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ…"</string> - <!-- no translation found for screenshot_saving_private_profile (8934706048497093297) --> - <skip /> + <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"ਸਕ੍ਰੀਨਸ਼ਾਟ ਨੂੰ ਪ੍ਰਾਈਵੇਟ ਵਜੋਂ ਰੱਖਿਅਤ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ"</string> <string name="screenshot_saved_title" msgid="8893267638659083153">"ਸਕ੍ਰੀਨਸ਼ਾਟ ਰੱਖਿਅਤ ਕੀਤਾ ਗਿਆ"</string> <string name="screenshot_failed_title" msgid="3259148215671936891">"ਸਕ੍ਰੀਨਸ਼ਾਟ ਰੱਖਿਅਤ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਿਆ"</string> <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"ਬਾਹਰੀ ਡਿਸਪਲੇ"</string> diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml index c8422cb3cfa9..e8e6f09d0d41 100644 --- a/packages/SystemUI/res/values-pl/strings.xml +++ b/packages/SystemUI/res/values-pl/strings.xml @@ -76,8 +76,7 @@ <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"wysłano obraz"</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"Zapisywanie zrzutu ekranu..."</string> <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"Zapisuję zrzut ekranu w profilu służbowym…"</string> - <!-- no translation found for screenshot_saving_private_profile (8934706048497093297) --> - <skip /> + <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"Zapisuję zrzut ekranu w profilu prywatnym"</string> <string name="screenshot_saved_title" msgid="8893267638659083153">"Zrzut ekranu został zapisany"</string> <string name="screenshot_failed_title" msgid="3259148215671936891">"Nie udało się zapisać zrzutu ekranu"</string> <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"Wyświetlacz zewnętrzny"</string> diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml index b399cfa950bb..b6614c671126 100644 --- a/packages/SystemUI/res/values-ro/strings.xml +++ b/packages/SystemUI/res/values-ro/strings.xml @@ -76,8 +76,7 @@ <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"a trimis o imagine"</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"Se salvează captura de ecran..."</string> <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"Se salvează captura în profilul de serviciu…"</string> - <!-- no translation found for screenshot_saving_private_profile (8934706048497093297) --> - <skip /> + <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"Se salvează captura de ecran în profilul privat"</string> <string name="screenshot_saved_title" msgid="8893267638659083153">"Captură de ecran salvată"</string> <string name="screenshot_failed_title" msgid="3259148215671936891">"Nu s-a putut salva captura de ecran"</string> <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"Afișaj extern"</string> diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml index a79818fed6a3..d6fa6abcbdc6 100644 --- a/packages/SystemUI/res/values-ru/strings.xml +++ b/packages/SystemUI/res/values-ru/strings.xml @@ -76,8 +76,7 @@ <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"отправлено изображение"</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"Сохранение..."</string> <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"Сохранение скриншота в рабочем профиле…"</string> - <!-- no translation found for screenshot_saving_private_profile (8934706048497093297) --> - <skip /> + <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"Сохранение скриншота в частный профиль…"</string> <string name="screenshot_saved_title" msgid="8893267638659083153">"Скриншот сохранен"</string> <string name="screenshot_failed_title" msgid="3259148215671936891">"Не удалось сохранить скриншот"</string> <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"Внешний дисплей"</string> diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml index 28d3220912b1..3de42252dfee 100644 --- a/packages/SystemUI/res/values-si/strings.xml +++ b/packages/SystemUI/res/values-si/strings.xml @@ -76,8 +76,7 @@ <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"රූපයක් එවන ලදී"</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"තිර රුව සුරැකෙමින් පවතී…"</string> <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"කාර්යාල පැතිකඩ වෙත තිර රුව සුරකිමින්…"</string> - <!-- no translation found for screenshot_saving_private_profile (8934706048497093297) --> - <skip /> + <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"තිර රුව පුද්ගලික ලෙස සුරැකේ"</string> <string name="screenshot_saved_title" msgid="8893267638659083153">"තිර රුව සුරකින ලදී"</string> <string name="screenshot_failed_title" msgid="3259148215671936891">"තිර රුව සුරැකිය නොහැකි විය"</string> <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"බාහිර සංදර්ශකය"</string> diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml index 051690b22399..cb8cf5beb172 100644 --- a/packages/SystemUI/res/values-sk/strings.xml +++ b/packages/SystemUI/res/values-sk/strings.xml @@ -76,8 +76,7 @@ <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"odoslal(a) obrázok"</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"Prebieha ukladanie snímky obrazovky..."</string> <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"Ukladá sa snímka obrazovky do pracovného profilu…"</string> - <!-- no translation found for screenshot_saving_private_profile (8934706048497093297) --> - <skip /> + <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"Snímka obrazovky sa ukladá do súkromného profilu"</string> <string name="screenshot_saved_title" msgid="8893267638659083153">"Snímka obrazovky bola uložená"</string> <string name="screenshot_failed_title" msgid="3259148215671936891">"Snímku obrazovky sa nepodarilo uložiť"</string> <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"Externá obrazovka"</string> diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml index 13bf885474d8..2b851b4b522b 100644 --- a/packages/SystemUI/res/values-sr/strings.xml +++ b/packages/SystemUI/res/values-sr/strings.xml @@ -76,8 +76,7 @@ <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"је послао/ла слику"</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"Чување снимка екрана..."</string> <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"Снимак екрана се чува на пословном профилу…"</string> - <!-- no translation found for screenshot_saving_private_profile (8934706048497093297) --> - <skip /> + <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"Снимак екрана се чува на приватном профилу"</string> <string name="screenshot_saved_title" msgid="8893267638659083153">"Снимак екрана је сачуван"</string> <string name="screenshot_failed_title" msgid="3259148215671936891">"Чување снимка екрана није успело"</string> <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"Спољни екран"</string> diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml index 2fec9aa54989..0d1614fa66bd 100644 --- a/packages/SystemUI/res/values-sv/strings.xml +++ b/packages/SystemUI/res/values-sv/strings.xml @@ -76,8 +76,7 @@ <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"har skickat en bild"</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"Skärmbilden sparas ..."</string> <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"Sparar skärmbild i jobbprofilen …"</string> - <!-- no translation found for screenshot_saving_private_profile (8934706048497093297) --> - <skip /> + <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"Sparar skärmbilden till privat profil"</string> <string name="screenshot_saved_title" msgid="8893267638659083153">"Skärmbilden har sparats"</string> <string name="screenshot_failed_title" msgid="3259148215671936891">"Det gick inte att spara skärmbilden"</string> <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"Extern skärm"</string> diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml index 1be2de14f788..670a85c181f5 100644 --- a/packages/SystemUI/res/values-sw/strings.xml +++ b/packages/SystemUI/res/values-sw/strings.xml @@ -76,8 +76,7 @@ <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"imetuma picha"</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"Inahifadhi picha ya skrini..."</string> <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"Inahifadhi picha ya skrini kwenye wasifu wa kazini…"</string> - <!-- no translation found for screenshot_saving_private_profile (8934706048497093297) --> - <skip /> + <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"Inahifadhi picha ya skrini kwenye wasifu wa faragha"</string> <string name="screenshot_saved_title" msgid="8893267638659083153">"Imehifadhi picha ya skrini"</string> <string name="screenshot_failed_title" msgid="3259148215671936891">"Imeshindwa kuhifadhi picha ya skrini"</string> <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"Skrini ya Nje"</string> diff --git a/packages/SystemUI/res/values-sw600dp/dimens.xml b/packages/SystemUI/res/values-sw600dp/dimens.xml index 2cfba01fe1c8..29e0dbea24f2 100644 --- a/packages/SystemUI/res/values-sw600dp/dimens.xml +++ b/packages/SystemUI/res/values-sw600dp/dimens.xml @@ -68,11 +68,6 @@ <dimen name="qs_brightness_margin_bottom">16dp</dimen> - <!-- For large screens the security footer appears below the footer, - same as phones in portrait --> - <dimen name="qs_security_footer_single_line_height">48dp</dimen> - <dimen name="qs_security_footer_background_inset">0dp</dimen> - <dimen name="qs_panel_padding_top">8dp</dimen> <!-- The width of large/content heavy dialogs (e.g. Internet, Media output, etc) --> @@ -102,6 +97,17 @@ <dimen name="lockscreen_shade_status_bar_transition_distance">@dimen/lockscreen_shade_full_transition_distance</dimen> <dimen name="lockscreen_shade_keyguard_transition_distance">@dimen/lockscreen_shade_media_transition_distance</dimen> + <!-- Dimensions for biometric prompt panel padding --> + <dimen name="biometric_prompt_one_pane_medium_top_guideline_padding">56dp</dimen> + <dimen name="biometric_prompt_one_pane_medium_horizontal_guideline_padding">@dimen/biometric_dialog_border_padding</dimen> + + <!-- Dimensions for biometric prompt scroll view padding --> + <dimen name="biometric_prompt_top_scroll_view_bottom_padding">32dp</dimen> + <dimen name="biometric_prompt_top_scroll_view_horizontal_padding">32dp</dimen> + + <!-- Dimensions for biometric prompt custom content view. --> + <dimen name="biometric_prompt_logo_description_top_padding">16dp</dimen> + <!-- Biometric Auth pattern view size, better to align keyguard_security_width --> <dimen name="biometric_auth_pattern_view_size">348dp</dimen> diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml index f716e612e637..224ed3989819 100644 --- a/packages/SystemUI/res/values-ta/strings.xml +++ b/packages/SystemUI/res/values-ta/strings.xml @@ -76,8 +76,7 @@ <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"படம் அனுப்பப்பட்டது"</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"ஸ்க்ரீன் ஷாட்டைச் சேமிக்கிறது…"</string> <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"பணிக் கணக்கில் ஸ்கிரீன்ஷாட் சேமிக்கப்படுகிறது…"</string> - <!-- no translation found for screenshot_saving_private_profile (8934706048497093297) --> - <skip /> + <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"தனிப்பட்ட சுயவிவரத்தில் ஸ்கிரீன்ஷாட் சேமிக்கப்படுகிறது"</string> <string name="screenshot_saved_title" msgid="8893267638659083153">"ஸ்கிரீன்ஷாட் சேமிக்கப்பட்டது"</string> <string name="screenshot_failed_title" msgid="3259148215671936891">"ஸ்கிரீன் ஷாட்டைச் சேமிக்க முடியவில்லை"</string> <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"வெளித் திரை"</string> diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml index 241e95248040..26bb871c7deb 100644 --- a/packages/SystemUI/res/values-te/strings.xml +++ b/packages/SystemUI/res/values-te/strings.xml @@ -76,8 +76,7 @@ <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"ఇమేజ్ను పంపారు"</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"స్క్రీన్షాట్ను సేవ్ చేస్తోంది…"</string> <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"స్క్రీన్షాట్ను వర్క్ ప్రొఫైల్కు సేవ్ చేస్తోంది…"</string> - <!-- no translation found for screenshot_saving_private_profile (8934706048497093297) --> - <skip /> + <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"స్క్రీన్షాట్ను ప్రైవేట్ ప్రొఫైల్కు సేవ్ చేస్తోంది"</string> <string name="screenshot_saved_title" msgid="8893267638659083153">"స్క్రీన్షాట్ సేవ్ చేయబడింది"</string> <string name="screenshot_failed_title" msgid="3259148215671936891">"స్క్రీన్షాట్ని సేవ్ చేయడం సాధ్యం కాలేదు"</string> <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"వెలుపలి డిస్ప్లే"</string> diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml index b063a8f0a076..02ee0175f0e7 100644 --- a/packages/SystemUI/res/values-th/strings.xml +++ b/packages/SystemUI/res/values-th/strings.xml @@ -76,8 +76,7 @@ <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"ส่งรูปภาพ"</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"กำลังบันทึกภาพหน้าจอ..."</string> <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"กำลังบันทึกภาพหน้าจอไปยังโปรไฟล์งาน…"</string> - <!-- no translation found for screenshot_saving_private_profile (8934706048497093297) --> - <skip /> + <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"กำลังบันทึกภาพหน้าจอลงในโปรไฟล์ส่วนตัว"</string> <string name="screenshot_saved_title" msgid="8893267638659083153">"บันทึกภาพหน้าจอแล้ว"</string> <string name="screenshot_failed_title" msgid="3259148215671936891">"บันทึกภาพหน้าจอไม่ได้"</string> <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"จอแสดงผลภายนอก"</string> diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml index 9bc0cc02dbe2..db84644d7a5e 100644 --- a/packages/SystemUI/res/values-tr/strings.xml +++ b/packages/SystemUI/res/values-tr/strings.xml @@ -76,8 +76,7 @@ <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"bir resim gönderildi"</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"Ekran görüntüsü kaydediliyor..."</string> <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"Ekran görüntüsü iş profiline kaydediliyor…"</string> - <!-- no translation found for screenshot_saving_private_profile (8934706048497093297) --> - <skip /> + <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"Ekran görüntüsü özel profile kaydediliyor"</string> <string name="screenshot_saved_title" msgid="8893267638659083153">"Ekran görüntüsü kaydedildi"</string> <string name="screenshot_failed_title" msgid="3259148215671936891">"Ekran görüntüsü kaydedilemedi"</string> <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"Harici Ekran"</string> diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml index 2f648128e964..1afcf2ac9065 100644 --- a/packages/SystemUI/res/values-uk/strings.xml +++ b/packages/SystemUI/res/values-uk/strings.xml @@ -76,8 +76,7 @@ <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"надіслане зображення"</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"Збереження знімка екрана..."</string> <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"Зберігання знімка екрана в робочому профілі…"</string> - <!-- no translation found for screenshot_saving_private_profile (8934706048497093297) --> - <skip /> + <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"Збереження знімка екрана в приватному профілі"</string> <string name="screenshot_saved_title" msgid="8893267638659083153">"Знімок екрана збережено"</string> <string name="screenshot_failed_title" msgid="3259148215671936891">"Не вдалося зберегти знімок екрана"</string> <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"Зовнішній дисплей"</string> diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml index e8d2cf441e27..d7c4c88cd802 100644 --- a/packages/SystemUI/res/values-ur/strings.xml +++ b/packages/SystemUI/res/values-ur/strings.xml @@ -76,8 +76,7 @@ <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"ایک تصویر بھیجی"</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"اسکرین شاٹ محفوظ ہو رہا ہے…"</string> <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"اسکرین شاٹ دفتری پروفائل میں محفوظ کیا جا رہا ہے…"</string> - <!-- no translation found for screenshot_saving_private_profile (8934706048497093297) --> - <skip /> + <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"اسکرین شاٹ کو نجی پروفائل میں محفوظ کیا جا رہا ہے"</string> <string name="screenshot_saved_title" msgid="8893267638659083153">"اسکرین شاٹ محفوظ ہو گیا"</string> <string name="screenshot_failed_title" msgid="3259148215671936891">"اسکرین شاٹ کو محفوظ نہیں کیا جا سکا"</string> <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"خارجی ڈسپلے"</string> diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml index 810fa4aca1cf..1a3055c3f226 100644 --- a/packages/SystemUI/res/values-zh-rCN/strings.xml +++ b/packages/SystemUI/res/values-zh-rCN/strings.xml @@ -76,8 +76,7 @@ <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"发送了一张图片"</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"正在保存屏幕截图..."</string> <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"正在将屏幕截图保存到工作资料…"</string> - <!-- no translation found for screenshot_saving_private_profile (8934706048497093297) --> - <skip /> + <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"正在将屏幕截图保存到个人资料"</string> <string name="screenshot_saved_title" msgid="8893267638659083153">"已保存屏幕截图"</string> <string name="screenshot_failed_title" msgid="3259148215671936891">"无法保存屏幕截图"</string> <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"外部显示屏"</string> diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml index f253d4e528a8..6644b53591a8 100644 --- a/packages/SystemUI/res/values-zu/strings.xml +++ b/packages/SystemUI/res/values-zu/strings.xml @@ -76,8 +76,7 @@ <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"uthumele isithombe"</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"Ilondoloz umfanekiso weskrini..."</string> <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"Ilondoloza isithombe-skrini kuphrofayela yomsebenzi…"</string> - <!-- no translation found for screenshot_saving_private_profile (8934706048497093297) --> - <skip /> + <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"Ilondoloza isithombe-skrini sibe esigodliwe"</string> <string name="screenshot_saved_title" msgid="8893267638659083153">"Isithombe-skrini silondoloziwe"</string> <string name="screenshot_failed_title" msgid="3259148215671936891">"Ayikwazanga ukulondoloza isithombe-skrini"</string> <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"Isiboniso Sangaphandle"</string> diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index 638785402055..fb883640c9a9 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -125,6 +125,20 @@ <!-- Use collapsed layout for media player in landscape QQS --> <bool name="config_quickSettingsMediaLandscapeCollapsed">true</bool> + <!-- For hearing devices related tool list. Need to be in ComponentName format (package/class). + Should be activity to be launched. + Already contains tool that holds intent: "com.android.settings.action.live_caption". + Maximum number is 3. --> + <string-array name="config_quickSettingsHearingDevicesRelatedToolName" translatable="false"> + </string-array> + + <!-- The drawable resource names. If provided, it will replace the corresponding icons in + config_quickSettingsHearingDevicesRelatedToolName. Can be empty to use original icons. + Already contains tool that holds intent: "com.android.settings.action.live_caption". + Maximum number is 3. --> + <string-array name="config_quickSettingsHearingDevicesRelatedToolIcon" translatable="false"> + </string-array> + <!-- Show indicator for Wifi on but not connected. --> <bool name="config_showWifiIndicatorWhenEnabled">false</bool> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 7d7a5d4dbf14..8ce20684d892 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -713,7 +713,6 @@ <dimen name="qs_header_mobile_icon_size">@dimen/status_bar_icon_drawing_size</dimen> <dimen name="qs_header_carrier_separator_width">6dp</dimen> <dimen name="qs_carrier_margin_width">4dp</dimen> - <dimen name="qs_footer_icon_size">20dp</dimen> <dimen name="qs_header_height">120dp</dimen> <dimen name="qs_header_row_min_height">48dp</dimen> @@ -721,11 +720,7 @@ <dimen name="new_qs_header_non_clickable_element_height">24sp</dimen> <dimen name="qs_footer_padding">20dp</dimen> - <dimen name="qs_security_footer_height">88dp</dimen> - <dimen name="qs_security_footer_single_line_height">48dp</dimen> <dimen name="qs_footers_margin_bottom">8dp</dimen> - <dimen name="qs_security_footer_background_inset">0dp</dimen> - <dimen name="qs_security_footer_corner_radius">28dp</dimen> <dimen name="segmented_button_spacing">0dp</dimen> <dimen name="borderless_button_radius">2dp</dimen> @@ -1119,11 +1114,18 @@ <dimen name="biometric_dialog_height">240dp</dimen> <!-- Dimensions for biometric prompt panel padding --> - <dimen name="biometric_prompt_small_horizontal_guideline_padding">344dp</dimen> - <dimen name="biometric_prompt_udfps_horizontal_guideline_padding">114dp</dimen> - <dimen name="biometric_prompt_udfps_mid_guideline_padding">409dp</dimen> - <dimen name="biometric_prompt_medium_horizontal_guideline_padding">640dp</dimen> - <dimen name="biometric_prompt_medium_mid_guideline_padding">330dp</dimen> + <dimen name="biometric_prompt_panel_max_width">640dp</dimen> + <dimen name="biometric_prompt_land_small_horizontal_guideline_padding">344dp</dimen> + <dimen name="biometric_prompt_two_pane_udfps_horizontal_guideline_padding">114dp</dimen> + <dimen name="biometric_prompt_two_pane_udfps_mid_guideline_padding">409dp</dimen> + <dimen name="biometric_prompt_two_pane_medium_horizontal_guideline_padding">640dp</dimen> + <dimen name="biometric_prompt_two_pane_medium_mid_guideline_padding">330dp</dimen> + <dimen name="biometric_prompt_one_pane_medium_top_guideline_padding">119dp</dimen> + <dimen name="biometric_prompt_one_pane_medium_horizontal_guideline_padding">0dp</dimen> + + <!-- Dimensions for biometric prompt scroll view padding --> + <dimen name="biometric_prompt_top_scroll_view_bottom_padding">24dp</dimen> + <dimen name="biometric_prompt_top_scroll_view_horizontal_padding">24dp</dimen> <!-- Dimensions for biometric prompt icon padding --> <dimen name="biometric_prompt_portrait_small_bottom_padding">60dp</dimen> @@ -1136,6 +1138,7 @@ <!-- Dimensions for biometric prompt custom content view. --> <dimen name="biometric_prompt_logo_size">32dp</dimen> + <dimen name="biometric_prompt_logo_description_top_padding">8dp</dimen> <dimen name="biometric_prompt_content_corner_radius">28dp</dimen> <dimen name="biometric_prompt_content_padding_horizontal">24dp</dimen> <dimen name="biometric_prompt_content_padding_vertical">16dp</dimen> @@ -1778,6 +1781,9 @@ <dimen name="hearing_devices_preset_spinner_text_padding_vertical">15dp</dimen> <dimen name="hearing_devices_preset_spinner_arrow_size">24dp</dimen> <dimen name="hearing_devices_preset_spinner_background_radius">28dp</dimen> + <dimen name="hearing_devices_tool_icon_frame_width">94dp</dimen> + <dimen name="hearing_devices_tool_icon_frame_height">64dp</dimen> + <dimen name="hearing_devices_tool_icon_size">28dp</dimen> <!-- Height percentage of the parent container occupied by the communal view --> <item name="communal_source_height_percentage" format="float" type="dimen">0.80</item> diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml index b993a5ad75b9..177ba598add7 100644 --- a/packages/SystemUI/res/values/ids.xml +++ b/packages/SystemUI/res/values/ids.xml @@ -259,9 +259,6 @@ <!-- ID of the Scene Container root Composable view --> <item type='id' name="scene_container_root_composable" /> - <!-- Tag set on the Compose implementation of the QS footer actions. --> - <item type="id" name="tag_compose_qs_footer_actions" /> - <!-- Ids for the device entry icon. device_entry_icon_view: parent view of both device_entry_icon and device_entry_icon_bg diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 6f2806d80ef3..c038a8207d43 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -917,6 +917,8 @@ <string name="hearing_devices_presets_error">Couldn\'t update preset</string> <!-- QuickSettings: Title for hearing aids presets. Preset is a set of hearing aid settings. User can apply different settings in different environments (e.g. Outdoor, Restaurant, Home) [CHAR LIMIT=40]--> <string name="hearing_devices_preset_label">Preset</string> + <!-- QuickSettings: Tool name for hearing devices dialog related tools [CHAR LIMIT=40]--> + <string name="live_caption_title">Live Caption</string> <!--- Title of dialog triggered if the microphone is disabled but an app tried to access it. [CHAR LIMIT=150] --> <string name="sensor_privacy_start_use_mic_dialog_title">Unblock device microphone?</string> diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml index b8f71c10dc89..64717fcc8c5d 100644 --- a/packages/SystemUI/res/values/styles.xml +++ b/packages/SystemUI/res/values/styles.xml @@ -149,11 +149,6 @@ <item name="android:letterSpacing">0.01</item> </style> - <style name="TextAppearance.QS.SecurityFooter" parent="@style/TextAppearance.QS.Status"> - <item name="android:fontFamily">@*android:string/config_bodyFontFamily</item> - <item name="android:textColor">?attr/onSurface</item> - </style> - <style name="TextAppearance.QS.Status.Carriers" /> <style name="TextAppearance.QS.Status.Carriers.NoCarrierText"> diff --git a/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/shared/model/PromptKind.kt b/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/shared/model/PromptKind.kt index b99c51489521..44f2059d4e41 100644 --- a/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/shared/model/PromptKind.kt +++ b/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/shared/model/PromptKind.kt @@ -22,15 +22,28 @@ sealed interface PromptKind { data class Biometric( /** The available modalities for the authentication on the prompt. */ val activeModalities: BiometricModalities = BiometricModalities(), - // TODO(b/330908557): Use this value to decide whether to show two pane layout, instead of - // simply depending on rotations. - val showTwoPane: Boolean = false, - ) : PromptKind + val paneType: PaneType = PaneType.ONE_PANE_PORTRAIT, + ) : PromptKind { + enum class PaneType { + TWO_PANE_LANDSCAPE, + ONE_PANE_PORTRAIT, + ONE_PANE_NO_SENSOR_LANDSCAPE, + ONE_PANE_LARGE_SCREEN_LANDSCAPE + } + } - object Pin : PromptKind - object Pattern : PromptKind - object Password : PromptKind + data object Pin : PromptKind + data object Pattern : PromptKind + data object Password : PromptKind fun isBiometric() = this is Biometric - fun isCredential() = (this is Pin) or (this is Pattern) or (this is Password) + fun isTwoPaneLandscapeBiometric(): Boolean = + (this as? Biometric)?.paneType == Biometric.PaneType.TWO_PANE_LANDSCAPE + fun isOnePanePortraitBiometric() = + (this as? Biometric)?.paneType == Biometric.PaneType.ONE_PANE_PORTRAIT + fun isOnePaneNoSensorLandscapeBiometric() = + (this as? Biometric)?.paneType == Biometric.PaneType.ONE_PANE_NO_SENSOR_LANDSCAPE + fun isOnePaneLargeScreenLandscapeBiometric() = + (this as? Biometric)?.paneType == Biometric.PaneType.ONE_PANE_LARGE_SCREEN_LANDSCAPE + fun isCredential() = (this is Pin) || (this is Pattern) || (this is Password) } diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java index 27b2b92ab899..63ad41a808dc 100644 --- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java +++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java @@ -20,7 +20,6 @@ import static androidx.dynamicanimation.animation.DynamicAnimation.TRANSLATION_X import static androidx.dynamicanimation.animation.FloatPropertyCompat.createFloatPropertyCompat; import static com.android.systemui.classifier.Classifier.NOTIFICATION_DISMISS; -import static com.android.systemui.flags.Flags.SWIPE_UNCLEARED_TRANSIENT_VIEW_FIX; import static com.android.systemui.statusbar.notification.NotificationUtils.logKey; import android.animation.Animator; @@ -481,16 +480,11 @@ public class SwipeHelper implements Gefingerpoken, Dumpable { updateSwipeProgressFromOffset(animView, canBeDismissed); mDismissPendingMap.remove(animView); boolean wasRemoved = false; - if (animView instanceof ExpandableNotificationRow) { - ExpandableNotificationRow row = (ExpandableNotificationRow) animView; - if (mFeatureFlags.isEnabled(SWIPE_UNCLEARED_TRANSIENT_VIEW_FIX)) { - // If the view is already removed from its parent and added as Transient, - // we need to clean the transient view upon animation end - wasRemoved = row.getTransientContainer() != null - || row.getParent() == null || row.isRemoved(); - } else { - wasRemoved = row.isRemoved(); - } + if (animView instanceof ExpandableNotificationRow row) { + // If the view is already removed from its parent and added as Transient, + // we need to clean the transient view upon animation end + wasRemoved = row.getTransientContainer() != null + || row.getParent() == null || row.isRemoved(); } if (!mCancelled || wasRemoved) { mCallback.onChildDismissed(animView); diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java index 28dd2338ff2b..961d6aa1b821 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java @@ -25,10 +25,14 @@ import android.bluetooth.BluetoothHapClient; import android.bluetooth.BluetoothHapPresetInfo; import android.content.Context; import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.content.res.Resources; import android.media.AudioManager; import android.os.Bundle; import android.os.Handler; import android.provider.Settings; +import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.View.Visibility; @@ -36,7 +40,10 @@ import android.view.accessibility.AccessibilityNodeInfo; import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.Button; +import android.widget.ImageView; +import android.widget.LinearLayout; import android.widget.Spinner; +import android.widget.TextView; import android.widget.Toast; import androidx.annotation.NonNull; @@ -69,6 +76,7 @@ import dagger.assisted.Assisted; import dagger.assisted.AssistedFactory; import dagger.assisted.AssistedInject; +import java.util.ArrayList; import java.util.List; import java.util.Objects; import java.util.stream.Collectors; @@ -78,12 +86,15 @@ import java.util.stream.Collectors; */ public class HearingDevicesDialogDelegate implements SystemUIDialog.Delegate, HearingDeviceItemCallback, BluetoothCallback { - + private static final String TAG = "HearingDevicesDialogDelegate"; @VisibleForTesting static final String ACTION_BLUETOOTH_DEVICE_DETAILS = "com.android.settings.BLUETOOTH_DEVICE_DETAIL_SETTINGS"; private static final String EXTRA_SHOW_FRAGMENT_ARGUMENTS = ":settings:show_fragment_args"; private static final String KEY_BLUETOOTH_ADDRESS = "device_address"; + @VisibleForTesting + static final Intent LIVE_CAPTION_INTENT = new Intent( + "com.android.settings.action.live_caption"); private final SystemUIDialog.Factory mSystemUIDialogFactory; private final DialogTransitionAnimator mDialogTransitionAnimator; private final ActivityStarter mActivityStarter; @@ -102,6 +113,7 @@ public class HearingDevicesDialogDelegate implements SystemUIDialog.Delegate, private Spinner mPresetSpinner; private ArrayAdapter<String> mPresetInfoAdapter; private Button mPairButton; + private LinearLayout mRelatedToolsContainer; private final HearingDevicesPresetsController.PresetCallback mPresetCallback = new HearingDevicesPresetsController.PresetCallback() { @Override @@ -253,10 +265,14 @@ public class HearingDevicesDialogDelegate implements SystemUIDialog.Delegate, mPairButton = dialog.requireViewById(R.id.pair_new_device_button); mDeviceList = dialog.requireViewById(R.id.device_list); mPresetSpinner = dialog.requireViewById(R.id.preset_spinner); + mRelatedToolsContainer = dialog.requireViewById(R.id.related_tools_container); setupDeviceListView(dialog); setupPresetSpinner(dialog); setupPairNewDeviceButton(dialog, mShowPairNewDevice ? VISIBLE : GONE); + if (com.android.systemui.Flags.hearingDevicesDialogRelatedTools()) { + setupRelatedToolsView(dialog); + } } @Override @@ -351,6 +367,34 @@ public class HearingDevicesDialogDelegate implements SystemUIDialog.Delegate, } } + private void setupRelatedToolsView(SystemUIDialog dialog) { + final Context context = dialog.getContext(); + final List<ToolItem> toolItemList = new ArrayList<>(); + final String[] toolNameArray; + final String[] toolIconArray; + + ToolItem preInstalledItem = getLiveCaption(context); + if (preInstalledItem != null) { + toolItemList.add(preInstalledItem); + } + try { + toolNameArray = context.getResources().getStringArray( + R.array.config_quickSettingsHearingDevicesRelatedToolName); + toolIconArray = context.getResources().getStringArray( + R.array.config_quickSettingsHearingDevicesRelatedToolIcon); + toolItemList.addAll( + HearingDevicesToolItemParser.parseStringArray(context, toolNameArray, + toolIconArray)); + } catch (Resources.NotFoundException e) { + Log.i(TAG, "No hearing devices related tool config resource"); + } + final int listSize = toolItemList.size(); + for (int i = 0; i < listSize; i++) { + View view = createHearingToolView(context, toolItemList.get(i)); + mRelatedToolsContainer.addView(view); + } + } + private void refreshPresetInfoAdapter(List<BluetoothHapPresetInfo> presetInfos, int activePresetIndex) { mPresetInfoAdapter.clear(); @@ -400,6 +444,40 @@ public class HearingDevicesDialogDelegate implements SystemUIDialog.Delegate, return null; } + @NonNull + private View createHearingToolView(Context context, ToolItem item) { + View view = LayoutInflater.from(context).inflate(R.layout.hearing_tool_item, + mRelatedToolsContainer, false); + ImageView icon = view.requireViewById(R.id.tool_icon); + TextView text = view.requireViewById(R.id.tool_name); + view.setContentDescription(item.getToolName()); + icon.setImageDrawable(item.getToolIcon()); + text.setText(item.getToolName()); + Intent intent = item.getToolIntent(); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); + view.setOnClickListener( + v -> { + dismissDialogIfExists(); + mActivityStarter.postStartActivityDismissingKeyguard(intent, /* delay= */ 0, + mDialogTransitionAnimator.createActivityTransitionController(view)); + }); + return view; + } + + private ToolItem getLiveCaption(Context context) { + final PackageManager packageManager = context.getPackageManager(); + LIVE_CAPTION_INTENT.setPackage(packageManager.getSystemCaptionsServicePackageName()); + final List<ResolveInfo> resolved = packageManager.queryIntentActivities(LIVE_CAPTION_INTENT, + /* flags= */ 0); + if (!resolved.isEmpty()) { + return new ToolItem(context.getString(R.string.live_caption_title), + context.getDrawable(R.drawable.ic_volume_odi_captions), + LIVE_CAPTION_INTENT); + } + + return null; + } + private void dismissDialogIfExists() { if (mDialog != null) { mDialog.dismiss(); diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesToolItemParser.java b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesToolItemParser.java new file mode 100644 index 000000000000..2006726e6847 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesToolItemParser.java @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.accessibility.hearingaid; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.pm.ActivityInfo; +import android.content.pm.PackageManager; +import android.content.res.Resources; +import android.graphics.drawable.Drawable; +import android.util.Log; + +import androidx.annotation.VisibleForTesting; + +import com.google.common.collect.ImmutableList; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * Utility class for managing and parsing tool items related to hearing devices. + */ +public class HearingDevicesToolItemParser { + private static final String TAG = "HearingDevicesToolItemParser"; + private static final String SPLIT_DELIMITER = "/"; + private static final String RES_TYPE = "drawable"; + @VisibleForTesting + static final int MAX_NUM = 3; + + /** + * Parses the string arrays to create a list of {@link ToolItem}. + * + * This method validates the structure of {@code toolNameArray} and {@code toolIconArray}. + * If {@code toolIconArray} is empty or mismatched in length with {@code toolNameArray}, the + * icon from {@link ActivityInfo#loadIcon(PackageManager)} will be used instead. + * + * @param context A valid context. + * @param toolNameArray An array of tool names in the format of {@link ComponentName}. + * @param toolIconArray An optional array of resource names for tool icons (can be empty). + * @return A list of {@link ToolItem} or an empty list if there are errors during parsing. + */ + public static ImmutableList<ToolItem> parseStringArray(Context context, String[] toolNameArray, + String[] toolIconArray) { + if (toolNameArray.length == 0) { + Log.i(TAG, "Empty hearing device related tool name in array."); + return ImmutableList.of(); + } + // For the performance concern, especially `getIdentifier` in `parseValidIcon`, we will + // limit the maximum number. + String[] nameArrayCpy = Arrays.copyOfRange(toolNameArray, 0, + Math.min(toolNameArray.length, MAX_NUM)); + String[] iconArrayCpy = Arrays.copyOfRange(toolIconArray, 0, + Math.min(toolIconArray.length, MAX_NUM)); + + final PackageManager packageManager = context.getPackageManager(); + final ImmutableList.Builder<ToolItem> toolItemList = ImmutableList.builder(); + final List<ActivityInfo> activityInfoList = parseValidActivityInfo(context, nameArrayCpy); + final List<Drawable> iconList = parseValidIcon(context, iconArrayCpy); + final int size = activityInfoList.size(); + // Only use custom icon if provided icon's list size is equal to provided name's list size. + final boolean useCustomIcons = (size == iconList.size()); + + for (int i = 0; i < size; i++) { + toolItemList.add(new ToolItem( + activityInfoList.get(i).loadLabel(packageManager).toString(), + useCustomIcons ? iconList.get(i) + : activityInfoList.get(i).loadIcon(packageManager), + new Intent(Intent.ACTION_MAIN).setComponent( + activityInfoList.get(i).getComponentName()) + )); + } + + return toolItemList.build(); + } + + private static List<ActivityInfo> parseValidActivityInfo(Context context, + String[] toolNameArray) { + final PackageManager packageManager = context.getPackageManager(); + final List<ActivityInfo> activityInfoList = new ArrayList<>(); + for (String toolName : toolNameArray) { + String[] nameParts = toolName.split(SPLIT_DELIMITER); + if (nameParts.length == 2) { + ComponentName componentName = ComponentName.unflattenFromString(toolName); + try { + ActivityInfo activityInfo = packageManager.getActivityInfo( + componentName, /* flags= */ 0); + activityInfoList.add(activityInfo); + } catch (PackageManager.NameNotFoundException e) { + Log.e(TAG, "Unable to find hearing device related tool: " + + componentName.flattenToString()); + } + } else { + Log.e(TAG, "Malformed hearing device related tool name item in array: " + + toolName); + } + } + return activityInfoList; + } + + private static List<Drawable> parseValidIcon(Context context, String[] toolIconArray) { + final List<Drawable> drawableList = new ArrayList<>(); + for (String icon : toolIconArray) { + int resId = context.getResources().getIdentifier(icon, RES_TYPE, + context.getPackageName()); + try { + drawableList.add(context.getDrawable(resId)); + } catch (Resources.NotFoundException e) { + Log.e(TAG, "Resource does not exist: " + icon); + } + } + return drawableList; + } +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinderKosmos.kt b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/ToolItem.kt index 24d2c2f5dae1..66bb2b5e2328 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinderKosmos.kt +++ b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/ToolItem.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023 The Android Open Source Project + * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,10 +14,13 @@ * limitations under the License. */ -package com.android.systemui.keyguard.ui.binder +package com.android.systemui.accessibility.hearingaid -import android.os.fakeExecutorHandler -import com.android.systemui.kosmos.Kosmos +import android.content.Intent +import android.graphics.drawable.Drawable -val Kosmos.keyguardBlueprintViewBinder by - Kosmos.Fixture { KeyguardBlueprintViewBinder(fakeExecutorHandler) } +data class ToolItem( + var toolName: String = "", + var toolIcon: Drawable, + var toolIntent: Intent, +) diff --git a/packages/SystemUI/src/com/android/systemui/ambient/touch/BouncerSwipeTouchHandler.java b/packages/SystemUI/src/com/android/systemui/ambient/touch/BouncerSwipeTouchHandler.java index 019f498a01f8..f905241addeb 100644 --- a/packages/SystemUI/src/com/android/systemui/ambient/touch/BouncerSwipeTouchHandler.java +++ b/packages/SystemUI/src/com/android/systemui/ambient/touch/BouncerSwipeTouchHandler.java @@ -269,6 +269,7 @@ public class BouncerSwipeTouchHandler implements TouchHandler { } mScrimManager.removeCallback(mScrimManagerCallback); mCapture = null; + mTouchSession = null; if (!Flags.communalBouncerDoNotModifyPluginOpen()) { mNotificationShadeWindowController.setForcePluginOpen(false, this); diff --git a/packages/SystemUI/src/com/android/systemui/ambient/touch/TouchMonitor.java b/packages/SystemUI/src/com/android/systemui/ambient/touch/TouchMonitor.java index 227e4dba5d04..61b4401d6b22 100644 --- a/packages/SystemUI/src/com/android/systemui/ambient/touch/TouchMonitor.java +++ b/packages/SystemUI/src/com/android/systemui/ambient/touch/TouchMonitor.java @@ -49,11 +49,14 @@ import com.android.systemui.util.display.DisplayHelper; import com.google.common.util.concurrent.ListenableFuture; +import kotlinx.coroutines.Job; + import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.Set; +import java.util.concurrent.CancellationException; import java.util.concurrent.Executor; import java.util.function.Consumer; import java.util.stream.Collectors; @@ -78,15 +81,7 @@ public class TouchMonitor { private final Lifecycle mLifecycle; private Rect mExclusionRect = null; - private ISystemGestureExclusionListener mGestureExclusionListener = - new ISystemGestureExclusionListener.Stub() { - @Override - public void onSystemGestureExclusionChanged(int displayId, - Region systemGestureExclusion, - Region systemGestureExclusionUnrestricted) { - mExclusionRect = systemGestureExclusion.getBounds(); - } - }; + private ISystemGestureExclusionListener mGestureExclusionListener; private Consumer<Rect> mMaxBoundsConsumer = rect -> mMaxBounds = rect; @@ -274,6 +269,14 @@ public class TouchMonitor { if (bouncerAreaExclusion()) { mBackgroundExecutor.execute(() -> { try { + mGestureExclusionListener = new ISystemGestureExclusionListener.Stub() { + @Override + public void onSystemGestureExclusionChanged(int displayId, + Region systemGestureExclusion, + Region systemGestureExclusionUnrestricted) { + mExclusionRect = systemGestureExclusion.getBounds(); + } + }; mWindowManagerService.registerSystemGestureExclusionListener( mGestureExclusionListener, mDisplayId); } catch (RemoteException e) { @@ -298,8 +301,11 @@ public class TouchMonitor { if (bouncerAreaExclusion()) { mBackgroundExecutor.execute(() -> { try { - mWindowManagerService.unregisterSystemGestureExclusionListener( - mGestureExclusionListener, mDisplayId); + if (mGestureExclusionListener != null) { + mWindowManagerService.unregisterSystemGestureExclusionListener( + mGestureExclusionListener, mDisplayId); + mGestureExclusionListener = null; + } } catch (RemoteException e) { // Handle the exception Log.e(TAG, "unregisterSystemGestureExclusionListener: failed", e); @@ -494,6 +500,10 @@ public class TouchMonitor { private Rect mMaxBounds; + private Job mBoundsFlow; + + private boolean mInitialized; + /** * Designated constructor for {@link TouchMonitor} @@ -535,10 +545,35 @@ public class TouchMonitor { * Initializes the monitor. should only be called once after creation. */ public void init() { + if (mInitialized) { + throw new IllegalStateException("TouchMonitor already initialized"); + } + mLifecycle.addObserver(mLifecycleObserver); if (Flags.ambientTouchMonitorListenToDisplayChanges()) { - collectFlow(mLifecycle, mConfigurationInteractor.getMaxBounds(), mMaxBoundsConsumer); + mBoundsFlow = collectFlow(mLifecycle, mConfigurationInteractor.getMaxBounds(), + mMaxBoundsConsumer); } + + mInitialized = true; + } + + /** + * Called when the TouchMonitor should be discarded and will not be used anymore. + */ + public void destroy() { + if (!mInitialized) { + throw new IllegalStateException("TouchMonitor not initialized"); + } + + stopMonitoring(true); + + mLifecycle.removeObserver(mLifecycleObserver); + if (Flags.ambientTouchMonitorListenToDisplayChanges()) { + mBoundsFlow.cancel(new CancellationException()); + } + + mInitialized = false; } private void isolate(Set<TouchSessionImpl> sessions) { diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java index b75b292be597..1ee4908437a6 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java @@ -29,6 +29,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.app.AlertDialog; import android.content.Context; +import android.content.res.Configuration; import android.content.res.TypedArray; import android.graphics.Color; import android.graphics.PixelFormat; @@ -360,15 +361,23 @@ public class AuthContainerView extends LinearLayout Utils.findFirstSensorProperties(fpProps, mConfig.mSensorIds), Utils.findFirstSensorProperties(faceProps, mConfig.mSensorIds)); + final boolean isLandscape = mContext.getResources().getConfiguration().orientation + == Configuration.ORIENTATION_LANDSCAPE; mPromptSelectorInteractorProvider = promptSelectorInteractorProvider; mPromptSelectorInteractorProvider.get().setPrompt(mConfig.mPromptInfo, mEffectiveUserId, getRequestId(), biometricModalities, mConfig.mOperationId, mConfig.mOpPackageName, - false /*onSwitchToCredential*/); + false /*onSwitchToCredential*/, isLandscape); final LayoutInflater layoutInflater = LayoutInflater.from(mContext); - if (constraintBp() && mPromptViewModel.getPromptKind().getValue().isBiometric()) { - mLayout = (ConstraintLayout) layoutInflater.inflate( - R.layout.biometric_prompt_constraint_layout, this, false /* attachToRoot */); + final PromptKind kind = mPromptViewModel.getPromptKind().getValue(); + if (constraintBp() && kind.isBiometric()) { + if (kind.isTwoPaneLandscapeBiometric()) { + mLayout = (ConstraintLayout) layoutInflater.inflate( + R.layout.biometric_prompt_two_pane_layout, this, false /* attachToRoot */); + } else { + mLayout = (ConstraintLayout) layoutInflater.inflate( + R.layout.biometric_prompt_one_pane_layout, this, false /* attachToRoot */); + } } else { mLayout = (FrameLayout) layoutInflater.inflate( R.layout.auth_container_view, this, false /* attachToRoot */); @@ -631,7 +640,7 @@ public class AuthContainerView extends LinearLayout if (fpProp != null && fpProp.isAnyUdfpsType()) { maybeUpdatePositionForUdfps(forceInvalidate /* invalidate */); } - if (faceProp != null && mBiometricView.isFaceOnly()) { + if (faceProp != null && mBiometricView != null && mBiometricView.isFaceOnly()) { alwaysUpdatePositionAtScreenBottom(forceInvalidate /* invalidate */); } if (fpProp != null && fpProp.sensorType == TYPE_POWER_BUTTON) { diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/DisplayStateRepository.kt b/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/DisplayStateRepository.kt index 8e5a97bd5d8d..9b14d6f68e35 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/DisplayStateRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/DisplayStateRepository.kt @@ -29,11 +29,10 @@ import com.android.systemui.display.data.repository.DeviceStateRepository import com.android.systemui.display.data.repository.DeviceStateRepository.DeviceState.REAR_DISPLAY import com.android.systemui.display.data.repository.DisplayRepository import javax.inject.Inject +import kotlin.math.min import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn @@ -58,7 +57,7 @@ interface DisplayStateRepository { val currentDisplaySize: StateFlow<Size> /** Provides whether the current display is large screen */ - val isLargeScreen: Flow<Boolean> + val isLargeScreen: StateFlow<Boolean> } @SysUISingleton @@ -127,16 +126,29 @@ constructor( ), ) - override val isLargeScreen: Flow<Boolean> = + override val isLargeScreen: StateFlow<Boolean> = currentDisplayInfo .map { - // TODO: This works, but investigate better way to handle this - it.logicalWidth * 160 / it.logicalDensityDpi > DisplayMetrics.DENSITY_XXXHIGH && - it.logicalHeight * 160 / it.logicalDensityDpi > DisplayMetrics.DENSITY_XXHIGH + // copied from systemui/shared/...Utilities.java + val smallestWidth = + dpiFromPx( + min(it.logicalWidth, it.logicalHeight).toFloat(), + context.resources.configuration.densityDpi + ) + smallestWidth >= LARGE_SCREEN_MIN_DPS } - .distinctUntilChanged() + .stateIn( + backgroundScope, + started = SharingStarted.WhileSubscribed(), + initialValue = false, + ) + private fun dpiFromPx(size: Float, densityDpi: Int): Float { + val densityRatio = densityDpi.toFloat() / DisplayMetrics.DENSITY_DEFAULT + return size / densityRatio + } companion object { const val TAG = "DisplayStateRepositoryImpl" + const val LARGE_SCREEN_MIN_DPS = 600f } } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/DisplayStateInteractor.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/DisplayStateInteractor.kt index 591da4096956..40313e3158aa 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/DisplayStateInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/DisplayStateInteractor.kt @@ -65,7 +65,8 @@ interface DisplayStateInteractor { /** Called on configuration changes, used to keep the display state in sync */ fun onConfigurationChanged(newConfig: Configuration) - val isLargeScreen: Flow<Boolean> + /** Provides whether the current display is large screen */ + val isLargeScreen: StateFlow<Boolean> } /** Encapsulates logic for interacting with the display state. */ @@ -127,7 +128,7 @@ constructor( override val isDefaultDisplayOff = displayRepository.defaultDisplayOff - override val isLargeScreen: Flow<Boolean> = displayStateRepository.isLargeScreen + override val isLargeScreen: StateFlow<Boolean> = displayStateRepository.isLargeScreen companion object { private const val TAG = "DisplayStateInteractor" diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractor.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractor.kt index dc338d07f9e7..c08756f6ae36 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractor.kt @@ -91,6 +91,7 @@ interface PromptSelectorInteractor { challenge: Long, opPackageName: String, onSwitchToCredential: Boolean, + isLandscape: Boolean, ) /** Unset the current authentication request. */ @@ -102,6 +103,7 @@ class PromptSelectorInteractorImpl @Inject constructor( fingerprintPropertyRepository: FingerprintPropertyRepository, + private val displayStateInteractor: DisplayStateInteractor, private val promptRepository: PromptRepository, private val lockPatternUtils: LockPatternUtils, ) : PromptSelectorInteractor { @@ -166,7 +168,9 @@ constructor( modalities, promptRepository.challenge.value!!, promptRepository.opPackageName.value!!, - true /*onSwitchToCredential*/ + onSwitchToCredential = true, + // isLandscape value is not important when onSwitchToCredential is true + isLandscape = false, ) } @@ -178,6 +182,7 @@ constructor( challenge: Long, opPackageName: String, onSwitchToCredential: Boolean, + isLandscape: Boolean, ) { val hasCredentialViewShown = promptKind.value.isCredential() val showBpForCredential = @@ -189,11 +194,30 @@ constructor( !promptInfo.isContentViewMoreOptionsButtonUsed val showBpWithoutIconForCredential = showBpForCredential && !hasCredentialViewShown var kind: PromptKind = PromptKind.None + if (onSwitchToCredential) { kind = getCredentialType(lockPatternUtils, effectiveUserId) } else if (Utils.isBiometricAllowed(promptInfo) || showBpWithoutIconForCredential) { - // TODO(b/330908557): check to show one pane or two pane - kind = PromptKind.Biometric(modalities) + // TODO(b/330908557): Subscribe to + // displayStateInteractor.currentRotation.value.isDefaultOrientation() for checking + // `isLandscape` after removing AuthContinerView. + kind = + if (isLandscape) { + val paneType = + when { + displayStateInteractor.isLargeScreen.value -> + PromptKind.Biometric.PaneType.ONE_PANE_LARGE_SCREEN_LANDSCAPE + showBpWithoutIconForCredential -> + PromptKind.Biometric.PaneType.ONE_PANE_NO_SENSOR_LANDSCAPE + else -> PromptKind.Biometric.PaneType.TWO_PANE_LANDSCAPE + } + PromptKind.Biometric( + modalities, + paneType = paneType, + ) + } else { + PromptKind.Biometric(modalities) + } } else if (isDeviceCredentialAllowed(promptInfo)) { kind = getCredentialType(lockPatternUtils, effectiveUserId) } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt index 47174c006735..c836f89a8ff4 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt @@ -93,6 +93,7 @@ object BiometricViewSizeBinder { if (constraintBp()) { val leftGuideline = view.requireViewById<Guideline>(R.id.leftGuideline) + val topGuideline = view.requireViewById<Guideline>(R.id.topGuideline) val rightGuideline = view.requireViewById<Guideline>(R.id.rightGuideline) val midGuideline = view.findViewById<Guideline>(R.id.midGuideline) @@ -355,6 +356,18 @@ object BiometricViewSizeBinder { ) } + if (bounds.top >= 0) { + mediumConstraintSet.setGuidelineBegin(topGuideline.id, bounds.top) + smallConstraintSet.setGuidelineBegin(topGuideline.id, bounds.top) + } else if (bounds.top < 0) { + mediumConstraintSet.setGuidelineEnd( + topGuideline.id, + abs(bounds.top) + ) + smallConstraintSet.setGuidelineEnd(topGuideline.id, abs(bounds.top)) + } + + // Use rect bottom to set mid guideline of two-pane. if (midGuideline != null) { if (bounds.bottom >= 0) { midGuideline.setGuidelineEnd(bounds.bottom) diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt index 156ec6b975a5..c17b83dd4fbe 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt @@ -261,10 +261,13 @@ constructor( combine( _forceLargeSize, displayStateInteractor.isLargeScreen, - displayStateInteractor.currentRotation + displayStateInteractor.currentRotation, ) { forceLarge, isLargeScreen, rotation -> when { - forceLarge || isLargeScreen -> PromptPosition.Bottom + forceLarge || + isLargeScreen || + promptKind.value.isOnePaneNoSensorLandscapeBiometric() -> + PromptPosition.Bottom rotation == DisplayRotation.ROTATION_90 -> PromptPosition.Right rotation == DisplayRotation.ROTATION_270 -> PromptPosition.Left rotation == DisplayRotation.ROTATION_180 -> PromptPosition.Top @@ -297,23 +300,27 @@ constructor( /** Prompt panel size padding */ private val smallHorizontalGuidelinePadding = context.resources.getDimensionPixelSize( - R.dimen.biometric_prompt_small_horizontal_guideline_padding + R.dimen.biometric_prompt_land_small_horizontal_guideline_padding ) private val udfpsHorizontalGuidelinePadding = context.resources.getDimensionPixelSize( - R.dimen.biometric_prompt_udfps_horizontal_guideline_padding + R.dimen.biometric_prompt_two_pane_udfps_horizontal_guideline_padding ) private val udfpsMidGuidelinePadding = context.resources.getDimensionPixelSize( - R.dimen.biometric_prompt_udfps_mid_guideline_padding + R.dimen.biometric_prompt_two_pane_udfps_mid_guideline_padding + ) + private val mediumTopGuidelinePadding = + context.resources.getDimensionPixelSize( + R.dimen.biometric_prompt_one_pane_medium_top_guideline_padding ) private val mediumHorizontalGuidelinePadding = context.resources.getDimensionPixelSize( - R.dimen.biometric_prompt_medium_horizontal_guideline_padding + R.dimen.biometric_prompt_two_pane_medium_horizontal_guideline_padding ) private val mediumMidGuidelinePadding = context.resources.getDimensionPixelSize( - R.dimen.biometric_prompt_medium_mid_guideline_padding + R.dimen.biometric_prompt_two_pane_medium_mid_guideline_padding ) /** Rect for positioning biometric icon */ @@ -416,9 +423,9 @@ constructor( * asset to be loaded before determining the prompt size. */ val isIconViewLoaded: Flow<Boolean> = - combine(modalities, _isIconViewLoaded.asStateFlow()) { modalities, isIconViewLoaded -> - val noIcon = modalities.isEmpty - noIcon || isIconViewLoaded + combine(hideSensorIcon, _isIconViewLoaded.asStateFlow()) { hideSensorIcon, isIconViewLoaded + -> + hideSensorIcon || isIconViewLoaded } .distinctUntilChanged() @@ -448,17 +455,24 @@ constructor( * from opposite side of the screen */ val guidelineBounds: Flow<Rect> = - combine(iconPosition, size, position, modalities) { _, size, position, modalities -> + combine(iconPosition, promptKind, size, position, modalities) { + _, + promptKind, + size, + position, + modalities -> when (position) { - PromptPosition.Bottom -> Rect(0, 0, 0, 0) + PromptPosition.Bottom -> + if (promptKind.isOnePaneNoSensorLandscapeBiometric()) { + Rect(0, 0, 0, 0) + } else { + Rect(0, mediumTopGuidelinePadding, 0, 0) + } PromptPosition.Right -> if (size.isSmall) { Rect(-smallHorizontalGuidelinePadding, 0, 0, 0) } else if (modalities.hasUdfps) { Rect(udfpsHorizontalGuidelinePadding, 0, 0, udfpsMidGuidelinePadding) - } else if (modalities.isEmpty) { - // TODO: Temporary fix until no biometric landscape layout is added - Rect(-mediumHorizontalGuidelinePadding, 0, 0, 6) } else { Rect(-mediumHorizontalGuidelinePadding, 0, 0, mediumMidGuidelinePadding) } @@ -467,9 +481,6 @@ constructor( Rect(0, 0, -smallHorizontalGuidelinePadding, 0) } else if (modalities.hasUdfps) { Rect(0, 0, udfpsHorizontalGuidelinePadding, -udfpsMidGuidelinePadding) - } else if (modalities.isEmpty) { - // TODO: Temporary fix until no biometric landscape layout is added - Rect(0, 0, -mediumHorizontalGuidelinePadding, -6) } else { Rect( 0, diff --git a/packages/SystemUI/src/com/android/systemui/brightness/dagger/ScreenBrightnessModule.kt b/packages/SystemUI/src/com/android/systemui/brightness/dagger/ScreenBrightnessModule.kt index 2b9fc73458d8..7a9429e56c88 100644 --- a/packages/SystemUI/src/com/android/systemui/brightness/dagger/ScreenBrightnessModule.kt +++ b/packages/SystemUI/src/com/android/systemui/brightness/dagger/ScreenBrightnessModule.kt @@ -20,8 +20,15 @@ import com.android.systemui.brightness.data.repository.BrightnessPolicyRepositor import com.android.systemui.brightness.data.repository.BrightnessPolicyRepositoryImpl import com.android.systemui.brightness.data.repository.ScreenBrightnessDisplayManagerRepository import com.android.systemui.brightness.data.repository.ScreenBrightnessRepository +import com.android.systemui.brightness.shared.model.BrightnessLog +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.log.LogBuffer +import com.android.systemui.log.LogBufferFactory +import com.android.systemui.log.table.TableLogBuffer +import com.android.systemui.log.table.TableLogBufferFactory import dagger.Binds import dagger.Module +import dagger.Provides @Module interface ScreenBrightnessModule { @@ -33,4 +40,20 @@ interface ScreenBrightnessModule { @Binds fun bindPolicyRepository(impl: BrightnessPolicyRepositoryImpl): BrightnessPolicyRepository + + companion object { + @Provides + @SysUISingleton + @BrightnessLog + fun providesBrightnessTableLog(factory: TableLogBufferFactory): TableLogBuffer { + return factory.create("BrightnessTableLog", 50) + } + + @Provides + @SysUISingleton + @BrightnessLog + fun providesBrightnessLog(factory: LogBufferFactory): LogBuffer { + return factory.create("BrightnessLog", 50) + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/brightness/data/repository/ScreenBrightnessRepository.kt b/packages/SystemUI/src/com/android/systemui/brightness/data/repository/ScreenBrightnessRepository.kt index 9ed11d13d4d4..37d1887730b9 100644 --- a/packages/SystemUI/src/com/android/systemui/brightness/data/repository/ScreenBrightnessRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/brightness/data/repository/ScreenBrightnessRepository.kt @@ -19,12 +19,18 @@ package com.android.systemui.brightness.data.repository import android.annotation.SuppressLint import android.hardware.display.BrightnessInfo import android.hardware.display.DisplayManager -import com.android.systemui.brightness.data.model.LinearBrightness +import com.android.systemui.brightness.shared.model.BrightnessLog +import com.android.systemui.brightness.shared.model.LinearBrightness +import com.android.systemui.brightness.shared.model.formatBrightness +import com.android.systemui.brightness.shared.model.logDiffForTable import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.DisplayId +import com.android.systemui.log.LogBuffer +import com.android.systemui.log.core.LogLevel +import com.android.systemui.log.table.TableLogBuffer import javax.inject.Inject import kotlin.coroutines.CoroutineContext import kotlinx.coroutines.CoroutineScope @@ -32,13 +38,13 @@ import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.channels.Channel.Factory.UNLIMITED import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onStart -import kotlinx.coroutines.flow.shareIn import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch import kotlinx.coroutines.withContext @@ -78,6 +84,8 @@ class ScreenBrightnessDisplayManagerRepository constructor( @DisplayId private val displayId: Int, private val displayManager: DisplayManager, + @BrightnessLog private val logBuffer: LogBuffer, + @BrightnessLog private val tableBuffer: TableLogBuffer, @Application private val applicationScope: CoroutineScope, @Background private val backgroundContext: CoroutineContext, ) : ScreenBrightnessRepository { @@ -100,6 +108,7 @@ constructor( displayManager.setBrightness(displayId, value) } } + logBrightnessChange(call is SetBrightnessMethod.Permanent, value) } } } @@ -147,13 +156,15 @@ constructor( brightnessInfo .filterNotNull() .map { LinearBrightness(it.brightnessMinimum) } - .shareIn(applicationScope, SharingStarted.WhileSubscribed()) + .logDiffForTable(tableBuffer, TABLE_PREFIX_LINEAR, TABLE_COLUMN_MIN, null) + .stateIn(applicationScope, SharingStarted.WhileSubscribed(), LinearBrightness(0f)) - override val maxLinearBrightness = + override val maxLinearBrightness: SharedFlow<LinearBrightness> = brightnessInfo .filterNotNull() .map { LinearBrightness(it.brightnessMaximum) } - .shareIn(applicationScope, SharingStarted.WhileSubscribed()) + .logDiffForTable(tableBuffer, TABLE_PREFIX_LINEAR, TABLE_COLUMN_MAX, null) + .stateIn(applicationScope, SharingStarted.WhileSubscribed(), LinearBrightness(1f)) override suspend fun getMinMaxLinearBrightness(): Pair<LinearBrightness, LinearBrightness> { val brightnessInfo = brightnessInfo.value ?: brightnessInfoValue() @@ -166,7 +177,8 @@ constructor( brightnessInfo .filterNotNull() .map { LinearBrightness(it.brightness) } - .shareIn(applicationScope, SharingStarted.WhileSubscribed()) + .logDiffForTable(tableBuffer, TABLE_PREFIX_LINEAR, TABLE_COLUMN_BRIGHTNESS, null) + .stateIn(applicationScope, SharingStarted.WhileSubscribed(), LinearBrightness(0f)) override fun setTemporaryBrightness(value: LinearBrightness) { apiQueue.trySend(SetBrightnessMethod.Temporary(value)) @@ -183,4 +195,21 @@ constructor( @JvmInline value class Permanent(override val value: LinearBrightness) : SetBrightnessMethod } + + private fun logBrightnessChange(permanent: Boolean, value: Float) { + logBuffer.log( + LOG_BUFFER_BRIGHTNESS_CHANGE_TAG, + if (permanent) LogLevel.DEBUG else LogLevel.VERBOSE, + { str1 = value.formatBrightness() }, + { "Change requested: $str1" } + ) + } + + private companion object { + const val TABLE_COLUMN_BRIGHTNESS = "brightness" + const val TABLE_COLUMN_MIN = "min" + const val TABLE_COLUMN_MAX = "max" + const val TABLE_PREFIX_LINEAR = "linear" + const val LOG_BUFFER_BRIGHTNESS_CHANGE_TAG = "BrightnessChange" + } } diff --git a/packages/SystemUI/src/com/android/systemui/brightness/domain/interactor/ScreenBrightnessInteractor.kt b/packages/SystemUI/src/com/android/systemui/brightness/domain/interactor/ScreenBrightnessInteractor.kt index 799a0a14c99d..5647f521762f 100644 --- a/packages/SystemUI/src/com/android/systemui/brightness/domain/interactor/ScreenBrightnessInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/brightness/domain/interactor/ScreenBrightnessInteractor.kt @@ -17,12 +17,20 @@ package com.android.systemui.brightness.domain.interactor import com.android.settingslib.display.BrightnessUtils -import com.android.systemui.brightness.data.model.LinearBrightness import com.android.systemui.brightness.data.repository.ScreenBrightnessRepository -import com.android.systemui.brightness.shared.GammaBrightness +import com.android.systemui.brightness.shared.model.BrightnessLog +import com.android.systemui.brightness.shared.model.GammaBrightness +import com.android.systemui.brightness.shared.model.LinearBrightness +import com.android.systemui.brightness.shared.model.logDiffForTable import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.log.table.TableLogBuffer import javax.inject.Inject +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.stateIn /** * Converts between [GammaBrightness] and [LinearBrightness]. @@ -34,6 +42,8 @@ class ScreenBrightnessInteractor @Inject constructor( private val screenBrightnessRepository: ScreenBrightnessRepository, + @Application private val applicationScope: CoroutineScope, + @BrightnessLog private val tableBuffer: TableLogBuffer, ) { /** Maximum value in the Gamma space for brightness */ val maxGammaBrightness = GammaBrightness(BrightnessUtils.GAMMA_SPACE_MAX) @@ -45,15 +55,17 @@ constructor( * Brightness in the Gamma space for the current display. It will always represent a value * between [minGammaBrightness] and [maxGammaBrightness] */ - val gammaBrightness = + val gammaBrightness: Flow<GammaBrightness> = with(screenBrightnessRepository) { combine( - linearBrightness, - minLinearBrightness, - maxLinearBrightness, - ) { brightness, min, max -> - brightness.toGammaBrightness(min, max) - } + linearBrightness, + minLinearBrightness, + maxLinearBrightness, + ) { brightness, min, max -> + brightness.toGammaBrightness(min, max) + } + .logDiffForTable(tableBuffer, TABLE_PREFIX_GAMMA, TABLE_COLUMN_BRIGHTNESS, null) + .stateIn(applicationScope, SharingStarted.WhileSubscribed(), GammaBrightness(0)) } /** Sets the brightness temporarily, while the user is changing it. */ @@ -91,4 +103,9 @@ constructor( BrightnessUtils.convertLinearToGammaFloat(floatValue, min.floatValue, max.floatValue) ) } + + private companion object { + const val TABLE_COLUMN_BRIGHTNESS = "brightness" + const val TABLE_PREFIX_GAMMA = "gamma" + } } diff --git a/packages/SystemUI/src/com/android/systemui/brightness/shared/GammaBrightness.kt b/packages/SystemUI/src/com/android/systemui/brightness/shared/model/BrightnessLog.kt index e20d003bb989..b514fefbff0e 100644 --- a/packages/SystemUI/src/com/android/systemui/brightness/shared/GammaBrightness.kt +++ b/packages/SystemUI/src/com/android/systemui/brightness/shared/model/BrightnessLog.kt @@ -14,16 +14,11 @@ * limitations under the License. */ -package com.android.systemui.brightness.shared +package com.android.systemui.brightness.shared.model -import androidx.annotation.IntRange -import com.android.settingslib.display.BrightnessUtils +import javax.inject.Qualifier -@JvmInline -value class GammaBrightness( - @IntRange( - from = BrightnessUtils.GAMMA_SPACE_MIN.toLong(), - to = BrightnessUtils.GAMMA_SPACE_MAX.toLong() - ) - val value: Int -) +@Qualifier +@MustBeDocumented +@Retention(AnnotationRetention.RUNTIME) +annotation class BrightnessLog() diff --git a/packages/SystemUI/src/com/android/systemui/brightness/shared/model/GammaBrightness.kt b/packages/SystemUI/src/com/android/systemui/brightness/shared/model/GammaBrightness.kt new file mode 100644 index 000000000000..7eba6268869c --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/brightness/shared/model/GammaBrightness.kt @@ -0,0 +1,50 @@ +/* + * 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.brightness.shared.model + +import androidx.annotation.IntRange +import com.android.settingslib.display.BrightnessUtils +import com.android.systemui.log.table.TableLogBuffer +import com.android.systemui.util.kotlin.pairwiseBy +import kotlinx.coroutines.flow.Flow + +@JvmInline +value class GammaBrightness( + @IntRange( + from = BrightnessUtils.GAMMA_SPACE_MIN.toLong(), + to = BrightnessUtils.GAMMA_SPACE_MAX.toLong() + ) + val value: Int +) + +internal fun Flow<GammaBrightness>.logDiffForTable( + tableLogBuffer: TableLogBuffer, + columnPrefix: String, + columnName: String, + initialValue: GammaBrightness?, +): Flow<GammaBrightness> { + val initialValueFun = { + tableLogBuffer.logChange(columnPrefix, columnName, initialValue?.value, isInitial = true) + initialValue + } + return this.pairwiseBy(initialValueFun) { prevVal: GammaBrightness?, newVal: GammaBrightness -> + if (prevVal != newVal) { + tableLogBuffer.logChange(columnPrefix, columnName, newVal.value) + } + newVal + } +} diff --git a/packages/SystemUI/src/com/android/systemui/brightness/shared/model/LinearBrightness.kt b/packages/SystemUI/src/com/android/systemui/brightness/shared/model/LinearBrightness.kt new file mode 100644 index 000000000000..1c886e6b1477 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/brightness/shared/model/LinearBrightness.kt @@ -0,0 +1,65 @@ +/* + * 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.brightness.shared.model + +import com.android.systemui.log.table.TableLogBuffer +import com.android.systemui.util.kotlin.pairwiseBy +import kotlinx.coroutines.flow.Flow + +@JvmInline +value class LinearBrightness(val floatValue: Float) { + fun clamp(min: LinearBrightness, max: LinearBrightness): LinearBrightness { + return if (floatValue < min.floatValue) { + min + } else if (floatValue > max.floatValue) { + max + } else { + this + } + } + + val loggableString: String + get() = floatValue.formatBrightness() +} + +fun Float.formatBrightness(): String { + return "%.3f".format(this) +} + +internal fun Flow<LinearBrightness>.logDiffForTable( + tableLogBuffer: TableLogBuffer, + columnPrefix: String, + columnName: String, + initialValue: LinearBrightness?, +): Flow<LinearBrightness> { + val initialValueFun = { + tableLogBuffer.logChange( + columnPrefix, + columnName, + initialValue?.loggableString, + isInitial = true + ) + initialValue + } + return this.pairwiseBy(initialValueFun) { prevVal: LinearBrightness?, newVal: LinearBrightness + -> + if (prevVal != newVal) { + tableLogBuffer.logChange(columnPrefix, columnName, newVal.loggableString) + } + newVal + } +} diff --git a/packages/SystemUI/src/com/android/systemui/brightness/ui/compose/BrightnessSlider.kt b/packages/SystemUI/src/com/android/systemui/brightness/ui/compose/BrightnessSlider.kt index a51d8ff4faa5..f991d5b8405f 100644 --- a/packages/SystemUI/src/com/android/systemui/brightness/ui/compose/BrightnessSlider.kt +++ b/packages/SystemUI/src/com/android/systemui/brightness/ui/compose/BrightnessSlider.kt @@ -33,14 +33,13 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.android.compose.PlatformSlider -import com.android.systemui.brightness.shared.GammaBrightness +import com.android.systemui.brightness.shared.model.GammaBrightness import com.android.systemui.brightness.ui.viewmodel.BrightnessSliderViewModel import com.android.systemui.brightness.ui.viewmodel.Drag import com.android.systemui.common.shared.model.Icon import com.android.systemui.common.shared.model.Text import com.android.systemui.common.ui.compose.Icon import com.android.systemui.utils.PolicyRestriction -import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch @Composable @@ -107,8 +106,8 @@ fun BrightnessSliderContainer( viewModel: BrightnessSliderViewModel, modifier: Modifier = Modifier, ) { - val gamma: Int by - viewModel.currentBrightness.map { it.value }.collectAsStateWithLifecycle(initialValue = 0) + val state by viewModel.currentBrightness.collectAsStateWithLifecycle() + val gamma = state.value val coroutineScope = rememberCoroutineScope() val restriction by viewModel.policyRestriction.collectAsStateWithLifecycle( diff --git a/packages/SystemUI/src/com/android/systemui/brightness/ui/viewmodel/BrightnessSliderViewModel.kt b/packages/SystemUI/src/com/android/systemui/brightness/ui/viewmodel/BrightnessSliderViewModel.kt index f0988ba96bcd..16a1dcc0aef5 100644 --- a/packages/SystemUI/src/com/android/systemui/brightness/ui/viewmodel/BrightnessSliderViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/brightness/ui/viewmodel/BrightnessSliderViewModel.kt @@ -18,14 +18,18 @@ package com.android.systemui.brightness.ui.viewmodel import com.android.systemui.brightness.domain.interactor.BrightnessPolicyEnforcementInteractor import com.android.systemui.brightness.domain.interactor.ScreenBrightnessInteractor -import com.android.systemui.brightness.shared.GammaBrightness +import com.android.systemui.brightness.shared.model.GammaBrightness import com.android.systemui.common.shared.model.ContentDescription import com.android.systemui.common.shared.model.Icon import com.android.systemui.common.shared.model.Text import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.res.R import com.android.systemui.utils.PolicyRestriction import javax.inject.Inject +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.stateIn @SysUISingleton class BrightnessSliderViewModel @@ -33,8 +37,14 @@ class BrightnessSliderViewModel constructor( private val screenBrightnessInteractor: ScreenBrightnessInteractor, private val brightnessPolicyEnforcementInteractor: BrightnessPolicyEnforcementInteractor, + @Application private val applicationScope: CoroutineScope, ) { - val currentBrightness = screenBrightnessInteractor.gammaBrightness + val currentBrightness = + screenBrightnessInteractor.gammaBrightness.stateIn( + applicationScope, + SharingStarted.WhileSubscribed(), + GammaBrightness(0) + ) val maxBrightness = screenBrightnessInteractor.maxGammaBrightness val minBrightness = screenBrightnessInteractor.minGammaBrightness diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSceneRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSceneRepository.kt index d6d08b4f1208..260dcbad6201 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSceneRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSceneRepository.kt @@ -22,6 +22,7 @@ import com.android.compose.animation.scene.TransitionKey import com.android.systemui.communal.dagger.Communal import com.android.systemui.communal.shared.model.CommunalScenes import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.scene.shared.model.SceneDataSource import javax.inject.Inject @@ -34,6 +35,7 @@ import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.stateIn +import kotlinx.coroutines.launch /** Encapsulates the state of communal mode. */ interface CommunalSceneRepository { @@ -64,6 +66,7 @@ interface CommunalSceneRepository { class CommunalSceneRepositoryImpl @Inject constructor( + @Application private val applicationScope: CoroutineScope, @Background backgroundScope: CoroutineScope, @Communal private val sceneDataSource: SceneDataSource, ) : CommunalSceneRepository { @@ -82,11 +85,19 @@ constructor( ) override fun changeScene(toScene: SceneKey, transitionKey: TransitionKey?) { - sceneDataSource.changeScene(toScene, transitionKey) + applicationScope.launch { + // SceneTransitionLayout state updates must be triggered on the thread the STL was + // created on. + sceneDataSource.changeScene(toScene, transitionKey) + } } override fun snapToScene(toScene: SceneKey) { - sceneDataSource.snapToScene(toScene) + applicationScope.launch { + // SceneTransitionLayout state updates must be triggered on the thread the STL was + // created on. + sceneDataSource.snapToScene(toScene) + } } /** diff --git a/packages/SystemUI/src/com/android/systemui/dock/DockManagerExtensions.kt b/packages/SystemUI/src/com/android/systemui/dock/DockManagerExtensions.kt index 4dbb32da62c2..1bbdfcd88548 100644 --- a/packages/SystemUI/src/com/android/systemui/dock/DockManagerExtensions.kt +++ b/packages/SystemUI/src/com/android/systemui/dock/DockManagerExtensions.kt @@ -19,16 +19,18 @@ package com.android.systemui.dock import com.android.systemui.common.coroutine.ConflatedCallbackFlow import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.distinctUntilChanged /** - * Retrieves whether or not the device is docked according to DockManager. Emits a starting value - * of isDocked. + * Retrieves whether or not the device is docked according to DockManager. Emits a starting value of + * isDocked. */ fun DockManager.retrieveIsDocked(): Flow<Boolean> = ConflatedCallbackFlow.conflatedCallbackFlow { - val callback = DockManager.DockEventListener { trySend(isDocked) } - addListener(callback) - trySend(isDocked) + val callback = DockManager.DockEventListener { trySend(isDocked) } + addListener(callback) + trySend(isDocked) - awaitClose { removeListener(callback) } - }
\ No newline at end of file + awaitClose { removeListener(callback) } + } + .distinctUntilChanged() diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java index aa7a7dae8f5e..96e708fca451 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java @@ -543,7 +543,11 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ mStateController.setEntryAnimationsFinished(false); mDreamOverlayContainerViewController = null; - mTouchMonitor = null; + + if (mTouchMonitor != null) { + mTouchMonitor.destroy(); + mTouchMonitor = null; + } mWindow = null; mStarted = false; diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt index 84980c1c521f..f4f8796ebffc 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt +++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt @@ -42,14 +42,6 @@ object Flags { @JvmField val NULL_FLAG = unreleasedFlag("null_flag") // 100 - notification - // TODO(b/297792660): Tracking Bug - @JvmField val UNCLEARED_TRANSIENT_HUN_FIX = - releasedFlag("uncleared_transient_hun_fix") - - // TODO(b/298308067): Tracking Bug - @JvmField val SWIPE_UNCLEARED_TRANSIENT_VIEW_FIX = - releasedFlag("swipe_uncleared_transient_view_fix") - // TODO(b/254512751): Tracking Bug val NOTIFICATION_PIPELINE_DEVELOPER_LOGGING = unreleasedFlag("notification_pipeline_developer_logging") @@ -467,14 +459,6 @@ object Flags { @JvmField val ENABLE_CLOCK_KEYGUARD_PRESENTATION = releasedFlag("enable_clock_keyguard_presentation") - /** Enable the Compose implementation of the PeopleSpaceActivity. */ - @JvmField - val COMPOSE_PEOPLE_SPACE = releasedFlag("compose_people_space") - - /** Enable the Compose implementation of the Quick Settings footer actions. */ - @JvmField - val COMPOSE_QS_FOOTER_ACTIONS = releasedFlag("compose_qs_footer_actions") - /** Enable the share wifi button in Quick Settings internet dialog. */ @JvmField val SHARE_WIFI_QS_BUTTON = releasedFlag("share_wifi_qs_button") diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt index a50cc8fbacb3..306f4ffa2a54 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt @@ -103,7 +103,6 @@ constructor( private val smartspaceViewModel: KeyguardSmartspaceViewModel, private val lockscreenContentViewModel: LockscreenContentViewModel, private val lockscreenSceneBlueprintsLazy: Lazy<Set<LockscreenSceneBlueprint>>, - private val keyguardBlueprintViewBinder: KeyguardBlueprintViewBinder, private val clockInteractor: KeyguardClockInteractor, private val keyguardViewMediator: KeyguardViewMediator, ) : CoreStartable { @@ -150,7 +149,7 @@ constructor( cs.connect(composeView.id, BOTTOM, PARENT_ID, BOTTOM) keyguardRootView.addView(composeView) } else { - keyguardBlueprintViewBinder.bind( + KeyguardBlueprintViewBinder.bind( keyguardRootView, keyguardBlueprintViewModel, keyguardClockViewModel, @@ -197,12 +196,14 @@ constructor( KeyguardRootViewBinder.bind( keyguardRootView, keyguardRootViewModel, + keyguardBlueprintViewModel, configuration, occludingAppDeviceEntryMessageViewModel, chipbarCoordinator, screenOffAnimationController, shadeInteractor, clockInteractor, + keyguardClockViewModel, interactionJankMonitor, deviceEntryHapticsInteractor, vibratorHelper, diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt index 9b07675f672c..756c6c20e58d 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt @@ -57,7 +57,7 @@ constructor( @Background private val scope: CoroutineScope, @Background bgDispatcher: CoroutineDispatcher, @Main mainDispatcher: CoroutineDispatcher, - private val keyguardInteractor: KeyguardInteractor, + keyguardInteractor: KeyguardInteractor, private val communalInteractor: CommunalInteractor, powerInteractor: PowerInteractor, keyguardOcclusionInteractor: KeyguardOcclusionInteractor, @@ -70,6 +70,7 @@ constructor( bgDispatcher = bgDispatcher, powerInteractor = powerInteractor, keyguardOcclusionInteractor = keyguardOcclusionInteractor, + keyguardInteractor = keyguardInteractor, ) { override fun start() { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt index 01109af79c1d..2a9ee9fb8779 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt @@ -48,7 +48,7 @@ constructor( @Background private val scope: CoroutineScope, @Background bgDispatcher: CoroutineDispatcher, @Main mainDispatcher: CoroutineDispatcher, - private val keyguardInteractor: KeyguardInteractor, + keyguardInteractor: KeyguardInteractor, powerInteractor: PowerInteractor, keyguardOcclusionInteractor: KeyguardOcclusionInteractor, val deviceEntryRepository: DeviceEntryRepository, @@ -60,6 +60,7 @@ constructor( bgDispatcher = bgDispatcher, powerInteractor = powerInteractor, keyguardOcclusionInteractor = keyguardOcclusionInteractor, + keyguardInteractor = keyguardInteractor, ) { override fun start() { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt index 7d3de306d621..f5e98f1fedfe 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt @@ -47,7 +47,7 @@ constructor( @Background private val scope: CoroutineScope, @Background bgDispatcher: CoroutineDispatcher, @Main mainDispatcher: CoroutineDispatcher, - private val keyguardInteractor: KeyguardInteractor, + keyguardInteractor: KeyguardInteractor, powerInteractor: PowerInteractor, private val communalInteractor: CommunalInteractor, keyguardOcclusionInteractor: KeyguardOcclusionInteractor, @@ -60,6 +60,7 @@ constructor( bgDispatcher = bgDispatcher, powerInteractor = powerInteractor, keyguardOcclusionInteractor = keyguardOcclusionInteractor, + keyguardInteractor = keyguardInteractor, ) { override fun start() { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingLockscreenHostedTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingLockscreenHostedTransitionInteractor.kt index 63294f7609a2..47aa02a0be52 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingLockscreenHostedTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingLockscreenHostedTransitionInteractor.kt @@ -45,7 +45,7 @@ constructor( @Background private val scope: CoroutineScope, @Background bgDispatcher: CoroutineDispatcher, @Main mainDispatcher: CoroutineDispatcher, - private val keyguardInteractor: KeyguardInteractor, + keyguardInteractor: KeyguardInteractor, powerInteractor: PowerInteractor, keyguardOcclusionInteractor: KeyguardOcclusionInteractor, ) : @@ -56,6 +56,7 @@ constructor( bgDispatcher = bgDispatcher, powerInteractor = powerInteractor, keyguardOcclusionInteractor = keyguardOcclusionInteractor, + keyguardInteractor = keyguardInteractor, ) { override fun start() { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt index 7961b45830d4..25c3b0d395c0 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt @@ -51,7 +51,7 @@ constructor( @Background private val scope: CoroutineScope, @Background bgDispatcher: CoroutineDispatcher, @Main mainDispatcher: CoroutineDispatcher, - private val keyguardInteractor: KeyguardInteractor, + keyguardInteractor: KeyguardInteractor, private val glanceableHubTransitions: GlanceableHubTransitions, powerInteractor: PowerInteractor, keyguardOcclusionInteractor: KeyguardOcclusionInteractor, @@ -63,6 +63,7 @@ constructor( bgDispatcher = bgDispatcher, powerInteractor = powerInteractor, keyguardOcclusionInteractor = keyguardOcclusionInteractor, + keyguardInteractor = keyguardInteractor, ) { override fun start() { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt index ca6ab3ef52d8..e516fa3c44bb 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt @@ -48,7 +48,7 @@ constructor( @Main mainDispatcher: CoroutineDispatcher, @Background bgDispatcher: CoroutineDispatcher, private val glanceableHubTransitions: GlanceableHubTransitions, - private val keyguardInteractor: KeyguardInteractor, + keyguardInteractor: KeyguardInteractor, override val transitionRepository: KeyguardTransitionRepository, transitionInteractor: KeyguardTransitionInteractor, powerInteractor: PowerInteractor, @@ -61,6 +61,7 @@ constructor( bgDispatcher = bgDispatcher, powerInteractor = powerInteractor, keyguardOcclusionInteractor = keyguardOcclusionInteractor, + keyguardInteractor = keyguardInteractor, ) { override fun start() { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt index 8ca29c80c2e9..a540d761c38f 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt @@ -49,7 +49,7 @@ constructor( @Background private val scope: CoroutineScope, @Background bgDispatcher: CoroutineDispatcher, @Main mainDispatcher: CoroutineDispatcher, - private val keyguardInteractor: KeyguardInteractor, + keyguardInteractor: KeyguardInteractor, powerInteractor: PowerInteractor, private val communalInteractor: CommunalInteractor, keyguardOcclusionInteractor: KeyguardOcclusionInteractor, @@ -64,6 +64,7 @@ constructor( bgDispatcher = bgDispatcher, powerInteractor = powerInteractor, keyguardOcclusionInteractor = keyguardOcclusionInteractor, + keyguardInteractor = keyguardInteractor, ) { override fun start() { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt index f1e98f3bbe6d..8cab3cd35dcf 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt @@ -58,7 +58,7 @@ constructor( @Background private val scope: CoroutineScope, @Background bgDispatcher: CoroutineDispatcher, @Main mainDispatcher: CoroutineDispatcher, - private val keyguardInteractor: KeyguardInteractor, + keyguardInteractor: KeyguardInteractor, private val flags: FeatureFlags, private val shadeRepository: ShadeRepository, powerInteractor: PowerInteractor, @@ -73,6 +73,7 @@ constructor( bgDispatcher = bgDispatcher, powerInteractor = powerInteractor, keyguardOcclusionInteractor = keyguardOcclusionInteractor, + keyguardInteractor = keyguardInteractor, ) { override fun start() { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt index 2603aab2781b..86d4cfb916ed 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt @@ -45,7 +45,7 @@ constructor( @Background private val scope: CoroutineScope, @Background bgDispatcher: CoroutineDispatcher, @Main mainDispatcher: CoroutineDispatcher, - private val keyguardInteractor: KeyguardInteractor, + keyguardInteractor: KeyguardInteractor, powerInteractor: PowerInteractor, private val communalInteractor: CommunalInteractor, keyguardOcclusionInteractor: KeyguardOcclusionInteractor, @@ -57,6 +57,7 @@ constructor( bgDispatcher = bgDispatcher, powerInteractor = powerInteractor, keyguardOcclusionInteractor = keyguardOcclusionInteractor, + keyguardInteractor = keyguardInteractor, ) { override fun start() { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt index 76a822369b0c..19b2b81c4b27 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt @@ -52,7 +52,7 @@ constructor( @Background private val scope: CoroutineScope, @Background bgDispatcher: CoroutineDispatcher, @Main mainDispatcher: CoroutineDispatcher, - private val keyguardInteractor: KeyguardInteractor, + keyguardInteractor: KeyguardInteractor, private val communalInteractor: CommunalInteractor, private val flags: FeatureFlags, private val keyguardSecurityModel: KeyguardSecurityModel, @@ -67,6 +67,7 @@ constructor( bgDispatcher = bgDispatcher, powerInteractor = powerInteractor, keyguardOcclusionInteractor = keyguardOcclusionInteractor, + keyguardInteractor = keyguardInteractor, ) { override fun start() { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt index ccce3bf1397c..8ffa4bb8e4fb 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt @@ -105,33 +105,35 @@ constructor( } return combine( - quickAffordanceAlwaysVisible(position), - keyguardInteractor.isDozing, - if (SceneContainerFlag.isEnabled) { - sceneInteractor - .get() - .transitionState - .map { - when (it) { - is ObservableTransitionState.Idle -> - it.currentScene == Scenes.Lockscreen - is ObservableTransitionState.Transition -> - it.fromScene == Scenes.Lockscreen || it.toScene == Scenes.Lockscreen + quickAffordanceAlwaysVisible(position), + keyguardInteractor.isDozing, + if (SceneContainerFlag.isEnabled) { + sceneInteractor + .get() + .transitionState + .map { + when (it) { + is ObservableTransitionState.Idle -> + it.currentScene == Scenes.Lockscreen + is ObservableTransitionState.Transition -> + it.fromScene == Scenes.Lockscreen || + it.toScene == Scenes.Lockscreen + } } - } - .distinctUntilChanged() - } else { - keyguardInteractor.isKeyguardShowing - }, - shadeInteractor.anyExpansion.map { it < 1.0f }.distinctUntilChanged(), - biometricSettingsRepository.isCurrentUserInLockdown, - ) { affordance, isDozing, isKeyguardShowing, isQuickSettingsVisible, isUserInLockdown -> - if (!isDozing && isKeyguardShowing && isQuickSettingsVisible && !isUserInLockdown) { - affordance - } else { - KeyguardQuickAffordanceModel.Hidden + .distinctUntilChanged() + } else { + keyguardInteractor.isKeyguardShowing + }, + shadeInteractor.anyExpansion.map { it < 1.0f }.distinctUntilChanged(), + biometricSettingsRepository.isCurrentUserInLockdown, + ) { affordance, isDozing, isKeyguardShowing, isQuickSettingsVisible, isUserInLockdown -> + if (!isDozing && isKeyguardShowing && isQuickSettingsVisible && !isUserInLockdown) { + affordance + } else { + KeyguardQuickAffordanceModel.Hidden + } } - } + .distinctUntilChanged() } /** diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt index 323ceef06a97..e14820714c9b 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt @@ -53,6 +53,7 @@ sealed class TransitionInteractor( val bgDispatcher: CoroutineDispatcher, val powerInteractor: PowerInteractor, val keyguardOcclusionInteractor: KeyguardOcclusionInteractor, + val keyguardInteractor: KeyguardInteractor, ) { val name = this::class.simpleName ?: "UnknownTransitionInteractor" abstract val transitionRepository: KeyguardTransitionRepository @@ -164,14 +165,10 @@ sealed class TransitionInteractor( @Deprecated("Will be merged into maybeStartTransitionToOccludedOrInsecureCamera") suspend fun maybeHandleInsecurePowerGesture(): Boolean { if (keyguardOcclusionInteractor.shouldTransitionFromPowerButtonGesture()) { - if (transitionInteractor.getCurrentState() == KeyguardState.GONE) { - // If the current state is GONE when the launch gesture is triggered, it means we - // were in transition from GONE -> DOZING/AOD due to the first power button tap. The - // second tap indicates that the user's intent was actually to launch the unlocked - // (insecure) camera, so we should transition back to GONE. + if (keyguardInteractor.isKeyguardDismissible.value) { startTransitionTo( KeyguardState.GONE, - ownerReason = "Power button gesture while GONE" + ownerReason = "Power button gesture while keyguard is dismissible" ) return true diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt index 816033561e94..bec8f3da9999 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt @@ -17,17 +17,12 @@ package com.android.systemui.keyguard.ui.binder -import android.os.Handler -import android.transition.Transition -import android.transition.TransitionManager import android.util.Log import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintSet import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle import com.android.app.tracing.coroutines.launch -import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.keyguard.KeyguardBottomAreaRefactor import com.android.systemui.keyguard.shared.model.KeyguardBlueprint import com.android.systemui.keyguard.ui.view.layout.blueprints.transitions.BaseBlueprintTransition @@ -40,47 +35,9 @@ import com.android.systemui.lifecycle.repeatWhenAttached import com.android.systemui.res.R import com.android.systemui.shared.R as sharedR import com.android.systemui.util.kotlin.pairwise -import javax.inject.Inject -import kotlin.math.max - -@SysUISingleton -class KeyguardBlueprintViewBinder -@Inject -constructor( - @Main private val handler: Handler, -) { - private var runningPriority = -1 - private val runningTransitions = mutableSetOf<Transition>() - private val isTransitionRunning: Boolean - get() = runningTransitions.size > 0 - private val transitionListener = - object : Transition.TransitionListener { - override fun onTransitionCancel(transition: Transition) { - if (DEBUG) Log.e(TAG, "onTransitionCancel: ${transition::class.simpleName}") - runningTransitions.remove(transition) - } - - override fun onTransitionEnd(transition: Transition) { - if (DEBUG) Log.e(TAG, "onTransitionEnd: ${transition::class.simpleName}") - runningTransitions.remove(transition) - } - - override fun onTransitionPause(transition: Transition) { - if (DEBUG) Log.i(TAG, "onTransitionPause: ${transition::class.simpleName}") - runningTransitions.remove(transition) - } - - override fun onTransitionResume(transition: Transition) { - if (DEBUG) Log.i(TAG, "onTransitionResume: ${transition::class.simpleName}") - runningTransitions.add(transition) - } - - override fun onTransitionStart(transition: Transition) { - if (DEBUG) Log.i(TAG, "onTransitionStart: ${transition::class.simpleName}") - runningTransitions.add(transition) - } - } +object KeyguardBlueprintViewBinder { + @JvmStatic fun bind( constraintLayout: ConstraintLayout, viewModel: KeyguardBlueprintViewModel, @@ -118,7 +75,7 @@ constructor( ) } - runTransition(constraintLayout, transition, config) { + viewModel.runTransition(constraintLayout, transition, config) { // Replace sections from the previous blueprint with the new ones blueprint.replaceViews( constraintLayout, @@ -146,7 +103,7 @@ constructor( viewModel.refreshTransition.collect { config -> val blueprint = viewModel.blueprint.value - runTransition( + viewModel.runTransition( constraintLayout, IntraBlueprintTransition(config, clockViewModel, smartspaceViewModel), config, @@ -167,50 +124,6 @@ constructor( } } - private fun runTransition( - constraintLayout: ConstraintLayout, - transition: Transition, - config: Config, - apply: () -> Unit, - ) { - val currentPriority = if (isTransitionRunning) runningPriority else -1 - if (config.checkPriority && config.type.priority < currentPriority) { - if (DEBUG) { - Log.w( - TAG, - "runTransition: skipping ${transition::class.simpleName}: " + - "currentPriority=$currentPriority; config=$config" - ) - } - apply() - return - } - - if (DEBUG) { - Log.i( - TAG, - "runTransition: running ${transition::class.simpleName}: " + - "currentPriority=$currentPriority; config=$config" - ) - } - - // beginDelayedTransition makes a copy, so we temporarially add the uncopied transition to - // the running set until the copy is started by the handler. - runningTransitions.add(transition) - transition.addListener(transitionListener) - runningPriority = max(currentPriority, config.type.priority) - - handler.post { - if (config.terminatePrevious) { - TransitionManager.endTransitions(constraintLayout) - } - - TransitionManager.beginDelayedTransition(constraintLayout, transition) - runningTransitions.remove(transition) - apply() - } - } - private fun logAlphaVisibilityOfAppliedConstraintSet( cs: ConstraintSet, viewModel: KeyguardClockViewModel @@ -237,8 +150,6 @@ constructor( ) } - companion object { - private const val TAG = "KeyguardBlueprintViewBinder" - private const val DEBUG = false - } + private const val TAG = "KeyguardBlueprintViewBinder" + private const val DEBUG = false } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardIndicationAreaBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardIndicationAreaBinder.kt index 23c2491813f7..807c322cc566 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardIndicationAreaBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardIndicationAreaBinder.kt @@ -65,7 +65,7 @@ object KeyguardIndicationAreaBinder { val configurationBasedDimensions = MutableStateFlow(loadFromResources(view)) val disposableHandle = view.repeatWhenAttached { - repeatOnLifecycle(Lifecycle.State.STARTED) { + repeatOnLifecycle(Lifecycle.State.CREATED) { launch("$TAG#viewModel.alpha") { // Do not independently apply alpha, as [KeyguardRootViewModel] should work // for this and all its children diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt index b9a79dccf76b..1cf009d13e9b 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt @@ -30,6 +30,7 @@ import androidx.core.view.isVisible import androidx.core.view.updateLayoutParams import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle +import com.android.app.tracing.coroutines.launch import com.android.settingslib.Utils import com.android.systemui.animation.Expandable import com.android.systemui.animation.view.LaunchableImageView @@ -80,8 +81,8 @@ object KeyguardQuickAffordanceViewBinder { val configurationBasedDimensions = MutableStateFlow(loadFromResources(view)) val disposableHandle = view.repeatWhenAttached { - repeatOnLifecycle(Lifecycle.State.STARTED) { - launch { + repeatOnLifecycle(Lifecycle.State.CREATED) { + launch("$TAG#viewModel.collect") { viewModel.collect { buttonModel -> updateButton( view = button, @@ -93,7 +94,7 @@ object KeyguardQuickAffordanceViewBinder { } } - launch { + launch("$TAG#updateButtonAlpha") { updateButtonAlpha( view = button, viewModel = viewModel, @@ -101,7 +102,7 @@ object KeyguardQuickAffordanceViewBinder { ) } - launch { + launch("$TAG#configurationBasedDimensions") { configurationBasedDimensions.collect { dimensions -> button.updateLayoutParams<ViewGroup.LayoutParams> { width = dimensions.buttonSizePx.width @@ -323,4 +324,6 @@ object KeyguardQuickAffordanceViewBinder { private data class ConfigurationBasedDimensions( val buttonSizePx: Size, ) + + private const val TAG = "KeyguardQuickAffordanceViewBinder" } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt index 39db22d5616d..fc92afe17eff 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt @@ -22,6 +22,7 @@ import android.annotation.DrawableRes import android.annotation.SuppressLint import android.graphics.Point import android.graphics.Rect +import android.util.Log import android.view.HapticFeedbackConstants import android.view.View import android.view.View.OnLayoutChangeListener @@ -56,8 +57,11 @@ import com.android.systemui.keyguard.shared.ComposeLockscreen import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.TransitionState import com.android.systemui.keyguard.ui.viewmodel.BurnInParameters +import com.android.systemui.keyguard.ui.viewmodel.KeyguardBlueprintViewModel +import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel import com.android.systemui.keyguard.ui.viewmodel.OccludingAppDeviceEntryMessageViewModel +import com.android.systemui.keyguard.ui.viewmodel.TransitionData import com.android.systemui.keyguard.ui.viewmodel.ViewStateAccessor import com.android.systemui.lifecycle.repeatWhenAttached import com.android.systemui.plugins.FalsingManager @@ -93,12 +97,14 @@ object KeyguardRootViewBinder { fun bind( view: ViewGroup, viewModel: KeyguardRootViewModel, + blueprintViewModel: KeyguardBlueprintViewModel, configuration: ConfigurationState, occludingAppDeviceEntryMessageViewModel: OccludingAppDeviceEntryMessageViewModel?, chipbarCoordinator: ChipbarCoordinator?, screenOffAnimationController: ScreenOffAnimationController, shadeInteractor: ShadeInteractor, clockInteractor: KeyguardClockInteractor, + clockViewModel: KeyguardClockViewModel, interactionJankMonitor: InteractionJankMonitor?, deviceEntryHapticsInteractor: DeviceEntryHapticsInteractor?, vibratorHelper: VibratorHelper?, @@ -348,7 +354,16 @@ object KeyguardRootViewBinder { } } - disposables += view.onLayoutChanged(OnLayoutChange(viewModel, childViews, burnInParams)) + disposables += + view.onLayoutChanged( + OnLayoutChange( + viewModel, + blueprintViewModel, + clockViewModel, + childViews, + burnInParams + ) + ) // Views will be added or removed after the call to bind(). This is needed to avoid many // calls to findViewById @@ -404,9 +419,13 @@ object KeyguardRootViewBinder { private class OnLayoutChange( private val viewModel: KeyguardRootViewModel, + private val blueprintViewModel: KeyguardBlueprintViewModel, + private val clockViewModel: KeyguardClockViewModel, private val childViews: Map<Int, View>, private val burnInParams: MutableStateFlow<BurnInParameters>, ) : OnLayoutChangeListener { + var prevTransition: TransitionData? = null + override fun onLayoutChange( view: View, left: Int, @@ -418,11 +437,21 @@ object KeyguardRootViewBinder { oldRight: Int, oldBottom: Int ) { + // After layout, ensure the notifications are positioned correctly childViews[nsslPlaceholderId]?.let { notificationListPlaceholder -> - // After layout, ensure the notifications are positioned correctly + // Do not update a second time while a blueprint transition is running + val transition = blueprintViewModel.currentTransition.value + val shouldAnimate = transition != null && transition.config.type.animateNotifChanges + if (prevTransition == transition && shouldAnimate) { + if (DEBUG) Log.w(TAG, "Skipping; layout during transition") + return + } + + prevTransition = transition viewModel.onNotificationContainerBoundsChanged( notificationListPlaceholder.top.toFloat(), notificationListPlaceholder.bottom.toFloat(), + animate = shouldAnimate ) } @@ -585,4 +614,6 @@ object KeyguardRootViewBinder { private const val ID = "occluding_app_device_entry_unlock_msg" private const val AOD_ICONS_APPEAR_DURATION: Long = 200 + private const val TAG = "KeyguardRootViewBinder" + private const val DEBUG = false } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt index fb1853f13ce9..777c873e47f0 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt @@ -68,7 +68,9 @@ import com.android.systemui.keyguard.ui.binder.KeyguardQuickAffordanceViewBinder import com.android.systemui.keyguard.ui.binder.KeyguardRootViewBinder import com.android.systemui.keyguard.ui.view.KeyguardRootView import com.android.systemui.keyguard.ui.view.layout.sections.DefaultShortcutsSection +import com.android.systemui.keyguard.ui.viewmodel.KeyguardBlueprintViewModel import com.android.systemui.keyguard.ui.viewmodel.KeyguardBottomAreaViewModel +import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel import com.android.systemui.keyguard.ui.viewmodel.KeyguardPreviewClockViewModel import com.android.systemui.keyguard.ui.viewmodel.KeyguardPreviewSmartspaceViewModel import com.android.systemui.keyguard.ui.viewmodel.KeyguardQuickAffordancesCombinedViewModel @@ -134,6 +136,7 @@ constructor( private val vibratorHelper: VibratorHelper, private val indicationController: KeyguardIndicationController, private val keyguardRootViewModel: KeyguardRootViewModel, + private val keyguardBlueprintViewModel: KeyguardBlueprintViewModel, @Assisted bundle: Bundle, private val occludingAppDeviceEntryMessageViewModel: OccludingAppDeviceEntryMessageViewModel, private val chipbarCoordinator: ChipbarCoordinator, @@ -143,6 +146,7 @@ constructor( private val communalTutorialViewModel: CommunalTutorialIndicatorViewModel, private val defaultShortcutsSection: DefaultShortcutsSection, private val keyguardClockInteractor: KeyguardClockInteractor, + private val keyguardClockViewModel: KeyguardClockViewModel, ) { val hostToken: IBinder? = bundle.getBinder(KEY_HOST_TOKEN) private val width: Int = bundle.getInt(KEY_VIEW_WIDTH) @@ -379,12 +383,14 @@ constructor( KeyguardRootViewBinder.bind( keyguardRootView, keyguardRootViewModel, + keyguardBlueprintViewModel, configuration, occludingAppDeviceEntryMessageViewModel, chipbarCoordinator, screenOffAnimationController, shadeInteractor, keyguardClockInteractor, + keyguardClockViewModel, null, // jank monitor not required for preview mode null, // device entry haptics not required preview mode null, // device entry haptics not required for preview mode diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/transitions/IntraBlueprintTransition.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/transitions/IntraBlueprintTransition.kt index 02e9ca5b6821..39f1ebe25299 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/transitions/IntraBlueprintTransition.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/transitions/IntraBlueprintTransition.kt @@ -31,16 +31,16 @@ class IntraBlueprintTransition( enum class Type( val priority: Int, + val animateNotifChanges: Boolean, ) { - ClockSize(100), - ClockCenter(99), - DefaultClockStepping(98), - AodNotifIconsTransition(97), - SmartspaceVisibility(2), - DefaultTransition(1), + ClockSize(100, true), + ClockCenter(99, false), + DefaultClockStepping(98, false), + SmartspaceVisibility(2, true), + DefaultTransition(1, false), // When transition between blueprint, we don't need any duration or interpolator but we need // all elements go to correct state - NoTransition(0), + NoTransition(0, false), } data class Config( diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodNotificationIconsSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodNotificationIconsSection.kt index 2832e9d8a35e..d77b54825664 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodNotificationIconsSection.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodNotificationIconsSection.kt @@ -19,6 +19,8 @@ package com.android.systemui.keyguard.ui.view.layout.sections import android.content.Context import android.view.View +import android.view.View.GONE +import android.view.View.VISIBLE import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintSet import androidx.constraintlayout.widget.ConstraintSet.BOTTOM @@ -29,6 +31,7 @@ import androidx.constraintlayout.widget.ConstraintSet.TOP import com.android.systemui.common.ui.ConfigurationState import com.android.systemui.keyguard.MigrateClocksToBlueprint import com.android.systemui.keyguard.shared.model.KeyguardSection +import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel import com.android.systemui.res.R import com.android.systemui.statusbar.notification.icon.ui.viewbinder.AlwaysOnDisplayNotificationIconViewStore import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerViewBinder @@ -38,6 +41,7 @@ import com.android.systemui.statusbar.notification.shared.NotificationIconContai import com.android.systemui.statusbar.phone.NotificationIconAreaController import com.android.systemui.statusbar.phone.NotificationIconContainer import com.android.systemui.statusbar.ui.SystemBarUtilsState +import com.android.systemui.util.ui.value import javax.inject.Inject import kotlinx.coroutines.DisposableHandle @@ -51,6 +55,7 @@ constructor( private val nicAodIconViewStore: AlwaysOnDisplayNotificationIconViewStore, private val notificationIconAreaController: NotificationIconAreaController, private val systemBarUtilsState: SystemBarUtilsState, + private val rootViewModel: KeyguardRootViewModel, ) : KeyguardSection() { private var nicBindingDisposable: DisposableHandle? = null @@ -101,20 +106,14 @@ constructor( if (!MigrateClocksToBlueprint.isEnabled) { return } + val bottomMargin = context.resources.getDimensionPixelSize(R.dimen.keyguard_status_view_bottom_margin) - - val useSplitShade = context.resources.getBoolean(R.bool.config_use_split_notification_shade) - - val topAlignment = - if (useSplitShade) { - TOP - } else { - BOTTOM - } + val isVisible = rootViewModel.isNotifIconContainerVisible.value constraintSet.apply { connect(nicId, TOP, R.id.smart_space_barrier_bottom, BOTTOM, bottomMargin) setGoneMargin(nicId, BOTTOM, bottomMargin) + setVisibility(nicId, if (isVisible.value) VISIBLE else GONE) connect( nicId, diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/ClockSizeTransition.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/ClockSizeTransition.kt index 7c745bc227f0..f17dbd24cf25 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/ClockSizeTransition.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/ClockSizeTransition.kt @@ -369,6 +369,21 @@ class ClockSizeTransition( addTarget(R.id.status_view_media_container) } + override fun mutateBounds( + view: View, + fromIsVis: Boolean, + toIsVis: Boolean, + fromBounds: Rect, + toBounds: Rect, + fromSSBounds: Rect?, + toSSBounds: Rect? + ) { + // If view is changing visibility, hold it in place + if (fromIsVis == toIsVis) return + if (DEBUG) Log.i(TAG, "Holding position of ${view.id}") + toBounds.set(fromBounds) + } + companion object { const val STATUS_AREA_MOVE_UP_MILLIS = 967L const val STATUS_AREA_MOVE_DOWN_MILLIS = 467L diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBlueprintViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBlueprintViewModel.kt index b1f189836903..7ac03bffd4f3 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBlueprintViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBlueprintViewModel.kt @@ -17,15 +17,119 @@ package com.android.systemui.keyguard.ui.viewmodel +import android.os.Handler +import android.transition.Transition +import android.transition.TransitionManager +import android.util.Log +import androidx.constraintlayout.widget.ConstraintLayout +import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor +import com.android.systemui.keyguard.ui.view.layout.blueprints.transitions.IntraBlueprintTransition.Config import javax.inject.Inject +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow + +data class TransitionData( + val config: Config, + val start: Long = System.currentTimeMillis(), +) class KeyguardBlueprintViewModel @Inject constructor( + @Main private val handler: Handler, keyguardBlueprintInteractor: KeyguardBlueprintInteractor, ) { val blueprint = keyguardBlueprintInteractor.blueprint val blueprintId = keyguardBlueprintInteractor.blueprintId val refreshTransition = keyguardBlueprintInteractor.refreshTransition + + private val _currentTransition = MutableStateFlow<TransitionData?>(null) + val currentTransition = _currentTransition.asStateFlow() + + private val runningTransitions = mutableSetOf<Transition>() + private val transitionListener = + object : Transition.TransitionListener { + override fun onTransitionCancel(transition: Transition) { + if (DEBUG) Log.e(TAG, "onTransitionCancel: ${transition::class.simpleName}") + updateTransitions(null) { remove(transition) } + } + + override fun onTransitionEnd(transition: Transition) { + if (DEBUG) Log.e(TAG, "onTransitionEnd: ${transition::class.simpleName}") + updateTransitions(null) { remove(transition) } + } + + override fun onTransitionPause(transition: Transition) { + if (DEBUG) Log.i(TAG, "onTransitionPause: ${transition::class.simpleName}") + updateTransitions(null) { remove(transition) } + } + + override fun onTransitionResume(transition: Transition) { + if (DEBUG) Log.i(TAG, "onTransitionResume: ${transition::class.simpleName}") + updateTransitions(null) { add(transition) } + } + + override fun onTransitionStart(transition: Transition) { + if (DEBUG) Log.i(TAG, "onTransitionStart: ${transition::class.simpleName}") + updateTransitions(null) { add(transition) } + } + } + + fun updateTransitions(data: TransitionData?, mutate: MutableSet<Transition>.() -> Unit) { + runningTransitions.mutate() + + if (runningTransitions.size <= 0) _currentTransition.value = null + else if (data != null) _currentTransition.value = data + } + + fun runTransition( + constraintLayout: ConstraintLayout, + transition: Transition, + config: Config, + apply: () -> Unit, + ) { + val currentPriority = currentTransition.value?.let { it.config.type.priority } ?: -1 + if (config.checkPriority && config.type.priority < currentPriority) { + if (DEBUG) { + Log.w( + TAG, + "runTransition: skipping ${transition::class.simpleName}: " + + "currentPriority=$currentPriority; config=$config" + ) + } + apply() + return + } + + if (DEBUG) { + Log.i( + TAG, + "runTransition: running ${transition::class.simpleName}: " + + "currentPriority=$currentPriority; config=$config" + ) + } + + // beginDelayedTransition makes a copy, so we temporarially add the uncopied transition to + // the running set until the copy is started by the handler. + updateTransitions(TransitionData(config)) { add(transition) } + transition.addListener(transitionListener) + + handler.post { + if (config.terminatePrevious) { + TransitionManager.endTransitions(constraintLayout) + } + + TransitionManager.beginDelayedTransition(constraintLayout, transition) + apply() + + // Delay removal until after copied transition has started + handler.post { updateTransitions(null) { remove(transition) } } + } + } + + companion object { + private const val TAG = "KeyguardBlueprintViewModel" + private const val DEBUG = true + } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModel.kt index 8409f15dca81..448a71c36a99 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModel.kt @@ -23,9 +23,12 @@ import com.android.systemui.keyguard.MigrateClocksToBlueprint import com.android.systemui.keyguard.domain.interactor.BurnInInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardBottomAreaInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor +import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor import com.android.systemui.keyguard.shared.model.BurnInModel +import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.res.R import javax.inject.Inject +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged @@ -42,6 +45,7 @@ constructor( private val burnInInteractor: BurnInInteractor, private val shortcutsCombinedViewModel: KeyguardQuickAffordancesCombinedViewModel, configurationInteractor: ConfigurationInteractor, + keyguardTransitionInteractor: KeyguardTransitionInteractor, ) { /** Notifies when a new configuration is set */ @@ -69,12 +73,22 @@ constructor( .distinctUntilChanged() } + @OptIn(ExperimentalCoroutinesApi::class) private val burnIn: Flow<BurnInModel> = - burnInInteractor - .burnIn( - xDimenResourceId = R.dimen.burn_in_prevention_offset_x, - yDimenResourceId = R.dimen.default_burn_in_prevention_offset, - ) + combine( + burnInInteractor.burnIn( + xDimenResourceId = R.dimen.burn_in_prevention_offset_x, + yDimenResourceId = R.dimen.default_burn_in_prevention_offset, + ), + keyguardTransitionInteractor.transitionValue(KeyguardState.AOD), + ) { burnIn, aodTransitionValue -> + BurnInModel( + (burnIn.translationX * aodTransitionValue).toInt(), + (burnIn.translationY * aodTransitionValue).toInt(), + burnIn.scale, + burnIn.scaleClockOnly, + ) + } .distinctUntilChanged() /** An observable for the x-offset by which the indication area should be translated. */ diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModel.kt index c4383fc0857d..244d842b7073 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModel.kt @@ -18,6 +18,7 @@ package com.android.systemui.keyguard.ui.viewmodel import androidx.annotation.VisibleForTesting +import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardQuickAffordanceInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor @@ -28,19 +29,23 @@ import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAfforda import com.android.systemui.shade.domain.interactor.ShadeInteractor import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots import javax.inject.Inject +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.merge +import kotlinx.coroutines.flow.stateIn @OptIn(ExperimentalCoroutinesApi::class) class KeyguardQuickAffordancesCombinedViewModel @Inject constructor( + @Application applicationScope: CoroutineScope, private val quickAffordanceInteractor: KeyguardQuickAffordanceInteractor, private val keyguardInteractor: KeyguardInteractor, shadeInteractor: ShadeInteractor, @@ -84,15 +89,20 @@ constructor( /** The only time the expansion is important is while lockscreen is actively displayed */ private val shadeExpansionAlpha = combine( - showingLockscreen, - shadeInteractor.anyExpansion, - ) { showingLockscreen, expansion -> - if (showingLockscreen) { - 1 - expansion - } else { - 0f + showingLockscreen, + shadeInteractor.anyExpansion, + ) { showingLockscreen, expansion -> + if (showingLockscreen) { + 1 - expansion + } else { + 0f + } } - } + .stateIn( + scope = applicationScope, + started = SharingStarted.Lazily, + initialValue = 0f, + ) /** * ID of the slot that's currently selected in the preview that renders exclusively in the diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt index aaec69f4f022..1ec2a4969f0d 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt @@ -58,6 +58,8 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.Job import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.combineTransform @@ -67,13 +69,14 @@ import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.merge import kotlinx.coroutines.flow.onStart +import kotlinx.coroutines.flow.stateIn @OptIn(ExperimentalCoroutinesApi::class) @SysUISingleton class KeyguardRootViewModel @Inject constructor( - @Application private val scope: CoroutineScope, + @Application private val applicationScope: CoroutineScope, private val deviceEntryInteractor: DeviceEntryInteractor, private val dozeParameters: DozeParameters, private val keyguardInteractor: KeyguardInteractor, @@ -280,7 +283,7 @@ constructor( burnInJob?.cancel() burnInJob = - scope.launch("$TAG#aodBurnInViewModel") { + applicationScope.launch("$TAG#aodBurnInViewModel") { aodBurnInViewModel.movement(params).collect { _burnInModel.value = it } } } @@ -294,7 +297,7 @@ constructor( } /** Is the notification icon container visible? */ - val isNotifIconContainerVisible: Flow<AnimatedValue<Boolean>> = + val isNotifIconContainerVisible: StateFlow<AnimatedValue<Boolean>> = combine( goneToAodTransitionRunning, keyguardTransitionInteractor.finishedKeyguardState.map { @@ -336,11 +339,15 @@ constructor( } } } - .distinctUntilChanged() + .stateIn( + scope = applicationScope, + started = SharingStarted.WhileSubscribed(), + initialValue = AnimatedValue.NotAnimating(false), + ) - fun onNotificationContainerBoundsChanged(top: Float, bottom: Float) { + fun onNotificationContainerBoundsChanged(top: Float, bottom: Float, animate: Boolean = false) { keyguardInteractor.setNotificationContainerBounds( - NotificationContainerBounds(top = top, bottom = bottom) + NotificationContainerBounds(top = top, bottom = bottom, isAnimated = animate) ) } diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.kt b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.kt index deb0fed0ffc8..954e94af1c1a 100644 --- a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.kt +++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.kt @@ -26,11 +26,7 @@ import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.lifecycleScope import androidx.lifecycle.repeatOnLifecycle import com.android.compose.theme.PlatformTheme -import com.android.systemui.flags.FeatureFlags -import com.android.systemui.flags.Flags import com.android.systemui.people.ui.compose.PeopleScreen -import com.android.systemui.people.ui.view.PeopleViewBinder -import com.android.systemui.people.ui.view.PeopleViewBinder.bind import com.android.systemui.people.ui.viewmodel.PeopleViewModel import javax.inject.Inject import kotlinx.coroutines.launch @@ -38,10 +34,7 @@ import kotlinx.coroutines.launch /** People Tile Widget configuration activity that shows the user their conversation tiles. */ class PeopleSpaceActivity @Inject -constructor( - private val viewModelFactory: PeopleViewModel.Factory, - private val featureFlags: FeatureFlags, -) : ComponentActivity() { +constructor(private val viewModelFactory: PeopleViewModel.Factory) : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setResult(RESULT_CANCELED) @@ -66,17 +59,7 @@ constructor( } // Set the content of the activity, using either the View or Compose implementation. - if (featureFlags.isEnabled(Flags.COMPOSE_PEOPLE_SPACE)) { - Log.d(TAG, "Using the Compose implementation of the PeopleSpaceActivity") - setContent { - PlatformTheme { PeopleScreen(viewModel, onResult = { finishActivity(it) }) } - } - } else { - Log.d(TAG, "Using the View implementation of the PeopleSpaceActivity") - val view = PeopleViewBinder.create(this) - bind(view, viewModel, lifecycleOwner = this, onResult = { finishActivity(it) }) - setContentView(view) - } + setContent { PlatformTheme { PeopleScreen(viewModel, onResult = { finishActivity(it) }) } } } private fun finishActivity(result: PeopleViewModel.Result) { diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceTileView.java b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceTileView.java deleted file mode 100644 index 59c76adb721b..000000000000 --- a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceTileView.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.people; - -import android.app.people.PeopleSpaceTile; -import android.content.Context; -import android.content.pm.LauncherApps; -import android.graphics.Bitmap; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.ImageView; -import android.widget.LinearLayout; -import android.widget.TextView; - -import com.android.systemui.res.R; - -/** - * PeopleSpaceTileView renders an individual person's tile with associated status. - */ -public class PeopleSpaceTileView extends LinearLayout { - - private View mTileView; - private TextView mNameView; - private ImageView mPersonIconView; - - public PeopleSpaceTileView(Context context, ViewGroup view, String shortcutId, boolean isLast) { - super(context); - mTileView = view.findViewWithTag(shortcutId); - if (mTileView == null) { - LayoutInflater inflater = LayoutInflater.from(context); - mTileView = inflater.inflate(R.layout.people_space_tile_view, view, false); - view.addView(mTileView, LayoutParams.MATCH_PARENT, - LayoutParams.MATCH_PARENT); - mTileView.setTag(shortcutId); - - // If it's not the last conversation in this section, add a divider. - if (!isLast) { - inflater.inflate(R.layout.people_space_activity_list_divider, view, true); - } - } - mNameView = mTileView.findViewById(R.id.tile_view_name); - mPersonIconView = mTileView.findViewById(R.id.tile_view_person_icon); - } - - /** Sets the name text on the tile. */ - public void setName(String name) { - mNameView.setText(name); - } - - /** Sets the person and package drawable on the tile. */ - public void setPersonIcon(Bitmap bitmap) { - mPersonIconView.setImageBitmap(bitmap); - } - - /** Sets the click listener of the tile. */ - public void setOnClickListener(LauncherApps launcherApps, PeopleSpaceTile tile) { - mTileView.setOnClickListener(v -> - launcherApps.startShortcut(tile.getPackageName(), tile.getId(), null, null, - tile.getUserHandle())); - } - - /** Sets the click listener of the tile directly. */ - public void setOnClickListener(OnClickListener onClickListener) { - mTileView.setOnClickListener(onClickListener); - } -} diff --git a/packages/SystemUI/src/com/android/systemui/people/ui/view/PeopleViewBinder.kt b/packages/SystemUI/src/com/android/systemui/people/ui/view/PeopleViewBinder.kt deleted file mode 100644 index 10a2b3ce7b85..000000000000 --- a/packages/SystemUI/src/com/android/systemui/people/ui/view/PeopleViewBinder.kt +++ /dev/null @@ -1,239 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.people.ui.view - -import android.content.Context -import android.graphics.Color -import android.graphics.Outline -import android.graphics.drawable.GradientDrawable -import android.util.Log -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import android.view.ViewOutlineProvider -import android.widget.LinearLayout -import androidx.lifecycle.Lifecycle -import androidx.lifecycle.Lifecycle.State.CREATED -import androidx.lifecycle.LifecycleOwner -import androidx.lifecycle.lifecycleScope -import androidx.lifecycle.repeatOnLifecycle -import com.android.systemui.people.PeopleSpaceTileView -import com.android.systemui.people.ui.viewmodel.PeopleTileViewModel -import com.android.systemui.people.ui.viewmodel.PeopleViewModel -import com.android.systemui.res.R -import kotlinx.coroutines.flow.collect -import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.launch - -/** A ViewBinder for [PeopleViewModel]. */ -object PeopleViewBinder { - private const val TAG = "PeopleViewBinder" - - /** - * The [ViewOutlineProvider] used to clip the corner radius of the recent and priority lists. - */ - private val ViewOutlineProvider = - object : ViewOutlineProvider() { - override fun getOutline(view: View, outline: Outline) { - outline.setRoundRect( - 0, - 0, - view.width, - view.height, - view.context.resources.getDimension(R.dimen.people_space_widget_radius), - ) - } - } - - /** Create a [View] that can later be [bound][bind] to a [PeopleViewModel]. */ - @JvmStatic - fun create(context: Context): ViewGroup { - return LayoutInflater.from(context) - .inflate(R.layout.people_space_activity, /* root= */ null) as ViewGroup - } - - /** Bind [view] to [viewModel]. */ - @JvmStatic - fun bind( - view: ViewGroup, - viewModel: PeopleViewModel, - lifecycleOwner: LifecycleOwner, - onResult: (PeopleViewModel.Result) -> Unit, - ) { - // Call [onResult] as soon as a result is available. - lifecycleOwner.lifecycleScope.launch { - lifecycleOwner.repeatOnLifecycle(CREATED) { - viewModel.result.collect { result -> - if (result != null) { - viewModel.clearResult() - onResult(result) - } - } - } - } - - // Start collecting the UI data once the Activity is STARTED. - lifecycleOwner.lifecycleScope.launch { - lifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) { - combine( - viewModel.priorityTiles, - viewModel.recentTiles, - ) { priority, recent -> - priority to recent - } - .collect { (priorityTiles, recentTiles) -> - if (priorityTiles.isNotEmpty() || recentTiles.isNotEmpty()) { - setConversationsContent( - view, - priorityTiles, - recentTiles, - viewModel.onTileClicked, - ) - } else { - setNoConversationsContent(view, viewModel.onUserJourneyCancelled) - } - } - } - } - } - - private fun setNoConversationsContent(view: ViewGroup, onGotItClicked: () -> Unit) { - // This should never happen. - if (view.childCount > 1) { - error("view has ${view.childCount} children, it should have maximum 1") - } - - // The static content for no conversations is already shown. - if (view.findViewById<View>(R.id.top_level_no_conversations) != null) { - return - } - - // If we were showing the content with conversations earlier, remove it. - if (view.childCount == 1) { - view.removeViewAt(0) - } - - val context = view.context - val noConversationsView = - LayoutInflater.from(context) - .inflate(R.layout.people_space_activity_no_conversations, /* root= */ view) - - noConversationsView.requireViewById<View>(R.id.got_it_button).setOnClickListener { - onGotItClicked() - } - - // The Tile preview has colorBackground as its background. Change it so it's different than - // the activity's background. - val item = noConversationsView.requireViewById<LinearLayout>(android.R.id.background) - val shape = item.background as GradientDrawable - val ta = - context.theme.obtainStyledAttributes( - intArrayOf(com.android.internal.R.attr.colorSurface) - ) - shape.setColor(ta.getColor(0, Color.WHITE)) - ta.recycle() - } - - private fun setConversationsContent( - view: ViewGroup, - priorityTiles: List<PeopleTileViewModel>, - recentTiles: List<PeopleTileViewModel>, - onTileClicked: (PeopleTileViewModel) -> Unit, - ) { - // This should never happen. - if (view.childCount > 1) { - error("view has ${view.childCount} children, it should have maximum 1") - } - - // Inflate the content with conversations, if it's not already. - if (view.findViewById<View>(R.id.top_level_with_conversations) == null) { - // If we were showing the content without conversations earlier, remove it. - if (view.childCount == 1) { - view.removeViewAt(0) - } - - LayoutInflater.from(view.context) - .inflate(R.layout.people_space_activity_with_conversations, /* root= */ view) - } - - // TODO(b/193782241): Replace the NestedScrollView + 2x LinearLayout from this layout into a - // single RecyclerView once this screen is tested by screenshot tests. Introduce a - // PeopleSpaceTileViewBinder that will properly create and bind the View associated to a - // PeopleSpaceTileViewModel (and remove the PeopleSpaceTileView class). - val conversationsView = view.requireViewById<View>(R.id.top_level_with_conversations) - setTileViews( - conversationsView, - R.id.priority, - R.id.priority_tiles, - priorityTiles, - onTileClicked, - ) - - setTileViews( - conversationsView, - R.id.recent, - R.id.recent_tiles, - recentTiles, - onTileClicked, - ) - } - - /** Sets a [PeopleSpaceTileView]s for each conversation. */ - private fun setTileViews( - root: View, - tilesListId: Int, - tilesId: Int, - tiles: List<PeopleTileViewModel>, - onTileClicked: (PeopleTileViewModel) -> Unit, - ) { - // Remove any previously added tile. - // TODO(b/193782241): Once this list is a big RecyclerView, set the current list and use - // DiffUtil to do as less addView/removeView as possible. - val layout = root.requireViewById<ViewGroup>(tilesId) - layout.removeAllViews() - layout.outlineProvider = ViewOutlineProvider - - val tilesListView = root.requireViewById<LinearLayout>(tilesListId) - if (tiles.isEmpty()) { - tilesListView.visibility = View.GONE - return - } - tilesListView.visibility = View.VISIBLE - - // Add each tile. - tiles.forEachIndexed { i, tile -> - val tileView = - PeopleSpaceTileView(root.context, layout, tile.key.shortcutId, i == tiles.size - 1) - bindTileView(tileView, tile, onTileClicked) - } - } - - /** Sets [tileView] with the data in [conversation]. */ - private fun bindTileView( - tileView: PeopleSpaceTileView, - tile: PeopleTileViewModel, - onTileClicked: (PeopleTileViewModel) -> Unit, - ) { - try { - tileView.setName(tile.username) - tileView.setPersonIcon(tile.icon) - tileView.setOnClickListener { onTileClicked(tile) } - } catch (e: Exception) { - Log.e(TAG, "Couldn't retrieve shortcut information", e) - } - } -} diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java index 4ee2db796aef..cc0901fca822 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java @@ -307,7 +307,7 @@ public class QSContainerImpl extends FrameLayout implements Dumpable { } else { // Set the horizontal paddings unless the view is the Compose implementation of the // footer actions. - if (view.getTag(R.id.tag_compose_qs_footer_actions) == null) { + if (view.getId() != R.id.qs_footer_actions) { view.setPaddingRelative( mContentHorizontalPadding, view.getPaddingTop(), diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSImpl.java index 1f4838e85e79..fb980d9a2d9b 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSImpl.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSImpl.java @@ -34,11 +34,11 @@ import android.util.IndentingPrintWriter; import android.util.Log; import android.view.View; import android.view.ViewGroup; -import android.widget.LinearLayout; import androidx.annotation.FloatRange; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; +import androidx.compose.ui.platform.ComposeView; import androidx.lifecycle.Lifecycle; import androidx.lifecycle.LifecycleOwner; import androidx.lifecycle.LifecycleRegistry; @@ -48,15 +48,12 @@ import com.android.keyguard.BouncerPanelExpansionCalculator; import com.android.systemui.Dumpable; import com.android.systemui.animation.ShadeInterpolation; import com.android.systemui.dump.DumpManager; -import com.android.systemui.flags.FeatureFlags; -import com.android.systemui.flags.Flags; import com.android.systemui.media.controls.ui.view.MediaHost; import com.android.systemui.plugins.qs.QS; import com.android.systemui.plugins.qs.QSContainerController; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.qs.customize.QSCustomizerController; import com.android.systemui.qs.dagger.QSComponent; -import com.android.systemui.qs.footer.ui.binder.FooterActionsViewBinder; import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel; import com.android.systemui.qs.logging.QSLogger; import com.android.systemui.res.R; @@ -117,11 +114,9 @@ public class QSImpl implements QS, CommandQueue.Callbacks, StatusBarStateControl private final MediaHost mQqsMediaHost; private final QSDisableFlagsLogger mQsDisableFlagsLogger; private final LargeScreenShadeInterpolator mLargeScreenShadeInterpolator; - private final FeatureFlags mFeatureFlags; private final QSLogger mLogger; private final FooterActionsController mFooterActionsController; private final FooterActionsViewModel.Factory mFooterActionsViewModelFactory; - private final FooterActionsViewBinder mFooterActionsViewBinder; private final ListeningAndVisibilityLifecycleOwner mListeningAndVisibilityLifecycleOwner; private boolean mShowCollapsedOnKeyguard; private boolean mLastKeyguardAndExpanded; @@ -172,7 +167,7 @@ public class QSImpl implements QS, CommandQueue.Callbacks, StatusBarStateControl private View mRootView; @Nullable - private View mFooterActionsView; + private ComposeView mFooterActionsView; @Inject public QSImpl(RemoteInputQuickSettingsDisabler remoteInputQsDisabler, @@ -184,23 +179,19 @@ public class QSImpl implements QS, CommandQueue.Callbacks, StatusBarStateControl DumpManager dumpManager, QSLogger qsLogger, FooterActionsController footerActionsController, FooterActionsViewModel.Factory footerActionsViewModelFactory, - FooterActionsViewBinder footerActionsViewBinder, - LargeScreenShadeInterpolator largeScreenShadeInterpolator, - FeatureFlags featureFlags) { + LargeScreenShadeInterpolator largeScreenShadeInterpolator) { mRemoteInputQuickSettingsDisabler = remoteInputQsDisabler; mQsMediaHost = qsMediaHost; mQqsMediaHost = qqsMediaHost; mQsDisableFlagsLogger = qsDisableFlagsLogger; mLogger = qsLogger; mLargeScreenShadeInterpolator = largeScreenShadeInterpolator; - mFeatureFlags = featureFlags; mCommandQueue = commandQueue; mBypassController = keyguardBypassController; mStatusBarStateController = statusBarStateController; mDumpManager = dumpManager; mFooterActionsController = footerActionsController; mFooterActionsViewModelFactory = footerActionsViewModelFactory; - mFooterActionsViewBinder = footerActionsViewBinder; mListeningAndVisibilityLifecycleOwner = new ListeningAndVisibilityLifecycleOwner(); if (SceneContainerFlag.isEnabled()) { mStatusBarState = StatusBarState.SHADE; @@ -294,43 +285,9 @@ public class QSImpl implements QS, CommandQueue.Callbacks, StatusBarStateControl } private void bindFooterActionsView(View root) { - LinearLayout footerActionsView = root.findViewById(R.id.qs_footer_actions); - - if (!mFeatureFlags.isEnabled(Flags.COMPOSE_QS_FOOTER_ACTIONS)) { - Log.d(TAG, "Binding the View implementation of the QS footer actions"); - mFooterActionsView = footerActionsView; - mFooterActionsViewBinder.bind(footerActionsView, mQSFooterActionsViewModel, - mListeningAndVisibilityLifecycleOwner); - return; - } - - // Compose is available, so let's use the Compose implementation of the footer actions. - Log.d(TAG, "Binding the Compose implementation of the QS footer actions"); - View composeView = QSUtils.createFooterActionsView(root.getContext(), + mFooterActionsView = root.findViewById(R.id.qs_footer_actions); + QSUtils.setFooterActionsViewContent(mFooterActionsView, mQSFooterActionsViewModel, mListeningAndVisibilityLifecycleOwner); - mFooterActionsView = composeView; - - // The id R.id.qs_footer_actions is used by QSContainerImpl to set the horizontal margin - // to all views except for qs_footer_actions, so we set it to the Compose view. - composeView.setId(R.id.qs_footer_actions); - - // Set this tag so that QSContainerImpl does not add horizontal paddings to this Compose - // implementation of the footer actions. They will be set in Compose instead so that the - // background fills the full screen width. - composeView.setTag(R.id.tag_compose_qs_footer_actions, true); - - // Set the same elevation as the View implementation, otherwise the footer actions will be - // drawn below the scroll view with QS grid and clicks won't get through on small devices - // where there isn't enough vertical space to show all the tiles and the footer actions. - composeView.setElevation( - composeView.getContext().getResources().getDimension(R.dimen.qs_panel_elevation)); - - // Replace the View by the Compose provided one. - ViewGroup parent = (ViewGroup) footerActionsView.getParent(); - ViewGroup.LayoutParams layoutParams = footerActionsView.getLayoutParams(); - int index = parent.indexOfChild(footerActionsView); - parent.removeViewAt(index); - parent.addView(composeView, index, layoutParams); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSUtils.kt b/packages/SystemUI/src/com/android/systemui/qs/QSUtils.kt index 15c3f271469d..5482e6da9a57 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSUtils.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/QSUtils.kt @@ -1,10 +1,9 @@ package com.android.systemui.qs import android.content.Context -import android.view.View +import androidx.compose.ui.platform.ComposeView import androidx.lifecycle.LifecycleOwner import com.android.compose.theme.PlatformTheme -import com.android.compose.ui.platform.DensityAwareComposeView import com.android.internal.policy.SystemBarUtils import com.android.systemui.qs.footer.ui.compose.FooterActions import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel @@ -29,13 +28,11 @@ object QSUtils { } @JvmStatic - fun createFooterActionsView( - context: Context, + fun setFooterActionsViewContent( + view: ComposeView, viewModel: FooterActionsViewModel, qsVisibilityLifecycleOwner: LifecycleOwner, - ): View { - return DensityAwareComposeView(context).apply { - setContent { PlatformTheme { FooterActions(viewModel, qsVisibilityLifecycleOwner) } } - } + ) { + view.setContent { PlatformTheme { FooterActions(viewModel, qsVisibilityLifecycleOwner) } } } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/footer/ui/binder/FooterActionsViewBinder.kt b/packages/SystemUI/src/com/android/systemui/qs/footer/ui/binder/FooterActionsViewBinder.kt deleted file mode 100644 index 0995dd4e592e..000000000000 --- a/packages/SystemUI/src/com/android/systemui/qs/footer/ui/binder/FooterActionsViewBinder.kt +++ /dev/null @@ -1,329 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.qs.footer.ui.binder - -import android.content.Context -import android.graphics.PorterDuff -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import android.widget.ImageView -import android.widget.LinearLayout -import android.widget.TextView -import androidx.core.view.isVisible -import androidx.lifecycle.Lifecycle -import androidx.lifecycle.LifecycleOwner -import androidx.lifecycle.lifecycleScope -import androidx.lifecycle.repeatOnLifecycle -import com.android.systemui.animation.Expandable -import com.android.systemui.common.ui.binder.IconViewBinder -import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.lifecycle.repeatWhenAttached -import com.android.systemui.people.ui.view.PeopleViewBinder.bind -import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsButtonViewModel -import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsForegroundServicesButtonViewModel -import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsSecurityButtonViewModel -import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel -import com.android.systemui.res.R -import javax.inject.Inject -import kotlin.math.roundToInt -import kotlinx.coroutines.launch - -/** A ViewBinder for [FooterActionsViewBinder]. */ -@SysUISingleton -class FooterActionsViewBinder @Inject constructor() { - /** Create a view that can later be [bound][bind] to a [FooterActionsViewModel]. */ - fun create(context: Context): LinearLayout { - return LayoutInflater.from(context).inflate(R.layout.footer_actions, /* root= */ null) - as LinearLayout - } - - /** Bind [view] to [viewModel]. */ - fun bind( - view: LinearLayout, - viewModel: FooterActionsViewModel, - qsVisibilityLifecycleOwner: LifecycleOwner, - ) { - view.importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_YES - - // Add the views used by this new implementation. - val context = view.context - val inflater = LayoutInflater.from(context) - - val securityHolder = TextButtonViewHolder.createAndAdd(inflater, view) - val foregroundServicesWithTextHolder = TextButtonViewHolder.createAndAdd(inflater, view) - val foregroundServicesWithNumberHolder = NumberButtonViewHolder.createAndAdd(inflater, view) - val userSwitcherHolder = IconButtonViewHolder.createAndAdd(inflater, view, isLast = false) - val settingsHolder = - IconButtonViewHolder.createAndAdd(inflater, view, isLast = viewModel.power == null) - - // Bind the static power and settings buttons. - bindButton(settingsHolder, viewModel.settings) - - if (viewModel.power != null) { - val powerHolder = IconButtonViewHolder.createAndAdd(inflater, view, isLast = true) - bindButton(powerHolder, viewModel.power) - } - - // There are 2 lifecycle scopes we are using here: - // 1) The scope created by [repeatWhenAttached] when [view] is attached, and destroyed - // when the [view] is detached. We use this as the parent scope for all our [viewModel] - // state collection, given that we don't want to do any work when [view] is detached. - // 2) The scope owned by [lifecycleOwner], which should be RESUMED only when Quick - // Settings are visible. We use this to make sure we collect UI state only when the - // View is visible. - // - // Given that we start our collection when the Quick Settings become visible, which happens - // every time the user swipes down the shade, we remember our previous UI state already - // bound to the UI to avoid binding the same values over and over for nothing. - - // TODO(b/242040009): Look into using only a single scope. - - var previousSecurity: FooterActionsSecurityButtonViewModel? = null - var previousForegroundServices: FooterActionsForegroundServicesButtonViewModel? = null - var previousUserSwitcher: FooterActionsButtonViewModel? = null - - // Listen for ViewModel updates when the View is attached. - view.repeatWhenAttached { - val attachedScope = this.lifecycleScope - - attachedScope.launch { - // Listen for dialog requests as soon as we are attached, even when not visible. - // TODO(b/242040009): Should this move somewhere else? - launch { viewModel.observeDeviceMonitoringDialogRequests(view.context) } - - // Make sure we set the correct alphas even when QS are not currently shown. - launch { viewModel.alpha.collect { view.alpha = it } } - launch { - viewModel.backgroundAlpha.collect { - view.background?.alpha = (it * 255).roundToInt() - } - } - } - - // Listen for model changes only when QS are visible. - qsVisibilityLifecycleOwner.repeatOnLifecycle(Lifecycle.State.RESUMED) { - // Security. - launch { - viewModel.security.collect { security -> - if (previousSecurity != security) { - bindSecurity(view.context, securityHolder, security) - previousSecurity = security - } - } - } - - // Foreground services. - launch { - viewModel.foregroundServices.collect { foregroundServices -> - if (previousForegroundServices != foregroundServices) { - bindForegroundService( - foregroundServicesWithNumberHolder, - foregroundServicesWithTextHolder, - foregroundServices, - ) - previousForegroundServices = foregroundServices - } - } - } - - // User switcher. - launch { - viewModel.userSwitcher.collect { userSwitcher -> - if (previousUserSwitcher != userSwitcher) { - bindButton(userSwitcherHolder, userSwitcher) - previousUserSwitcher = userSwitcher - } - } - } - } - } - } - - private fun bindSecurity( - quickSettingsContext: Context, - securityHolder: TextButtonViewHolder, - security: FooterActionsSecurityButtonViewModel?, - ) { - val securityView = securityHolder.view - securityView.isVisible = security != null - if (security == null) { - return - } - - // Make sure that the chevron is visible and that the button is clickable if there is a - // listener. - val chevron = securityHolder.chevron - val onClick = security.onClick - if (onClick != null) { - securityView.isClickable = true - securityView.setOnClickListener { - onClick(quickSettingsContext, Expandable.fromView(securityView)) - } - chevron.isVisible = true - } else { - securityView.isClickable = false - securityView.setOnClickListener(null) - chevron.isVisible = false - } - - securityHolder.text.text = security.text - securityHolder.newDot.isVisible = false - IconViewBinder.bind(security.icon, securityHolder.icon) - } - - private fun bindForegroundService( - foregroundServicesWithNumberHolder: NumberButtonViewHolder, - foregroundServicesWithTextHolder: TextButtonViewHolder, - foregroundServices: FooterActionsForegroundServicesButtonViewModel?, - ) { - val foregroundServicesWithNumberView = foregroundServicesWithNumberHolder.view - val foregroundServicesWithTextView = foregroundServicesWithTextHolder.view - if (foregroundServices == null) { - foregroundServicesWithNumberView.isVisible = false - foregroundServicesWithTextView.isVisible = false - return - } - - val foregroundServicesCount = foregroundServices.foregroundServicesCount - if (foregroundServices.displayText) { - // Button with text, icon and chevron. - foregroundServicesWithNumberView.isVisible = false - - foregroundServicesWithTextView.isVisible = true - foregroundServicesWithTextView.setOnClickListener { - foregroundServices.onClick(Expandable.fromView(foregroundServicesWithTextView)) - } - foregroundServicesWithTextHolder.text.text = foregroundServices.text - foregroundServicesWithTextHolder.newDot.isVisible = foregroundServices.hasNewChanges - } else { - // Small button with the number only. - foregroundServicesWithTextView.isVisible = false - - foregroundServicesWithNumberView.isVisible = true - foregroundServicesWithNumberView.setOnClickListener { - foregroundServices.onClick(Expandable.fromView(foregroundServicesWithNumberView)) - } - foregroundServicesWithNumberHolder.number.text = foregroundServicesCount.toString() - foregroundServicesWithNumberHolder.number.contentDescription = foregroundServices.text - foregroundServicesWithNumberHolder.newDot.isVisible = foregroundServices.hasNewChanges - } - } - - private fun bindButton(button: IconButtonViewHolder, model: FooterActionsButtonViewModel?) { - val buttonView = button.view - buttonView.id = model?.id ?: View.NO_ID - buttonView.isVisible = model != null - if (model == null) { - return - } - - val backgroundResource = - when (model.backgroundColor) { - R.attr.shadeInactive -> R.drawable.qs_footer_action_circle - R.attr.shadeActive -> R.drawable.qs_footer_action_circle_color - else -> error("Unsupported icon background resource ${model.backgroundColor}") - } - buttonView.setBackgroundResource(backgroundResource) - buttonView.setOnClickListener { model.onClick(Expandable.fromView(buttonView)) } - - val icon = model.icon - val iconView = button.icon - - IconViewBinder.bind(icon, iconView) - if (model.iconTint != null) { - iconView.setColorFilter(model.iconTint, PorterDuff.Mode.SRC_IN) - } else { - iconView.clearColorFilter() - } - } -} - -private class TextButtonViewHolder(val view: View) { - val icon = view.requireViewById<ImageView>(R.id.icon) - val text = view.requireViewById<TextView>(R.id.text) - val newDot = view.requireViewById<ImageView>(R.id.new_dot) - val chevron = view.requireViewById<ImageView>(R.id.chevron_icon) - - companion object { - fun createAndAdd(inflater: LayoutInflater, root: ViewGroup): TextButtonViewHolder { - val view = - inflater.inflate( - R.layout.footer_actions_text_button, - /* root= */ root, - /* attachToRoot= */ false, - ) - root.addView(view) - return TextButtonViewHolder(view) - } - } -} - -private class NumberButtonViewHolder(val view: View) { - val number = view.requireViewById<TextView>(R.id.number) - val newDot = view.requireViewById<ImageView>(R.id.new_dot) - - companion object { - fun createAndAdd(inflater: LayoutInflater, root: ViewGroup): NumberButtonViewHolder { - val view = - inflater.inflate( - R.layout.footer_actions_number_button, - /* root= */ root, - /* attachToRoot= */ false, - ) - root.addView(view) - return NumberButtonViewHolder(view) - } - } -} - -private class IconButtonViewHolder(val view: View) { - val icon = view.requireViewById<ImageView>(R.id.icon) - - companion object { - fun createAndAdd( - inflater: LayoutInflater, - root: ViewGroup, - isLast: Boolean, - ): IconButtonViewHolder { - val view = - inflater.inflate( - R.layout.footer_actions_icon_button, - /* root= */ root, - /* attachToRoot= */ false, - ) - - // All buttons have a background with an inset of qs_footer_action_inset, so the last - // button must have a negative inset of -qs_footer_action_inset to compensate and be - // aligned with its parent. - val marginEnd = - if (isLast) { - -view.context.resources.getDimensionPixelSize(R.dimen.qs_footer_action_inset) - } else { - 0 - } - - val size = - view.context.resources.getDimensionPixelSize(R.dimen.qs_footer_action_button_size) - root.addView( - view, - LinearLayout.LayoutParams(size, size).apply { this.marginEnd = marginEnd }, - ) - return IconButtonViewHolder(view) - } - } -} diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/TileSpecRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/TileSpecRepository.kt index 214e9f097642..24b80b8b069a 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/TileSpecRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/TileSpecRepository.kt @@ -158,6 +158,9 @@ constructor( override suspend fun prependDefault( userId: Int, ) { + if (retailModeRepository.inRetailMode) { + return + } userTileRepositories.get(userId)?.prependDefault() } diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/UserTileSpecRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/UserTileSpecRepository.kt index 8ad5cb2c0a34..aca8733bff7b 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/UserTileSpecRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/UserTileSpecRepository.kt @@ -60,15 +60,21 @@ constructor( _tiles = changeEvents .scan(loadTilesFromSettingsAndParse(userId)) { current, change -> - change.apply(current).also { - if (current != it) { - if (change is RestoreTiles) { - logger.logTilesRestoredAndReconciled(current, it, userId) - } else { - logger.logProcessTileChange(change, it, userId) + change + .apply(current) + .also { + if (current != it) { + if (change is RestoreTiles) { + logger.logTilesRestoredAndReconciled(current, it, userId) + } else { + logger.logProcessTileChange(change, it, userId) + } } } - } + // Distinct preserves the order of the elements removing later + // duplicates, + // all tiles should be different + .distinct() } .flowOn(backgroundDispatcher) .stateIn(applicationScope) diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt index b7fcef4376ea..97b5e87d7167 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt @@ -43,6 +43,7 @@ import com.android.systemui.qs.pipeline.shared.TileSpec import com.android.systemui.qs.pipeline.shared.logging.QSPipelineLogger import com.android.systemui.qs.tiles.di.NewQSTileFactory import com.android.systemui.qs.toProto +import com.android.systemui.retail.data.repository.RetailModeRepository import com.android.systemui.settings.UserTracker import com.android.systemui.user.data.repository.UserRepository import com.android.systemui.util.kotlin.pairwise @@ -137,6 +138,7 @@ constructor( private val installedTilesComponentRepository: InstalledTilesComponentRepository, private val userRepository: UserRepository, private val minimumTilesRepository: MinimumTilesRepository, + private val retailModeRepository: RetailModeRepository, private val customTileStatePersister: CustomTileStatePersister, private val newQSTileFactory: Lazy<NewQSTileFactory>, private val tileFactory: QSFactory, @@ -178,6 +180,14 @@ constructor( installedTilesComponentRepository.getInstalledTilesComponents(it) } + private val minTiles: Int + get() = + if (retailModeRepository.inRetailMode) { + 1 + } else { + minimumTilesRepository.minNumberOfTiles + } + init { if (featureFlags.pipelineEnabled) { startTileCollection() @@ -273,7 +283,7 @@ constructor( newTileMap.filter { it.value is TileOrNotInstalled.NotInstalled }.keys, newUser ) - if (newResolvedTiles.size < minimumTilesRepository.minNumberOfTiles) { + if (newResolvedTiles.size < minTiles) { // We ended up with not enough tiles (some may be not installed). // Prepend the default set of tiles launch { tileSpecRepository.prependDefault(currentUser.value) } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt index c6dfdd5c137b..1143c304f594 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt @@ -332,6 +332,21 @@ open class QSTileViewImpl @JvmOverloads constructor( override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) { super.onLayout(changed, l, t, r, b) updateHeight() + maybeUpdateLongPressEffectDimensions() + } + + private fun maybeUpdateLongPressEffectDimensions() { + if (!isLongClickable || longPressEffect == null) return + + val actualHeight = if (heightOverride != HeightOverrideable.NO_OVERRIDE) { + heightOverride + } else { + measuredHeight + } + initialLongPressProperties?.height = actualHeight.toFloat() + initialLongPressProperties?.width = measuredWidth.toFloat() + finalLongPressProperties?.height = LONG_PRESS_EFFECT_HEIGHT_SCALE * actualHeight + finalLongPressProperties?.width = LONG_PRESS_EFFECT_WIDTH_SCALE * measuredWidth } override fun onFocusChanged(gainFocus: Boolean, direction: Int, previouslyFocusedRect: Rect?) { diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java index 7bc76af68c6b..c091ac3dea50 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java @@ -936,6 +936,10 @@ public class InternetDialogController implements AccessPointController.AccessPoi mHasActiveSubIdOnDds = false; Log.e(TAG, "Can't get DDS subscriptionInfo"); return; + } else if (ddsSubInfo.isOnlyNonTerrestrialNetwork()) { + mHasActiveSubIdOnDds = false; + Log.d(TAG, "This is NTN, so do not show mobile data"); + return; } mHasActiveSubIdOnDds = isEmbeddedSubscriptionVisible(ddsSubInfo); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGroupingUtil.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGroupingUtil.java index 3cf61e211e42..8d3f7284e359 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGroupingUtil.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGroupingUtil.java @@ -362,20 +362,34 @@ public class NotificationGroupingUtil { } protected boolean hasSameIcon(Object parentData, Object childData) { - Icon parentIcon = ((Notification) parentData).getSmallIcon(); - Icon childIcon = ((Notification) childData).getSmallIcon(); + Icon parentIcon = getIcon((Notification) parentData); + Icon childIcon = getIcon((Notification) childData); return parentIcon.sameAs(childIcon); } + private static Icon getIcon(Notification notification) { + if (notification.shouldUseAppIcon()) { + return notification.getAppIcon(); + } + return notification.getSmallIcon(); + } + /** * @return whether two ImageViews have the same colorFilterSet or none at all */ protected boolean hasSameColor(Object parentData, Object childData) { - int parentColor = ((Notification) parentData).color; - int childColor = ((Notification) childData).color; + int parentColor = getColor((Notification) parentData); + int childColor = getColor((Notification) childData); return parentColor == childColor; } + private static int getColor(Notification notification) { + if (notification.shouldUseAppIcon()) { + return 0; // the color filter isn't applied if using the app icon + } + return notification.color; + } + @Override public boolean isEmpty(View view) { return false; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java index 03c667034609..6d34a0fa9c24 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java @@ -16,6 +16,7 @@ package com.android.systemui.statusbar; import static com.android.systemui.Flags.mediaControlsUserInitiatedDeleteintent; +import static com.android.systemui.Flags.notificationMediaManagerBackgroundExecution; import android.annotation.NonNull; import android.annotation.Nullable; @@ -26,12 +27,16 @@ import android.media.MediaMetadata; import android.media.session.MediaController; import android.media.session.MediaSession; import android.media.session.PlaybackState; +import android.os.Handler; import android.service.notification.NotificationStats; import android.service.notification.StatusBarNotification; import android.util.Log; +import androidx.annotation.VisibleForTesting; + import com.android.systemui.Dumpable; import com.android.systemui.dagger.qualifiers.Background; +import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dump.DumpManager; import com.android.systemui.media.controls.domain.pipeline.MediaDataManager; import com.android.systemui.media.controls.shared.model.MediaData; @@ -48,6 +53,7 @@ import java.io.PrintWriter; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; +import java.util.List; import java.util.Objects; import java.util.Optional; import java.util.concurrent.Executor; @@ -80,13 +86,16 @@ public class NotificationMediaManager implements Dumpable { private final ArrayList<MediaListener> mMediaListeners; private final Executor mBackgroundExecutor; + private final Handler mHandler; protected NotificationPresenter mPresenter; - private MediaController mMediaController; + @VisibleForTesting + MediaController mMediaController; private String mMediaNotificationKey; private MediaMetadata mMediaMetadata; - private final MediaController.Callback mMediaListener = new MediaController.Callback() { + @VisibleForTesting + final MediaController.Callback mMediaListener = new MediaController.Callback() { @Override public void onPlaybackStateChanged(PlaybackState state) { super.onPlaybackStateChanged(state); @@ -107,11 +116,20 @@ public class NotificationMediaManager implements Dumpable { if (DEBUG_MEDIA) { Log.v(TAG, "DEBUG_MEDIA: onMetadataChanged: " + metadata); } - mMediaMetadata = metadata; + if (notificationMediaManagerBackgroundExecution()) { + mBackgroundExecutor.execute(() -> setMediaMetadata(metadata)); + } else { + setMediaMetadata(metadata); + } + dispatchUpdateMediaMetaData(); } }; + private void setMediaMetadata(MediaMetadata metadata) { + mMediaMetadata = metadata; + } + /** * Injected constructor. See {@link CentralSurfacesModule}. */ @@ -122,7 +140,9 @@ public class NotificationMediaManager implements Dumpable { NotifCollection notifCollection, MediaDataManager mediaDataManager, DumpManager dumpManager, - @Background Executor backgroundExecutor) { + @Background Executor backgroundExecutor, + @Main Handler handler + ) { mContext = context; mMediaListeners = new ArrayList<>(); mVisibilityProvider = visibilityProvider; @@ -130,6 +150,7 @@ public class NotificationMediaManager implements Dumpable { mNotifPipeline = notifPipeline; mNotifCollection = notifCollection; mBackgroundExecutor = backgroundExecutor; + mHandler = handler; setupNotifPipeline(); @@ -262,6 +283,14 @@ public class NotificationMediaManager implements Dumpable { public void addCallback(MediaListener callback) { mMediaListeners.add(callback); + if (notificationMediaManagerBackgroundExecution()) { + mBackgroundExecutor.execute(() -> updateMediaMetaData(callback)); + } else { + updateMediaMetaData(callback); + } + } + + private void updateMediaMetaData(MediaListener callback) { callback.onPrimaryMetadataOrStateChanged(mMediaMetadata, getMediaControllerPlaybackState(mMediaController)); } @@ -273,7 +302,11 @@ public class NotificationMediaManager implements Dumpable { public void findAndUpdateMediaNotifications() { // TODO(b/169655907): get the semi-filtered notifications for current user Collection<NotificationEntry> allNotifications = mNotifPipeline.getAllNotifs(); - findPlayingMediaNotification(allNotifications); + if (notificationMediaManagerBackgroundExecution()) { + mBackgroundExecutor.execute(() -> findPlayingMediaNotification(allNotifications)); + } else { + findPlayingMediaNotification(allNotifications); + } dispatchUpdateMediaMetaData(); } @@ -312,7 +345,7 @@ public class NotificationMediaManager implements Dumpable { // We have a new media session clearCurrentMediaNotificationSession(); mMediaController = controller; - mMediaController.registerCallback(mMediaListener); + mMediaController.registerCallback(mMediaListener, mHandler); mMediaMetadata = mMediaController.getMetadata(); if (DEBUG_MEDIA) { Log.v(TAG, "DEBUG_MEDIA: insert listener, found new controller: " @@ -331,13 +364,29 @@ public class NotificationMediaManager implements Dumpable { } public void clearCurrentMediaNotification() { + if (notificationMediaManagerBackgroundExecution()) { + mBackgroundExecutor.execute(this::clearMediaNotification); + } else { + clearMediaNotification(); + } + } + + private void clearMediaNotification() { mMediaNotificationKey = null; clearCurrentMediaNotificationSession(); } private void dispatchUpdateMediaMetaData() { - @PlaybackState.State int state = getMediaControllerPlaybackState(mMediaController); ArrayList<MediaListener> callbacks = new ArrayList<>(mMediaListeners); + if (notificationMediaManagerBackgroundExecution()) { + mBackgroundExecutor.execute(() -> updateMediaMetaData(callbacks)); + } else { + updateMediaMetaData(callbacks); + } + } + + private void updateMediaMetaData(List<MediaListener> callbacks) { + @PlaybackState.State int state = getMediaControllerPlaybackState(mMediaController); for (int i = 0; i < callbacks.size(); i++) { callbacks.get(i).onPrimaryMetadataOrStateChanged(mMediaMetadata, state); } @@ -393,7 +442,6 @@ public class NotificationMediaManager implements Dumpable { Log.v(TAG, "DEBUG_MEDIA: Disconnecting from old controller: " + mMediaController.getPackageName()); } - // TODO(b/336612071): move to background thread mMediaController.unregisterCallback(mMediaListener); } mMediaController = null; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractor.kt new file mode 100644 index 000000000000..ac16d26e415c --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractor.kt @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.chips.mediaprojection.domain.interactor + +import com.android.systemui.common.shared.model.ContentDescription +import com.android.systemui.common.shared.model.Icon +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.mediaprojection.data.model.MediaProjectionState +import com.android.systemui.mediaprojection.data.repository.MediaProjectionRepository +import com.android.systemui.res.R +import com.android.systemui.statusbar.chips.domain.interactor.OngoingActivityChipInteractor +import com.android.systemui.statusbar.chips.domain.model.OngoingActivityChipModel +import com.android.systemui.util.time.SystemClock +import javax.inject.Inject +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.stateIn + +/** + * Interactor for media-projection-related chips in the status bar. + * + * There are two kinds of media projection events that will show chips in the status bar: + * 1) Share-to-app: Sharing your phone screen content to another app on the same device. (Triggered + * from within each individual app.) + * 2) Cast-to-other-device: Sharing your phone screen content to a different device. (Triggered from + * the Quick Settings Cast tile or from the Settings app.) This interactor handles both of those + * event types (though maybe not audio-only casting -- see b/342169876). + */ +@SysUISingleton +class MediaProjectionChipInteractor +@Inject +constructor( + @Application scope: CoroutineScope, + mediaProjectionRepository: MediaProjectionRepository, + val systemClock: SystemClock, +) : OngoingActivityChipInteractor { + override val chip: StateFlow<OngoingActivityChipModel> = + mediaProjectionRepository.mediaProjectionState + .map { state -> + when (state) { + is MediaProjectionState.NotProjecting -> OngoingActivityChipModel.Hidden + is MediaProjectionState.EntireScreen, + is MediaProjectionState.SingleTask -> { + // TODO(b/332662551): Distinguish between cast-to-other-device and + // share-to-app. + OngoingActivityChipModel.Shown( + icon = + Icon.Resource( + R.drawable.ic_cast_connected, + ContentDescription.Resource(R.string.accessibility_casting) + ), + // TODO(b/332662551): See if we can use a MediaProjection API to fetch + // this time. + startTimeMs = systemClock.elapsedRealtime() + ) { + // TODO(b/332662551): Implement the pause dialog. + } + } + } + } + .stateIn(scope, SharingStarted.WhileSubscribed(), OngoingActivityChipModel.Hidden) +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractor.kt index bff5686641c2..585ff5f78ff8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractor.kt @@ -34,7 +34,7 @@ import kotlinx.coroutines.flow.stateIn /** Interactor for the screen recording chip shown in the status bar. */ @SysUISingleton -open class ScreenRecordChipInteractor +class ScreenRecordChipInteractor @Inject constructor( @Application scope: CoroutineScope, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/binder/ChipChronometerBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/binder/ChipChronometerBinder.kt new file mode 100644 index 000000000000..2032ec8af78c --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/binder/ChipChronometerBinder.kt @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.chips.ui.binder + +import com.android.systemui.statusbar.chips.ui.view.ChipChronometer + +object ChipChronometerBinder { + /** + * Updates the given [view] chronometer with a new start time and starts it. + * + * @param startTimeMs the time this event started, relative to + * [com.android.systemui.util.time.SystemClock.elapsedRealtime]. See + * [android.widget.Chronometer.setBase]. + */ + fun bind(startTimeMs: Long, view: ChipChronometer) { + view.base = startTimeMs + view.start() + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModel.kt index 208eb50487e3..edb2983720a8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModel.kt @@ -20,6 +20,7 @@ import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.statusbar.chips.call.domain.interactor.CallChipInteractor import com.android.systemui.statusbar.chips.domain.model.OngoingActivityChipModel +import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractor import com.android.systemui.statusbar.chips.screenrecord.domain.interactor.ScreenRecordChipInteractor import javax.inject.Inject import kotlinx.coroutines.CoroutineScope @@ -40,6 +41,7 @@ class OngoingActivityChipsViewModel constructor( @Application scope: CoroutineScope, screenRecordChipInteractor: ScreenRecordChipInteractor, + mediaProjectionChipInteractor: MediaProjectionChipInteractor, callChipInteractor: CallChipInteractor, ) { @@ -51,10 +53,19 @@ constructor( * actually displaying the chip. */ val chip: StateFlow<OngoingActivityChipModel> = - combine(screenRecordChipInteractor.chip, callChipInteractor.chip) { screenRecord, call -> + combine( + screenRecordChipInteractor.chip, + mediaProjectionChipInteractor.chip, + callChipInteractor.chip + ) { screenRecord, mediaProjection, call -> // This `when` statement shows the priority order of the chips when { + // Screen recording also activates the media projection APIs, so whenever the + // screen recording chip is active, the media projection chip would also be + // active. We want the screen-recording-specific chip shown in this case, so we + // give the screen recording chip priority. See b/296461748. screenRecord is OngoingActivityChipModel.Shown -> screenRecord + mediaProjection is OngoingActivityChipModel.Shown -> mediaProjection else -> call } } 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 05245898c161..7df7ef187e26 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java @@ -19,6 +19,7 @@ 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; import android.service.dreams.IDreamManager; import android.util.Log; @@ -99,7 +100,8 @@ public interface CentralSurfacesDependenciesModule { NotifCollection notifCollection, MediaDataManager mediaDataManager, DumpManager dumpManager, - @Background Executor backgroundExecutor) { + @Background Executor backgroundExecutor, + @Main Handler handler) { return new NotificationMediaManager( context, visibilityProvider, @@ -107,7 +109,8 @@ public interface CentralSurfacesDependenciesModule { notifCollection, mediaDataManager, dumpManager, - backgroundExecutor); + backgroundExecutor, + handler); } /** */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java index 5bbd77ee6dd9..60d846ebacac 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java @@ -23,6 +23,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityManager; +import android.app.ActivityOptions; import android.app.ActivityTaskManager; import android.app.ActivityTaskManager.RootTaskInfo; import android.app.AppGlobals; @@ -271,13 +272,16 @@ public class InstantAppNotifier .addFlags(Intent.FLAG_IGNORE_EPHEMERAL) .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + ActivityOptions options = ActivityOptions.makeBasic() + .setPendingIntentCreatorBackgroundActivityStartMode( + ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED); PendingIntent pendingIntent = PendingIntent.getActivityAsUser( mContext, 0 /* requestCode */, browserIntent, PendingIntent.FLAG_IMMUTABLE /* flags */, - null, + options.toBundle(), user); ComponentName aiaComponent = null; try { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconBuilder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconBuilder.kt index 319b49972bd2..16d0cc42db7f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconBuilder.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconBuilder.kt @@ -18,25 +18,27 @@ package com.android.systemui.statusbar.notification.icon import android.app.Notification import android.content.Context +import android.graphics.drawable.Drawable import com.android.systemui.statusbar.StatusBarIconView import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.contentDescForNotification import javax.inject.Inject -/** - * Testable wrapper around Context. - */ -class IconBuilder @Inject constructor( - private val context: Context -) { +/** Testable wrapper around Context. */ +class IconBuilder @Inject constructor(private val context: Context) { fun createIconView(entry: NotificationEntry): StatusBarIconView { return StatusBarIconView( - context, - "${entry.sbn.packageName}/0x${Integer.toHexString(entry.sbn.id)}", - entry.sbn) + context, + "${entry.sbn.packageName}/0x${Integer.toHexString(entry.sbn.id)}", + entry.sbn + ) } fun getIconContentDescription(n: Notification): CharSequence { return contentDescForNotification(context, n) } + + fun getAppIcon(n: Notification): Drawable { + return n.loadHeaderAppIcon(context) + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt index 271b0a86ca12..3df9374da914 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt @@ -20,6 +20,8 @@ import android.app.Notification import android.app.Notification.MessagingStyle import android.app.Person import android.content.pm.LauncherApps +import android.graphics.drawable.AdaptiveIconDrawable +import android.graphics.drawable.Drawable import android.graphics.drawable.Icon import android.os.Build import android.os.Bundle @@ -165,7 +167,7 @@ constructor( Log.wtf( TAG, "Updating using the cache is not supported when the " + - "notifications_background_conversation_icons flag is off" + "notifications_background_icons flag is off" ) } if (!usingCache || !Flags.notificationsBackgroundIcons()) { @@ -216,39 +218,85 @@ constructor( @Throws(InflationException::class) private fun getIconDescriptor(entry: NotificationEntry, redact: Boolean): StatusBarIcon { - val n = entry.sbn.notification val showPeopleAvatar = !redact && isImportantConversation(entry) + // If the descriptor is already cached, return it + getCachedIconDescriptor(entry, showPeopleAvatar)?.also { + return it + } + + val n = entry.sbn.notification + var usingMonochromeAppIcon = false + val icon: Icon? + if (showPeopleAvatar) { + icon = createPeopleAvatar(entry) + } else if (android.app.Flags.notificationsUseMonochromeAppIcon()) { + if (n.shouldUseAppIcon()) { + icon = + getMonochromeAppIcon(entry)?.also { usingMonochromeAppIcon = true } + ?: n.smallIcon + } else { + icon = n.smallIcon + } + } else { + icon = n.smallIcon + } + + if (icon == null) { + throw InflationException("No icon in notification from ${entry.sbn.packageName}") + } + + val sbi = icon.toStatusBarIcon(entry) + cacheIconDescriptor(entry, sbi, showPeopleAvatar, usingMonochromeAppIcon) + return sbi + } + + private fun getCachedIconDescriptor( + entry: NotificationEntry, + showPeopleAvatar: Boolean + ): StatusBarIcon? { val peopleAvatarDescriptor = entry.icons.peopleAvatarDescriptor + val appIconDescriptor = entry.icons.appIconDescriptor val smallIconDescriptor = entry.icons.smallIconDescriptor // If cached, return corresponding cached values - if (showPeopleAvatar && peopleAvatarDescriptor != null) { - return peopleAvatarDescriptor - } else if (!showPeopleAvatar && smallIconDescriptor != null) { - return smallIconDescriptor + return when { + showPeopleAvatar && peopleAvatarDescriptor != null -> peopleAvatarDescriptor + android.app.Flags.notificationsUseMonochromeAppIcon() && appIconDescriptor != null -> + appIconDescriptor + smallIconDescriptor != null -> smallIconDescriptor + else -> null } + } - val icon = - (if (showPeopleAvatar) { - createPeopleAvatar(entry) + private fun cacheIconDescriptor( + entry: NotificationEntry, + descriptor: StatusBarIcon, + showPeopleAvatar: Boolean, + usingMonochromeAppIcon: Boolean + ) { + if (android.app.Flags.notificationsUseAppIcon() || + android.app.Flags.notificationsUseMonochromeAppIcon() + ) { + // If either of the new icon flags is enabled, we cache the icon all the time. + if (showPeopleAvatar) { + entry.icons.peopleAvatarDescriptor = descriptor + } else if (usingMonochromeAppIcon) { + // When notificationsUseMonochromeAppIcon is enabled, we use the appIconDescriptor. + entry.icons.appIconDescriptor = descriptor } else { - n.smallIcon - }) - ?: throw InflationException("No icon in notification from " + entry.sbn.packageName) - - val sbi = icon.toStatusBarIcon(entry) - - // Cache if important conversation or app icon. - if (isImportantConversation(entry) || android.app.Flags.notificationsUseAppIcon()) { + // When notificationsUseAppIcon is enabled, the app icon overrides the small icon. + // But either way, it's a good idea to cache the descriptor. + entry.icons.smallIconDescriptor = descriptor + } + } else if (isImportantConversation(entry)) { + // Old approach: cache only if important conversation. if (showPeopleAvatar) { - entry.icons.peopleAvatarDescriptor = sbi + entry.icons.peopleAvatarDescriptor = descriptor } else { - entry.icons.smallIconDescriptor = sbi + entry.icons.smallIconDescriptor = descriptor } } - - return sbi } @Throws(InflationException::class) @@ -276,6 +324,29 @@ constructor( ) } + // TODO(b/335211019): Should we merge this with the method in GroupHelper? + private fun getMonochromeAppIcon(entry: NotificationEntry): Icon? { + // TODO(b/335211019): This should be done in the background. + var monochromeIcon: Icon? = null + try { + val appIcon: Drawable = iconBuilder.getAppIcon(entry.sbn.notification) + if (appIcon is AdaptiveIconDrawable) { + if (appIcon.monochrome != null) { + monochromeIcon = + Icon.createWithResourceAdaptiveDrawable( + /* resPackage = */ entry.sbn.packageName, + /* resId = */ appIcon.sourceDrawableResId, + /* useMonochrome = */ true, + /* inset = */ -3.0f * AdaptiveIconDrawable.getExtraInsetFraction() + ) + } + } + } catch (e: Exception) { + Log.e(TAG, "Failed to getAppIcon() in getMonochromeAppIcon()", e) + } + return monochromeIcon + } + private suspend fun getLauncherShortcutIconForPeopleAvatar(entry: NotificationEntry) = withContext(bgCoroutineContext) { var icon: Icon? = null diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconPack.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconPack.java index 442c0978fd77..d029ce722af9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconPack.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconPack.java @@ -33,6 +33,7 @@ public final class IconPack { @Nullable private final StatusBarIconView mAodIcon; @Nullable private StatusBarIcon mSmallIconDescriptor; + @Nullable private StatusBarIcon mAppIconDescriptor; @Nullable private StatusBarIcon mPeopleAvatarDescriptor; private boolean mIsImportantConversation; @@ -111,6 +112,15 @@ public final class IconPack { mPeopleAvatarDescriptor = peopleAvatarDescriptor; } + @Nullable + StatusBarIcon getAppIconDescriptor() { + return mAppIconDescriptor; + } + + void setAppIconDescriptor(@Nullable StatusBarIcon appIconDescriptor) { + mAppIconDescriptor = appIconDescriptor; + } + boolean isImportantConversation() { return mIsImportantConversation; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/AvalancheProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/AvalancheProvider.kt index c74c396741d7..c29d700396af 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/AvalancheProvider.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/AvalancheProvider.kt @@ -21,9 +21,9 @@ import android.content.Context import android.content.Intent import android.content.IntentFilter import android.util.Log +import com.android.internal.logging.UiEventLogger import com.android.systemui.broadcast.BroadcastDispatcher import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.util.time.SystemClock import javax.inject.Inject // Class to track avalanche trigger event time. @@ -33,6 +33,7 @@ class AvalancheProvider constructor( private val broadcastDispatcher: BroadcastDispatcher, private val logger: VisualInterruptionDecisionLogger, + private val uiEventLogger: UiEventLogger, ) { val TAG = "AvalancheProvider" val timeoutMs = 120000 @@ -56,6 +57,7 @@ constructor( return } Log.d(TAG, "broadcastReceiver received intent.action=" + intent.action) + uiEventLogger.log(AvalancheSuppressor.AvalancheEvent.START); startTime = System.currentTimeMillis() } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/CommonVisualInterruptionSuppressors.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/CommonVisualInterruptionSuppressors.kt index 938a71fd7b82..42a5bdf0f19b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/CommonVisualInterruptionSuppressors.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/CommonVisualInterruptionSuppressors.kt @@ -33,6 +33,8 @@ import android.os.PowerManager import android.provider.Settings import android.provider.Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED import android.provider.Settings.Global.HEADS_UP_OFF +import com.android.internal.logging.UiEventLogger +import com.android.internal.logging.UiEvent; import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.settings.UserTracker @@ -241,12 +243,12 @@ class AlertKeyguardVisibilitySuppressor( override fun shouldSuppress(entry: NotificationEntry) = keyguardNotificationVisibilityProvider.shouldHideNotification(entry) } - class AvalancheSuppressor( private val avalancheProvider: AvalancheProvider, private val systemClock: SystemClock, private val systemSettings: SystemSettings, private val packageManager: PackageManager, + private val uiEventLogger: UiEventLogger, ) : VisualInterruptionFilter( types = setOf(PEEK, PULSE), @@ -266,6 +268,44 @@ class AvalancheSuppressor( SUPPRESS } + enum class AvalancheEvent(private val id: Int) : UiEventLogger.UiEventEnum { + @UiEvent(doc = "An avalanche event occurred but this notification was suppressed by a " + + "non-avalanche suppressor.") + START(1802), + + @UiEvent(doc = "HUN was suppressed in avalanche.") + SUPPRESS(1803), + + @UiEvent(doc = "HUN allowed during avalanche because it is high priority.") + ALLOW_CONVERSATION_AFTER_AVALANCHE(1804), + + @UiEvent(doc = "HUN allowed during avalanche because it is a high priority conversation.") + ALLOW_HIGH_PRIORITY_CONVERSATION_ANY_TIME(1805), + + @UiEvent(doc = "HUN allowed during avalanche because it is a call.") + ALLOW_CALLSTYLE(1806), + + @UiEvent(doc = "HUN allowed during avalanche because it is a calendar notification.") + ALLOW_CATEGORY_REMINDER(1807), + + @UiEvent(doc = "HUN allowed during avalanche because it is a calendar notification.") + ALLOW_CATEGORY_EVENT(1808), + + @UiEvent(doc = "HUN allowed during avalanche because it has a full screen intent and " + + "the full screen intent permission is granted.") + ALLOW_FSI_WITH_PERMISSION_ON(1809), + + @UiEvent(doc = "HUN allowed during avalanche because it is colorized.") + ALLOW_COLORIZED(1810), + + @UiEvent(doc = "HUN allowed during avalanche because it is an emergency notification.") + ALLOW_EMERGENCY(1811); + + override fun getId(): Int { + return id + } + } + override fun shouldSuppress(entry: NotificationEntry): Boolean { if (!isCooldownEnabled()) { return false @@ -287,41 +327,46 @@ class AvalancheSuppressor( entry.ranking.isConversation && entry.sbn.notification.getWhen() > avalancheProvider.startTime ) { + uiEventLogger.log(AvalancheEvent.ALLOW_CONVERSATION_AFTER_AVALANCHE) return State.ALLOW_CONVERSATION_AFTER_AVALANCHE } if (entry.channel?.isImportantConversation == true) { + uiEventLogger.log(AvalancheEvent.ALLOW_HIGH_PRIORITY_CONVERSATION_ANY_TIME) return State.ALLOW_HIGH_PRIORITY_CONVERSATION_ANY_TIME } if (entry.sbn.notification.isStyle(Notification.CallStyle::class.java)) { + uiEventLogger.log(AvalancheEvent.ALLOW_CALLSTYLE) return State.ALLOW_CALLSTYLE } if (entry.sbn.notification.category == CATEGORY_REMINDER) { + uiEventLogger.log(AvalancheEvent.ALLOW_CATEGORY_REMINDER) return State.ALLOW_CATEGORY_REMINDER } if (entry.sbn.notification.category == CATEGORY_EVENT) { + uiEventLogger.log(AvalancheEvent.ALLOW_CATEGORY_EVENT) return State.ALLOW_CATEGORY_EVENT } if (entry.sbn.notification.fullScreenIntent != null) { + uiEventLogger.log(AvalancheEvent.ALLOW_FSI_WITH_PERMISSION_ON) return State.ALLOW_FSI_WITH_PERMISSION_ON } - - if (entry.sbn.notification.isColorized) { - return State.ALLOW_COLORIZED - } if (entry.sbn.notification.isColorized) { + uiEventLogger.log(AvalancheEvent.ALLOW_COLORIZED) return State.ALLOW_COLORIZED } if ( packageManager.checkPermission(RECEIVE_EMERGENCY_BROADCAST, entry.sbn.packageName) == PERMISSION_GRANTED ) { + uiEventLogger.log(AvalancheEvent.ALLOW_EMERGENCY) return State.ALLOW_EMERGENCY } + uiEventLogger.log(AvalancheEvent.SUPPRESS) return State.SUPPRESS } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImpl.kt index 7e16cd5a693f..84f8662f5fee 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImpl.kt @@ -178,7 +178,8 @@ constructor( if (NotificationAvalancheSuppression.isEnabled) { addFilter( - AvalancheSuppressor(avalancheProvider, systemClock, systemSettings, packageManager) + AvalancheSuppressor(avalancheProvider, systemClock, systemSettings, packageManager, + uiEventLogger) ) avalancheProvider.register() } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java index bd7f766c6860..d1fabb168d90 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java @@ -191,8 +191,12 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper imple updateTransformedTypes(); addRemainingTransformTypes(); updateCropToPaddingForImageViews(); - Notification notification = row.getEntry().getSbn().getNotification(); - mIcon.setTag(ImageTransformState.ICON_TAG, notification.getSmallIcon()); + Notification n = row.getEntry().getSbn().getNotification(); + if (n.shouldUseAppIcon()) { + mIcon.setTag(ImageTransformState.ICON_TAG, n.getAppIcon()); + } else { + mIcon.setTag(ImageTransformState.ICON_TAG, n.getSmallIcon()); + } // We need to reset all views that are no longer transforming in case a view was previously // transformed, but now we decided to transform its container instead. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/NotificationHeadsUpCycling.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/NotificationHeadsUpCycling.kt index d6c73a9dda9e..2dccea668916 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/NotificationHeadsUpCycling.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/NotificationHeadsUpCycling.kt @@ -16,24 +16,22 @@ package com.android.systemui.statusbar.notification.shared -import com.android.systemui.Flags import com.android.systemui.flags.FlagToken -import com.android.systemui.flags.RefactorFlagUtils /** Helper for reading or using the heads-up cycling flag state. */ @Suppress("NOTHING_TO_INLINE") object NotificationHeadsUpCycling { - /** The aconfig flag name - enable this feature when FLAG_NOTIFICATION_THROTTLE_HUN is on. */ - const val FLAG_NAME = Flags.FLAG_NOTIFICATION_THROTTLE_HUN + /** The aconfig flag name */ + const val FLAG_NAME = NotificationThrottleHun.FLAG_NAME /** A token used for dependency declaration */ val token: FlagToken - get() = FlagToken(FLAG_NAME, isEnabled) + get() = NotificationThrottleHun.token /** Is the heads-up cycling animation enabled */ @JvmStatic inline val isEnabled - get() = Flags.notificationThrottleHun() + get() = NotificationThrottleHun.isEnabled /** Whether to animate the bottom line when transiting from a tall HUN to a short HUN */ @JvmStatic @@ -46,13 +44,12 @@ object NotificationHeadsUpCycling { * build to ensure that the refactor author catches issues in testing. */ @JvmStatic - inline fun isUnexpectedlyInLegacyMode() = - RefactorFlagUtils.isUnexpectedlyInLegacyMode(isEnabled, FLAG_NAME) + inline fun isUnexpectedlyInLegacyMode() = NotificationThrottleHun.isUnexpectedlyInLegacyMode() /** * 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) + inline fun assertInLegacyMode() = NotificationThrottleHun.assertInLegacyMode() } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/NotificationThrottleHun.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/NotificationThrottleHun.kt index dd81d42b58ee..71f0de08ece3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/NotificationThrottleHun.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/NotificationThrottleHun.kt @@ -24,7 +24,7 @@ import com.android.systemui.flags.RefactorFlagUtils @Suppress("NOTHING_TO_INLINE") object NotificationThrottleHun { /** The aconfig flag name */ - const val FLAG_NAME = Flags.FLAG_NOTIFICATION_THROTTLE_HUN + const val FLAG_NAME = Flags.FLAG_NOTIFICATION_AVALANCHE_THROTTLE_HUN /** A token used for dependency declaration */ val token: FlagToken @@ -33,7 +33,7 @@ object NotificationThrottleHun { /** Is the refactor enabled */ @JvmStatic inline val isEnabled - get() = Flags.notificationThrottleHun() + get() = Flags.notificationAvalancheThrottleHun() /** * Called to ensure code is only run when the flag is enabled. This protects users from the diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java index 84b74782d09f..fe22cc628b5f 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 @@ -24,7 +24,6 @@ import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_ import static com.android.internal.jank.InteractionJankMonitor.CUJ_SHADE_CLEAR_ALL; import static com.android.systemui.Flags.newAodTransition; import static com.android.systemui.Flags.notificationOverExpansionClippingFix; -import static com.android.systemui.flags.Flags.UNCLEARED_TRANSIENT_HUN_FIX; import static com.android.systemui.statusbar.notification.stack.NotificationPriorityBucketKt.BUCKET_SILENT; import static com.android.systemui.statusbar.notification.stack.StackStateAnimator.ANIMATION_DURATION_SWIPE; import static com.android.systemui.util.DumpUtilsKt.println; @@ -2844,23 +2843,15 @@ public class NotificationStackScrollLayout mAddedHeadsUpChildren.remove(child); return false; } - if (mFeatureFlags.isEnabled(UNCLEARED_TRANSIENT_HUN_FIX)) { - // Skip adding animation for clicked heads up notifications when the - // Shade is closed, because the animation event is generated in - // generateHeadsUpAnimationEvents. Only report that an animation was - // actually generated (thus requesting the transient view be added) - // if a removal animation is in progress. - if (!isExpanded() && isClickedHeadsUp(child)) { - // An animation is already running, add it transiently - mClearTransientViewsWhenFinished.add(child); - return child.inRemovalAnimation(); - } - } else { - if (isClickedHeadsUp(child)) { - // An animation is already running, add it transiently - mClearTransientViewsWhenFinished.add(child); - return true; - } + // Skip adding animation for clicked heads up notifications when the + // Shade is closed, because the animation event is generated in + // generateHeadsUpAnimationEvents. Only report that an animation was + // actually generated (thus requesting the transient view be added) + // if a removal animation is in progress. + if (!isExpanded() && isClickedHeadsUp(child)) { + // An animation is already running, add it transiently + mClearTransientViewsWhenFinished.add(child); + return child.inRemovalAnimation(); } if (mDebugRemoveAnimation) { Log.d(TAG, "generateRemove " + key diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationPlaceholderRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationPlaceholderRepository.kt index db544ce59aa1..f6722a4ccff0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationPlaceholderRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationPlaceholderRepository.kt @@ -38,9 +38,6 @@ class NotificationPlaceholderRepository @Inject constructor() { */ val shadeScrimBounds = MutableStateFlow<ShadeScrimBounds?>(null) - /** the y position of the top of the HUN area */ - val headsUpTop = MutableStateFlow(0f) - /** height made available to the notifications in the size-constrained mode of lock screen. */ val constrainedAvailableSpace = MutableStateFlow(0) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractor.kt index afcf3ae7d5b2..8557afc6ebd3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractor.kt @@ -79,9 +79,6 @@ constructor( */ val scrolledToTop: StateFlow<Boolean> = placeholderRepository.scrolledToTop.asStateFlow() - /** The y-coordinate in px of bottom of the contents of the HUN. */ - val headsUpTop: StateFlow<Float> = placeholderRepository.headsUpTop.asStateFlow() - /** * The amount in px that the notification stack should scroll due to internal expansion. This * should only happen when a notification expansion hits the bottom of the screen, so it is @@ -125,8 +122,4 @@ constructor( fun setConstrainedAvailableSpace(height: Int) { placeholderRepository.constrainedAvailableSpace.value = height } - - fun setHeadsUpTop(headsUpTop: Float) { - placeholderRepository.headsUpTop.value = headsUpTop - } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt index a99fbfcc7907..e90a64a32fba 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt @@ -139,8 +139,6 @@ constructor( */ val scrolledToTop: Flow<Boolean> = stackAppearanceInteractor.scrolledToTop.dumpValue("scrolledToTop") - /** The y-coordinate in px of bottom of the contents of the HUN. */ - val headsUpTop: Flow<Float> = stackAppearanceInteractor.headsUpTop.dumpValue("headsUpTop") /** Receives the amount (px) that the stack should scroll due to internal expansion. */ val syntheticScrollConsumer: (Float) -> Unit = stackAppearanceInteractor::setSyntheticScroll diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt index ea33be0ea4ed..634bd7e4cd41 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt @@ -60,10 +60,6 @@ constructor( interactor.setConstrainedAvailableSpace(height) } - fun onHeadsUpTopChanged(headsUpTop: Float) { - interactor.setHeadsUpTop(headsUpTop) - } - /** Sets the content alpha for the current state of the brightness mirror */ fun setAlphaForBrightnessMirror(alpha: Float) { interactor.setAlphaForBrightnessMirror(alpha) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java index 97f9e066ded5..aac211ae6b46 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java @@ -39,6 +39,7 @@ import com.android.app.animation.Interpolators; import com.android.app.animation.InterpolatorsAndroidX; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.systemui.Dumpable; +import com.android.systemui.Flags; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.demomode.DemoMode; import com.android.systemui.demomode.DemoModeController; @@ -51,7 +52,6 @@ import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.OperatorNameView; import com.android.systemui.statusbar.OperatorNameViewController; import com.android.systemui.statusbar.StatusBarState; -import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipsViewModel; import com.android.systemui.statusbar.disableflags.DisableFlagsLogger.DisableState; import com.android.systemui.statusbar.events.SystemStatusAnimationCallback; import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler; @@ -135,8 +135,6 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue private final CollapsedStatusBarFragmentLogger mCollapsedStatusBarFragmentLogger; private final OperatorNameViewController.Factory mOperatorNameViewControllerFactory; private final OngoingCallController mOngoingCallController; - // TODO(b/332662551): Use this view model to show the ongoing activity chips. - private final OngoingActivityChipsViewModel mOngoingActivityChipsViewModel; private final SystemStatusAnimationScheduler mAnimationScheduler; private final StatusBarLocationPublisher mLocationPublisher; private final NotificationIconAreaController mNotificationIconAreaController; @@ -207,6 +205,11 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue private boolean mTransitionFromLockscreenToDreamStarted = false; /** + * True if there's an active ongoing activity that should be showing a chip and false otherwise. + */ + private boolean mHasOngoingActivity; + + /** * Listener that updates {@link #mWaitingForWindowStateChangeAfterCameraLaunch} when it receives * a new status bar window state. */ @@ -216,11 +219,12 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue }; private DisposableHandle mNicBindingDisposable; + private boolean mAnimationsEnabled = true; + @Inject public CollapsedStatusBarFragment( StatusBarFragmentComponent.Factory statusBarFragmentComponentFactory, OngoingCallController ongoingCallController, - OngoingActivityChipsViewModel ongoingActivityChipsViewModel, SystemStatusAnimationScheduler animationScheduler, StatusBarLocationPublisher locationPublisher, NotificationIconAreaController notificationIconAreaController, @@ -246,7 +250,6 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue DemoModeController demoModeController) { mStatusBarFragmentComponentFactory = statusBarFragmentComponentFactory; mOngoingCallController = ongoingCallController; - mOngoingActivityChipsViewModel = ongoingActivityChipsViewModel; mAnimationScheduler = animationScheduler; mLocationPublisher = locationPublisher; mNotificationIconAreaController = notificationIconAreaController; @@ -401,6 +404,17 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue return mBlockedIcons; } + + @VisibleForTesting + void enableAnimationsForTesting() { + mAnimationsEnabled = true; + } + + @VisibleForTesting + void disableAnimationsForTesting() { + mAnimationsEnabled = false; + } + @Override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); @@ -476,7 +490,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue notificationIconArea.addView(mNotificationIconAreaInner); } - updateNotificationIconAreaAndCallChip(/* animate= */ false); + updateNotificationIconAreaAndOngoingActivityChip(/* animate= */ false); Trace.endSection(); } @@ -493,15 +507,21 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue private StatusBarVisibilityChangeListener mStatusBarVisibilityChangeListener = new StatusBarVisibilityChangeListener() { - @Override - public void onStatusBarVisibilityMaybeChanged() { - updateStatusBarVisibilities(/* animate= */ true); - } + @Override + public void onStatusBarVisibilityMaybeChanged() { + updateStatusBarVisibilities(/* animate= */ true); + } - @Override - public void onTransitionFromLockscreenToDreamStarted() { - mTransitionFromLockscreenToDreamStarted = true; - } + @Override + public void onTransitionFromLockscreenToDreamStarted() { + mTransitionFromLockscreenToDreamStarted = true; + } + + @Override + public void onOngoingActivityStatusChanged(boolean hasOngoingActivity) { + mHasOngoingActivity = hasOngoingActivity; + updateStatusBarVisibilities(/* animate= */ true); + } }; @Override @@ -532,11 +552,14 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue } } - // The ongoing call chip and notification icon visibilities are intertwined, so update both - // if either change. - if (newModel.getShowNotificationIcons() != previousModel.getShowNotificationIcons() - || newModel.getShowOngoingCallChip() != previousModel.getShowOngoingCallChip()) { - updateNotificationIconAreaAndCallChip(animate); + // The ongoing activity chip and notification icon visibilities are intertwined, so update + // both if either change. + boolean notifsChanged = + newModel.getShowNotificationIcons() != previousModel.getShowNotificationIcons(); + boolean ongoingActivityChanged = + newModel.getShowOngoingActivityChip() != previousModel.getShowOngoingActivityChip(); + if (notifsChanged || ongoingActivityChanged) { + updateNotificationIconAreaAndOngoingActivityChip(animate); } // The clock may have already been hidden, but we might want to shift its @@ -566,45 +589,58 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue return new StatusBarVisibilityModel( /* showClock= */ false, /* showNotificationIcons= */ false, - /* showOngoingCallChip= */ false, + /* showOngoingActivityChip= */ false, /* showSystemInfo= */ false); } boolean showClock = externalModel.getShowClock() && !headsUpVisible; - boolean showOngoingCallChip = mOngoingCallController.hasOngoingCall() && !headsUpVisible; + + boolean showOngoingActivityChip; + if (Flags.statusBarScreenSharingChips()) { + // If this flag is on, the ongoing activity status comes from + // CollapsedStatusBarViewBinder, which updates the mHasOngoingActivity variable. + showOngoingActivityChip = mHasOngoingActivity; + } else { + // If this flag is off, the only ongoing activity is the ongoing call, and we pull it + // from the controller directly. + showOngoingActivityChip = mOngoingCallController.hasOngoingCall(); + } + return new StatusBarVisibilityModel( showClock, externalModel.getShowNotificationIcons(), - showOngoingCallChip, + showOngoingActivityChip && !headsUpVisible, externalModel.getShowSystemInfo()); } /** - * Updates the visibility of the notification icon area and ongoing call chip based on disabled1 - * state. + * Updates the visibility of the notification icon area and ongoing activity chip based on + * mLastModifiedVisibility. */ - private void updateNotificationIconAreaAndCallChip(boolean animate) { + private void updateNotificationIconAreaAndOngoingActivityChip(boolean animate) { StatusBarVisibilityModel visibilityModel = mLastModifiedVisibility; boolean disableNotifications = !visibilityModel.getShowNotificationIcons(); - boolean hasOngoingCall = visibilityModel.getShowOngoingCallChip(); + boolean hasOngoingActivity = visibilityModel.getShowOngoingActivityChip(); - // Hide notifications if the disable flag is set or we have an ongoing call. - if (disableNotifications || hasOngoingCall) { - hideNotificationIconArea(animate && !hasOngoingCall); + // Hide notifications if the disable flag is set or we have an ongoing activity. + if (disableNotifications || hasOngoingActivity) { + hideNotificationIconArea(animate && !hasOngoingActivity); } else { showNotificationIconArea(animate); } - // Show the ongoing call chip only if there is an ongoing call *and* notification icons - // are allowed. (The ongoing call chip occupies the same area as the notification icons, - // so if the icons are disabled then the call chip should be, too.) - boolean showOngoingCallChip = hasOngoingCall && !disableNotifications; - if (showOngoingCallChip) { + // Show the ongoing activity chip only if there is an ongoing activity *and* notification + // icons are allowed. (The ongoing activity chip occupies the same area as the notification, + // icons so if the icons are disabled then the activity chip should be, too.) + boolean showOngoingActivityChip = hasOngoingActivity && !disableNotifications; + if (showOngoingActivityChip) { showOngoingActivityChip(animate); } else { hideOngoingActivityChip(animate); } - mOngoingCallController.notifyChipVisibilityChanged(showOngoingCallChip); + if (!Flags.statusBarScreenSharingChips()) { + mOngoingCallController.notifyChipVisibilityChanged(showOngoingActivityChip); + } } private boolean shouldHideStatusBar() { @@ -702,8 +738,9 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue /** * Displays the ongoing activity chip. * - * For now, this chip will only ever contain the ongoing call information, but after b/332662551 - * feature is implemented, it will support different kinds of ongoing activities. + * If Flags.statusBarScreenSharingChips is disabled, this chip will only ever contain the + * ongoing call information, If that flag is enabled, it will support different kinds of ongoing + * activities. See b/332662551. */ private void showOngoingActivityChip(boolean animate) { animateShow(mOngoingActivityChip, animate); @@ -746,7 +783,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue */ private void animateHiddenState(final View v, int state, boolean animate) { v.animate().cancel(); - if (!animate) { + if (!animate || !mAnimationsEnabled) { v.setAlpha(0f); v.setVisibility(state); return; @@ -773,7 +810,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue private void animateShow(View v, boolean animate) { v.animate().cancel(); v.setVisibility(View.VISIBLE); - if (!animate) { + if (!animate || !mAnimationsEnabled) { v.setAlpha(1f); return; } @@ -872,6 +909,8 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue @Override public void dump(PrintWriter printWriter, String[] args) { IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, /* singleIndent= */" "); + pw.println("mHasOngoingActivity=" + mHasOngoingActivity); + pw.println("mAnimationsEnabled=" + mAnimationsEnabled); StatusBarFragmentComponent component = mStatusBarFragmentComponent; if (component == null) { pw.println("StatusBarFragmentComponent is null"); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentLogger.kt index 7cdb9c0a7aa8..0a19023d9e8c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentLogger.kt @@ -59,13 +59,13 @@ class CollapsedStatusBarFragmentLogger @Inject constructor( { bool1 = model.showClock bool2 = model.showNotificationIcons - bool3 = model.showOngoingCallChip + bool3 = model.showOngoingActivityChip bool4 = model.showSystemInfo }, { "New visibilities calculated internally. " + "showClock=$bool1 " + "showNotificationIcons=$bool2 " + - "showOngoingCallChip=$bool3 " + + "showOngoingActivityChip=$bool3 " + "showSystemInfo=$bool4" } ) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/StatusBarVisibilityModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/StatusBarVisibilityModel.kt index cf54cb7aa954..fe24faece1d3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/StatusBarVisibilityModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/StatusBarVisibilityModel.kt @@ -26,7 +26,7 @@ import android.app.StatusBarManager.DISABLE_SYSTEM_INFO data class StatusBarVisibilityModel( val showClock: Boolean, val showNotificationIcons: Boolean, - val showOngoingCallChip: Boolean, + val showOngoingActivityChip: Boolean, val showSystemInfo: Boolean, ) { companion object { @@ -48,7 +48,7 @@ data class StatusBarVisibilityModel( showNotificationIcons = (disabled1 and DISABLE_NOTIFICATION_ICONS) == 0, // TODO(b/279899176): [CollapsedStatusBarFragment] always overwrites this with the // value of [OngoingCallController]. Do we need to process the flag here? - showOngoingCallChip = (disabled1 and DISABLE_ONGOING_CALL_CHIP) == 0, + showOngoingActivityChip = (disabled1 and DISABLE_ONGOING_CALL_CHIP) == 0, showSystemInfo = (disabled1 and DISABLE_SYSTEM_INFO) == 0 && (disabled2 and DISABLE2_SYSTEM_ICONS) == 0 diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/CollapsedStatusBarViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/CollapsedStatusBarViewBinder.kt index 7d7f49bb8d17..a2ec1f21a35c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/CollapsedStatusBarViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/CollapsedStatusBarViewBinder.kt @@ -19,11 +19,17 @@ package com.android.systemui.statusbar.pipeline.shared.ui.binder import android.animation.Animator import android.animation.AnimatorListenerAdapter import android.view.View +import android.widget.ImageView import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle +import com.android.systemui.Flags +import com.android.systemui.common.ui.binder.IconViewBinder import com.android.systemui.dagger.SysUISingleton import com.android.systemui.lifecycle.repeatWhenAttached import com.android.systemui.res.R +import com.android.systemui.statusbar.chips.domain.model.OngoingActivityChipModel +import com.android.systemui.statusbar.chips.ui.binder.ChipChronometerBinder +import com.android.systemui.statusbar.chips.ui.view.ChipChronometer import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.CollapsedStatusBarViewModel import javax.inject.Inject @@ -75,6 +81,35 @@ class CollapsedStatusBarViewBinderImpl @Inject constructor() : CollapsedStatusBa } } } + + if (Flags.statusBarScreenSharingChips()) { + val chipView: View = view.requireViewById(R.id.ongoing_activity_chip) + val chipIconView: ImageView = + chipView.requireViewById(R.id.ongoing_activity_chip_icon) + val chipTimeView: ChipChronometer = + chipView.requireViewById(R.id.ongoing_activity_chip_time) + launch { + viewModel.ongoingActivityChip.collect { chipModel -> + when (chipModel) { + is OngoingActivityChipModel.Shown -> { + IconViewBinder.bind(chipModel.icon, chipIconView) + ChipChronometerBinder.bind(chipModel.startTimeMs, chipTimeView) + // TODO(b/332662551): Attach click listener to chip + + listener.onOngoingActivityStatusChanged( + hasOngoingActivity = true + ) + } + is OngoingActivityChipModel.Hidden -> { + chipTimeView.stop() + listener.onOngoingActivityStatusChanged( + hasOngoingActivity = false + ) + } + } + } + } + } } } } @@ -120,4 +155,7 @@ interface StatusBarVisibilityChangeListener { /** Called when a transition from lockscreen to dream has started. */ fun onTransitionFromLockscreenToDreamStarted() + + /** Called when the status of the ongoing activity chip (active or not active) has changed. */ + fun onOngoingActivityStatusChanged(hasOngoingActivity: Boolean) } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModel.kt index 0a6e95eee127..bb3a67ed49bd 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModel.kt @@ -24,6 +24,8 @@ import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED import com.android.systemui.keyguard.shared.model.TransitionState +import com.android.systemui.statusbar.chips.domain.model.OngoingActivityChipModel +import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipsViewModel import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor import com.android.systemui.statusbar.phone.domain.interactor.LightsOutInteractor @@ -59,6 +61,9 @@ interface CollapsedStatusBarViewModel { /** Emits whenever a transition from lockscreen to dream has started. */ val transitionFromLockscreenToDreamStartedEvent: Flow<Unit> + /** The ongoing activity chip that should be shown on the left-hand side of the status bar. */ + val ongoingActivityChip: StateFlow<OngoingActivityChipModel> + /** * Apps can request a low profile mode [android.view.View.SYSTEM_UI_FLAG_LOW_PROFILE] where * status bar and navigation icons dim. In this mode, a notification dot appears where the @@ -78,6 +83,7 @@ constructor( private val lightsOutInteractor: LightsOutInteractor, private val notificationsInteractor: ActiveNotificationsInteractor, keyguardTransitionInteractor: KeyguardTransitionInteractor, + ongoingActivityChipsViewModel: OngoingActivityChipsViewModel, @Application coroutineScope: CoroutineScope, ) : CollapsedStatusBarViewModel { override val isTransitioningFromLockscreenToOccluded: StateFlow<Boolean> = @@ -91,6 +97,8 @@ constructor( .filter { it.transitionState == TransitionState.STARTED } .map {} + override val ongoingActivityChip = ongoingActivityChipsViewModel.chip + override fun areNotificationsLightsOut(displayId: Int): Flow<Boolean> = if (NotificationsLiveDataStoreRefactor.isUnexpectedlyInLegacyMode()) { emptyFlow() diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/JavaAdapter.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/JavaAdapter.kt index 1ec86a4d1dfc..8c46f9af04e5 100644 --- a/packages/SystemUI/src/com/android/systemui/util/kotlin/JavaAdapter.kt +++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/JavaAdapter.kt @@ -88,8 +88,8 @@ fun <T> collectFlow( flow: Flow<T>, consumer: Consumer<T>, state: Lifecycle.State = Lifecycle.State.CREATED, -) { - lifecycle.coroutineScope.launch { +): Job { + return lifecycle.coroutineScope.launch { lifecycle.repeatOnLifecycle(state) { flow.collect { consumer.accept(it) } } } } diff --git a/packages/SystemUI/src/com/android/systemui/volume/domain/interactor/AudioOutputInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/domain/interactor/AudioOutputInteractor.kt index b6246da4a7dd..c6b0dc542087 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/domain/interactor/AudioOutputInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/volume/domain/interactor/AudioOutputInteractor.kt @@ -30,7 +30,6 @@ import com.android.settingslib.volume.data.repository.AudioSharingRepository import com.android.settingslib.volume.domain.interactor.AudioModeInteractor import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.volume.domain.model.AudioOutputDevice -import com.android.systemui.volume.panel.component.mediaoutput.data.repository.LocalMediaRepositoryFactory import com.android.systemui.volume.panel.component.mediaoutput.domain.interactor.MediaOutputInteractor import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope import javax.inject.Inject @@ -58,8 +57,7 @@ constructor( private val bluetoothAdapter: BluetoothAdapter?, private val deviceIconInteractor: DeviceIconInteractor, private val mediaOutputInteractor: MediaOutputInteractor, - private val localMediaRepositoryFactory: LocalMediaRepositoryFactory, - private val audioSharingRepository: AudioSharingRepository, + audioSharingRepository: AudioSharingRepository, ) { val currentAudioDevice: Flow<AudioOutputDevice> = diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/data/repository/LocalMediaRepositoryFactory.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/data/repository/LocalMediaRepositoryFactory.kt index 0dc264781070..79a4ae74b217 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/data/repository/LocalMediaRepositoryFactory.kt +++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/data/repository/LocalMediaRepositoryFactory.kt @@ -19,14 +19,13 @@ import com.android.settingslib.volume.data.repository.LocalMediaRepository import com.android.settingslib.volume.data.repository.LocalMediaRepositoryImpl import com.android.settingslib.volume.shared.AudioManagerEventsReceiver import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.media.controls.util.LocalMediaManagerFactory import javax.inject.Inject import kotlinx.coroutines.CoroutineScope interface LocalMediaRepositoryFactory { - fun create(packageName: String?): LocalMediaRepository + fun create(packageName: String?, coroutineScope: CoroutineScope): LocalMediaRepository } @SysUISingleton @@ -35,10 +34,12 @@ class LocalMediaRepositoryFactoryImpl constructor( private val eventsReceiver: AudioManagerEventsReceiver, private val localMediaManagerFactory: LocalMediaManagerFactory, - @Application private val coroutineScope: CoroutineScope, ) : LocalMediaRepositoryFactory { - override fun create(packageName: String?): LocalMediaRepository = + override fun create( + packageName: String?, + coroutineScope: CoroutineScope + ): LocalMediaRepository = LocalMediaRepositoryImpl( eventsReceiver, localMediaManagerFactory.create(packageName), diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputInteractor.kt index 9fbd79accf80..31a89775e916 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputInteractor.kt @@ -35,6 +35,7 @@ import javax.inject.Inject import kotlin.coroutines.CoroutineContext import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow @@ -46,6 +47,7 @@ import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.merge import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.flow.stateIn +import kotlinx.coroutines.flow.transformLatest import kotlinx.coroutines.withContext /** Provides observable models about the current media session state. */ @@ -105,12 +107,9 @@ constructor( .filterData() .map { it?.packageName } .distinctUntilChanged() - .map { localMediaRepositoryFactory.create(it) } - .stateIn( - coroutineScope, - SharingStarted.Eagerly, - localMediaRepositoryFactory.create(null) - ) + .transformLatest { + coroutineScope { emit(localMediaRepositoryFactory.create(it, this)) } + } /** Currently connected [MediaDevice]. */ val currentConnectedDevice: Flow<MediaDevice?> = 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 fd01b4864772..850162e65aa6 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 @@ -146,14 +146,18 @@ constructor( isEnabled = isEnabled, a11yStep = volumeRange.step, a11yClickDescription = - context.getString( - if (isMuted) { - R.string.volume_panel_hint_unmute - } else { - R.string.volume_panel_hint_mute - }, - label, - ), + if (isAffectedByMute) { + context.getString( + if (isMuted) { + R.string.volume_panel_hint_unmute + } else { + R.string.volume_panel_hint_mute + }, + label, + ) + } else { + null + }, a11yStateDescription = if (volume == volumeRange.first) { context.getString( diff --git a/packages/SystemUI/src/com/android/systemui/volume/ui/navigation/VolumeNavigator.kt b/packages/SystemUI/src/com/android/systemui/volume/ui/navigation/VolumeNavigator.kt index 03c8af9020e1..99f956489bc3 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/ui/navigation/VolumeNavigator.kt +++ b/packages/SystemUI/src/com/android/systemui/volume/ui/navigation/VolumeNavigator.kt @@ -97,7 +97,14 @@ constructor( } private fun showNewVolumePanel() { - volumePanelGlobalStateInteractor.setVisible(true) + activityStarter.dismissKeyguardThenExecute( + { + volumePanelGlobalStateInteractor.setVisible(true) + false + }, + {}, + true + ) } private fun createNewVolumePanelDialog(): Dialog { diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java index 8895a5e1a97c..0db0de2bcd7e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java @@ -16,6 +16,7 @@ package com.android.systemui.accessibility.hearingaid; +import static com.android.systemui.accessibility.hearingaid.HearingDevicesDialogDelegate.LIVE_CAPTION_INTENT; import static com.android.systemui.statusbar.phone.SystemUIDialog.DEFAULT_DISMISS_ON_DEVICE_LOCK; import static com.google.common.truth.Truth.assertThat; @@ -24,16 +25,24 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.content.ComponentName; import android.content.Intent; +import android.content.pm.ActivityInfo; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.graphics.drawable.Drawable; import android.media.AudioManager; import android.os.Handler; +import android.platform.test.annotations.EnableFlags; import android.provider.Settings; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.view.View; +import android.widget.LinearLayout; import androidx.test.filters.SmallTest; @@ -44,6 +53,7 @@ import com.android.settingslib.bluetooth.HapClientProfile; import com.android.settingslib.bluetooth.LocalBluetoothAdapter; import com.android.settingslib.bluetooth.LocalBluetoothManager; import com.android.settingslib.bluetooth.LocalBluetoothProfileManager; +import com.android.systemui.Flags; import com.android.systemui.SysuiTestCase; import com.android.systemui.animation.DialogTransitionAnimator; import com.android.systemui.bluetooth.qsdialog.DeviceItem; @@ -54,6 +64,7 @@ import com.android.systemui.res.R; import com.android.systemui.statusbar.phone.SystemUIDialog; import com.android.systemui.statusbar.phone.SystemUIDialogManager; +import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -75,6 +86,10 @@ public class HearingDevicesDialogDelegateTest extends SysuiTestCase { public MockitoRule mockito = MockitoJUnit.rule(); private static final String DEVICE_ADDRESS = "AA:BB:CC:DD:EE:FF"; + private static final String TEST_PKG = "pkg"; + private static final String TEST_CLS = "cls"; + private static final ComponentName TEST_COMPONENT = new ComponentName(TEST_PKG, TEST_CLS); + private static final String TEST_LABEL = "label"; @Mock private SystemUIDialog.Factory mSystemUIDialogFactory; @@ -104,6 +119,12 @@ public class HearingDevicesDialogDelegateTest extends SysuiTestCase { private CachedBluetoothDevice mCachedDevice; @Mock private DeviceItem mHearingDeviceItem; + @Mock + private PackageManager mPackageManager; + @Mock + private ActivityInfo mActivityInfo; + @Mock + private Drawable mDrawable; private SystemUIDialog mDialog; private HearingDevicesDialogDelegate mDialogDelegate; private TestableLooper mTestableLooper; @@ -122,6 +143,7 @@ public class HearingDevicesDialogDelegateTest extends SysuiTestCase { when(mSysUiState.setFlag(anyLong(), anyBoolean())).thenReturn(mSysUiState); when(mCachedDevice.getAddress()).thenReturn(DEVICE_ADDRESS); when(mHearingDeviceItem.getCachedBluetoothDevice()).thenReturn(mCachedDevice); + mContext.setMockPackageManager(mPackageManager); setUpPairNewDeviceDialog(); @@ -170,6 +192,45 @@ public class HearingDevicesDialogDelegateTest extends SysuiTestCase { verify(mCachedDevice).disconnect(); } + @Test + @EnableFlags(Flags.FLAG_HEARING_DEVICES_DIALOG_RELATED_TOOLS) + public void showDialog_hasLiveCaption_noRelatedToolsInConfig_showOneRelatedTool() { + when(mPackageManager.queryIntentActivities( + eq(LIVE_CAPTION_INTENT), anyInt())).thenReturn( + List.of(new ResolveInfo())); + mContext.getOrCreateTestableResources().addOverride( + R.array.config_quickSettingsHearingDevicesRelatedToolName, new String[]{}); + + setUpPairNewDeviceDialog(); + mDialog.show(); + + LinearLayout relatedToolsView = (LinearLayout) getRelatedToolsView(mDialog); + assertThat(relatedToolsView.getChildCount()).isEqualTo(1); + } + + @Test + @EnableFlags(Flags.FLAG_HEARING_DEVICES_DIALOG_RELATED_TOOLS) + public void showDialog_hasLiveCaption_oneRelatedToolInConfig_showTwoRelatedTools() + throws PackageManager.NameNotFoundException { + when(mPackageManager.queryIntentActivities( + eq(LIVE_CAPTION_INTENT), anyInt())).thenReturn( + List.of(new ResolveInfo())); + mContext.getOrCreateTestableResources().addOverride( + R.array.config_quickSettingsHearingDevicesRelatedToolName, + new String[]{TEST_PKG + "/" + TEST_CLS}); + when(mPackageManager.getActivityInfo(eq(TEST_COMPONENT), anyInt())).thenReturn( + mActivityInfo); + when(mActivityInfo.loadLabel(mPackageManager)).thenReturn(TEST_LABEL); + when(mActivityInfo.loadIcon(mPackageManager)).thenReturn(mDrawable); + when(mActivityInfo.getComponentName()).thenReturn(TEST_COMPONENT); + + setUpPairNewDeviceDialog(); + mDialog.show(); + + LinearLayout relatedToolsView = (LinearLayout) getRelatedToolsView(mDialog); + assertThat(relatedToolsView.getChildCount()).isEqualTo(2); + } + private void setUpPairNewDeviceDialog() { mDialogDelegate = new HearingDevicesDialogDelegate( mContext, @@ -219,4 +280,18 @@ public class HearingDevicesDialogDelegateTest extends SysuiTestCase { private View getPairNewDeviceButton(SystemUIDialog dialog) { return dialog.requireViewById(R.id.pair_new_device_button); } + + private View getRelatedToolsView(SystemUIDialog dialog) { + return dialog.requireViewById(R.id.related_tools_container); + } + + @After + public void reset() { + if (mDialogDelegate != null) { + mDialogDelegate = null; + } + if (mDialog != null) { + mDialog.dismiss(); + } + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesToolItemParserTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesToolItemParserTest.java new file mode 100644 index 000000000000..717292378913 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesToolItemParserTest.java @@ -0,0 +1,134 @@ +/* + * 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.accessibility.hearingaid; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.when; + +import static java.util.Collections.emptyList; + +import android.content.ComponentName; +import android.content.pm.ActivityInfo; +import android.content.pm.PackageManager; +import android.graphics.drawable.Drawable; +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; + +import androidx.test.filters.SmallTest; + +import com.android.systemui.SysuiTestCase; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +import java.util.List; + +/** + * Tests for {@link HearingDevicesToolItemParser}. + */ +@RunWith(AndroidTestingRunner.class) +@TestableLooper.RunWithLooper(setAsMainLooper = true) +@SmallTest +public class HearingDevicesToolItemParserTest extends SysuiTestCase { + @Rule + public MockitoRule mockito = MockitoJUnit.rule(); + + @Mock + private PackageManager mPackageManager; + @Mock + private ActivityInfo mActivityInfo; + @Mock + private Drawable mDrawable; + private static final String TEST_PKG = "pkg"; + private static final String TEST_CLS = "cls"; + private static final ComponentName TEST_COMPONENT = new ComponentName(TEST_PKG, TEST_CLS); + private static final String TEST_NO_EXIST_PKG = "NoPkg"; + private static final String TEST_NO_EXIST_CLS = "NoCls"; + private static final ComponentName TEST_NO_EXIST_COMPONENT = new ComponentName( + TEST_NO_EXIST_PKG, TEST_NO_EXIST_CLS); + + private static final String TEST_LABEL = "label"; + + @Before + public void setUp() throws PackageManager.NameNotFoundException { + mContext.setMockPackageManager(mPackageManager); + + when(mPackageManager.getActivityInfo(eq(TEST_COMPONENT), anyInt())).thenReturn( + mActivityInfo); + when(mPackageManager.getActivityInfo(eq(TEST_NO_EXIST_COMPONENT), anyInt())).thenThrow( + new PackageManager.NameNotFoundException()); + when(mActivityInfo.loadLabel(mPackageManager)).thenReturn(TEST_LABEL); + when(mActivityInfo.loadIcon(mPackageManager)).thenReturn(mDrawable); + when(mActivityInfo.getComponentName()).thenReturn(TEST_COMPONENT); + } + + @Test + public void parseStringArray_noString_emptyResult() { + assertThat(HearingDevicesToolItemParser.parseStringArray(mContext, new String[]{}, + new String[]{})).isEqualTo(emptyList()); + } + + @Test + public void parseStringArray_oneToolName_oneExpectedToolItem() { + String[] toolName = new String[]{TEST_PKG + "/" + TEST_CLS}; + + List<ToolItem> toolItemList = HearingDevicesToolItemParser.parseStringArray(mContext, + toolName, new String[]{}); + + assertThat(toolItemList.size()).isEqualTo(1); + assertThat(toolItemList.get(0).getToolName()).isEqualTo(TEST_LABEL); + assertThat(toolItemList.get(0).getToolIntent().getComponent()).isEqualTo(TEST_COMPONENT); + } + + @Test + public void parseStringArray_fourToolName_maxThreeToolItem() { + String componentNameString = TEST_PKG + "/" + TEST_CLS; + String[] fourToolName = + new String[]{componentNameString, componentNameString, componentNameString, + componentNameString}; + + List<ToolItem> toolItemList = HearingDevicesToolItemParser.parseStringArray(mContext, + fourToolName, new String[]{}); + assertThat(toolItemList.size()).isEqualTo(HearingDevicesToolItemParser.MAX_NUM); + } + + @Test + public void parseStringArray_oneWrongFormatToolName_noToolItem() { + String[] wrongFormatToolName = new String[]{TEST_PKG}; + + List<ToolItem> toolItemList = HearingDevicesToolItemParser.parseStringArray(mContext, + wrongFormatToolName, new String[]{}); + assertThat(toolItemList.size()).isEqualTo(0); + } + + @Test + public void parseStringArray_oneNotExistToolName_noToolItem() { + String[] notExistToolName = new String[]{TEST_NO_EXIST_PKG + "/" + TEST_NO_EXIST_CLS}; + + List<ToolItem> toolItemList = HearingDevicesToolItemParser.parseStringArray(mContext, + notExistToolName, new String[]{}); + assertThat(toolItemList.size()).isEqualTo(0); + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/ambient/touch/TouchMonitorTest.java b/packages/SystemUI/tests/src/com/android/systemui/ambient/touch/TouchMonitorTest.java index d01d57e18223..358e8cbd4a3c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/ambient/touch/TouchMonitorTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/ambient/touch/TouchMonitorTest.java @@ -22,6 +22,8 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.atLeast; +import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; @@ -45,6 +47,7 @@ import android.view.WindowMetrics; import androidx.annotation.NonNull; import androidx.lifecycle.Lifecycle; +import androidx.lifecycle.LifecycleObserver; import androidx.lifecycle.LifecycleOwner; import androidx.lifecycle.LifecycleRegistry; import androidx.test.filters.SmallTest; @@ -67,6 +70,7 @@ import org.mockito.ArgumentCaptor; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; +import java.util.ArrayList; import java.util.HashSet; import java.util.Set; import java.util.concurrent.ExecutionException; @@ -119,6 +123,8 @@ public class TouchMonitorTest extends SysuiTestCase { private final KosmosJavaAdapter mKosmos; + private ArrayList<LifecycleObserver> mLifecycleObservers = new ArrayList<>(); + Environment(Set<TouchHandler> handlers, KosmosJavaAdapter kosmos) { mLifecycleOwner = new SimpleLifecycleOwner(); @@ -147,8 +153,14 @@ public class TouchMonitorTest extends SysuiTestCase { mMonitor = new TouchMonitor(mExecutor, mBackgroundExecutor, mLifecycleRegistry, mInputFactory, mDisplayHelper, mKosmos.getConfigurationInteractor(), handlers, mIWindowManager, 0); + clearInvocations(mLifecycleRegistry); mMonitor.init(); + ArgumentCaptor<LifecycleObserver> observerCaptor = + ArgumentCaptor.forClass(LifecycleObserver.class); + verify(mLifecycleRegistry, atLeast(1)).addObserver(observerCaptor.capture()); + mLifecycleObservers.addAll(observerCaptor.getAllValues()); + updateLifecycle(Lifecycle.State.RESUMED); // Capture creation request. @@ -187,6 +199,16 @@ public class TouchMonitorTest extends SysuiTestCase { verify(mInputSession).dispose(); Mockito.clearInvocations(mInputSession); } + + void destroyMonitor() { + mMonitor.destroy(); + } + + void verifyLifecycleObserversUnregistered() { + for (LifecycleObserver observer : mLifecycleObservers) { + verify(mLifecycleRegistry).removeObserver(observer); + } + } } @Test @@ -642,6 +664,16 @@ public class TouchMonitorTest extends SysuiTestCase { verify(callback).onRemoved(); } + @Test + public void testDestroy_cleansUpLifecycleObserver() { + final TouchHandler touchHandler = createTouchHandler(); + + final Environment environment = new Environment(Stream.of(touchHandler) + .collect(Collectors.toCollection(HashSet::new)), mKosmos); + environment.destroyMonitor(); + environment.verifyLifecycleObserversUnregistered(); + } + private GestureDetector.OnGestureListener registerGestureListener(TouchHandler handler) { final GestureDetector.OnGestureListener gestureListener = Mockito.mock( GestureDetector.OnGestureListener.class); diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt index e81369d9631c..9a99ed7bb512 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt @@ -18,6 +18,7 @@ package com.android.systemui.biometrics import android.app.ActivityTaskManager import android.app.admin.DevicePolicyManager import android.content.pm.PackageManager +import android.content.res.Configuration import android.hardware.biometrics.BiometricAuthenticator import android.hardware.biometrics.BiometricConstants import android.hardware.biometrics.BiometricManager @@ -127,6 +128,12 @@ open class AuthContainerViewTest : SysuiTestCase() { private lateinit var packageManager: PackageManager @Mock private lateinit var activityTaskManager: ActivityTaskManager + private lateinit var displayRepository: FakeDisplayRepository + private lateinit var displayStateInteractor: DisplayStateInteractor + private lateinit var udfpsOverlayInteractor: UdfpsOverlayInteractor + private lateinit var biometricStatusInteractor: BiometricStatusInteractor + private lateinit var iconProvider: IconProvider + private val testScope = TestScope(StandardTestDispatcher()) private val fakeExecutor = FakeExecutor(FakeSystemClock()) private val biometricPromptRepository = FakePromptRepository() @@ -142,17 +149,12 @@ open class AuthContainerViewTest : SysuiTestCase() { private val promptSelectorInteractor by lazy { PromptSelectorInteractorImpl( fingerprintRepository, + displayStateInteractor, biometricPromptRepository, lockPatternUtils, ) } - private lateinit var displayRepository: FakeDisplayRepository - private lateinit var displayStateInteractor: DisplayStateInteractor - private lateinit var udfpsOverlayInteractor: UdfpsOverlayInteractor - private lateinit var biometricStatusInteractor: BiometricStatusInteractor - private lateinit var iconProvider: IconProvider - private val credentialViewModel = CredentialViewModel(mContext, bpCredentialInteractor) private val defaultLogoIcon = context.getDrawable(R.drawable.ic_android) @@ -392,6 +394,33 @@ open class AuthContainerViewTest : SysuiTestCase() { } @Test + fun testAnimateToCredentialUI_rotateCredentialUI() { + val container = initializeFingerprintContainer( + authenticators = BiometricManager.Authenticators.BIOMETRIC_WEAK or + BiometricManager.Authenticators.DEVICE_CREDENTIAL + ) + container.animateToCredentialUI(false) + waitForIdleSync() + + assertThat(container.hasCredentialView()).isTrue() + assertThat(container.hasBiometricPrompt()).isFalse() + + // Check credential view persists after new attachment + container.onAttachedToWindow() + + assertThat(container.hasCredentialView()).isTrue() + assertThat(container.hasBiometricPrompt()).isFalse() + + val configuration = Configuration(context.resources.configuration) + configuration.orientation = Configuration.ORIENTATION_LANDSCAPE + container.dispatchConfigurationChanged(configuration) + waitForIdleSync() + + assertThat(container.hasCredentialView()).isTrue() + assertThat(container.hasBiometricPrompt()).isFalse() + } + + @Test fun testShowBiometricUI() { mSetFlagsRule.disableFlags(FLAG_CONSTRAINT_BP) val container = initializeFingerprintContainer() diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractorImplTest.kt index 3102a84b852a..6e78e334891b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractorImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractorImplTest.kt @@ -25,13 +25,16 @@ import android.hardware.biometrics.PromptVerticalListContentView import androidx.test.filters.SmallTest import com.android.internal.widget.LockPatternUtils import com.android.systemui.SysuiTestCase +import com.android.systemui.biometrics.data.repository.FakeDisplayStateRepository import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository import com.android.systemui.biometrics.data.repository.FakePromptRepository import com.android.systemui.biometrics.faceSensorPropertiesInternal import com.android.systemui.biometrics.fingerprintSensorPropertiesInternal import com.android.systemui.biometrics.shared.model.BiometricModalities +import com.android.systemui.biometrics.shared.model.DisplayRotation import com.android.systemui.biometrics.shared.model.PromptKind import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.display.data.repository.FakeDisplayRepository import com.android.systemui.util.concurrency.FakeExecutor import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.whenever @@ -75,12 +78,30 @@ class PromptSelectorInteractorImplTest : SysuiTestCase() { private val promptRepository = FakePromptRepository() private val fakeExecutor = FakeExecutor(FakeSystemClock()) + private lateinit var displayStateRepository: FakeDisplayStateRepository + private lateinit var displayRepository: FakeDisplayRepository + private lateinit var displayStateInteractor: DisplayStateInteractor private lateinit var interactor: PromptSelectorInteractor @Before fun setup() { + displayStateRepository = FakeDisplayStateRepository() + displayRepository = FakeDisplayRepository() + displayStateInteractor = + DisplayStateInteractorImpl( + testScope.backgroundScope, + mContext, + fakeExecutor, + displayStateRepository, + displayRepository, + ) interactor = - PromptSelectorInteractorImpl(fingerprintRepository, promptRepository, lockPatternUtils) + PromptSelectorInteractorImpl( + fingerprintRepository, + displayStateInteractor, + promptRepository, + lockPatternUtils + ) } private fun basicPromptInfo() = @@ -155,7 +176,8 @@ class PromptSelectorInteractorImplTest : SysuiTestCase() { modalities, CHALLENGE, OP_PACKAGE_NAME, - false /*onSwitchToCredential*/ + onSwitchToCredential = false, + isLandscape = false, ) assertThat(currentPrompt).isNotNull() @@ -200,22 +222,49 @@ class PromptSelectorInteractorImplTest : SysuiTestCase() { fun promptKind_isBiometric_whenBiometricAllowed() = testScope.runTest { setUserCredentialType(isPassword = true) - val info = basicPromptInfo() val promptKind by collectLastValue(interactor.promptKind) assertThat(promptKind).isEqualTo(PromptKind.None) - interactor.setPrompt( - info, - USER_ID, - REQUEST_ID, - modalities, - CHALLENGE, - OP_PACKAGE_NAME, - false /*onSwitchToCredential*/ - ) + setPrompt() + + assertThat(promptKind?.isOnePanePortraitBiometric()).isTrue() + + interactor.resetPrompt(REQUEST_ID) + verifyUnset() + } + + @Test + fun promptKind_isBiometricTwoPane_whenBiometricAllowed_landscape() = + testScope.runTest { + setUserCredentialType(isPassword = true) + displayStateRepository.setIsLargeScreen(false) + displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_90) + + val promptKind by collectLastValue(interactor.promptKind) + assertThat(promptKind).isEqualTo(PromptKind.None) + + setPrompt() + + assertThat(promptKind?.isTwoPaneLandscapeBiometric()).isTrue() + + interactor.resetPrompt(REQUEST_ID) + verifyUnset() + } + + @Test + fun promptKind_isBiometricOnePane_whenBiometricAllowed_largeScreenLandscape() = + testScope.runTest { + setUserCredentialType(isPassword = true) + displayStateRepository.setIsLargeScreen(true) + displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_90) + + val promptKind by collectLastValue(interactor.promptKind) + assertThat(promptKind).isEqualTo(PromptKind.None) - assertThat(promptKind?.isBiometric()).isTrue() + setPrompt() + + assertThat(promptKind?.isOnePaneLargeScreenLandscapeBiometric()).isTrue() interactor.resetPrompt(REQUEST_ID) verifyUnset() @@ -225,20 +274,11 @@ class PromptSelectorInteractorImplTest : SysuiTestCase() { fun promptKind_isCredential_onSwitchToCredential() = testScope.runTest { setUserCredentialType(isPassword = true) - val info = basicPromptInfo() val promptKind by collectLastValue(interactor.promptKind) assertThat(promptKind).isEqualTo(PromptKind.None) - interactor.setPrompt( - info, - USER_ID, - REQUEST_ID, - modalities, - CHALLENGE, - OP_PACKAGE_NAME, - true /*onSwitchToCredential*/ - ) + setPrompt(onSwitchToCredential = true) assertThat(promptKind).isEqualTo(PromptKind.Password) @@ -259,15 +299,7 @@ class PromptSelectorInteractorImplTest : SysuiTestCase() { val promptKind by collectLastValue(interactor.promptKind) assertThat(promptKind).isEqualTo(PromptKind.None) - interactor.setPrompt( - info, - USER_ID, - REQUEST_ID, - modalities, - CHALLENGE, - OP_PACKAGE_NAME, - false /*onSwitchToCredential*/ - ) + setPrompt(info) assertThat(promptKind).isEqualTo(PromptKind.Password) @@ -292,15 +324,7 @@ class PromptSelectorInteractorImplTest : SysuiTestCase() { val promptKind by collectLastValue(interactor.promptKind) assertThat(promptKind).isEqualTo(PromptKind.None) - interactor.setPrompt( - info, - USER_ID, - REQUEST_ID, - modalities, - CHALLENGE, - OP_PACKAGE_NAME, - false /*onSwitchToCredential*/ - ) + setPrompt(info) assertThat(promptKind).isEqualTo(PromptKind.Password) @@ -312,6 +336,7 @@ class PromptSelectorInteractorImplTest : SysuiTestCase() { fun promptKind_isBiometric_whenBiometricIsNotAllowed_withVerticalList() = testScope.runTest { setUserCredentialType(isPassword = true) + displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_90) val info = basicPromptInfo().apply { isDeviceCredentialAllowed = true @@ -322,22 +347,32 @@ class PromptSelectorInteractorImplTest : SysuiTestCase() { val promptKind by collectLastValue(interactor.promptKind) assertThat(promptKind).isEqualTo(PromptKind.None) - interactor.setPrompt( - info, - USER_ID, - REQUEST_ID, - modalities, - CHALLENGE, - OP_PACKAGE_NAME, - false /*onSwitchToCredential*/ - ) + setPrompt(info) - assertThat(promptKind?.isBiometric()).isTrue() + assertThat(promptKind?.isOnePaneNoSensorLandscapeBiometric()).isTrue() interactor.resetPrompt(REQUEST_ID) verifyUnset() } + private fun setPrompt( + info: PromptInfo = basicPromptInfo(), + onSwitchToCredential: Boolean = false + ) { + interactor.setPrompt( + info, + USER_ID, + REQUEST_ID, + modalities, + CHALLENGE, + OP_PACKAGE_NAME, + onSwitchToCredential = onSwitchToCredential, + isLandscape = + displayStateRepository.currentRotation.value == DisplayRotation.ROTATION_90 || + displayStateRepository.currentRotation.value == DisplayRotation.ROTATION_270, + ) + } + private fun TestScope.useCredentialAndReset(kind: PromptKind) { setUserCredentialType( isPin = kind == PromptKind.Pin, @@ -366,7 +401,8 @@ class PromptSelectorInteractorImplTest : SysuiTestCase() { BiometricModalities(), CHALLENGE, OP_PACKAGE_NAME, - false /*onSwitchToCredential*/ + onSwitchToCredential = false, + isLandscape = false, ) // not using biometrics, should be null with no fallback option diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt index c177511a2df4..1167fce7524b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt @@ -1407,7 +1407,12 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa runningTaskInfo.topActivity = topActivity whenever(activityTaskManager.getTasks(1)).thenReturn(listOf(runningTaskInfo)) selector = - PromptSelectorInteractorImpl(fingerprintRepository, promptRepository, lockPatternUtils) + PromptSelectorInteractorImpl( + fingerprintRepository, + displayStateInteractor, + promptRepository, + lockPatternUtils + ) selector.resetPrompt(REQUEST_ID) viewModel = @@ -1643,7 +1648,8 @@ private fun PromptSelectorInteractor.initializePrompt( BiometricModalities(fingerprintProperties = fingerprint, faceProperties = face), CHALLENGE, packageName, - false /*onUseDeviceCredential*/ + onSwitchToCredential = false, + isLandscape = false, ) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBlueprintViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBlueprintViewModelTest.kt index a18b0330d48f..ec2a1d305ab3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBlueprintViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBlueprintViewModelTest.kt @@ -17,9 +17,11 @@ package com.android.systemui.keyguard.ui.viewmodel +import android.os.fakeExecutorHandler import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor +import com.android.systemui.testKosmos import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @@ -33,12 +35,16 @@ import org.mockito.MockitoAnnotations class KeyguardBlueprintViewModelTest : SysuiTestCase() { @Mock private lateinit var keyguardBlueprintInteractor: KeyguardBlueprintInteractor private lateinit var undertest: KeyguardBlueprintViewModel + private val kosmos = testKosmos() @Before fun setup() { MockitoAnnotations.initMocks(this) undertest = - KeyguardBlueprintViewModel(keyguardBlueprintInteractor = keyguardBlueprintInteractor) + KeyguardBlueprintViewModel( + handler = kosmos.fakeExecutorHandler, + keyguardBlueprintInteractor = keyguardBlueprintInteractor, + ) } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt index 8a12a90efc4c..c1e3e84f2bf4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt @@ -49,6 +49,7 @@ import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.quickaffordance.ActivationState import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancePosition import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancesMetricsLogger +import com.android.systemui.kosmos.applicationCoroutineScope import com.android.systemui.kosmos.testDispatcher import com.android.systemui.kosmos.testScope import com.android.systemui.plugins.ActivityStarter @@ -289,6 +290,7 @@ class KeyguardQuickAffordancesCombinedViewModelTest : SysuiTestCase() { underTest = KeyguardQuickAffordancesCombinedViewModel( + applicationScope = kosmos.applicationCoroutineScope, quickAffordanceInteractor = KeyguardQuickAffordanceInteractor( keyguardInteractor = keyguardInteractor, diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionManagerRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionManagerRepositoryTest.kt index ef73e2e493b1..e4877808f133 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionManagerRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionManagerRepositoryTest.kt @@ -28,7 +28,6 @@ import com.android.systemui.mediaprojection.taskswitcher.FakeActivityTaskManager import com.android.systemui.mediaprojection.taskswitcher.FakeActivityTaskManager.Companion.createToken import com.android.systemui.mediaprojection.taskswitcher.fakeActivityTaskManager import com.android.systemui.mediaprojection.taskswitcher.fakeMediaProjectionManager -import com.android.systemui.mediaprojection.taskswitcher.mediaProjectionManagerRepository import com.android.systemui.mediaprojection.taskswitcher.taskSwitcherKosmos import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.test.runTest @@ -45,7 +44,7 @@ class MediaProjectionManagerRepositoryTest : SysuiTestCase() { private val fakeMediaProjectionManager = kosmos.fakeMediaProjectionManager private val fakeActivityTaskManager = kosmos.fakeActivityTaskManager - private val repo = kosmos.mediaProjectionManagerRepository + private val repo = kosmos.realMediaProjectionRepository @Test fun switchProjectedTask_stateIsUpdatedWithNewTask() = diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSImplTest.java index 6956beab418e..a1c5a5feb197 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSImplTest.java @@ -39,7 +39,6 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyZeroInteractions; import static org.mockito.Mockito.when; -import android.annotation.Nullable; import android.content.Context; import android.graphics.Rect; import android.os.Bundle; @@ -51,6 +50,7 @@ import android.view.View; import android.view.ViewGroup; import android.widget.FrameLayout; +import androidx.compose.ui.platform.ComposeView; import androidx.lifecycle.Lifecycle; import androidx.test.filters.SmallTest; @@ -58,12 +58,10 @@ import com.android.keyguard.BouncerPanelExpansionCalculator; import com.android.systemui.SysuiTestCase; import com.android.systemui.dump.DumpManager; import com.android.systemui.flags.EnableSceneContainer; -import com.android.systemui.flags.FeatureFlagsClassic; import com.android.systemui.media.controls.ui.view.MediaHost; import com.android.systemui.qs.customize.QSCustomizerController; import com.android.systemui.qs.dagger.QSComponent; import com.android.systemui.qs.external.TileServiceRequestController; -import com.android.systemui.qs.footer.ui.binder.FooterActionsViewBinder; import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel; import com.android.systemui.qs.logging.QSLogger; import com.android.systemui.res.R; @@ -112,9 +110,7 @@ public class QSImplTest extends SysuiTestCase { @Mock private QSSquishinessController mSquishinessController; @Mock private FooterActionsViewModel mFooterActionsViewModel; @Mock private FooterActionsViewModel.Factory mFooterActionsViewModelFactory; - @Mock private FooterActionsViewBinder mFooterActionsViewBinder; @Mock private LargeScreenShadeInterpolator mLargeScreenShadeInterpolator; - @Mock private FeatureFlagsClassic mFeatureFlags; private ViewGroup mQsView; private final CommandQueue mCommandQueue = @@ -496,18 +492,13 @@ public class QSImplTest extends SysuiTestCase { @Test @EnableSceneContainer public void testSceneContainerFlagsEnabled_FooterActionsRemoved_controllerNotStarted() { - clearInvocations( - mFooterActionsViewBinder, mFooterActionsViewModel, mFooterActionsViewModelFactory); + clearInvocations(mFooterActionsViewModel, mFooterActionsViewModelFactory); QSImpl other = instantiate(); other.onComponentCreated(mQsComponent, null); assertThat((View) other.getView().findViewById(R.id.qs_footer_actions)).isNull(); - verifyZeroInteractions( - mFooterActionsViewModel, - mFooterActionsViewBinder, - mFooterActionsViewModelFactory - ); + verifyZeroInteractions(mFooterActionsViewModel, mFooterActionsViewModelFactory); } @Test @@ -553,9 +544,7 @@ public class QSImplTest extends SysuiTestCase { mock(QSLogger.class), mock(FooterActionsController.class), mFooterActionsViewModelFactory, - mFooterActionsViewBinder, - mLargeScreenShadeInterpolator, - mFeatureFlags + mLargeScreenShadeInterpolator ); } @@ -589,41 +578,20 @@ public class QSImplTest extends SysuiTestCase { customizer.setId(android.R.id.edit); mQsView.addView(customizer); - View footerActionsView = new FooterActionsViewBinder().create(mContext); + ComposeView footerActionsView = new ComposeView(mContext); footerActionsView.setId(R.id.qs_footer_actions); mQsView.addView(footerActionsView); } private void setUpInflater() { - LayoutInflater realInflater = LayoutInflater.from(mContext); - when(mLayoutInflater.cloneInContext(any(Context.class))).thenReturn(mLayoutInflater); when(mLayoutInflater.inflate(anyInt(), nullable(ViewGroup.class), anyBoolean())) - .thenAnswer((invocation) -> inflate(realInflater, (int) invocation.getArgument(0), - (ViewGroup) invocation.getArgument(1), - (boolean) invocation.getArgument(2))); + .thenAnswer((invocation) -> mQsView); when(mLayoutInflater.inflate(anyInt(), nullable(ViewGroup.class))) - .thenAnswer((invocation) -> inflate(realInflater, (int) invocation.getArgument(0), - (ViewGroup) invocation.getArgument(1))); + .thenAnswer((invocation) -> mQsView); mContext.addMockSystemService(Context.LAYOUT_INFLATER_SERVICE, mLayoutInflater); } - private View inflate(LayoutInflater realInflater, int layoutRes, @Nullable ViewGroup root) { - return inflate(realInflater, layoutRes, root, root != null); - } - - private View inflate(LayoutInflater realInflater, int layoutRes, @Nullable ViewGroup root, - boolean attachToRoot) { - if (layoutRes == R.layout.footer_actions - || layoutRes == R.layout.footer_actions_text_button - || layoutRes == R.layout.footer_actions_number_button - || layoutRes == R.layout.footer_actions_icon_button) { - return realInflater.inflate(layoutRes, root, attachToRoot); - } - - return mQsView; - } - private void setupQsComponent() { when(mQsComponent.getQSPanelController()).thenReturn(mQSPanelController); when(mQsComponent.getQuickQSPanelController()).thenReturn(mQuickQSPanelController); diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateControllerTest.java index 9798562ab5a8..29487cdace2d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateControllerTest.java @@ -1116,6 +1116,34 @@ public class InternetDialogDelegateControllerTest extends SysuiTestCase { assertThat(mInternetDialogController.hasActiveSubIdOnDds()).isFalse(); } + @Test + public void hasActiveSubIdOnDds_activeDdsAndIsOnlyNonTerrestrialNetwork_returnFalse() { + when(SubscriptionManager.getDefaultDataSubscriptionId()) + .thenReturn(SUB_ID); + SubscriptionInfo info = mock(SubscriptionInfo.class); + when(info.isEmbedded()).thenReturn(true); + when(info.isOnlyNonTerrestrialNetwork()).thenReturn(true); + when(mSubscriptionManager.getActiveSubscriptionInfo(SUB_ID)).thenReturn(info); + + mInternetDialogController.mOnSubscriptionsChangedListener.onSubscriptionsChanged(); + + assertFalse(mInternetDialogController.hasActiveSubIdOnDds()); + } + + @Test + public void hasActiveSubIdOnDds_activeDdsAndIsNotOnlyNonTerrestrialNetwork_returnTrue() { + when(SubscriptionManager.getDefaultDataSubscriptionId()) + .thenReturn(SUB_ID); + SubscriptionInfo info = mock(SubscriptionInfo.class); + when(info.isEmbedded()).thenReturn(true); + when(info.isOnlyNonTerrestrialNetwork()).thenReturn(false); + when(mSubscriptionManager.getActiveSubscriptionInfo(SUB_ID)).thenReturn(info); + + mInternetDialogController.mOnSubscriptionsChangedListener.onSubscriptionsChanged(); + + assertTrue(mInternetDialogController.hasActiveSubIdOnDds()); + } + private String getResourcesString(String name) { return mContext.getResources().getString(getResourcesId(name)); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractorTest.kt new file mode 100644 index 000000000000..0f33b9dfc077 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractorTest.kt @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.chips.mediaprojection.domain.interactor + +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.common.shared.model.Icon +import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.testScope +import com.android.systemui.mediaprojection.data.model.MediaProjectionState +import com.android.systemui.mediaprojection.data.repository.fakeMediaProjectionRepository +import com.android.systemui.mediaprojection.taskswitcher.FakeActivityTaskManager.Companion.createTask +import com.android.systemui.res.R +import com.android.systemui.statusbar.chips.domain.model.OngoingActivityChipModel +import com.android.systemui.statusbar.chips.ui.viewmodel.mediaProjectionChipInteractor +import com.android.systemui.util.time.fakeSystemClock +import com.google.common.truth.Truth.assertThat +import kotlin.test.Test +import kotlinx.coroutines.test.runTest + +@SmallTest +class MediaProjectionChipInteractorTest : SysuiTestCase() { + private val kosmos = Kosmos() + private val testScope = kosmos.testScope + private val mediaProjectionRepo = kosmos.fakeMediaProjectionRepository + private val systemClock = kosmos.fakeSystemClock + + private val underTest = kosmos.mediaProjectionChipInteractor + + @Test + fun chip_notProjectingState_isHidden() = + testScope.runTest { + val latest by collectLastValue(underTest.chip) + + mediaProjectionRepo.mediaProjectionState.value = MediaProjectionState.NotProjecting + + assertThat(latest).isInstanceOf(OngoingActivityChipModel.Hidden::class.java) + } + + @Test + fun chip_singleTaskState_isShownWithIcon() = + testScope.runTest { + val latest by collectLastValue(underTest.chip) + + mediaProjectionRepo.mediaProjectionState.value = + MediaProjectionState.SingleTask(createTask(taskId = 1)) + + assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java) + val icon = (latest as OngoingActivityChipModel.Shown).icon + assertThat((icon as Icon.Resource).res).isEqualTo(R.drawable.ic_cast_connected) + } + + @Test + fun chip_entireScreenState_isShownWithIcon() = + testScope.runTest { + val latest by collectLastValue(underTest.chip) + + mediaProjectionRepo.mediaProjectionState.value = MediaProjectionState.EntireScreen + + assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java) + val icon = (latest as OngoingActivityChipModel.Shown).icon + assertThat((icon as Icon.Resource).res).isEqualTo(R.drawable.ic_cast_connected) + } + + @Test + fun chip_timeResetsOnEachNewShare() = + testScope.runTest { + val latest by collectLastValue(underTest.chip) + + systemClock.setElapsedRealtime(1234) + mediaProjectionRepo.mediaProjectionState.value = MediaProjectionState.EntireScreen + + assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java) + assertThat((latest as OngoingActivityChipModel.Shown).startTimeMs).isEqualTo(1234) + + mediaProjectionRepo.mediaProjectionState.value = MediaProjectionState.NotProjecting + assertThat(latest).isInstanceOf(OngoingActivityChipModel.Hidden::class.java) + + systemClock.setElapsedRealtime(5678) + mediaProjectionRepo.mediaProjectionState.value = MediaProjectionState.EntireScreen + + assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java) + assertThat((latest as OngoingActivityChipModel.Shown).startTimeMs).isEqualTo(5678) + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt index 1260f07d0ba1..121229c321b6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt @@ -23,6 +23,9 @@ import com.android.systemui.common.shared.model.Icon import com.android.systemui.coroutines.collectLastValue import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.testScope +import com.android.systemui.mediaprojection.data.model.MediaProjectionState +import com.android.systemui.mediaprojection.data.repository.fakeMediaProjectionRepository +import com.android.systemui.mediaprojection.taskswitcher.FakeActivityTaskManager.Companion.createTask import com.android.systemui.res.R import com.android.systemui.screenrecord.data.model.ScreenRecordModel import com.android.systemui.screenrecord.data.repository.screenRecordRepository @@ -35,13 +38,20 @@ import org.junit.Test class OngoingActivityChipsViewModelTest : SysuiTestCase() { private val kosmos = Kosmos() + private val testScope = kosmos.testScope + + private val screenRecordState = kosmos.screenRecordRepository.screenRecordState + private val mediaProjectionState = kosmos.fakeMediaProjectionRepository.mediaProjectionState + private val callState = kosmos.callChipInteractor.chip + private val underTest = kosmos.ongoingActivityChipsViewModel @Test fun chip_allHidden_hidden() = - kosmos.testScope.runTest { - kosmos.screenRecordRepository.screenRecordState.value = ScreenRecordModel.DoingNothing - kosmos.callChipInteractor.chip.value = OngoingActivityChipModel.Hidden + testScope.runTest { + screenRecordState.value = ScreenRecordModel.DoingNothing + mediaProjectionState.value = MediaProjectionState.NotProjecting + callState.value = OngoingActivityChipModel.Hidden val latest by collectLastValue(underTest.chip) @@ -50,9 +60,10 @@ class OngoingActivityChipsViewModelTest : SysuiTestCase() { @Test fun chip_screenRecordShow_restHidden_screenRecordShown() = - kosmos.testScope.runTest { - kosmos.screenRecordRepository.screenRecordState.value = ScreenRecordModel.Recording - kosmos.callChipInteractor.chip.value = OngoingActivityChipModel.Hidden + testScope.runTest { + screenRecordState.value = ScreenRecordModel.Recording + mediaProjectionState.value = MediaProjectionState.NotProjecting + callState.value = OngoingActivityChipModel.Hidden val latest by collectLastValue(underTest.chip) @@ -61,15 +72,15 @@ class OngoingActivityChipsViewModelTest : SysuiTestCase() { @Test fun chip_screenRecordShowAndCallShow_screenRecordShown() = - kosmos.testScope.runTest { - kosmos.screenRecordRepository.screenRecordState.value = ScreenRecordModel.Recording + testScope.runTest { + screenRecordState.value = ScreenRecordModel.Recording val callChip = OngoingActivityChipModel.Shown( Icon.Resource(R.drawable.ic_call, ContentDescription.Loaded("icon")), startTimeMs = 600L, ) {} - kosmos.callChipInteractor.chip.value = callChip + callState.value = callChip val latest by collectLastValue(underTest.chip) @@ -77,16 +88,46 @@ class OngoingActivityChipsViewModelTest : SysuiTestCase() { } @Test - fun chip_screenRecordHideAndCallShown_callShown() = - kosmos.testScope.runTest { - kosmos.screenRecordRepository.screenRecordState.value = ScreenRecordModel.DoingNothing + fun chip_screenRecordShowAndMediaProjectionShow_screenRecordShown() = + testScope.runTest { + screenRecordState.value = ScreenRecordModel.Recording + mediaProjectionState.value = MediaProjectionState.EntireScreen + callState.value = OngoingActivityChipModel.Hidden + + val latest by collectLastValue(underTest.chip) + + assertIsScreenRecordChip(latest) + } + @Test + fun chip_mediaProjectionShowAndCallShow_mediaProjectionShown() = + testScope.runTest { + screenRecordState.value = ScreenRecordModel.DoingNothing + mediaProjectionState.value = MediaProjectionState.EntireScreen val callChip = OngoingActivityChipModel.Shown( Icon.Resource(R.drawable.ic_call, ContentDescription.Loaded("icon")), startTimeMs = 600L, ) {} - kosmos.callChipInteractor.chip.value = callChip + callState.value = callChip + + val latest by collectLastValue(underTest.chip) + + assertIsMediaProjectionChip(latest) + } + + @Test + fun chip_screenRecordAndMediaProjectionHideAndCallShown_callShown() = + testScope.runTest { + screenRecordState.value = ScreenRecordModel.DoingNothing + mediaProjectionState.value = MediaProjectionState.NotProjecting + + val callChip = + OngoingActivityChipModel.Shown( + Icon.Resource(R.drawable.ic_call, ContentDescription.Loaded("icon")), + startTimeMs = 600L, + ) {} + callState.value = callChip val latest by collectLastValue(underTest.chip) @@ -95,22 +136,29 @@ class OngoingActivityChipsViewModelTest : SysuiTestCase() { @Test fun chip_higherPriorityChipAdded_lowerPriorityChipReplaced() = - kosmos.testScope.runTest { + testScope.runTest { // Start with just the lower priority call chip val callChip = OngoingActivityChipModel.Shown( Icon.Resource(R.drawable.ic_call, ContentDescription.Loaded("icon")), startTimeMs = 600L, ) {} - kosmos.callChipInteractor.chip.value = callChip - kosmos.screenRecordRepository.screenRecordState.value = ScreenRecordModel.DoingNothing + callState.value = callChip + mediaProjectionState.value = MediaProjectionState.NotProjecting + screenRecordState.value = ScreenRecordModel.DoingNothing val latest by collectLastValue(underTest.chip) assertThat(latest).isEqualTo(callChip) + // WHEN the higher priority media projection chip is added + mediaProjectionState.value = MediaProjectionState.SingleTask(createTask(taskId = 1)) + + // THEN the higher priority media projection chip is used + assertIsMediaProjectionChip(latest) + // WHEN the higher priority screen record chip is added - kosmos.screenRecordRepository.screenRecordState.value = ScreenRecordModel.Recording + screenRecordState.value = ScreenRecordModel.Recording // THEN the higher priority screen record chip is used assertIsScreenRecordChip(latest) @@ -118,31 +166,47 @@ class OngoingActivityChipsViewModelTest : SysuiTestCase() { @Test fun chip_highestPriorityChipRemoved_showsNextPriorityChip() = - kosmos.testScope.runTest { - // Start with both the higher priority screen record chip and lower priority call chip - kosmos.screenRecordRepository.screenRecordState.value = ScreenRecordModel.Recording + testScope.runTest { + // WHEN all chips are active + screenRecordState.value = ScreenRecordModel.Recording + mediaProjectionState.value = MediaProjectionState.EntireScreen val callChip = OngoingActivityChipModel.Shown( Icon.Resource(R.drawable.ic_call, ContentDescription.Loaded("icon")), startTimeMs = 600L, ) {} - kosmos.callChipInteractor.chip.value = callChip + callState.value = callChip val latest by collectLastValue(underTest.chip) + // THEN the highest priority screen record is used assertIsScreenRecordChip(latest) // WHEN the higher priority screen record is removed - kosmos.screenRecordRepository.screenRecordState.value = ScreenRecordModel.DoingNothing + screenRecordState.value = ScreenRecordModel.DoingNothing + + // THEN the lower priority media projection is used + assertIsMediaProjectionChip(latest) + + // WHEN the higher priority media projection is removed + mediaProjectionState.value = MediaProjectionState.NotProjecting // THEN the lower priority call is used assertThat(latest).isEqualTo(callChip) } - private fun assertIsScreenRecordChip(latest: OngoingActivityChipModel?) { - assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java) - val icon = (latest as OngoingActivityChipModel.Shown).icon - assertThat((icon as Icon.Resource).res).isEqualTo(R.drawable.stat_sys_screen_record) + companion object { + fun assertIsScreenRecordChip(latest: OngoingActivityChipModel?) { + assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java) + val icon = (latest as OngoingActivityChipModel.Shown).icon + assertThat((icon as Icon.Resource).res).isEqualTo(R.drawable.stat_sys_screen_record) + } + + fun assertIsMediaProjectionChip(latest: OngoingActivityChipModel?) { + assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java) + val icon = (latest as OngoingActivityChipModel.Shown).icon + assertThat((icon as Icon.Resource).res).isEqualTo(R.drawable.ic_cast_connected) + } } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImplTest.kt index 7903a731c1d0..e984200c305e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImplTest.kt @@ -91,7 +91,8 @@ class VisualInterruptionDecisionProviderImplTest : VisualInterruptionDecisionPro avalancheProvider.startTime = whenAgo(10) withFilter( - AvalancheSuppressor(avalancheProvider, systemClock, systemSettings, packageManager) + AvalancheSuppressor(avalancheProvider, systemClock, systemSettings, packageManager, + uiEventLogger) ) { ensurePeekState() assertShouldHeadsUp( @@ -110,7 +111,8 @@ class VisualInterruptionDecisionProviderImplTest : VisualInterruptionDecisionPro avalancheProvider.startTime = whenAgo(10) withFilter( - AvalancheSuppressor(avalancheProvider, systemClock, systemSettings, packageManager) + AvalancheSuppressor(avalancheProvider, systemClock, systemSettings, packageManager, + uiEventLogger) ) { ensurePeekState() assertShouldNotHeadsUp( @@ -129,7 +131,8 @@ class VisualInterruptionDecisionProviderImplTest : VisualInterruptionDecisionPro avalancheProvider.startTime = whenAgo(10) withFilter( - AvalancheSuppressor(avalancheProvider, systemClock, systemSettings, packageManager) + AvalancheSuppressor(avalancheProvider, systemClock, systemSettings, packageManager, + uiEventLogger) ) { ensurePeekState() assertShouldHeadsUp( @@ -146,7 +149,8 @@ class VisualInterruptionDecisionProviderImplTest : VisualInterruptionDecisionPro avalancheProvider.startTime = whenAgo(10) withFilter( - AvalancheSuppressor(avalancheProvider, systemClock, systemSettings, packageManager) + AvalancheSuppressor(avalancheProvider, systemClock, systemSettings, packageManager, + uiEventLogger) ) { ensurePeekState() assertShouldHeadsUp( @@ -163,7 +167,8 @@ class VisualInterruptionDecisionProviderImplTest : VisualInterruptionDecisionPro avalancheProvider.startTime = whenAgo(10) withFilter( - AvalancheSuppressor(avalancheProvider, systemClock, systemSettings, packageManager) + AvalancheSuppressor(avalancheProvider, systemClock, systemSettings, packageManager, + uiEventLogger) ) { ensurePeekState() assertShouldHeadsUp( @@ -180,7 +185,8 @@ class VisualInterruptionDecisionProviderImplTest : VisualInterruptionDecisionPro avalancheProvider.startTime = whenAgo(10) withFilter( - AvalancheSuppressor(avalancheProvider, systemClock, systemSettings, packageManager) + AvalancheSuppressor(avalancheProvider, systemClock, systemSettings, packageManager, + uiEventLogger) ) { ensurePeekState() assertShouldHeadsUp( @@ -197,7 +203,8 @@ class VisualInterruptionDecisionProviderImplTest : VisualInterruptionDecisionPro avalancheProvider.startTime = whenAgo(10) withFilter( - AvalancheSuppressor(avalancheProvider, systemClock, systemSettings, packageManager) + AvalancheSuppressor(avalancheProvider, systemClock, systemSettings, packageManager, + uiEventLogger) ) { assertFsiNotSuppressed() } @@ -208,7 +215,8 @@ class VisualInterruptionDecisionProviderImplTest : VisualInterruptionDecisionPro avalancheProvider.startTime = whenAgo(10) withFilter( - AvalancheSuppressor(avalancheProvider, systemClock, systemSettings, packageManager) + AvalancheSuppressor(avalancheProvider, systemClock, systemSettings, packageManager, + uiEventLogger) ) { ensurePeekState() assertShouldHeadsUp( @@ -232,7 +240,8 @@ class VisualInterruptionDecisionProviderImplTest : VisualInterruptionDecisionPro ).thenReturn(PERMISSION_GRANTED) withFilter( - AvalancheSuppressor(avalancheProvider, systemClock, systemSettings, packageManager) + AvalancheSuppressor(avalancheProvider, systemClock, systemSettings, packageManager, + uiEventLogger) ) { ensurePeekState() assertShouldHeadsUp( 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 f461e2f67d20..12f3ef3cf553 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 @@ -171,7 +171,6 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { // and then we would test both configurations, but currently they are all read // in the constructor. mSetFlagsRule.enableFlags(FLAG_NEW_AOD_TRANSITION); - mFeatureFlags.setDefault(Flags.UNCLEARED_TRANSIENT_HUN_FIX); // Inject dependencies before initializing the layout mDependency.injectTestDependency(FeatureFlags.class, mFeatureFlags); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentLoggerTest.kt index 87d813c6d19f..e0f1e1a46d3b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentLoggerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentLoggerTest.kt @@ -63,7 +63,7 @@ class CollapsedStatusBarFragmentLoggerTest : SysuiTestCase() { StatusBarVisibilityModel( showClock = false, showNotificationIcons = true, - showOngoingCallChip = false, + showOngoingActivityChip = false, showSystemInfo = true, ) ) @@ -74,7 +74,7 @@ class CollapsedStatusBarFragmentLoggerTest : SysuiTestCase() { assertThat(actualString).contains("showClock=false") assertThat(actualString).contains("showNotificationIcons=true") - assertThat(actualString).contains("showOngoingCallChip=false") + assertThat(actualString).contains("showOngoingActivityChip=false") assertThat(actualString).contains("showSystemInfo=true") } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java index ff182ad4a5ea..ee27cea48565 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java @@ -16,6 +16,7 @@ package com.android.systemui.statusbar.phone.fragment; import static android.view.Display.DEFAULT_DISPLAY; +import static com.android.systemui.Flags.FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS; import static com.android.systemui.shade.ShadeExpansionStateManagerKt.STATE_CLOSED; import static com.android.systemui.shade.ShadeExpansionStateManagerKt.STATE_OPEN; @@ -33,6 +34,8 @@ import android.app.StatusBarManager; import android.content.Context; import android.os.Bundle; import android.os.UserHandle; +import android.platform.test.annotations.DisableFlags; +import android.platform.test.annotations.EnableFlags; import android.provider.Settings; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper.RunWithLooper; @@ -46,7 +49,6 @@ import com.android.systemui.SysuiBaseFragmentTest; import com.android.systemui.animation.AnimatorTestRule; import com.android.systemui.demomode.DemoModeController; import com.android.systemui.dump.DumpManager; -import com.android.systemui.kosmos.KosmosJavaAdapter; import com.android.systemui.log.LogBuffer; import com.android.systemui.log.LogcatEchoTracker; import com.android.systemui.plugins.DarkIconDispatcher; @@ -56,7 +58,6 @@ import com.android.systemui.shade.ShadeExpansionStateManager; import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.OperatorNameViewController; -import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipsViewModel; import com.android.systemui.statusbar.disableflags.DisableFlagsLogger; import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler; import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerStatusBarViewBinder; @@ -92,11 +93,9 @@ import java.util.List; @RunWithLooper(setAsMainLooper = true) @SmallTest public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { - private final KosmosJavaAdapter mKosmos = new KosmosJavaAdapter(); private NotificationIconAreaController mMockNotificationAreaController; private ShadeExpansionStateManager mShadeExpansionStateManager; private OngoingCallController mOngoingCallController; - private OngoingActivityChipsViewModel mOngoingActivityChipsViewModel; private SystemStatusAnimationScheduler mAnimationScheduler; private StatusBarLocationPublisher mLocationPublisher; // Set in instantiate() @@ -421,6 +420,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { } @Test + @DisableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS) public void disable_noOngoingCall_chipHidden() { CollapsedStatusBarFragment fragment = resumeAndGetFragment(); @@ -433,6 +433,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { } @Test + @DisableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS) public void disable_hasOngoingCall_chipDisplayedAndNotificationIconsHidden() { CollapsedStatusBarFragment fragment = resumeAndGetFragment(); @@ -446,6 +447,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { } @Test + @DisableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS) public void disable_hasOngoingCallButNotificationIconsDisabled_chipHidden() { CollapsedStatusBarFragment fragment = resumeAndGetFragment(); @@ -459,6 +461,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { } @Test + @DisableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS) public void disable_hasOngoingCallButAlsoHun_chipHidden() { CollapsedStatusBarFragment fragment = resumeAndGetFragment(); @@ -472,6 +475,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { } @Test + @DisableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS) public void disable_ongoingCallEnded_chipHidden() { CollapsedStatusBarFragment fragment = resumeAndGetFragment(); @@ -498,8 +502,11 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { } @Test + @DisableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS) public void disable_hasOngoingCall_hidesNotifsWithoutAnimation() { CollapsedStatusBarFragment fragment = resumeAndGetFragment(); + // Enable animations for testing so that we can verify we still aren't animating + fragment.enableAnimationsForTesting(); fragment.disable(DEFAULT_DISPLAY, 0, 0, false); // Ongoing call started @@ -512,6 +519,161 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { } @Test + @DisableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS) + public void screenSharingChipsDisabled_ignoresNewCallback() { + CollapsedStatusBarFragment fragment = resumeAndGetFragment(); + + // WHEN there *is* an ongoing call via old callback + when(mOngoingCallController.hasOngoingCall()).thenReturn(true); + fragment.disable(DEFAULT_DISPLAY, 0, 0, true); + + // WHEN there's *no* ongoing activity via new callback + mCollapsedStatusBarViewBinder.getListener().onOngoingActivityStatusChanged( + /* hasOngoingActivity= */ false); + + // THEN the old callback value is used, so the view is shown + assertEquals(View.VISIBLE, + mFragment.getView().findViewById(R.id.ongoing_activity_chip).getVisibility()); + + // WHEN there's *no* ongoing call via old callback + when(mOngoingCallController.hasOngoingCall()).thenReturn(false); + fragment.disable(DEFAULT_DISPLAY, 0, 0, false); + + // WHEN there *is* an ongoing activity via new callback + mCollapsedStatusBarViewBinder.getListener().onOngoingActivityStatusChanged( + /* hasOngoingActivity= */ true); + + // THEN the old callback value is used, so the view is hidden + assertEquals(View.GONE, + mFragment.getView().findViewById(R.id.ongoing_activity_chip).getVisibility()); + } + + @Test + @EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS) + public void noOngoingActivity_chipHidden() { + resumeAndGetFragment(); + + // TODO(b/332662551): We *should* be able to just set a value on + // mCollapsedStatusBarViewModel.getOngoingActivityChip() instead of manually invoking the + // listener, but I'm unable to get the fragment to get attached so that the binder starts + // listening to flows. + mCollapsedStatusBarViewBinder.getListener().onOngoingActivityStatusChanged( + /* hasOngoingActivity= */ false); + + assertEquals(View.GONE, + mFragment.getView().findViewById(R.id.ongoing_activity_chip).getVisibility()); + } + + @Test + @EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS) + public void hasOngoingActivity_chipDisplayedAndNotificationIconsHidden() { + resumeAndGetFragment(); + + mCollapsedStatusBarViewBinder.getListener().onOngoingActivityStatusChanged( + /* hasOngoingActivity= */ true); + + assertEquals(View.VISIBLE, + mFragment.getView().findViewById(R.id.ongoing_activity_chip).getVisibility()); + assertEquals(View.INVISIBLE, getNotificationAreaView().getVisibility()); + } + + @Test + @EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS) + public void hasOngoingActivityButNotificationIconsDisabled_chipHidden() { + CollapsedStatusBarFragment fragment = resumeAndGetFragment(); + + mCollapsedStatusBarViewBinder.getListener().onOngoingActivityStatusChanged( + /* hasOngoingActivity= */ true); + + fragment.disable(DEFAULT_DISPLAY, + StatusBarManager.DISABLE_NOTIFICATION_ICONS, 0, false); + + assertEquals(View.GONE, + mFragment.getView().findViewById(R.id.ongoing_activity_chip).getVisibility()); + } + + @Test + @EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS) + public void hasOngoingActivityButAlsoHun_chipHidden() { + CollapsedStatusBarFragment fragment = resumeAndGetFragment(); + + mCollapsedStatusBarViewBinder.getListener().onOngoingActivityStatusChanged( + /* hasOngoingActivity= */ true); + when(mHeadsUpAppearanceController.shouldBeVisible()).thenReturn(true); + + fragment.disable(DEFAULT_DISPLAY, 0, 0, false); + + assertEquals(View.GONE, + mFragment.getView().findViewById(R.id.ongoing_activity_chip).getVisibility()); + } + + @Test + @EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS) + public void ongoingActivityEnded_chipHidden() { + resumeAndGetFragment(); + + // Ongoing activity started + mCollapsedStatusBarViewBinder.getListener().onOngoingActivityStatusChanged( + /* hasOngoingActivity= */ true); + + assertEquals(View.VISIBLE, + mFragment.getView().findViewById(R.id.ongoing_activity_chip).getVisibility()); + + // Ongoing activity ended + mCollapsedStatusBarViewBinder.getListener().onOngoingActivityStatusChanged( + /* hasOngoingActivity= */ false); + + assertEquals(View.GONE, + mFragment.getView().findViewById(R.id.ongoing_activity_chip).getVisibility()); + } + + @Test + @EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS) + public void hasOngoingActivity_hidesNotifsWithoutAnimation() { + CollapsedStatusBarFragment fragment = resumeAndGetFragment(); + // Enable animations for testing so that we can verify we still aren't animating + fragment.enableAnimationsForTesting(); + + // Ongoing call started + mCollapsedStatusBarViewBinder.getListener().onOngoingActivityStatusChanged( + /* hasOngoingActivity= */ true); + + // Notification area is hidden without delay + assertEquals(0f, getNotificationAreaView().getAlpha(), 0.01); + assertEquals(View.INVISIBLE, getNotificationAreaView().getVisibility()); + } + + @Test + @EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS) + public void screenSharingChipsEnabled_ignoresOngoingCallController() { + CollapsedStatusBarFragment fragment = resumeAndGetFragment(); + + // WHEN there *is* an ongoing call via old callback + when(mOngoingCallController.hasOngoingCall()).thenReturn(true); + fragment.disable(DEFAULT_DISPLAY, 0, 0, true); + + // WHEN there's *no* ongoing activity via new callback + mCollapsedStatusBarViewBinder.getListener().onOngoingActivityStatusChanged( + /* hasOngoingActivity= */ false); + + // THEN the new callback value is used, so the view is hidden + assertEquals(View.GONE, + mFragment.getView().findViewById(R.id.ongoing_activity_chip).getVisibility()); + + // WHEN there's *no* ongoing call via old callback + when(mOngoingCallController.hasOngoingCall()).thenReturn(false); + fragment.disable(DEFAULT_DISPLAY, 0, 0, false); + + // WHEN there *is* an ongoing activity via new callback + mCollapsedStatusBarViewBinder.getListener().onOngoingActivityStatusChanged( + /* hasOngoingActivity= */ true); + + // THEN the new callback value is used, so the view is shown + assertEquals(View.VISIBLE, + mFragment.getView().findViewById(R.id.ongoing_activity_chip).getVisibility()); + } + + @Test public void disable_isDozing_clockAndSystemInfoVisible() { CollapsedStatusBarFragment fragment = resumeAndGetFragment(); when(mStatusBarStateController.isDozing()).thenReturn(true); @@ -670,7 +832,6 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { MockitoAnnotations.initMocks(this); setUpDaggerComponent(); mOngoingCallController = mock(OngoingCallController.class); - mOngoingActivityChipsViewModel = mKosmos.getOngoingActivityChipsViewModel(); mAnimationScheduler = mock(SystemStatusAnimationScheduler.class); mLocationPublisher = mock(StatusBarLocationPublisher.class); mStatusBarIconController = mock(StatusBarIconController.class); @@ -691,7 +852,6 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { return new CollapsedStatusBarFragment( mStatusBarFragmentComponentFactory, mOngoingCallController, - mOngoingActivityChipsViewModel, mAnimationScheduler, mLocationPublisher, mMockNotificationAreaController, @@ -773,7 +933,9 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { private CollapsedStatusBarFragment resumeAndGetFragment() { mFragments.dispatchResume(); processAllMessages(); - return (CollapsedStatusBarFragment) mFragment; + CollapsedStatusBarFragment fragment = (CollapsedStatusBarFragment) mFragment; + fragment.disableAnimationsForTesting(); + return fragment; } private View getUserChipView() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/StatusBarVisibilityModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/StatusBarVisibilityModelTest.kt index 8e789cb2cae6..022b5d295256 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/StatusBarVisibilityModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/StatusBarVisibilityModelTest.kt @@ -36,7 +36,7 @@ class StatusBarVisibilityModelTest : SysuiTestCase() { StatusBarVisibilityModel( showClock = true, showNotificationIcons = true, - showOngoingCallChip = true, + showOngoingActivityChip = true, showSystemInfo = true, ) @@ -72,17 +72,17 @@ class StatusBarVisibilityModelTest : SysuiTestCase() { } @Test - fun createModelFromFlags_ongoingCallChipNotDisabled_showOngoingCallChipTrue() { + fun createModelFromFlags_ongoingCallChipNotDisabled_showOngoingActivityChipTrue() { val result = createModelFromFlags(disabled1 = 0, disabled2 = 0) - assertThat(result.showOngoingCallChip).isTrue() + assertThat(result.showOngoingActivityChip).isTrue() } @Test - fun createModelFromFlags_ongoingCallChipDisabled_showOngoingCallChipFalse() { + fun createModelFromFlags_ongoingCallChipDisabled_showOngoingActivityChipFalse() { val result = createModelFromFlags(disabled1 = DISABLE_ONGOING_CALL_CHIP, disabled2 = 0) - assertThat(result.showOngoingCallChip).isFalse() + assertThat(result.showOngoingActivityChip).isFalse() } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModelImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModelImplTest.kt index 606feab86d58..c9fe44918757 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModelImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModelImplTest.kt @@ -32,6 +32,14 @@ import com.android.systemui.kosmos.applicationCoroutineScope import com.android.systemui.kosmos.testDispatcher import com.android.systemui.kosmos.testScope import com.android.systemui.log.assertLogsWtf +import com.android.systemui.mediaprojection.data.model.MediaProjectionState +import com.android.systemui.mediaprojection.data.repository.fakeMediaProjectionRepository +import com.android.systemui.screenrecord.data.model.ScreenRecordModel +import com.android.systemui.screenrecord.data.repository.screenRecordRepository +import com.android.systemui.statusbar.chips.domain.model.OngoingActivityChipModel +import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipsViewModelTest.Companion.assertIsMediaProjectionChip +import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipsViewModelTest.Companion.assertIsScreenRecordChip +import com.android.systemui.statusbar.chips.ui.viewmodel.ongoingActivityChipsViewModel import com.android.systemui.statusbar.data.model.StatusBarMode import com.android.systemui.statusbar.data.repository.FakeStatusBarModeRepository.Companion.DISPLAY_ID import com.android.systemui.statusbar.data.repository.fakeStatusBarModeRepository @@ -65,6 +73,7 @@ class CollapsedStatusBarViewModelImplTest : SysuiTestCase() { kosmos.lightsOutInteractor, kosmos.activeNotificationsInteractor, kosmos.keyguardTransitionInteractor, + kosmos.ongoingActivityChipsViewModel, kosmos.applicationCoroutineScope, ) @@ -382,6 +391,25 @@ class CollapsedStatusBarViewModelImplTest : SysuiTestCase() { } } + @Test + fun ongoingActivityChip_matchesViewModel() = + testScope.runTest { + val latest by collectLastValue(underTest.ongoingActivityChip) + + kosmos.screenRecordRepository.screenRecordState.value = ScreenRecordModel.Recording + + assertIsScreenRecordChip(latest) + + kosmos.screenRecordRepository.screenRecordState.value = ScreenRecordModel.DoingNothing + + assertThat(latest).isEqualTo(OngoingActivityChipModel.Hidden) + + kosmos.fakeMediaProjectionRepository.mediaProjectionState.value = + MediaProjectionState.EntireScreen + + assertIsMediaProjectionChip(latest) + } + private fun activeNotificationsStore(notifications: List<ActiveNotificationModel>) = ActiveNotificationsStore.Builder() .apply { notifications.forEach(::addIndividualNotif) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeCollapsedStatusBarViewModel.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeCollapsedStatusBarViewModel.kt index bc50f7967403..c3c9907bc891 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeCollapsedStatusBarViewModel.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeCollapsedStatusBarViewModel.kt @@ -16,6 +16,7 @@ package com.android.systemui.statusbar.pipeline.shared.ui.viewmodel +import com.android.systemui.statusbar.chips.domain.model.OngoingActivityChipModel import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow @@ -27,6 +28,9 @@ class FakeCollapsedStatusBarViewModel : CollapsedStatusBarViewModel { override val transitionFromLockscreenToDreamStartedEvent = MutableSharedFlow<Unit>() + override val ongoingActivityChip: MutableStateFlow<OngoingActivityChipModel> = + MutableStateFlow(OngoingActivityChipModel.Hidden) + override fun areNotificationsLightsOut(displayId: Int): Flow<Boolean> = areNotificationLightsOut fun setNotificationLightsOut(lightsOut: Boolean) { diff --git a/packages/SystemUI/tests/utils/src/android/graphics/drawable/TestStubDrawable.kt b/packages/SystemUI/tests/utils/src/android/graphics/drawable/TestStubDrawable.kt index 1a9f4b40c179..430fb5985848 100644 --- a/packages/SystemUI/tests/utils/src/android/graphics/drawable/TestStubDrawable.kt +++ b/packages/SystemUI/tests/utils/src/android/graphics/drawable/TestStubDrawable.kt @@ -27,8 +27,11 @@ import android.graphics.PixelFormat class TestStubDrawable(private val name: String? = null) : Drawable() { override fun draw(canvas: Canvas) = Unit + override fun setAlpha(alpha: Int) = Unit + override fun setColorFilter(colorFilter: ColorFilter?) = Unit + override fun getOpacity(): Int = PixelFormat.UNKNOWN override fun toString(): String { @@ -38,6 +41,10 @@ class TestStubDrawable(private val name: String? = null) : Drawable() { override fun getConstantState(): ConstantState = TestStubConstantState(this, changingConfigurations) + override fun equals(other: Any?): Boolean { + return (other as? TestStubDrawable ?: return false).name == name + } + private class TestStubConstantState( private val drawable: Drawable, private val changingConfigurations: Int, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiBaseFragmentTest.java b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiBaseFragmentTest.java index e470406499b6..a5819931549d 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiBaseFragmentTest.java +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiBaseFragmentTest.java @@ -19,6 +19,7 @@ import static org.mockito.Mockito.when; import android.app.Fragment; import android.app.Instrumentation; +import android.platform.test.flag.junit.SetFlagsRule; import android.testing.BaseFragmentTest; import android.testing.DexmakerShareClassLoaderRule; @@ -31,6 +32,7 @@ import com.android.systemui.utils.leaks.LeakCheckedTest.SysuiLeakCheck; import org.junit.After; import org.junit.AfterClass; import org.junit.Before; +import org.junit.ClassRule; import org.junit.Rule; import org.mockito.Mockito; @@ -43,6 +45,12 @@ public abstract class SysuiBaseFragmentTest extends BaseFragmentTest { @Rule public final SysuiLeakCheck mLeakCheck = new SysuiLeakCheck(); + @ClassRule + public static final SetFlagsRule.ClassRule mSetFlagsClassRule = + new SetFlagsRule.ClassRule( + com.android.systemui.Flags.class); + @Rule public final SetFlagsRule mSetFlagsRule = mSetFlagsClassRule.createSetFlagsRule(); + @Rule public final DexmakerShareClassLoaderRule mDexmakerShareClassLoaderRule = new DexmakerShareClassLoaderRule(); diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakeDisplayStateRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakeDisplayStateRepository.kt index 9765d531472c..53285eb715ba 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakeDisplayStateRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakeDisplayStateRepository.kt @@ -23,7 +23,6 @@ import com.android.systemui.dagger.SysUISingleton import dagger.Binds import dagger.Module import javax.inject.Inject -import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow @@ -40,7 +39,7 @@ class FakeDisplayStateRepository @Inject constructor() : DisplayStateRepository override val currentDisplaySize: StateFlow<Size> = _currentDisplaySize.asStateFlow() private val _isLargeScreen = MutableStateFlow<Boolean>(false) - override val isLargeScreen: Flow<Boolean> = _isLargeScreen.asStateFlow() + override val isLargeScreen: StateFlow<Boolean> = _isLargeScreen.asStateFlow() override val isReverseDefaultRotation = false @@ -55,6 +54,10 @@ class FakeDisplayStateRepository @Inject constructor() : DisplayStateRepository fun setCurrentDisplaySize(size: Size) { _currentDisplaySize.value = size } + + fun setIsLargeScreen(isLargeScreen: Boolean) { + _isLargeScreen.value = isLargeScreen + } } @Module diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractorKosmos.kt index 7f9a71cd149e..56297f0d7f43 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractorKosmos.kt @@ -25,6 +25,7 @@ import com.android.systemui.kosmos.Kosmos.Fixture val Kosmos.promptSelectorInteractor by Fixture { PromptSelectorInteractorImpl( fingerprintPropertyRepository = fingerprintPropertyRepository, + displayStateInteractor = displayStateInteractor, promptRepository = promptRepository, lockPatternUtils = lockPatternUtils ) diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/brightness/data/repository/FakeScreenBrightnessRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/brightness/data/repository/FakeScreenBrightnessRepository.kt index a05b5e65ce9d..ad5242e2e036 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/brightness/data/repository/FakeScreenBrightnessRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/brightness/data/repository/FakeScreenBrightnessRepository.kt @@ -19,7 +19,7 @@ package com.android.systemui.brightness.data.repository import android.hardware.display.BrightnessInfo import android.hardware.display.BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE import android.hardware.display.BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF -import com.android.systemui.brightness.data.model.LinearBrightness +import com.android.systemui.brightness.shared.model.LinearBrightness import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.map diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/brightness/domain/interactor/ScreenBrightnessInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/brightness/domain/interactor/ScreenBrightnessInteractorKosmos.kt index 22784e47d277..0e8427310895 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/brightness/domain/interactor/ScreenBrightnessInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/brightness/domain/interactor/ScreenBrightnessInteractorKosmos.kt @@ -18,6 +18,15 @@ package com.android.systemui.brightness.domain.interactor import com.android.systemui.brightness.data.repository.screenBrightnessRepository import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.applicationCoroutineScope +import com.android.systemui.log.table.TableLogBuffer +import com.android.systemui.util.mockito.mock val Kosmos.screenBrightnessInteractor by - Kosmos.Fixture { ScreenBrightnessInteractor(screenBrightnessRepository) } + Kosmos.Fixture { + ScreenBrightnessInteractor( + screenBrightnessRepository, + applicationCoroutineScope, + mock<TableLogBuffer>(), + ) + } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBlueprintViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBlueprintViewModelKosmos.kt index 63b87c075378..0c538ff1d6fe 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBlueprintViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBlueprintViewModelKosmos.kt @@ -16,8 +16,14 @@ package com.android.systemui.keyguard.ui.viewmodel +import android.os.fakeExecutorHandler import com.android.systemui.keyguard.domain.interactor.keyguardBlueprintInteractor import com.android.systemui.kosmos.Kosmos val Kosmos.keyguardBlueprintViewModel by - Kosmos.Fixture { KeyguardBlueprintViewModel(keyguardBlueprintInteractor) } + Kosmos.Fixture { + KeyguardBlueprintViewModel( + fakeExecutorHandler, + keyguardBlueprintInteractor, + ) + } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt index f856d2700270..2567ffee9be8 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt @@ -32,7 +32,7 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi val Kosmos.keyguardRootViewModel by Fixture { KeyguardRootViewModel( - scope = applicationCoroutineScope, + applicationScope = applicationCoroutineScope, deviceEntryInteractor = deviceEntryInteractor, dozeParameters = dozeParameters, keyguardInteractor = keyguardInteractor, diff --git a/packages/SystemUI/src/com/android/systemui/brightness/data/model/LinearBrightness.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/mediaprojection/data/repository/FakeMediaProjectionRepository.kt index 608f301da85d..c4365c9093d2 100644 --- a/packages/SystemUI/src/com/android/systemui/brightness/data/model/LinearBrightness.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/mediaprojection/data/repository/FakeMediaProjectionRepository.kt @@ -14,17 +14,15 @@ * limitations under the License. */ -package com.android.systemui.brightness.data.model +package com.android.systemui.mediaprojection.data.repository -@JvmInline -value class LinearBrightness(val floatValue: Float) { - fun clamp(min: LinearBrightness, max: LinearBrightness): LinearBrightness { - return if (floatValue < min.floatValue) { - min - } else if (floatValue > max.floatValue) { - max - } else { - this - } - } +import android.app.ActivityManager +import com.android.systemui.mediaprojection.data.model.MediaProjectionState +import kotlinx.coroutines.flow.MutableStateFlow + +class FakeMediaProjectionRepository : MediaProjectionRepository { + override suspend fun switchProjectedTask(task: ActivityManager.RunningTaskInfo) {} + + override val mediaProjectionState: MutableStateFlow<MediaProjectionState> = + MutableStateFlow(MediaProjectionState.NotProjecting) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionRepositoryKosmos.kt new file mode 100644 index 000000000000..f253e949375e --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionRepositoryKosmos.kt @@ -0,0 +1,39 @@ +/* + * 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.mediaprojection.data.repository + +import android.os.Handler +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.applicationCoroutineScope +import com.android.systemui.kosmos.testDispatcher +import com.android.systemui.mediaprojection.taskswitcher.activityTaskManagerTasksRepository +import com.android.systemui.mediaprojection.taskswitcher.fakeMediaProjectionManager + +val Kosmos.fakeMediaProjectionRepository: FakeMediaProjectionRepository by + Kosmos.Fixture { FakeMediaProjectionRepository() } + +val Kosmos.realMediaProjectionRepository by + Kosmos.Fixture { + MediaProjectionManagerRepository( + mediaProjectionManager = fakeMediaProjectionManager.mediaProjectionManager, + handler = Handler.getMain(), + applicationScope = applicationCoroutineScope, + tasksRepository = activityTaskManagerTasksRepository, + backgroundDispatcher = testDispatcher, + mediaProjectionServiceHelper = fakeMediaProjectionManager.helper, + ) + } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/mediaprojection/taskswitcher/TaskSwitcherKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/mediaprojection/taskswitcher/TaskSwitcherKosmos.kt index 5b1f95a654cb..5acadd7f192a 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/mediaprojection/taskswitcher/TaskSwitcherKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/mediaprojection/taskswitcher/TaskSwitcherKosmos.kt @@ -16,11 +16,10 @@ package com.android.systemui.mediaprojection.taskswitcher -import android.os.Handler import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.applicationCoroutineScope import com.android.systemui.kosmos.testDispatcher -import com.android.systemui.mediaprojection.data.repository.MediaProjectionManagerRepository +import com.android.systemui.mediaprojection.data.repository.realMediaProjectionRepository import com.android.systemui.mediaprojection.taskswitcher.data.repository.ActivityTaskManagerTasksRepository import com.android.systemui.mediaprojection.taskswitcher.domain.interactor.TaskSwitchInteractor import com.android.systemui.mediaprojection.taskswitcher.ui.viewmodel.TaskSwitcherNotificationViewModel @@ -40,21 +39,9 @@ val Kosmos.activityTaskManagerTasksRepository by ) } -val Kosmos.mediaProjectionManagerRepository by - Kosmos.Fixture { - MediaProjectionManagerRepository( - mediaProjectionManager = fakeMediaProjectionManager.mediaProjectionManager, - handler = Handler.getMain(), - applicationScope = applicationCoroutineScope, - tasksRepository = activityTaskManagerTasksRepository, - backgroundDispatcher = testDispatcher, - mediaProjectionServiceHelper = fakeMediaProjectionManager.helper, - ) - } - val Kosmos.taskSwitcherInteractor by Kosmos.Fixture { - TaskSwitchInteractor(mediaProjectionManagerRepository, activityTaskManagerTasksRepository) + TaskSwitchInteractor(realMediaProjectionRepository, activityTaskManagerTasksRepository) } val Kosmos.taskSwitcherViewModel by diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/repository/QSPipelineRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/repository/QSPipelineRepositoryKosmos.kt index 604c16fd9e74..5ff44e5d33c5 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/repository/QSPipelineRepositoryKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/repository/QSPipelineRepositoryKosmos.kt @@ -17,6 +17,8 @@ package com.android.systemui.qs.pipeline.data.repository import com.android.systemui.kosmos.Kosmos +import com.android.systemui.retail.data.repository.FakeRetailModeRepository +import com.android.systemui.retail.data.repository.RetailModeRepository /** This fake uses 0 as the minimum number of tiles. That means that no tiles is a valid state. */ var Kosmos.fakeMinimumTilesRepository by Kosmos.Fixture { MinimumTilesFixedRepository(0) } @@ -46,3 +48,6 @@ var Kosmos.installedTilesRepository: InstalledTilesComponentRepository by val Kosmos.fakeCustomTileAddedRepository by Kosmos.Fixture { FakeCustomTileAddedRepository() } var Kosmos.customTileAddedRepository: CustomTileAddedRepository by Kosmos.Fixture { fakeCustomTileAddedRepository } + +val Kosmos.fakeRetailModeRepository by Kosmos.Fixture { FakeRetailModeRepository() } +var Kosmos.retailModeRepository: RetailModeRepository by Kosmos.Fixture { fakeRetailModeRepository } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorKosmos.kt index b870039982f1..d97a5b2bede2 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorKosmos.kt @@ -24,6 +24,7 @@ import com.android.systemui.qs.external.tileLifecycleManagerFactory import com.android.systemui.qs.pipeline.data.repository.customTileAddedRepository import com.android.systemui.qs.pipeline.data.repository.installedTilesRepository import com.android.systemui.qs.pipeline.data.repository.minimumTilesRepository +import com.android.systemui.qs.pipeline.data.repository.retailModeRepository import com.android.systemui.qs.pipeline.data.repository.tileSpecRepository import com.android.systemui.qs.pipeline.shared.logging.qsLogger import com.android.systemui.qs.pipeline.shared.pipelineFlagsRepository @@ -39,6 +40,7 @@ val Kosmos.currentTilesInteractor: CurrentTilesInteractor by installedTilesRepository, userRepository, minimumTilesRepository, + retailModeRepository, customTileStatePersister, { newQSTileFactory }, qsTileFactory, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelKosmos.kt index 9e02df9c3f41..88bde2ed5d8f 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelKosmos.kt @@ -19,7 +19,9 @@ package com.android.systemui.statusbar.chips.ui.viewmodel import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.applicationCoroutineScope import com.android.systemui.kosmos.testScope +import com.android.systemui.mediaprojection.data.repository.fakeMediaProjectionRepository import com.android.systemui.screenrecord.data.repository.screenRecordRepository +import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractor import com.android.systemui.statusbar.chips.screenrecord.domain.interactor.ScreenRecordChipInteractor import com.android.systemui.util.time.fakeSystemClock @@ -32,6 +34,15 @@ val Kosmos.screenRecordChipInteractor: ScreenRecordChipInteractor by ) } +val Kosmos.mediaProjectionChipInteractor: MediaProjectionChipInteractor by + Kosmos.Fixture { + MediaProjectionChipInteractor( + scope = applicationCoroutineScope, + mediaProjectionRepository = fakeMediaProjectionRepository, + systemClock = fakeSystemClock, + ) + } + val Kosmos.callChipInteractor: FakeCallChipInteractor by Kosmos.Fixture { FakeCallChipInteractor() } val Kosmos.ongoingActivityChipsViewModel: OngoingActivityChipsViewModel by @@ -39,6 +50,7 @@ val Kosmos.ongoingActivityChipsViewModel: OngoingActivityChipsViewModel by OngoingActivityChipsViewModel( testScope.backgroundScope, screenRecordChipInteractor = screenRecordChipInteractor, + mediaProjectionChipInteractor = mediaProjectionChipInteractor, callChipInteractor = callChipInteractor, ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/data/repository/TestAudioDevicesFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/data/repository/TestAudioDevicesFactory.kt new file mode 100644 index 000000000000..3ac712918100 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/data/repository/TestAudioDevicesFactory.kt @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.volume.data.repository + +import android.annotation.SuppressLint +import android.media.AudioDeviceInfo +import android.media.AudioDevicePort + +@SuppressLint("VisibleForTests") +object TestAudioDevicesFactory { + + fun builtInDevice(deviceName: String = "built_in"): AudioDeviceInfo { + return AudioDeviceInfo( + AudioDevicePort.createForTesting( + AudioDeviceInfo.TYPE_BUILTIN_SPEAKER, + deviceName, + "", + ) + ) + } + + fun wiredDevice(deviceName: String = "wired"): AudioDeviceInfo { + return AudioDeviceInfo( + AudioDevicePort.createForTesting( + AudioDeviceInfo.TYPE_WIRED_HEADPHONES, + deviceName, + "", + ) + ) + } + + fun bluetoothDevice( + deviceName: String = "bt", + deviceAddress: String = "test_address", + ): AudioDeviceInfo { + return AudioDeviceInfo( + AudioDevicePort.createForTesting( + AudioDeviceInfo.TYPE_BLE_HEADSET, + deviceName, + deviceAddress, + ) + ) + } +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/domain/interactor/AudioOutputInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/domain/interactor/AudioOutputInteractorKosmos.kt index 63c3ee55ef40..3f51a790aade 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/domain/interactor/AudioOutputInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/domain/interactor/AudioOutputInteractorKosmos.kt @@ -22,7 +22,6 @@ import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.testScope import com.android.systemui.volume.data.repository.audioRepository import com.android.systemui.volume.data.repository.audioSharingRepository -import com.android.systemui.volume.localMediaRepositoryFactory import com.android.systemui.volume.mediaOutputInteractor val Kosmos.audioOutputInteractor by @@ -36,7 +35,6 @@ val Kosmos.audioOutputInteractor by bluetoothAdapter, deviceIconInteractor, mediaOutputInteractor, - localMediaRepositoryFactory, audioSharingRepository, ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/captioning/CaptioningModuleKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/captioning/CaptioningModuleKosmos.kt new file mode 100644 index 000000000000..e7162d27a031 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/captioning/CaptioningModuleKosmos.kt @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.volume.panel.component.captioning + +import com.android.internal.logging.uiEventLogger +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.testScope +import com.android.systemui.view.accessibility.data.repository.captioningInteractor +import com.android.systemui.volume.panel.component.button.ui.composable.ToggleButtonComponent +import com.android.systemui.volume.panel.component.captioning.domain.CaptioningAvailabilityCriteria +import com.android.systemui.volume.panel.component.captioning.ui.viewmodel.captioningViewModel + +val Kosmos.captioningComponent by + Kosmos.Fixture { + ToggleButtonComponent( + captioningViewModel.buttonViewModel, + captioningViewModel::setIsSystemAudioCaptioningEnabled, + ) + } +val Kosmos.captioningAvailabilityCriteria by + Kosmos.Fixture { + CaptioningAvailabilityCriteria( + captioningInteractor, + testScope.backgroundScope, + uiEventLogger, + ) + } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/captioning/ui/viewmodel/CaptioningViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/captioning/ui/viewmodel/CaptioningViewModelKosmos.kt new file mode 100644 index 000000000000..0edd9e026912 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/captioning/ui/viewmodel/CaptioningViewModelKosmos.kt @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.volume.panel.component.captioning.ui.viewmodel + +import android.content.applicationContext +import com.android.internal.logging.uiEventLogger +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.testScope +import com.android.systemui.view.accessibility.data.repository.captioningInteractor + +val Kosmos.captioningViewModel by + Kosmos.Fixture { + CaptioningViewModel( + applicationContext, + captioningInteractor, + testScope.backgroundScope, + uiEventLogger, + ) + } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/mediaoutput/data/repository/FakeLocalMediaRepositoryFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/mediaoutput/data/repository/FakeLocalMediaRepositoryFactory.kt index 9c902cf57fde..680535dfa909 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/mediaoutput/data/repository/FakeLocalMediaRepositoryFactory.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/mediaoutput/data/repository/FakeLocalMediaRepositoryFactory.kt @@ -17,6 +17,7 @@ package com.android.systemui.volume.panel.component.mediaoutput.data.repository import com.android.settingslib.volume.data.repository.LocalMediaRepository +import kotlinx.coroutines.CoroutineScope class FakeLocalMediaRepositoryFactory(private val defaultProvider: () -> LocalMediaRepository) : LocalMediaRepositoryFactory { @@ -27,6 +28,8 @@ class FakeLocalMediaRepositoryFactory(private val defaultProvider: () -> LocalMe repositories[packageName] = localMediaRepository } - override fun create(packageName: String?): LocalMediaRepository = - repositories[packageName] ?: defaultProvider() + override fun create( + packageName: String?, + coroutineScope: CoroutineScope + ): LocalMediaRepository = repositories[packageName] ?: defaultProvider() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/TestMediaDevicesFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/TestMediaDevicesFactory.kt index 40296099bfe0..141f2426f365 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/TestMediaDevicesFactory.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/TestMediaDevicesFactory.kt @@ -18,6 +18,7 @@ package com.android.systemui.volume.panel.component.mediaoutput.domain.interacto import android.annotation.SuppressLint import android.bluetooth.BluetoothDevice +import android.graphics.drawable.Drawable import android.graphics.drawable.TestStubDrawable import com.android.settingslib.bluetooth.CachedBluetoothDevice import com.android.settingslib.media.BluetoothMediaDevice @@ -29,29 +30,44 @@ import com.android.systemui.util.mockito.whenever @SuppressLint("StaticFieldLeak") // These are mocks object TestMediaDevicesFactory { - fun builtInMediaDevice(): MediaDevice = mock { - whenever(name).thenReturn("built_in_media") - whenever(icon).thenReturn(TestStubDrawable()) + fun builtInMediaDevice( + deviceName: String = "built_in_media", + deviceIcon: Drawable? = TestStubDrawable(), + ): MediaDevice = mock { + whenever(name).thenReturn(deviceName) + whenever(icon).thenReturn(deviceIcon) } - fun wiredMediaDevice(): MediaDevice = + fun wiredMediaDevice( + deviceName: String = "wired_media", + deviceIcon: Drawable? = TestStubDrawable(), + ): MediaDevice = mock<PhoneMediaDevice> { whenever(deviceType) .thenReturn(MediaDevice.MediaDeviceType.TYPE_3POINT5_MM_AUDIO_DEVICE) - whenever(name).thenReturn("wired_media") - whenever(icon).thenReturn(TestStubDrawable()) + whenever(name).thenReturn(deviceName) + whenever(icon).thenReturn(deviceIcon) } - fun bluetoothMediaDevice(): MediaDevice { - val bluetoothDevice = mock<BluetoothDevice>() + fun bluetoothMediaDevice( + deviceName: String = "bt_media", + deviceIcon: Drawable? = TestStubDrawable(), + deviceAddress: String = "bt_media_device", + ): BluetoothMediaDevice { + val bluetoothDevice = + mock<BluetoothDevice> { + whenever(name).thenReturn(deviceName) + whenever(address).thenReturn(deviceAddress) + } val cachedBluetoothDevice: CachedBluetoothDevice = mock { whenever(isHearingAidDevice).thenReturn(true) - whenever(address).thenReturn("bt_media_device") + whenever(address).thenReturn(deviceAddress) whenever(device).thenReturn(bluetoothDevice) + whenever(name).thenReturn(deviceName) } return mock<BluetoothMediaDevice> { - whenever(name).thenReturn("bt_media") - whenever(icon).thenReturn(TestStubDrawable()) + whenever(name).thenReturn(deviceName) + whenever(icon).thenReturn(deviceIcon) whenever(cachedDevice).thenReturn(cachedBluetoothDevice) } } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/spatial/SpatialAudioModuleKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/spatial/SpatialAudioModuleKosmos.kt new file mode 100644 index 000000000000..ea5d70d35030 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/spatial/SpatialAudioModuleKosmos.kt @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.volume.panel.component.spatial + +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.volume.panel.component.button.ui.composable.ButtonComponent +import com.android.systemui.volume.panel.component.spatial.domain.SpatialAudioAvailabilityCriteria +import com.android.systemui.volume.panel.component.spatial.domain.interactor.spatialAudioComponentInteractor +import com.android.systemui.volume.panel.component.spatial.ui.viewmodel.spatialAudioViewModel + +val Kosmos.spatialAudioComponent by + Kosmos.Fixture { ButtonComponent(spatialAudioViewModel.spatialAudioButton) { _, _ -> } } +val Kosmos.spatialAudioAvailabilityCriteria by + Kosmos.Fixture { SpatialAudioAvailabilityCriteria(spatialAudioComponentInteractor) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/spatial/domain/interactor/SpatialAudioComponentInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/spatial/domain/interactor/SpatialAudioComponentInteractorKosmos.kt new file mode 100644 index 000000000000..95a7b9bb185b --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/spatial/domain/interactor/SpatialAudioComponentInteractorKosmos.kt @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.volume.panel.component.spatial.domain.interactor + +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.testScope +import com.android.systemui.media.spatializerInteractor +import com.android.systemui.volume.domain.interactor.audioOutputInteractor + +val Kosmos.spatialAudioComponentInteractor by + Kosmos.Fixture { + SpatialAudioComponentInteractor( + audioOutputInteractor, + spatializerInteractor, + testScope.backgroundScope, + ) + } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/spatial/ui/viewmodel/SpatialAudioViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/spatial/ui/viewmodel/SpatialAudioViewModelKosmos.kt new file mode 100644 index 000000000000..1b8a3fcfd311 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/spatial/ui/viewmodel/SpatialAudioViewModelKosmos.kt @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.volume.panel.component.spatial.ui.viewmodel + +import android.content.applicationContext +import com.android.internal.logging.uiEventLogger +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.testScope +import com.android.systemui.volume.panel.component.spatial.domain.interactor.spatialAudioComponentInteractor +import com.android.systemui.volume.panel.component.spatial.spatialAudioAvailabilityCriteria + +val Kosmos.spatialAudioViewModel by + Kosmos.Fixture { + SpatialAudioViewModel( + applicationContext, + testScope.backgroundScope, + spatialAudioAvailabilityCriteria, + spatialAudioComponentInteractor, + uiEventLogger, + ) + } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/domain/interactor/ComponentsInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/domain/interactor/ComponentsInteractorKosmos.kt index 8862942aa083..a18f498e5441 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/domain/interactor/ComponentsInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/domain/interactor/ComponentsInteractorKosmos.kt @@ -19,8 +19,10 @@ package com.android.systemui.volume.panel.domain.interactor import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.testScope import com.android.systemui.volume.panel.component.bottombar.ui.bottomBarAvailabilityCriteria +import com.android.systemui.volume.panel.component.captioning.captioningAvailabilityCriteria import com.android.systemui.volume.panel.component.mediaoutput.mediaOutputAvailabilityCriteria import com.android.systemui.volume.panel.component.shared.model.VolumePanelComponents +import com.android.systemui.volume.panel.component.spatial.spatialAudioAvailabilityCriteria import com.android.systemui.volume.panel.component.volume.volumeSlidersAvailabilityCriteria import com.android.systemui.volume.panel.domain.ComponentAvailabilityCriteria import com.android.systemui.volume.panel.domain.defaultCriteria @@ -36,6 +38,8 @@ var Kosmos.prodCriteriaByKey: mapOf( VolumePanelComponents.MEDIA_OUTPUT to Provider { mediaOutputAvailabilityCriteria }, VolumePanelComponents.VOLUME_SLIDERS to Provider { volumeSlidersAvailabilityCriteria }, + VolumePanelComponents.CAPTIONING to Provider { captioningAvailabilityCriteria }, + VolumePanelComponents.SPATIAL_AUDIO to Provider { spatialAudioAvailabilityCriteria }, VolumePanelComponents.BOTTOM_BAR to Provider { bottomBarAvailabilityCriteria }, ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/ui/composable/ComponentsFactoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/ui/composable/ComponentsFactoryKosmos.kt index bacf22c0fef6..6bea416fa6a0 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/ui/composable/ComponentsFactoryKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/ui/composable/ComponentsFactoryKosmos.kt @@ -18,8 +18,10 @@ package com.android.systemui.volume.panel.ui.composable import com.android.systemui.kosmos.Kosmos import com.android.systemui.volume.panel.component.bottombar.ui.bottomBarComponent +import com.android.systemui.volume.panel.component.captioning.captioningComponent import com.android.systemui.volume.panel.component.mediaoutput.mediaOutputComponent import com.android.systemui.volume.panel.component.shared.model.VolumePanelComponents +import com.android.systemui.volume.panel.component.spatial.spatialAudioComponent import com.android.systemui.volume.panel.component.volume.volumeSlidersComponent import com.android.systemui.volume.panel.shared.model.VolumePanelComponentKey import com.android.systemui.volume.panel.shared.model.VolumePanelUiComponent @@ -30,9 +32,11 @@ var Kosmos.componentByKey: Map<VolumePanelComponentKey, Provider<VolumePanelUiCo var Kosmos.prodComponentByKey: Map<VolumePanelComponentKey, Provider<VolumePanelUiComponent>> by Kosmos.Fixture { mapOf( - VolumePanelComponents.BOTTOM_BAR to Provider { bottomBarComponent }, VolumePanelComponents.MEDIA_OUTPUT to Provider { mediaOutputComponent }, VolumePanelComponents.VOLUME_SLIDERS to Provider { volumeSlidersComponent }, + VolumePanelComponents.CAPTIONING to Provider { captioningComponent }, + VolumePanelComponents.SPATIAL_AUDIO to Provider { spatialAudioComponent }, + VolumePanelComponents.BOTTOM_BAR to Provider { bottomBarComponent }, ) } var Kosmos.enabledComponents: Collection<VolumePanelComponentKey> by diff --git a/packages/services/VirtualCamera/OWNERS b/packages/services/VirtualCamera/OWNERS deleted file mode 100644 index c66443fb8a14..000000000000 --- a/packages/services/VirtualCamera/OWNERS +++ /dev/null @@ -1,3 +0,0 @@ -include /services/companion/java/com/android/server/companion/virtual/OWNERS -caen@google.com -jsebechlebsky@google.com
\ No newline at end of file diff --git a/services/accessibility/accessibility.aconfig b/services/accessibility/accessibility.aconfig index 1c57dd3f5d5a..93531508b3eb 100644 --- a/services/accessibility/accessibility.aconfig +++ b/services/accessibility/accessibility.aconfig @@ -220,3 +220,13 @@ flag { description: "Feature allows users to change color correction saturation for daltonizer." bug: "322829049" } + +flag { + name: "skip_package_change_before_user_switch" + namespace: "accessibility" + description: "Skip onSomePackageChanged callback if the SwitchUser signal is not received yet." + bug: "340927041" + metadata { + purpose: PURPOSE_BUGFIX + } +} diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index 053a1a7c4ae9..20b727cd6f09 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -588,6 +588,15 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub return Thread.holdsLock(mLock); } + /** + * Returns if the service is initialized. + * + * The service is considered initialized when the user switch happened. + */ + private boolean isServiceInitializedLocked() { + return mInitialized; + } + @Override public int getCurrentUserIdLocked() { return mCurrentUserId; @@ -6234,6 +6243,15 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub if (userId != mManagerService.getCurrentUserIdLocked()) { return; } + + // Only continue setting up the packages if the service has been initialized. + // See: b/340927041 + if (Flags.skipPackageChangeBeforeUserSwitch() + && !mManagerService.isServiceInitializedLocked()) { + Slog.w(LOG_TAG, + "onSomePackagesChanged: service not initialized, skip the callback."); + return; + } mManagerService.onSomePackagesChangedLocked(parsedAccessibilityServiceInfos, parsedAccessibilityShortcutInfos); } diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java index 30e4a3eb77e6..0d0c21dcf49c 100644 --- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java +++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java @@ -34,7 +34,6 @@ import static com.android.internal.util.function.pooled.PooledLambda.obtainMessa import static com.android.server.companion.utils.PackageUtils.enforceUsesCompanionDeviceFeature; import static com.android.server.companion.utils.PackageUtils.getPackageInfo; import static com.android.server.companion.utils.PackageUtils.isRestrictedSettingsAllowed; -import static com.android.server.companion.utils.PermissionsUtils.checkCallerCanManageCompanionDevice; import static com.android.server.companion.utils.PermissionsUtils.enforceCallerCanManageAssociationsForPackage; import static com.android.server.companion.utils.PermissionsUtils.enforceCallerIsSystemOr; import static com.android.server.companion.utils.PermissionsUtils.enforceCallerIsSystemOrCanInteractWithUserId; @@ -335,12 +334,6 @@ public class CompanionDeviceManagerService extends SystemService { enforceCallerCanManageAssociationsForPackage(getContext(), userId, packageName, "get associations"); - if (!checkCallerCanManageCompanionDevice(getContext())) { - // If the caller neither is system nor holds MANAGE_COMPANION_DEVICES: it needs to - // request the feature (also: the caller is the app itself). - enforceUsesCompanionDeviceFeature(getContext(), userId, packageName); - } - return mAssociationStore.getActiveAssociationsByPackage(userId, packageName); } diff --git a/services/companion/java/com/android/server/companion/association/AssociationRequestsProcessor.java b/services/companion/java/com/android/server/companion/association/AssociationRequestsProcessor.java index d09d7e672f9d..3fbd8560b82c 100644 --- a/services/companion/java/com/android/server/companion/association/AssociationRequestsProcessor.java +++ b/services/companion/java/com/android/server/companion/association/AssociationRequestsProcessor.java @@ -347,6 +347,8 @@ public class AssociationRequestsProcessor { * Set association tag. */ public void setAssociationTag(int associationId, String tag) { + Slog.i(TAG, "Setting association tag=[" + tag + "] to id=[" + associationId + "]..."); + AssociationInfo association = mAssociationStore.getAssociationWithCallerChecks( associationId); association = (new AssociationInfo.Builder(association)).setTag(tag).build(); diff --git a/services/companion/java/com/android/server/companion/association/AssociationStore.java b/services/companion/java/com/android/server/companion/association/AssociationStore.java index 29e8095f8680..757abd927ac8 100644 --- a/services/companion/java/com/android/server/companion/association/AssociationStore.java +++ b/services/companion/java/com/android/server/companion/association/AssociationStore.java @@ -18,7 +18,7 @@ package com.android.server.companion.association; import static com.android.server.companion.utils.MetricUtils.logCreateAssociation; import static com.android.server.companion.utils.MetricUtils.logRemoveAssociation; -import static com.android.server.companion.utils.PermissionsUtils.checkCallerCanManageAssociationsForPackage; +import static com.android.server.companion.utils.PermissionsUtils.enforceCallerCanManageAssociationsForPackage; import android.annotation.IntDef; import android.annotation.NonNull; @@ -457,6 +457,10 @@ public class AssociationStore { /** * Get association by id with caller checks. + * + * If the association is not found, an IllegalArgumentException would be thrown. + * + * If the caller can't access the association, a SecurityException would be thrown. */ @NonNull public AssociationInfo getAssociationWithCallerChecks(int associationId) { @@ -466,13 +470,9 @@ public class AssociationStore { "getAssociationWithCallerChecks() Association id=[" + associationId + "] doesn't exist."); } - if (checkCallerCanManageAssociationsForPackage(mContext, association.getUserId(), - association.getPackageName())) { - return association; - } - - throw new IllegalArgumentException( - "The caller can't interact with the association id=[" + associationId + "]."); + enforceCallerCanManageAssociationsForPackage(mContext, association.getUserId(), + association.getPackageName(), null); + return association; } /** diff --git a/services/companion/java/com/android/server/companion/association/DisassociationProcessor.java b/services/companion/java/com/android/server/companion/association/DisassociationProcessor.java index 8c1116b7a612..6f0baef019b3 100644 --- a/services/companion/java/com/android/server/companion/association/DisassociationProcessor.java +++ b/services/companion/java/com/android/server/companion/association/DisassociationProcessor.java @@ -98,7 +98,6 @@ public class DisassociationProcessor { Slog.i(TAG, "Disassociating id=[" + id + "]..."); final AssociationInfo association = mAssociationStore.getAssociationWithCallerChecks(id); - final int userId = association.getUserId(); final String packageName = association.getPackageName(); final String deviceProfile = association.getDeviceProfile(); diff --git a/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java b/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java index 026d29c9f821..00e049c1e1ff 100644 --- a/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java +++ b/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java @@ -122,7 +122,6 @@ public class SystemDataTransferProcessor { */ public boolean isPermissionTransferUserConsented(int associationId) { mAssociationStore.getAssociationWithCallerChecks(associationId); - PermissionSyncRequest request = getPermissionSyncRequest(associationId); if (request == null) { return false; @@ -147,12 +146,12 @@ public class SystemDataTransferProcessor { return null; } - final AssociationInfo association = mAssociationStore.getAssociationWithCallerChecks( - associationId); - Slog.i(LOG_TAG, "Creating permission sync intent for userId [" + userId + "] associationId [" + associationId + "]"); + final AssociationInfo association = mAssociationStore.getAssociationWithCallerChecks( + associationId); + // Create an internal intent to launch the user consent dialog final Bundle extras = new Bundle(); PermissionSyncRequest request = new PermissionSyncRequest(associationId); @@ -220,7 +219,9 @@ public class SystemDataTransferProcessor { * Enable perm sync for the association */ public void enablePermissionsSync(int associationId) { - int userId = mAssociationStore.getAssociationWithCallerChecks(associationId).getUserId(); + AssociationInfo association = mAssociationStore.getAssociationWithCallerChecks( + associationId); + int userId = association.getUserId(); PermissionSyncRequest request = new PermissionSyncRequest(associationId); request.setUserConsented(true); mSystemDataTransferRequestStore.writeRequest(userId, request); @@ -230,7 +231,9 @@ public class SystemDataTransferProcessor { * Disable perm sync for the association */ public void disablePermissionsSync(int associationId) { - int userId = mAssociationStore.getAssociationWithCallerChecks(associationId).getUserId(); + AssociationInfo association = mAssociationStore.getAssociationWithCallerChecks( + associationId); + int userId = association.getUserId(); PermissionSyncRequest request = new PermissionSyncRequest(associationId); request.setUserConsented(false); mSystemDataTransferRequestStore.writeRequest(userId, request); @@ -241,8 +244,9 @@ public class SystemDataTransferProcessor { */ @Nullable public PermissionSyncRequest getPermissionSyncRequest(int associationId) { - int userId = mAssociationStore.getAssociationWithCallerChecks(associationId) - .getUserId(); + AssociationInfo association = mAssociationStore.getAssociationWithCallerChecks( + associationId); + int userId = association.getUserId(); List<SystemDataTransferRequest> requests = mSystemDataTransferRequestStore.readRequestsByAssociationId(userId, associationId); @@ -259,7 +263,9 @@ public class SystemDataTransferProcessor { */ public void removePermissionSyncRequest(int associationId) { Binder.withCleanCallingIdentity(() -> { - int userId = mAssociationStore.getAssociationById(associationId).getUserId(); + AssociationInfo association = mAssociationStore.getAssociationWithCallerChecks( + associationId); + int userId = association.getUserId(); mSystemDataTransferRequestStore.removeRequestsByAssociationId(userId, associationId); }); } diff --git a/services/companion/java/com/android/server/companion/utils/PermissionsUtils.java b/services/companion/java/com/android/server/companion/utils/PermissionsUtils.java index f397814f7ad7..796d2851760f 100644 --- a/services/companion/java/com/android/server/companion/utils/PermissionsUtils.java +++ b/services/companion/java/com/android/server/companion/utils/PermissionsUtils.java @@ -149,21 +149,6 @@ public final class PermissionsUtils { } /** - * Check if the caller is system UID or the provided user. - */ - public static boolean checkCallerIsSystemOr(@UserIdInt int userId, - @NonNull String packageName) { - final int callingUid = getCallingUid(); - if (callingUid == SYSTEM_UID) return true; - - if (getCallingUserId() != userId) return false; - - if (!checkPackage(callingUid, packageName)) return false; - - return true; - } - - /** * Check if the calling user id matches the userId, and if the package belongs to * the calling uid. */ @@ -184,21 +169,30 @@ public final class PermissionsUtils { } /** - * Check if the caller holds the necessary permission to manage companion devices. - */ - public static boolean checkCallerCanManageCompanionDevice(@NonNull Context context) { - if (getCallingUid() == SYSTEM_UID) return true; - - return context.checkCallingPermission(MANAGE_COMPANION_DEVICES) == PERMISSION_GRANTED; - } - - /** * Require the caller to be able to manage the associations for the package. */ public static void enforceCallerCanManageAssociationsForPackage(@NonNull Context context, @UserIdInt int userId, @NonNull String packageName, @Nullable String actionDescription) { - if (checkCallerCanManageAssociationsForPackage(context, userId, packageName)) return; + final int callingUid = getCallingUid(); + + // If the caller is the system + if (callingUid == SYSTEM_UID) { + return; + } + + // If caller can manage the package or has the permissions to manage companion devices + boolean canInteractAcrossUsers = context.checkCallingPermission(INTERACT_ACROSS_USERS) + == PERMISSION_GRANTED; + boolean canManageCompanionDevices = context.checkCallingPermission(MANAGE_COMPANION_DEVICES) + == PERMISSION_GRANTED; + if (getCallingUserId() == userId) { + if (checkPackage(callingUid, packageName) || canManageCompanionDevices) { + return; + } + } else if (canInteractAcrossUsers && canManageCompanionDevices) { + return; + } throw new SecurityException("Caller (uid=" + getCallingUid() + ") does not have " + "permissions to " @@ -219,25 +213,6 @@ public final class PermissionsUtils { } } - /** - * Check if the caller is either: - * <ul> - * <li> the package itself - * <li> the System ({@link android.os.Process#SYSTEM_UID}) - * <li> holds {@link Manifest.permission#MANAGE_COMPANION_DEVICES} and, if belongs to a - * different user, also holds {@link Manifest.permission#INTERACT_ACROSS_USERS}. - * </ul> - * @return whether the caller is one of the above. - */ - public static boolean checkCallerCanManageAssociationsForPackage(@NonNull Context context, - @UserIdInt int userId, @NonNull String packageName) { - if (checkCallerIsSystemOr(userId, packageName)) return true; - - if (!checkCallerCanInteractWithUserId(context, userId)) return false; - - return checkCallerCanManageCompanionDevice(context); - } - private static boolean checkPackage(@UserIdInt int uid, @NonNull String packageName) { try { return getAppOpsService().checkPackage(uid, packageName) == MODE_ALLOWED; diff --git a/services/core/java/com/android/server/PackageWatchdog.java b/services/core/java/com/android/server/PackageWatchdog.java index a61925732256..966478e33c73 100644 --- a/services/core/java/com/android/server/PackageWatchdog.java +++ b/services/core/java/com/android/server/PackageWatchdog.java @@ -138,6 +138,12 @@ public class PackageWatchdog { static final long DEFAULT_BOOT_LOOP_TRIGGER_WINDOW_MS = TimeUnit.MINUTES.toMillis(10); + // Time needed to apply mitigation + private static final String MITIGATION_WINDOW_MS = + "persist.device_config.configuration.mitigation_window_ms"; + @VisibleForTesting + static final long DEFAULT_MITIGATION_WINDOW_MS = TimeUnit.SECONDS.toMillis(5); + // Threshold level at which or above user might experience significant disruption. private static final String MAJOR_USER_IMPACT_LEVEL_THRESHOLD = "persist.device_config.configuration.major_user_impact_level_threshold"; @@ -210,6 +216,9 @@ public class PackageWatchdog { @GuardedBy("mLock") private boolean mSyncRequired = false; + @GuardedBy("mLock") + private long mLastMitigation = -1000000; + @FunctionalInterface @VisibleForTesting interface SystemClock { @@ -400,6 +409,16 @@ public class PackageWatchdog { Slog.w(TAG, "Could not resolve a list of failing packages"); return; } + synchronized (mLock) { + final long now = mSystemClock.uptimeMillis(); + if (Flags.recoverabilityDetection()) { + if (now >= mLastMitigation + && (now - mLastMitigation) < getMitigationWindowMs()) { + Slog.i(TAG, "Skipping onPackageFailure mitigation"); + return; + } + } + } mLongTaskHandler.post(() -> { synchronized (mLock) { if (mAllObservers.isEmpty()) { @@ -500,10 +519,17 @@ public class PackageWatchdog { int currentObserverImpact, int mitigationCount) { if (currentObserverImpact < getUserImpactLevelLimit()) { + synchronized (mLock) { + mLastMitigation = mSystemClock.uptimeMillis(); + } currentObserverToNotify.execute(versionedPackage, failureReason, mitigationCount); } } + private long getMitigationWindowMs() { + return SystemProperties.getLong(MITIGATION_WINDOW_MS, DEFAULT_MITIGATION_WINDOW_MS); + } + /** * Called when the system server boots. If the system server is detected to be in a boot loop, diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java index 4dd3a8f67b0d..b35959f1a6e8 100644 --- a/services/core/java/com/android/server/StorageManagerService.java +++ b/services/core/java/com/android/server/StorageManagerService.java @@ -3980,7 +3980,7 @@ class StorageManagerService extends IStorageManager.Stub if (resUuids.contains(rec.fsUuid)) continue; // Treat as recent if mounted within the last week - if (rec.lastSeenMillis > 0 && rec.lastSeenMillis < lastWeek) { + if (rec.lastSeenMillis > 0 && rec.lastSeenMillis >= lastWeek) { final StorageVolume userVol = rec.buildStorageVolume(mContext); res.add(userVol); resUuids.add(userVol.getUuid()); diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java index 8647750d510f..ab34dd4477fd 100644 --- a/services/core/java/com/android/server/am/OomAdjuster.java +++ b/services/core/java/com/android/server/am/OomAdjuster.java @@ -2205,12 +2205,15 @@ public class OomAdjuster { != 0 ? PROCESS_CAPABILITY_FOREGROUND_LOCATION : 0; if (roForegroundAudioControl()) { // flag check - final int fgsAudioType = FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK - | FOREGROUND_SERVICE_TYPE_CAMERA - | FOREGROUND_SERVICE_TYPE_MICROPHONE - | FOREGROUND_SERVICE_TYPE_PHONE_CALL; - capabilityFromFGS |= (psr.getForegroundServiceTypes() & fgsAudioType) != 0 - ? PROCESS_CAPABILITY_FOREGROUND_AUDIO_CONTROL : 0; + // TODO revisit restriction of FOREGROUND_AUDIO_CONTROL when it can be + // limited to specific FGS types + //final int fgsAudioType = FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK + // | FOREGROUND_SERVICE_TYPE_CAMERA + // | FOREGROUND_SERVICE_TYPE_MICROPHONE + // | FOREGROUND_SERVICE_TYPE_PHONE_CALL; + //capabilityFromFGS |= (psr.getForegroundServiceTypes() & fgsAudioType) != 0 + // ? PROCESS_CAPABILITY_FOREGROUND_AUDIO_CONTROL : 0; + capabilityFromFGS |= PROCESS_CAPABILITY_FOREGROUND_AUDIO_CONTROL; } final boolean enabled = state.getCachedCompatChange( diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java index 1dc1846fbb96..1d21ccb62b8c 100644 --- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java +++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java @@ -15,6 +15,8 @@ */ package com.android.server.audio; +import static android.media.audio.Flags.scoManagedByAudio; + import android.annotation.NonNull; import android.annotation.Nullable; import android.app.compat.CompatChanges; @@ -54,6 +56,7 @@ import android.os.RemoteException; import android.os.SystemClock; import android.os.UserHandle; import android.provider.Settings; +import android.sysprop.BluetoothProperties; import android.text.TextUtils; import android.util.Log; import android.util.Pair; @@ -74,7 +77,6 @@ import java.util.Objects; import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; - /** * @hide * (non final for mocking/spying) @@ -167,6 +169,15 @@ public class AudioDeviceBroker { @EnabledSince(targetSdkVersion = android.os.Build.VERSION_CODES.S_V2) public static final long USE_SET_COMMUNICATION_DEVICE = 243827847L; + /** Indicates if headset profile connection and SCO audio control use the new implementation + * aligned with other BT profiles. True if both the feature flag Flags.scoManagedByAudio() and + * the system property audio.sco.managed.by.audio are true. + */ + private final boolean mScoManagedByAudio; + /*package*/ boolean isScoManagedByAudio() { + return mScoManagedByAudio; + } + //------------------------------------------------------------------- /*package*/ AudioDeviceBroker(@NonNull Context context, @NonNull AudioService service, @NonNull AudioSystemAdapter audioSystem) { @@ -176,7 +187,8 @@ public class AudioDeviceBroker { mDeviceInventory = new AudioDeviceInventory(this); mSystemServer = SystemServerAdapter.getDefaultAdapter(mContext); mAudioSystem = audioSystem; - + mScoManagedByAudio = scoManagedByAudio() + && BluetoothProperties.isScoManagedByAudioEnabled().orElse(false); init(); } @@ -192,7 +204,8 @@ public class AudioDeviceBroker { mDeviceInventory = mockDeviceInventory; mSystemServer = mockSystemServer; mAudioSystem = audioSystem; - + mScoManagedByAudio = scoManagedByAudio() + && BluetoothProperties.isScoManagedByAudioEnabled().orElse(false); init(); } @@ -400,24 +413,24 @@ public class AudioDeviceBroker { if (client == null) { return; } - - boolean isBtScoRequested = isBluetoothScoRequested(); - if (isBtScoRequested && (!wasBtScoRequested || !isBluetoothScoActive())) { - if (!mBtHelper.startBluetoothSco(scoAudioMode, eventSource)) { - Log.w(TAG, "setCommunicationRouteForClient: failure to start BT SCO for uid: " - + uid); - // clean up or restore previous client selection - if (prevClientDevice != null) { - addCommunicationRouteClient(cb, uid, prevClientDevice, prevPrivileged); - } else { - removeCommunicationRouteClient(cb, true); + if (!mScoManagedByAudio) { + boolean isBtScoRequested = isBluetoothScoRequested(); + if (isBtScoRequested && (!wasBtScoRequested || !isBluetoothScoActive())) { + if (!mBtHelper.startBluetoothSco(scoAudioMode, eventSource)) { + Log.w(TAG, "setCommunicationRouteForClient: failure to start BT SCO for uid: " + + uid); + // clean up or restore previous client selection + if (prevClientDevice != null) { + addCommunicationRouteClient(cb, uid, prevClientDevice, prevPrivileged); + } else { + removeCommunicationRouteClient(cb, true); + } + postBroadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED); } - postBroadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED); + } else if (!isBtScoRequested && wasBtScoRequested) { + mBtHelper.stopBluetoothSco(eventSource); } - } else if (!isBtScoRequested && wasBtScoRequested) { - mBtHelper.stopBluetoothSco(eventSource); } - // In BT classic for communication, the device changes from a2dp to sco device, but for // LE Audio it stays the same and we must trigger the proper stream volume alignment, if // LE Audio communication device is activated after the audio system has already switched to @@ -1685,6 +1698,8 @@ public class AudioDeviceBroker { pw.println("\n" + prefix + "mAudioModeOwner: " + mAudioModeOwner); + pw.println("\n" + prefix + "mScoManagedByAudio: " + mScoManagedByAudio); + mBtHelper.dump(pw, prefix); } @@ -1837,10 +1852,10 @@ public class AudioDeviceBroker { ? mAudioService.getBluetoothContextualVolumeStream() : AudioSystem.STREAM_DEFAULT); if (btInfo.mProfile == BluetoothProfile.LE_AUDIO - || btInfo.mProfile - == BluetoothProfile.HEARING_AID) { - onUpdateCommunicationRouteClient( - isBluetoothScoRequested(), + || btInfo.mProfile == BluetoothProfile.HEARING_AID + || (mScoManagedByAudio + && btInfo.mProfile == BluetoothProfile.HEADSET)) { + onUpdateCommunicationRouteClient(isBluetoothScoRequested(), "setBluetoothActiveDevice"); } } @@ -2511,7 +2526,7 @@ public class AudioDeviceBroker { setCommunicationRouteForClient(crc.getBinder(), crc.getUid(), crc.getDevice(), BtHelper.SCO_MODE_UNDEFINED, crc.isPrivileged(), eventSource); } else { - if (!isBluetoothScoRequested() && wasBtScoRequested) { + if (!mScoManagedByAudio && !isBluetoothScoRequested() && wasBtScoRequested) { mBtHelper.stopBluetoothSco(eventSource); } updateCommunicationRoute(eventSource); @@ -2815,4 +2830,5 @@ public class AudioDeviceBroker { void clearDeviceInventory() { mDeviceInventory.clearDeviceInventory(); } + } diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java index e0790da7cd09..287c92f86f0f 100644 --- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java +++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java @@ -859,6 +859,15 @@ public class AudioDeviceInventory { btInfo, streamType, codec, "onSetBtActiveDevice"); } break; + case BluetoothProfile.HEADSET: + if (mDeviceBroker.isScoManagedByAudio()) { + if (switchToUnavailable) { + mDeviceBroker.onSetBtScoActiveDevice(null); + } else if (switchToAvailable) { + mDeviceBroker.onSetBtScoActiveDevice(btInfo.mDevice); + } + } + break; default: throw new IllegalArgumentException("Invalid profile " + BluetoothProfile.getProfileName(btInfo.mProfile)); } diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index baa7f2f389e8..2a23b9ca522e 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -48,6 +48,7 @@ import static android.media.audio.Flags.automaticBtDeviceType; import static android.media.audio.Flags.featureSpatialAudioHeadtrackingLowLatency; import static android.media.audio.Flags.focusFreezeTestApi; import static android.media.audio.Flags.roForegroundAudioControl; +import static android.media.audio.Flags.scoManagedByAudio; import static android.media.audiopolicy.Flags.enableFadeManagerConfiguration; import static android.os.Process.FIRST_APPLICATION_UID; import static android.os.Process.INVALID_UID; @@ -1013,7 +1014,7 @@ public class AudioService extends IAudioService.Stub SystemServerAdapter.getDefaultAdapter(context), SettingsAdapter.getDefaultAdapter(), new AudioVolumeGroupHelper(), - new DefaultAudioPolicyFacade(), + new DefaultAudioPolicyFacade(r -> r.run()), null); } @@ -1503,7 +1504,9 @@ public class AudioService extends IAudioService.Stub // Register for device connection intent broadcasts. IntentFilter intentFilter = new IntentFilter(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED); - intentFilter.addAction(BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED); + if (!mDeviceBroker.isScoManagedByAudio()) { + intentFilter.addAction(BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED); + } intentFilter.addAction(Intent.ACTION_DOCK_EVENT); if (mDisplayManager == null) { intentFilter.addAction(Intent.ACTION_SCREEN_ON); @@ -1901,7 +1904,6 @@ public class AudioService extends IAudioService.Stub } mSpatializerHelper.reset(/* featureEnabled */ mHasSpatializerEffect); - mSoundDoseHelper.reset(); // Restore rotation information. if (mMonitorRotation) { @@ -1912,6 +1914,8 @@ public class AudioService extends IAudioService.Stub // indicate the end of reconfiguration phase to audio HAL AudioSystem.setParameters("restarting=false"); + mSoundDoseHelper.reset(/*resetISoundDose=*/true); + sendMsg(mAudioHandler, MSG_DISPATCH_AUDIO_SERVER_STATE, SENDMSG_QUEUE, 1, 0, null, 0); @@ -4528,11 +4532,12 @@ public class AudioService extends IAudioService.Stub + focusFreezeTestApi()); pw.println("\tcom.android.media.audio.disablePrescaleAbsoluteVolume:" + disablePrescaleAbsoluteVolume()); - pw.println("\tcom.android.media.audio.setStreamVolumeOrder:" + setStreamVolumeOrder()); pw.println("\tandroid.media.audio.roForegroundAudioControl:" + roForegroundAudioControl()); + pw.println("\tandroid.media.audio.scoManagedByAudio:" + + scoManagedByAudio()); pw.println("\tcom.android.media.audio.vgsVssSyncMuteOrder:" + vgsVssSyncMuteOrder()); } @@ -7858,7 +7863,8 @@ public class AudioService extends IAudioService.Stub if (profile != BluetoothProfile.A2DP && profile != BluetoothProfile.A2DP_SINK && profile != BluetoothProfile.LE_AUDIO && profile != BluetoothProfile.LE_AUDIO_BROADCAST - && profile != BluetoothProfile.HEARING_AID) { + && profile != BluetoothProfile.HEARING_AID + && !(mDeviceBroker.isScoManagedByAudio() && profile == BluetoothProfile.HEADSET)) { throw new IllegalArgumentException("Illegal BluetoothProfile profile for device " + previousDevice + " -> " + newDevice + ". Got: " + profile); } diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java index 07daecdfc9f6..6bb3eb1c3078 100644 --- a/services/core/java/com/android/server/audio/BtHelper.java +++ b/services/core/java/com/android/server/audio/BtHelper.java @@ -94,14 +94,14 @@ public class BtHelper { private final Map<BluetoothDevice, AudioDeviceAttributes> mResolvedScoAudioDevices = new HashMap<>(); - private @Nullable BluetoothHearingAid mHearingAid; + private @Nullable BluetoothHearingAid mHearingAid = null; - private @Nullable BluetoothLeAudio mLeAudio; + private @Nullable BluetoothLeAudio mLeAudio = null; private @Nullable BluetoothLeAudioCodecConfig mLeAudioCodecConfig; // Reference to BluetoothA2dp to query for AbsoluteVolume. - private @Nullable BluetoothA2dp mA2dp; + private @Nullable BluetoothA2dp mA2dp = null; private @Nullable BluetoothCodecConfig mA2dpCodecConfig; @@ -401,50 +401,67 @@ public class BtHelper { private void onScoAudioStateChanged(int state) { boolean broadcast = false; int scoAudioState = AudioManager.SCO_AUDIO_STATE_ERROR; - switch (state) { - case BluetoothHeadset.STATE_AUDIO_CONNECTED: - scoAudioState = AudioManager.SCO_AUDIO_STATE_CONNECTED; - if (mScoAudioState != SCO_STATE_ACTIVE_INTERNAL - && mScoAudioState != SCO_STATE_DEACTIVATE_REQ) { - mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL; - } else if (mDeviceBroker.isBluetoothScoRequested()) { - // broadcast intent if the connection was initated by AudioService + if (mDeviceBroker.isScoManagedByAudio()) { + switch (state) { + case BluetoothHeadset.STATE_AUDIO_CONNECTED: + mDeviceBroker.setBluetoothScoOn(true, "BtHelper.onScoAudioStateChanged"); + scoAudioState = AudioManager.SCO_AUDIO_STATE_CONNECTED; broadcast = true; - } - mDeviceBroker.setBluetoothScoOn(true, "BtHelper.onScoAudioStateChanged"); - break; - case BluetoothHeadset.STATE_AUDIO_DISCONNECTED: - mDeviceBroker.setBluetoothScoOn(false, "BtHelper.onScoAudioStateChanged"); - scoAudioState = AudioManager.SCO_AUDIO_STATE_DISCONNECTED; - // There are two cases where we want to immediately reconnect audio: - // 1) If a new start request was received while disconnecting: this was - // notified by requestScoState() setting state to SCO_STATE_ACTIVATE_REQ. - // 2) If audio was connected then disconnected via Bluetooth APIs and - // we still have pending activation requests by apps: this is indicated by - // state SCO_STATE_ACTIVE_EXTERNAL and BT SCO is requested. - if (mScoAudioState == SCO_STATE_ACTIVATE_REQ) { - if (mBluetoothHeadset != null && mBluetoothHeadsetDevice != null - && connectBluetoothScoAudioHelper(mBluetoothHeadset, - mBluetoothHeadsetDevice, mScoAudioMode)) { - mScoAudioState = SCO_STATE_ACTIVE_INTERNAL; - scoAudioState = AudioManager.SCO_AUDIO_STATE_CONNECTING; + break; + case BluetoothHeadset.STATE_AUDIO_DISCONNECTED: + mDeviceBroker.setBluetoothScoOn(false, "BtHelper.onScoAudioStateChanged"); + scoAudioState = AudioManager.SCO_AUDIO_STATE_DISCONNECTED; + broadcast = true; + break; + default: + break; + } + } else { + switch (state) { + case BluetoothHeadset.STATE_AUDIO_CONNECTED: + scoAudioState = AudioManager.SCO_AUDIO_STATE_CONNECTED; + if (mScoAudioState != SCO_STATE_ACTIVE_INTERNAL + && mScoAudioState != SCO_STATE_DEACTIVATE_REQ) { + mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL; + } else if (mDeviceBroker.isBluetoothScoRequested()) { + // broadcast intent if the connection was initated by AudioService broadcast = true; - break; } - } - if (mScoAudioState != SCO_STATE_ACTIVE_EXTERNAL) { - broadcast = true; - } - mScoAudioState = SCO_STATE_INACTIVE; - break; - case BluetoothHeadset.STATE_AUDIO_CONNECTING: - if (mScoAudioState != SCO_STATE_ACTIVE_INTERNAL - && mScoAudioState != SCO_STATE_DEACTIVATE_REQ) { - mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL; - } - break; - default: - break; + mDeviceBroker.setBluetoothScoOn(true, "BtHelper.onScoAudioStateChanged"); + break; + case BluetoothHeadset.STATE_AUDIO_DISCONNECTED: + mDeviceBroker.setBluetoothScoOn(false, "BtHelper.onScoAudioStateChanged"); + scoAudioState = AudioManager.SCO_AUDIO_STATE_DISCONNECTED; + // There are two cases where we want to immediately reconnect audio: + // 1) If a new start request was received while disconnecting: this was + // notified by requestScoState() setting state to SCO_STATE_ACTIVATE_REQ. + // 2) If audio was connected then disconnected via Bluetooth APIs and + // we still have pending activation requests by apps: this is indicated by + // state SCO_STATE_ACTIVE_EXTERNAL and BT SCO is requested. + if (mScoAudioState == SCO_STATE_ACTIVATE_REQ) { + if (mBluetoothHeadset != null && mBluetoothHeadsetDevice != null + && connectBluetoothScoAudioHelper(mBluetoothHeadset, + mBluetoothHeadsetDevice, mScoAudioMode)) { + mScoAudioState = SCO_STATE_ACTIVE_INTERNAL; + scoAudioState = AudioManager.SCO_AUDIO_STATE_CONNECTING; + broadcast = true; + break; + } + } + if (mScoAudioState != SCO_STATE_ACTIVE_EXTERNAL) { + broadcast = true; + } + mScoAudioState = SCO_STATE_INACTIVE; + break; + case BluetoothHeadset.STATE_AUDIO_CONNECTING: + if (mScoAudioState != SCO_STATE_ACTIVE_INTERNAL + && mScoAudioState != SCO_STATE_DEACTIVATE_REQ) { + mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL; + } + break; + default: + break; + } } if (broadcast) { broadcastScoConnectionState(scoAudioState); @@ -454,7 +471,6 @@ public class BtHelper { newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, scoAudioState); sendStickyBroadcastToAll(newIntent); } - } /** * @@ -577,7 +593,11 @@ public class BtHelper { mHearingAid = null; break; case BluetoothProfile.LE_AUDIO: + if (mLeAudio != null && mLeAudioCallback != null) { + mLeAudio.unregisterCallback(mLeAudioCallback); + } mLeAudio = null; + mLeAudioCallback = null; mLeAudioCodecConfig = null; break; case BluetoothProfile.LE_AUDIO_BROADCAST: @@ -596,8 +616,6 @@ public class BtHelper { // BluetoothLeAudio callback used to update the list of addresses in the same group as a // connected LE Audio device - MyLeAudioCallback mLeAudioCallback = null; - class MyLeAudioCallback implements BluetoothLeAudio.Callback { @Override public void onCodecConfigChanged(int groupId, @@ -620,6 +638,8 @@ public class BtHelper { } } + MyLeAudioCallback mLeAudioCallback = null; + // @GuardedBy("mDeviceBroker.mSetModeLock") @GuardedBy("AudioDeviceBroker.this.mDeviceStateLock") /*package*/ synchronized void onBtProfileConnected(int profile, BluetoothProfile proxy) { @@ -635,18 +655,28 @@ public class BtHelper { onHeadsetProfileConnected((BluetoothHeadset) proxy); return; case BluetoothProfile.A2DP: + if (((BluetoothA2dp) proxy).equals(mA2dp)) { + return; + } mA2dp = (BluetoothA2dp) proxy; break; case BluetoothProfile.HEARING_AID: + if (((BluetoothHearingAid) proxy).equals(mHearingAid)) { + return; + } mHearingAid = (BluetoothHearingAid) proxy; break; case BluetoothProfile.LE_AUDIO: - if (mLeAudio == null) { - mLeAudioCallback = new MyLeAudioCallback(); - ((BluetoothLeAudio) proxy).registerCallback( - mContext.getMainExecutor(), mLeAudioCallback); + if (((BluetoothLeAudio) proxy).equals(mLeAudio)) { + return; + } + if (mLeAudio != null && mLeAudioCallback != null) { + mLeAudio.unregisterCallback(mLeAudioCallback); } mLeAudio = (BluetoothLeAudio) proxy; + mLeAudioCallback = new MyLeAudioCallback(); + mLeAudio.registerCallback( + mContext.getMainExecutor(), mLeAudioCallback); break; case BluetoothProfile.A2DP_SINK: case BluetoothProfile.LE_AUDIO_BROADCAST: diff --git a/services/core/java/com/android/server/audio/DefaultAudioPolicyFacade.java b/services/core/java/com/android/server/audio/DefaultAudioPolicyFacade.java index 37b812685a3d..75febbc1cf9c 100644 --- a/services/core/java/com/android/server/audio/DefaultAudioPolicyFacade.java +++ b/services/core/java/com/android/server/audio/DefaultAudioPolicyFacade.java @@ -16,100 +16,43 @@ package com.android.server.audio; -import android.annotation.NonNull; -import android.annotation.Nullable; import android.media.IAudioPolicyService; -import android.media.permission.ClearCallingIdentityContext; -import android.media.permission.SafeCloseable; import android.os.IBinder; import android.os.RemoteException; -import android.os.ServiceManager; -import android.util.Log; -import com.android.internal.annotations.GuardedBy; +import java.util.concurrent.Executor; +import java.util.function.Function; /** - * Default implementation of a facade to IAudioPolicyManager which fulfills AudioService - * dependencies. This forwards calls as-is to IAudioPolicyManager. - * Public methods throw IllegalStateException if AudioPolicy is not initialized/available + * Default implementation of a facade to IAudioPolicyService which fulfills AudioService + * dependencies. This forwards calls as-is to IAudioPolicyService. */ -public class DefaultAudioPolicyFacade implements AudioPolicyFacade, IBinder.DeathRecipient { +public class DefaultAudioPolicyFacade implements AudioPolicyFacade { - private static final String TAG = "DefaultAudioPolicyFacade"; private static final String AUDIO_POLICY_SERVICE_NAME = "media.audio_policy"; - private final Object mServiceLock = new Object(); - @GuardedBy("mServiceLock") - private IAudioPolicyService mAudioPolicy; + private final ServiceHolder<IAudioPolicyService> mServiceHolder; - public DefaultAudioPolicyFacade() { - try { - getAudioPolicyOrInit(); - } catch (IllegalStateException e) { - // Log and suppress this exception, we may be able to connect later - Log.e(TAG, "Failed to initialize APM connection", e); - } + /** + * @param e - Executor for service start tasks + */ + public DefaultAudioPolicyFacade(Executor e) { + mServiceHolder = + new ServiceHolder( + AUDIO_POLICY_SERVICE_NAME, + (Function<IBinder, IAudioPolicyService>) + IAudioPolicyService.Stub::asInterface, + e); } @Override public boolean isHotwordStreamSupported(boolean lookbackAudio) { - IAudioPolicyService ap = getAudioPolicyOrInit(); - try (SafeCloseable ignored = ClearCallingIdentityContext.create()) { + IAudioPolicyService ap = mServiceHolder.waitForService(); + try { return ap.isHotwordStreamSupported(lookbackAudio); } catch (RemoteException e) { - resetServiceConnection(ap.asBinder()); - throw new IllegalStateException(e); - } - } - - @Override - public void binderDied() { - Log.wtf(TAG, "Unexpected binderDied without IBinder object"); - } - - @Override - public void binderDied(@NonNull IBinder who) { - resetServiceConnection(who); - } - - private void resetServiceConnection(@Nullable IBinder deadAudioPolicy) { - synchronized (mServiceLock) { - if (mAudioPolicy != null && mAudioPolicy.asBinder().equals(deadAudioPolicy)) { - mAudioPolicy.asBinder().unlinkToDeath(this, 0); - mAudioPolicy = null; - } - } - } - - private @Nullable IAudioPolicyService getAudioPolicy() { - synchronized (mServiceLock) { - return mAudioPolicy; - } - } - - /* - * Does not block. - * @throws IllegalStateException for any failed connection - */ - private @NonNull IAudioPolicyService getAudioPolicyOrInit() { - synchronized (mServiceLock) { - if (mAudioPolicy != null) { - return mAudioPolicy; - } - // Do not block while attempting to connect to APM. Defer to caller. - IAudioPolicyService ap = IAudioPolicyService.Stub.asInterface( - ServiceManager.checkService(AUDIO_POLICY_SERVICE_NAME)); - if (ap == null) { - throw new IllegalStateException(TAG + ": Unable to connect to AudioPolicy"); - } - try { - ap.asBinder().linkToDeath(this, 0); - } catch (RemoteException e) { - throw new IllegalStateException( - TAG + ": Unable to link deathListener to AudioPolicy", e); - } - mAudioPolicy = ap; - return mAudioPolicy; + mServiceHolder.attemptClear(ap.asBinder()); + throw new IllegalStateException(); } } } diff --git a/services/core/java/com/android/server/audio/ServiceHolder.java b/services/core/java/com/android/server/audio/ServiceHolder.java new file mode 100644 index 000000000000..e2588fb4fdb1 --- /dev/null +++ b/services/core/java/com/android/server/audio/ServiceHolder.java @@ -0,0 +1,219 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.audio; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.IBinder; +import android.os.IInterface; +import android.os.IServiceCallback; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.util.Log; + +import java.util.Objects; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.Executor; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Consumer; +import java.util.function.Function; + +/** + * Manages a remote service which can start and stop. Allows clients to add tasks to run when the + * remote service starts or dies. + * + * <p>Example usage should look something like: + * + * <pre> + * var service = mServiceHolder.checkService(); + * if (service == null) handleFailure(); + * try { + * service.foo(); + * } catch (RemoteException e) { + * mServiceHolder.attemptClear(service.asBinder()); + * handleFailure(); + * } + * </pre> + */ +public class ServiceHolder<I extends IInterface> implements IBinder.DeathRecipient { + + private final String mTag; + private final String mServiceName; + private final Function<? super IBinder, ? extends I> mCastFunction; + private final Executor mExecutor; + private final ServiceProviderFacade mServiceProvider; + + private final AtomicReference<I> mService = new AtomicReference(); + private final Set<Consumer<I>> mOnStartTasks = ConcurrentHashMap.newKeySet(); + private final Set<Consumer<I>> mOnDeathTasks = ConcurrentHashMap.newKeySet(); + + private final IServiceCallback mServiceListener = + new IServiceCallback.Stub() { + @Override + public void onRegistration(String name, IBinder binder) { + onServiceInited(binder); + } + }; + + // For test purposes + public static interface ServiceProviderFacade { + public void registerForNotifications(String name, IServiceCallback listener); + + public IBinder checkService(String name); + + public IBinder waitForService(String name); + } + + public ServiceHolder( + @NonNull String serviceName, + @NonNull Function<? super IBinder, ? extends I> castFunction, + @NonNull Executor executor) { + this( + serviceName, + castFunction, + executor, + new ServiceProviderFacade() { + @Override + public void registerForNotifications(String name, IServiceCallback listener) { + try { + ServiceManager.registerForNotifications(name, listener); + } catch (RemoteException e) { + throw new IllegalStateException("ServiceManager died!!", e); + } + } + + @Override + public IBinder checkService(String name) { + return ServiceManager.checkService(name); + } + + @Override + public IBinder waitForService(String name) { + return ServiceManager.waitForService(name); + } + }); + } + + public ServiceHolder( + @NonNull String serviceName, + @NonNull Function<? super IBinder, ? extends I> castFunction, + @NonNull Executor executor, + @NonNull ServiceProviderFacade provider) { + mServiceName = Objects.requireNonNull(serviceName); + mCastFunction = Objects.requireNonNull(castFunction); + mExecutor = Objects.requireNonNull(executor); + mServiceProvider = Objects.requireNonNull(provider); + mTag = "ServiceHolder: " + serviceName; + mServiceProvider.registerForNotifications(mServiceName, mServiceListener); + } + + /** + * Add tasks to run when service becomes available. Ran on the executor provided at + * construction. Note, for convenience, if the service is already connected, the task is + * immediately run. + */ + public void registerOnStartTask(Consumer<I> task) { + mOnStartTasks.add(task); + I i; + if ((i = mService.get()) != null) { + mExecutor.execute(() -> task.accept(i)); + } + } + + public void unregisterOnStartTask(Consumer<I> task) { + mOnStartTasks.remove(task); + } + + /** + * Add tasks to run when service goes down. Ran on the executor provided at construction. Should + * be called before getService to avoid dropping a death notification. + */ + public void registerOnDeathTask(Consumer<I> task) { + mOnDeathTasks.add(task); + } + + public void unregisterOnDeathTask(Consumer<I> task) { + mOnDeathTasks.remove(task); + } + + @Override + public void binderDied(@NonNull IBinder who) { + attemptClear(who); + } + + @Override + public void binderDied() { + throw new AssertionError("Wrong binderDied called, this should never happen"); + } + + /** + * Notify the holder that the service has gone done, usually in response to a RemoteException. + * Equivalent to receiving a binder death notification. + */ + public void attemptClear(IBinder who) { + // Possibly prone to weird races, resulting in spurious dead/revive, + // but that should be fine. + var current = mService.get(); + if (current != null + && Objects.equals(current.asBinder(), who) + && mService.compareAndSet(current, null)) { + who.unlinkToDeath(this, 0); + for (var r : mOnDeathTasks) { + mExecutor.execute(() -> r.accept(current)); + } + } + } + + /** Get the service, without blocking. Can trigger start tasks, on the provided executor. */ + public @Nullable I checkService() { + var s = mService.get(); + if (s != null) return s; + IBinder registered = mServiceProvider.checkService(mServiceName); + if (registered == null) return null; + return onServiceInited(registered); + } + + /** Get the service, but block. Can trigger start tasks, on the provided executor. */ + public @NonNull I waitForService() { + var s = mService.get(); + return (s != null) ? s : onServiceInited(mServiceProvider.waitForService(mServiceName)); + } + + /* + * Called when the native service is initialized. + */ + private @NonNull I onServiceInited(@NonNull IBinder who) { + var service = mCastFunction.apply(who); + Objects.requireNonNull(service); + if (!mService.compareAndSet(null, service)) { + return service; + } + // Even if the service has immediately died, we should perform these tasks for consistency + for (var r : mOnStartTasks) { + mExecutor.execute(() -> r.accept(service)); + } + try { + who.linkToDeath(this, 0); + } catch (RemoteException e) { + Log.e(mTag, "Immediate service death. Service crash-looping"); + attemptClear(who); + } + // This interface is non-null, but could represent a dead object + return service; + } +} diff --git a/services/core/java/com/android/server/audio/SoundDoseHelper.java b/services/core/java/com/android/server/audio/SoundDoseHelper.java index 9610034caf01..e28ae952e65a 100644 --- a/services/core/java/com/android/server/audio/SoundDoseHelper.java +++ b/services/core/java/com/android/server/audio/SoundDoseHelper.java @@ -856,11 +856,12 @@ public class SoundDoseHelper { pw.println(); } - /*package*/void reset() { + /*package*/void reset(boolean resetISoundDose) { Log.d(TAG, "Reset the sound dose helper"); - mSoundDose.compareAndExchange(/*expectedValue=*/null, - AudioSystem.getSoundDoseInterface(mSoundDoseCallback)); + if (resetISoundDose) { + mSoundDose.set(AudioSystem.getSoundDoseInterface(mSoundDoseCallback)); + } synchronized (mCsdStateLock) { try { @@ -972,7 +973,7 @@ public class SoundDoseHelper { } } - reset(); + reset(/*resetISoundDose=*/false); } private void onConfigureSafeMedia(boolean force, String caller) { diff --git a/services/core/java/com/android/server/biometrics/biometrics.aconfig b/services/core/java/com/android/server/biometrics/biometrics.aconfig index 712dcee55b7b..92fd9cbcf14e 100644 --- a/services/core/java/com/android/server/biometrics/biometrics.aconfig +++ b/services/core/java/com/android/server/biometrics/biometrics.aconfig @@ -14,10 +14,3 @@ flag { description: "This flag controls whether virtual HAL is used for testing instead of TestHal " bug: "294254230" } - -flag { - name: "mandatory_biometrics" - namespace: "biometrics_framework" - description: "This flag controls whether LSKF fallback is removed from biometric prompt when the phone is outside trusted locations" - bug: "322081563" -} diff --git a/services/core/java/com/android/server/display/DisplayOffloadSessionImpl.java b/services/core/java/com/android/server/display/DisplayOffloadSessionImpl.java index f77a360addd0..0fef55da5749 100644 --- a/services/core/java/com/android/server/display/DisplayOffloadSessionImpl.java +++ b/services/core/java/com/android/server/display/DisplayOffloadSessionImpl.java @@ -21,13 +21,21 @@ import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIG import android.annotation.Nullable; import android.hardware.display.DisplayManagerInternal; import android.os.Trace; +import android.util.Slog; import android.view.Display; +import com.android.server.display.utils.DebugUtils; + /** * An implementation of the offload session that keeps track of whether the session is active. * An offload session is used to control the display's brightness using the offload chip. */ public class DisplayOffloadSessionImpl implements DisplayManagerInternal.DisplayOffloadSession { + private static final String TAG = "DisplayOffloadSessionImpl"; + + // To enable these logs, run: + // 'adb shell setprop persist.log.tag.DisplayOffloadSessionImpl DEBUG && adb reboot' + private static final boolean DEBUG = DebugUtils.isDebuggable(TAG); @Nullable private final DisplayManagerInternal.DisplayOffloader mDisplayOffloader; @@ -99,9 +107,14 @@ public class DisplayOffloadSessionImpl implements DisplayManagerInternal.Display if (mDisplayOffloader == null || mIsActive) { return false; } + Trace.traceBegin(Trace.TRACE_TAG_POWER, "DisplayOffloader#startOffload"); try { - return mIsActive = mDisplayOffloader.startOffload(); + mIsActive = mDisplayOffloader.startOffload(); + if (DEBUG) { + Slog.d(TAG, "startOffload = " + mIsActive); + } + return mIsActive; } finally { Trace.traceEnd(Trace.TRACE_TAG_POWER); } @@ -118,6 +131,9 @@ public class DisplayOffloadSessionImpl implements DisplayManagerInternal.Display try { mDisplayOffloader.stopOffload(); mIsActive = false; + if (DEBUG) { + Slog.i(TAG, "stopOffload"); + } } finally { Trace.traceEnd(Trace.TRACE_TAG_POWER); } diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java index 7d482f74d5b3..b97053b21b8e 100644 --- a/services/core/java/com/android/server/display/DisplayPowerController.java +++ b/services/core/java/com/android/server/display/DisplayPowerController.java @@ -791,10 +791,11 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call @Override public void overrideDozeScreenState(int displayState, @Display.StateReason int reason) { + Slog.i(TAG, "New offload doze override: " + Display.stateToString(displayState)); mHandler.postAtTime(() -> { if (mDisplayOffloadSession == null || !(DisplayOffloadSession.isSupportedOffloadState(displayState) - || displayState == Display.STATE_UNKNOWN)) { + || displayState == Display.STATE_UNKNOWN)) { return; } mDisplayStateController.overrideDozeScreenState(displayState, reason); @@ -1279,7 +1280,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call private void updatePowerStateInternal() { // Update the power state request. - final boolean mustNotify; + boolean mustNotify = false; final int previousPolicy; boolean mustInitialize = false; mBrightnessReasonTemp.set(null); @@ -1327,6 +1328,30 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call initialize(readyToUpdateDisplayState() ? state : Display.STATE_UNKNOWN); } + if (mFlags.isOffloadDozeOverrideHoldsWakelockEnabled()) { + // Sometimes, a display-state change can come without an associated PowerRequest, + // as with DisplayOffload. For those cases, we have to make sure to also mark the + // display as "not ready" so that we can inform power-manager when the state-change is + // complete. + if (mPowerState.getScreenState() != state) { + final boolean wasReady; + synchronized (mLock) { + wasReady = mDisplayReadyLocked; + mDisplayReadyLocked = false; + mustNotify = true; + } + + if (wasReady) { + // If we went from ready to not-ready from the state-change (instead of a + // PowerRequest) there's a good chance that nothing is keeping PowerManager + // from suspending. Grab the unfinished business suspend blocker to keep the + // device awake until the display-state change goes into effect. + mWakelockController.acquireWakelock( + WakelockController.WAKE_LOCK_UNFINISHED_BUSINESS); + } + } + } + // Animate the screen state change unless already animating. // The transition may be deferred, so after this point we will use the // actual state instead of the desired one. diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java index 182b05a68028..44846f310348 100644 --- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java +++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java @@ -168,6 +168,12 @@ final class LocalDisplayAdapter extends DisplayAdapter { } SurfaceControl.DesiredDisplayModeSpecs modeSpecs = mSurfaceControlProxy.getDesiredDisplayModeSpecs(displayToken); + if (modeSpecs == null) { + // If mode specs is null, it most probably means that display got + // unplugged very rapidly. + Slog.w(TAG, "Desired display mode specs from SurfaceFlinger are null"); + return; + } LocalDisplayDevice device = mDevices.get(physicalDisplayId); if (device == null) { // Display was added. diff --git a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java index 8f775a54a8cd..f923cbc978ff 100644 --- a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java +++ b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java @@ -174,6 +174,11 @@ public class DisplayManagerFlags { Flags::enableSynthetic60hzModes ); + private final FlagState mOffloadDozeOverrideHoldsWakelock = new FlagState( + Flags.FLAG_OFFLOAD_DOZE_OVERRIDE_HOLDS_WAKELOCK, + Flags::offloadDozeOverrideHoldsWakelock + ); + /** * @return {@code true} if 'port' is allowed in display layout configuration file. */ @@ -343,6 +348,10 @@ public class DisplayManagerFlags { return mPeakRefreshRatePhysicalLimit.isEnabled(); } + public boolean isOffloadDozeOverrideHoldsWakelockEnabled() { + return mOffloadDozeOverrideHoldsWakelock.isEnabled(); + } + /** * @return Whether to ignore preferredRefreshRate app request or not */ @@ -389,6 +398,7 @@ public class DisplayManagerFlags { pw.println(" " + mPeakRefreshRatePhysicalLimit); pw.println(" " + mIgnoreAppPreferredRefreshRate); pw.println(" " + mSynthetic60hzModes); + pw.println(" " + mOffloadDozeOverrideHoldsWakelock); } private static class FlagState { diff --git a/services/core/java/com/android/server/display/feature/display_flags.aconfig b/services/core/java/com/android/server/display/feature/display_flags.aconfig index 697218dc0f41..95d0ca381f77 100644 --- a/services/core/java/com/android/server/display/feature/display_flags.aconfig +++ b/services/core/java/com/android/server/display/feature/display_flags.aconfig @@ -289,3 +289,13 @@ flag { } } +flag { + name: "offload_doze_override_holds_wakelock" + namespace: "display_manager" + description: "DisplayPowerController holds a suspend-blocker while changing the display state on behalf of offload doze override." + bug: "338403827" + is_fixed_read_only: true + metadata { + purpose: PURPOSE_BUGFIX + } +} diff --git a/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java b/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java index d876a381ca7a..3c3bdd5b69f6 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java @@ -269,8 +269,7 @@ public class HdmiCecMessageValidator { addValidationInfo(Constants.MESSAGE_REQUEST_SHORT_AUDIO_DESCRIPTOR, oneByteValidator, ADDR_NOT_UNREGISTERED, ADDR_DIRECT); addValidationInfo(Constants.MESSAGE_SET_SYSTEM_AUDIO_MODE, - new MinimumOneByteRangeValidator(0x00, 0x01), - ADDR_NOT_UNREGISTERED, ADDR_ALL); + new SingleByteRangeValidator(0x00, 0x01), ADDR_AUDIO_SYSTEM, ADDR_ALL); addValidationInfo(Constants.MESSAGE_SYSTEM_AUDIO_MODE_STATUS, new SingleByteRangeValidator(0x00, 0x01), ADDR_NOT_UNREGISTERED, ADDR_DIRECT); diff --git a/services/core/java/com/android/server/hdmi/HdmiUtils.java b/services/core/java/com/android/server/hdmi/HdmiUtils.java index 5646e1b9a9ef..0688fbf358ad 100644 --- a/services/core/java/com/android/server/hdmi/HdmiUtils.java +++ b/services/core/java/com/android/server/hdmi/HdmiUtils.java @@ -175,14 +175,15 @@ final class HdmiUtils { * * @param logicalAddress the logical address to verify * @param deviceType the device type to check - * @throws IllegalArgumentException */ - static void verifyAddressType(int logicalAddress, int deviceType) { + static boolean verifyAddressType(int logicalAddress, int deviceType) { List<Integer> actualDeviceTypes = getTypeFromAddress(logicalAddress); if (!actualDeviceTypes.contains(deviceType)) { - throw new IllegalArgumentException("Device type missmatch:[Expected:" + deviceType - + ", Actual:" + actualDeviceTypes); + Slog.w(TAG,"Device type mismatch:[Expected:" + deviceType + + ", Actual:" + actualDeviceTypes + "]"); + return false; } + return true; } /** diff --git a/services/core/java/com/android/server/hdmi/RequestArcAction.java b/services/core/java/com/android/server/hdmi/RequestArcAction.java index 54c8c00b8889..58e146ecaa78 100644 --- a/services/core/java/com/android/server/hdmi/RequestArcAction.java +++ b/services/core/java/com/android/server/hdmi/RequestArcAction.java @@ -19,6 +19,7 @@ package com.android.server.hdmi; import android.hardware.hdmi.HdmiControlManager; import android.hardware.hdmi.HdmiDeviceInfo; import android.hardware.hdmi.IHdmiControlCallback; +import android.util.Slog; /** * Base feature action class for <Request ARC Initiation>/<Request ARC Termination>. @@ -38,13 +39,14 @@ abstract class RequestArcAction extends HdmiCecFeatureAction { * @param source {@link HdmiCecLocalDevice} instance * @param avrAddress address of AV receiver. It should be AUDIO_SYSTEM type * @param callback callback to inform about the status of the action - * @throws IllegalArgumentException if device type of sourceAddress and avrAddress - * is invalid */ RequestArcAction(HdmiCecLocalDevice source, int avrAddress, IHdmiControlCallback callback) { super(source, callback); - HdmiUtils.verifyAddressType(getSourceAddress(), HdmiDeviceInfo.DEVICE_TV); - HdmiUtils.verifyAddressType(avrAddress, HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM); + if (!HdmiUtils.verifyAddressType(getSourceAddress(), HdmiDeviceInfo.DEVICE_TV) || + !HdmiUtils.verifyAddressType(avrAddress, HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM)) { + Slog.w(TAG, "Device type mismatch, stop the action."); + finish(); + } mAvrAddress = avrAddress; } diff --git a/services/core/java/com/android/server/hdmi/SetArcTransmissionStateAction.java b/services/core/java/com/android/server/hdmi/SetArcTransmissionStateAction.java index 32e274ece9ab..5ab22e1dcd61 100644 --- a/services/core/java/com/android/server/hdmi/SetArcTransmissionStateAction.java +++ b/services/core/java/com/android/server/hdmi/SetArcTransmissionStateAction.java @@ -47,8 +47,11 @@ final class SetArcTransmissionStateAction extends HdmiCecFeatureAction { SetArcTransmissionStateAction(HdmiCecLocalDevice source, int avrAddress, boolean enabled) { super(source); - HdmiUtils.verifyAddressType(getSourceAddress(), HdmiDeviceInfo.DEVICE_TV); - HdmiUtils.verifyAddressType(avrAddress, HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM); + if (!HdmiUtils.verifyAddressType(getSourceAddress(), HdmiDeviceInfo.DEVICE_TV) || + !HdmiUtils.verifyAddressType(avrAddress, HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM)) { + Slog.w(TAG, "Device type mismatch, stop the action."); + finish(); + } mAvrAddress = avrAddress; mEnabled = enabled; } diff --git a/services/core/java/com/android/server/hdmi/SystemAudioAction.java b/services/core/java/com/android/server/hdmi/SystemAudioAction.java index e96963b9ae3f..f14cda1e6509 100644 --- a/services/core/java/com/android/server/hdmi/SystemAudioAction.java +++ b/services/core/java/com/android/server/hdmi/SystemAudioAction.java @@ -20,6 +20,7 @@ import android.hardware.hdmi.HdmiControlManager; import android.hardware.hdmi.HdmiDeviceInfo; import android.hardware.hdmi.IHdmiControlCallback; import android.hardware.tv.cec.V1_0.SendMessageResult; +import android.util.Slog; import java.util.List; @@ -56,12 +57,14 @@ abstract class SystemAudioAction extends HdmiCecFeatureAction { * @param avrAddress logical address of AVR device * @param targetStatus Whether to enable the system audio mode or not * @param callback callback interface to be notified when it's done - * @throws IllegalArgumentException if device type of sourceAddress and avrAddress is invalid */ SystemAudioAction(HdmiCecLocalDevice source, int avrAddress, boolean targetStatus, IHdmiControlCallback callback) { super(source, callback); - HdmiUtils.verifyAddressType(avrAddress, HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM); + if (!HdmiUtils.verifyAddressType(avrAddress, HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM)) { + Slog.w(TAG, "Device type mismatch, stop the action."); + finish(); + } mAvrLogicalAddress = avrAddress; mTargetAudioStatus = targetStatus; } diff --git a/services/core/java/com/android/server/hdmi/SystemAudioActionFromAvr.java b/services/core/java/com/android/server/hdmi/SystemAudioActionFromAvr.java index 99148c4ea114..08a938731dae 100644 --- a/services/core/java/com/android/server/hdmi/SystemAudioActionFromAvr.java +++ b/services/core/java/com/android/server/hdmi/SystemAudioActionFromAvr.java @@ -19,12 +19,14 @@ package com.android.server.hdmi; import android.hardware.hdmi.HdmiControlManager; import android.hardware.hdmi.HdmiDeviceInfo; import android.hardware.hdmi.IHdmiControlCallback; +import android.util.Slog; /** * Feature action that handles System Audio initiated by AVR devices. */ // Seq #33 final class SystemAudioActionFromAvr extends SystemAudioAction { + private static final String TAG = "SystemAudioActionFromAvr"; /** * Constructor * @@ -32,12 +34,14 @@ final class SystemAudioActionFromAvr extends SystemAudioAction { * @param avrAddress logical address of AVR device * @param targetStatus Whether to enable the system audio mode or not * @param callback callback interface to be notified when it's done - * @throws IllegalArgumentException if device type of tvAddress and avrAddress is invalid */ SystemAudioActionFromAvr(HdmiCecLocalDevice source, int avrAddress, boolean targetStatus, IHdmiControlCallback callback) { super(source, avrAddress, targetStatus, callback); - HdmiUtils.verifyAddressType(getSourceAddress(), HdmiDeviceInfo.DEVICE_TV); + if (!HdmiUtils.verifyAddressType(getSourceAddress(), HdmiDeviceInfo.DEVICE_TV)) { + Slog.w(TAG, "Device type mismatch, stop the action."); + finish(); + } } @Override diff --git a/services/core/java/com/android/server/hdmi/SystemAudioActionFromTv.java b/services/core/java/com/android/server/hdmi/SystemAudioActionFromTv.java index 5c0c272f59e0..675aa3171fbd 100644 --- a/services/core/java/com/android/server/hdmi/SystemAudioActionFromTv.java +++ b/services/core/java/com/android/server/hdmi/SystemAudioActionFromTv.java @@ -18,13 +18,14 @@ package com.android.server.hdmi; import android.hardware.hdmi.HdmiDeviceInfo; import android.hardware.hdmi.IHdmiControlCallback; +import android.util.Slog; /** * Feature action that handles System Audio initiated by TV devices. */ final class SystemAudioActionFromTv extends SystemAudioAction { - + private static final String TAG = "SystemAudioActionFromTv"; /** * Constructor * @@ -32,12 +33,14 @@ final class SystemAudioActionFromTv extends SystemAudioAction { * @param avrAddress logical address of AVR device * @param targetStatus Whether to enable the system audio mode or not * @param callback callback interface to be notified when it's done - * @throws IllegalArgumentException if device type of tvAddress is invalid */ SystemAudioActionFromTv(HdmiCecLocalDevice sourceAddress, int avrAddress, boolean targetStatus, IHdmiControlCallback callback) { super(sourceAddress, avrAddress, targetStatus, callback); - HdmiUtils.verifyAddressType(getSourceAddress(), HdmiDeviceInfo.DEVICE_TV); + if (!HdmiUtils.verifyAddressType(getSourceAddress(), HdmiDeviceInfo.DEVICE_TV)) { + Slog.w(TAG, "Device type mismatch, stop the action."); + finish(); + } } @Override diff --git a/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java b/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java index 1c958a929546..23f947cc8452 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java @@ -367,9 +367,9 @@ final class InputMethodSubtypeSwitchingController { } protected void dump(final Printer pw, final String prefix) { - for (int i = 0; i < mUsageHistoryOfSubtypeListItemIndex.length; ++i) { - final int rank = mUsageHistoryOfSubtypeListItemIndex[i]; - final ImeSubtypeListItem item = mImeSubtypeList.get(i); + for (int rank = 0; rank < mUsageHistoryOfSubtypeListItemIndex.length; ++rank) { + final int index = mUsageHistoryOfSubtypeListItemIndex[rank]; + final ImeSubtypeListItem item = mImeSubtypeList.get(index); pw.println(prefix + "rank=" + rank + " item=" + item); } } diff --git a/services/core/java/com/android/server/locales/LocaleManagerBackupHelper.java b/services/core/java/com/android/server/locales/LocaleManagerBackupHelper.java index 563f93e96331..b9e09605477a 100644 --- a/services/core/java/com/android/server/locales/LocaleManagerBackupHelper.java +++ b/services/core/java/com/android/server/locales/LocaleManagerBackupHelper.java @@ -84,9 +84,16 @@ class LocaleManagerBackupHelper { * from the delegate selector. */ private static final String LOCALES_FROM_DELEGATE_PREFS = "LocalesFromDelegatePrefs.xml"; + private static final String LOCALES_STAGED_DATA_PREFS = "LocalesStagedDataPrefs.xml"; + private static final String ARCHIVED_PACKAGES_PREFS = "ArchivedPackagesPrefs.xml"; // Stage data would be deleted on reboot since it's stored in memory. So it's retained until // retention period OR next reboot, whichever happens earlier. private static final Duration STAGE_DATA_RETENTION_PERIOD = Duration.ofDays(3); + // Store the locales staged data for the specified package in the SharedPreferences. The format + // is locales s:setFromDelegate + // For example: en-US s:true + private static final String STRING_SPLIT = " s:"; + private static final String KEY_STAGED_DATA_TIME = "staged_data_time"; private final LocaleManagerService mLocaleManagerService; private final PackageManager mPackageManager; @@ -94,39 +101,34 @@ class LocaleManagerBackupHelper { private final Context mContext; private final Object mStagedDataLock = new Object(); - // Staged data map keyed by user-id to handle multi-user scenario / work profiles. We are using - // SparseArray because it is more memory-efficient than a HashMap. - private final SparseArray<StagedData> mStagedData; - // SharedPreferences to store packages whose app-locale was set by a delegate, as opposed to // the application setting the app-locale itself. private final SharedPreferences mDelegateAppLocalePackages; + // For unit tests + private final SparseArray<File> mStagedDataFiles; + private final File mArchivedPackagesFile; + private final BroadcastReceiver mUserMonitor; - // To determine whether an app is pre-archived, check for Intent.EXTRA_ARCHIVAL upon receiving - // the initial PACKAGE_ADDED broadcast. If it is indeed pre-archived, perform the data - // restoration during the second PACKAGE_ADDED broadcast, which is sent subsequently when the - // app is installed. - private final Set<String> mPkgsToRestore; LocaleManagerBackupHelper(LocaleManagerService localeManagerService, PackageManager packageManager, HandlerThread broadcastHandlerThread) { this(localeManagerService.mContext, localeManagerService, packageManager, Clock.systemUTC(), - new SparseArray<>(), broadcastHandlerThread, null); + broadcastHandlerThread, null, null, null); } - @VisibleForTesting LocaleManagerBackupHelper(Context context, - LocaleManagerService localeManagerService, - PackageManager packageManager, Clock clock, SparseArray<StagedData> stagedData, - HandlerThread broadcastHandlerThread, SharedPreferences delegateAppLocalePackages) { + @VisibleForTesting + LocaleManagerBackupHelper(Context context, LocaleManagerService localeManagerService, + PackageManager packageManager, Clock clock, HandlerThread broadcastHandlerThread, + SparseArray<File> stagedDataFiles, File archivedPackagesFile, + SharedPreferences delegateAppLocalePackages) { mContext = context; mLocaleManagerService = localeManagerService; mPackageManager = packageManager; mClock = clock; - mStagedData = stagedData; mDelegateAppLocalePackages = delegateAppLocalePackages != null ? delegateAppLocalePackages - : createPersistedInfo(); - mPkgsToRestore = new ArraySet<>(); - + : createPersistedInfo(); + mArchivedPackagesFile = archivedPackagesFile; + mStagedDataFiles = stagedDataFiles; mUserMonitor = new UserMonitor(); IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_USER_REMOVED); @@ -148,7 +150,7 @@ class LocaleManagerBackupHelper { } synchronized (mStagedDataLock) { - cleanStagedDataForOldEntriesLocked(); + cleanStagedDataForOldEntriesLocked(userId); } HashMap<String, LocalesInfo> pkgStates = new HashMap<>(); @@ -207,14 +209,11 @@ class LocaleManagerBackupHelper { return out.toByteArray(); } - private void cleanStagedDataForOldEntriesLocked() { - for (int i = 0; i < mStagedData.size(); i++) { - int userId = mStagedData.keyAt(i); - StagedData stagedData = mStagedData.get(userId); - if (stagedData.mCreationTimeMillis - < mClock.millis() - STAGE_DATA_RETENTION_PERIOD.toMillis()) { - deleteStagedDataLocked(userId); - } + private void cleanStagedDataForOldEntriesLocked(@UserIdInt int userId) { + Long created_time = getStagedDataSp(userId).getLong(KEY_STAGED_DATA_TIME, -1); + if (created_time != -1 + && created_time < mClock.millis() - STAGE_DATA_RETENTION_PERIOD.toMillis()) { + deleteStagedDataLocked(userId); } } @@ -252,20 +251,16 @@ class LocaleManagerBackupHelper { // performed simultaneously. synchronized (mStagedDataLock) { // Backups for apps which are yet to be installed. - StagedData stagedData = new StagedData(mClock.millis(), new HashMap<>()); - for (String pkgName : pkgStates.keySet()) { LocalesInfo localesInfo = pkgStates.get(pkgName); // Check if the application is already installed for the concerned user. if (isPackageInstalledForUser(pkgName, userId)) { - if (mPkgsToRestore != null) { - mPkgsToRestore.remove(pkgName); - } + removeFromArchivedPackagesInfo(userId, pkgName); // Don't apply the restore if the locales have already been set for the app. checkExistingLocalesAndApplyRestore(pkgName, localesInfo, userId); } else { // Stage the data if the app isn't installed. - stagedData.mPackageStates.put(pkgName, localesInfo); + storeStagedDataInfo(userId, pkgName, localesInfo); if (DEBUG) { Slog.d(TAG, "Add locales=" + localesInfo.mLocales + " fromDelegate=" + localesInfo.mSetFromDelegate @@ -274,8 +269,9 @@ class LocaleManagerBackupHelper { } } - if (!stagedData.mPackageStates.isEmpty()) { - mStagedData.put(userId, stagedData); + // Create the time if the data is being staged. + if (!getStagedDataSp(userId).getAll().isEmpty()) { + storeStagedDataCreatedTime(userId); } } } @@ -293,14 +289,23 @@ class LocaleManagerBackupHelper { * added on device. */ void onPackageAddedWithExtras(String packageName, int uid, Bundle extras) { - boolean archived = false; + int userId = UserHandle.getUserId(uid); if (extras != null) { - archived = extras.getBoolean(Intent.EXTRA_ARCHIVAL, false); - if (archived && mPkgsToRestore != null) { - mPkgsToRestore.add(packageName); + // To determine whether an app is pre-archived, check for Intent.EXTRA_ARCHIVAL upon + // receiving the initial PACKAGE_ADDED broadcast. If it is indeed pre-archived, perform + // the data restoration during the second PACKAGE_ADDED broadcast, which is sent + // subsequently when the app is installed. + boolean archived = extras.getBoolean(Intent.EXTRA_ARCHIVAL, false); + if (DEBUG) { + Slog.d(TAG, + "onPackageAddedWithExtras packageName: " + packageName + ", userId: " + + userId + ", archived: " + archived); + } + if (archived) { + addInArchivedPackagesInfo(userId, packageName); } } - checkStageDataAndApplyRestore(packageName, uid); + checkStageDataAndApplyRestore(packageName, userId); } /** @@ -310,9 +315,32 @@ class LocaleManagerBackupHelper { */ void onPackageUpdateFinished(String packageName, int uid) { int userId = UserHandle.getUserId(uid); - if (mPkgsToRestore != null && mPkgsToRestore.contains(packageName)) { - mPkgsToRestore.remove(packageName); - checkStageDataAndApplyRestore(packageName, uid); + if (DEBUG) { + Slog.d(TAG, + "onPackageUpdateFinished userId: " + userId + ", packageName: " + packageName); + } + String user = Integer.toString(userId); + File file = getArchivedPackagesFile(); + if (file.exists()) { + SharedPreferences sp = getArchivedPackagesSp(file); + Set<String> packageNames = new ArraySet<>(sp.getStringSet(user, new ArraySet<>())); + if (packageNames.remove(packageName)) { + SharedPreferences.Editor editor = sp.edit(); + if (packageNames.isEmpty()) { + if (!editor.remove(user).commit()) { + Slog.e(TAG, "Failed to remove the user"); + } + if (sp.getAll().isEmpty()) { + file.delete(); + } + } else { + // commit and log the result. + if (!editor.putStringSet(user, packageNames).commit()) { + Slog.e(TAG, "failed to remove the package"); + } + } + checkStageDataAndApplyRestore(packageName, userId); + } } cleanApplicationLocalesIfNeeded(packageName, userId); } @@ -347,16 +375,16 @@ class LocaleManagerBackupHelper { } } - private void checkStageDataAndApplyRestore(String packageName, int uid) { + private void checkStageDataAndApplyRestore(String packageName, int userId) { try { synchronized (mStagedDataLock) { - cleanStagedDataForOldEntriesLocked(); - - int userId = UserHandle.getUserId(uid); - if (mStagedData.contains(userId)) { - if (mPkgsToRestore != null) { - mPkgsToRestore.remove(packageName); + cleanStagedDataForOldEntriesLocked(userId); + if (!getStagedDataSp(userId).getString(packageName, "").isEmpty()) { + if (DEBUG) { + Slog.d(TAG, + "checkStageDataAndApplyRestore, remove package and restore data"); } + removeFromArchivedPackagesInfo(userId, packageName); // Perform lazy restore only if the staged data exists. doLazyRestoreLocked(packageName, userId); } @@ -417,8 +445,17 @@ class LocaleManagerBackupHelper { } } - private void deleteStagedDataLocked(@UserIdInt int userId) { - mStagedData.remove(userId); + void deleteStagedDataLocked(@UserIdInt int userId) { + File stagedFile = getStagedDataFile(userId); + SharedPreferences sp = getStagedDataSp(stagedFile); + // commit and log the result. + if (!sp.edit().clear().commit()) { + Slog.e(TAG, "Failed to commit data!"); + } + + if (stagedFile.exists()) { + stagedFile.delete(); + } } /** @@ -473,16 +510,6 @@ class LocaleManagerBackupHelper { out.endDocument(); } - static class StagedData { - final long mCreationTimeMillis; - final HashMap<String, LocalesInfo> mPackageStates; - - StagedData(long creationTimeMillis, HashMap<String, LocalesInfo> pkgStates) { - mCreationTimeMillis = creationTimeMillis; - mPackageStates = pkgStates; - } - } - static class LocalesInfo { final String mLocales; final boolean mSetFromDelegate; @@ -508,6 +535,7 @@ class LocaleManagerBackupHelper { synchronized (mStagedDataLock) { deleteStagedDataLocked(userId); removeProfileFromPersistedInfo(userId); + removeArchivedPackagesForUser(userId); } } } catch (Exception e) { @@ -533,29 +561,162 @@ class LocaleManagerBackupHelper { return; } - StagedData stagedData = mStagedData.get(userId); - for (String pkgName : stagedData.mPackageStates.keySet()) { - LocalesInfo localesInfo = stagedData.mPackageStates.get(pkgName); + SharedPreferences sp = getStagedDataSp(userId); + String value = sp.getString(packageName, ""); + if (!value.isEmpty()) { + String[] info = value.split(STRING_SPLIT); + if (info == null || info.length != 2) { + Slog.e(TAG, "Failed to restore data"); + return; + } + LocalesInfo localesInfo = new LocalesInfo(info[0], Boolean.parseBoolean(info[1])); + checkExistingLocalesAndApplyRestore(packageName, localesInfo, userId); - if (pkgName.equals(packageName)) { + // Remove the restored entry from the staged data list. + if (!sp.edit().remove(packageName).commit()) { + Slog.e(TAG, "Failed to commit data!"); + } + } - checkExistingLocalesAndApplyRestore(pkgName, localesInfo, userId); + // Remove the stage data entry for user if there are no more packages to restore. + if (sp.getAll().size() == 1 && sp.getLong(KEY_STAGED_DATA_TIME, -1) != -1) { + deleteStagedDataLocked(userId); + } + } - // Remove the restored entry from the staged data list. - stagedData.mPackageStates.remove(pkgName); + private File getStagedDataFile(@UserIdInt int userId) { + return mStagedDataFiles == null ? new File(Environment.getDataSystemDeDirectory(userId), + LOCALES_STAGED_DATA_PREFS) : mStagedDataFiles.get(userId); + } - // Remove the stage data entry for user if there are no more packages to restore. - if (stagedData.mPackageStates.isEmpty()) { - mStagedData.remove(userId); - } + private SharedPreferences getStagedDataSp(File file) { + return mStagedDataFiles == null ? mContext.createDeviceProtectedStorageContext() + .getSharedPreferences(file, Context.MODE_PRIVATE) + : mContext.getSharedPreferences(file, Context.MODE_PRIVATE); + } + + private SharedPreferences getStagedDataSp(@UserIdInt int userId) { + return mStagedDataFiles == null ? mContext.createDeviceProtectedStorageContext() + .getSharedPreferences(getStagedDataFile(userId), Context.MODE_PRIVATE) + : mContext.getSharedPreferences(mStagedDataFiles.get(userId), Context.MODE_PRIVATE); + } - // No need to loop further after restoring locales because the staged data will - // contain at most one entry for the newly added package. - break; + /** + * Store the staged locales info. + */ + private void storeStagedDataInfo(@UserIdInt int userId, @NonNull String packageName, + @NonNull LocalesInfo localesInfo) { + if (DEBUG) { + Slog.d(TAG, "storeStagedDataInfo, userId: " + userId + ", packageName: " + packageName + + ", localesInfo.mLocales: " + localesInfo.mLocales + + ", localesInfo.mSetFromDelegate: " + localesInfo.mSetFromDelegate); + } + String info = + localesInfo.mLocales + STRING_SPLIT + String.valueOf(localesInfo.mSetFromDelegate); + SharedPreferences sp = getStagedDataSp(userId); + // commit and log the result. + if (!sp.edit().putString(packageName, info).commit()) { + Slog.e(TAG, "Failed to commit data!"); + } + } + + /** + * Store the time of creation for staged locales info. + */ + private void storeStagedDataCreatedTime(@UserIdInt int userId) { + SharedPreferences sp = getStagedDataSp(userId); + // commit and log the result. + if (!sp.edit().putLong(KEY_STAGED_DATA_TIME, mClock.millis()).commit()) { + Slog.e(TAG, "Failed to commit data!"); + } + } + + private File getArchivedPackagesFile() { + return mArchivedPackagesFile == null ? new File( + Environment.getDataSystemDeDirectory(UserHandle.USER_SYSTEM), + ARCHIVED_PACKAGES_PREFS) : mArchivedPackagesFile; + } + + private SharedPreferences getArchivedPackagesSp(File file) { + return mArchivedPackagesFile == null ? mContext.createDeviceProtectedStorageContext() + .getSharedPreferences(file, Context.MODE_PRIVATE) + : mContext.getSharedPreferences(file, Context.MODE_PRIVATE); + } + + /** + * Add the package into the archived packages list. + */ + private void addInArchivedPackagesInfo(@UserIdInt int userId, @NonNull String packageName) { + String user = Integer.toString(userId); + SharedPreferences sp = getArchivedPackagesSp(getArchivedPackagesFile()); + Set<String> packageNames = new ArraySet<>(sp.getStringSet(user, new ArraySet<>())); + if (DEBUG) { + Slog.d(TAG, "addInArchivedPackagesInfo before packageNames: " + packageNames + + ", packageName: " + packageName); + } + if (packageNames.add(packageName)) { + // commit and log the result. + if (!sp.edit().putStringSet(user, packageNames).commit()) { + Slog.e(TAG, "failed to add the package"); + } + } + } + + /** + * Remove the package from the archived packages list. + */ + private void removeFromArchivedPackagesInfo(@UserIdInt int userId, + @NonNull String packageName) { + File file = getArchivedPackagesFile(); + if (file.exists()) { + String user = Integer.toString(userId); + SharedPreferences sp = getArchivedPackagesSp(getArchivedPackagesFile()); + Set<String> packageNames = new ArraySet<>(sp.getStringSet(user, new ArraySet<>())); + if (DEBUG) { + Slog.d(TAG, "removeFromArchivedPackagesInfo before packageNames: " + packageNames + + ", packageName: " + packageName); + } + if (packageNames.remove(packageName)) { + SharedPreferences.Editor editor = sp.edit(); + if (packageNames.isEmpty()) { + if (!editor.remove(user).commit()) { + Slog.e(TAG, "Failed to remove user"); + } + if (sp.getAll().isEmpty()) { + file.delete(); + } + } else { + // commit and log the result. + if (!editor.putStringSet(user, packageNames).commit()) { + Slog.e(TAG, "failed to remove the package"); + } + } } } } + /** + * Remove the user from the archived packages list. + */ + private void removeArchivedPackagesForUser(@UserIdInt int userId) { + String user = Integer.toString(userId); + File file = getArchivedPackagesFile(); + SharedPreferences sp = getArchivedPackagesSp(file); + + if (sp == null || !sp.contains(user)) { + Slog.w(TAG, "The profile is not existed in the archived package info"); + return; + } + + if (!sp.edit().remove(user).commit()) { + Slog.e(TAG, "Failed to remove user"); + } + + if (sp.getAll().isEmpty() && file.exists()) { + file.delete(); + } + } + SharedPreferences createPersistedInfo() { final File prefsFile = new File( Environment.getDataSystemDeDirectory(UserHandle.USER_SYSTEM), diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java index b14702dc6647..b3ab229927fe 100644 --- a/services/core/java/com/android/server/locksettings/LockSettingsService.java +++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java @@ -1243,23 +1243,24 @@ public class LockSettingsService extends ILockSettings.Stub { } } - private void enforceFrpResolved() { + private void enforceFrpNotActive() { final int mainUserId = mInjector.getUserManagerInternal().getMainUserId(); if (mainUserId < 0) { - Slog.d(TAG, "No Main user on device; skipping enforceFrpResolved"); + Slog.d(TAG, "No Main user on device; skipping enforceFrpNotActive"); return; } - final ContentResolver cr = mContext.getContentResolver(); + final ContentResolver cr = mContext.getContentResolver(); final boolean inSetupWizard = Settings.Secure.getIntForUser(cr, Settings.Secure.USER_SETUP_COMPLETE, 0, mainUserId) == 0; - final boolean secureFrp = android.security.Flags.frpEnforcement() + final boolean isFrpActive = android.security.Flags.frpEnforcement() ? mStorage.isFactoryResetProtectionActive() - : (Settings.Global.getInt(cr, Settings.Global.SECURE_FRP_MODE, 0) == 1); + : (Settings.Global.getInt(cr, Settings.Global.SECURE_FRP_MODE, 0) == 1) + && inSetupWizard; - if (inSetupWizard && secureFrp) { - throw new SecurityException("Cannot change credential in SUW while factory reset" - + " protection is not resolved yet"); + if (isFrpActive) { + throw new SecurityException("Cannot change credential while factory reset protection" + + " is active"); } } @@ -1831,7 +1832,7 @@ public class LockSettingsService extends ILockSettings.Stub { final long identity = Binder.clearCallingIdentity(); try { - enforceFrpResolved(); + enforceFrpNotActive(); // When changing credential for profiles with unified challenge, some callers // will pass in empty credential while others will pass in the credential of // the parent user. setLockCredentialInternal() handles the formal case (empty diff --git a/services/core/java/com/android/server/notification/NotificationShellCmd.java b/services/core/java/com/android/server/notification/NotificationShellCmd.java index 9f3104cbd7b0..10169d544b73 100644 --- a/services/core/java/com/android/server/notification/NotificationShellCmd.java +++ b/services/core/java/com/android/server/notification/NotificationShellCmd.java @@ -66,7 +66,7 @@ public class NotificationShellCmd extends ShellCommand { + " disallow_listener COMPONENT [user_id (current user if not specified)]\n" + " allow_assistant COMPONENT [user_id (current user if not specified)]\n" + " remove_assistant COMPONENT [user_id (current user if not specified)]\n" - + " set_dnd [on|none (same as on)|priority|alarms|all|off (same as all)]" + + " set_dnd [on|none (same as on)|priority|alarms|all|off (same as all)]\n" + " allow_dnd PACKAGE [user_id (current user if not specified)]\n" + " disallow_dnd PACKAGE [user_id (current user if not specified)]\n" + " reset_assistant_user_set [user_id (current user if not specified)]\n" diff --git a/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerService.java b/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerService.java index dd7603714718..f540f1db6952 100644 --- a/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerService.java +++ b/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerService.java @@ -925,10 +925,11 @@ public class OnDeviceIntelligenceManagerService extends SystemService { } } - @RequiresPermission(Manifest.permission.USE_ON_DEVICE_INTELLIGENCE) + /** + * Reset the temporary services set in CTS tests, this method is primarily used to only revert + * the changes caused by CTS tests. + */ public void resetTemporaryServices() { - mContext.enforceCallingPermission( - Manifest.permission.USE_ON_DEVICE_INTELLIGENCE, TAG); synchronized (mLock) { if (mTemporaryHandler != null) { mTemporaryHandler.removeMessages(MSG_RESET_TEMPORARY_SERVICE); diff --git a/services/core/java/com/android/server/pm/InstantAppResolver.java b/services/core/java/com/android/server/pm/InstantAppResolver.java index 92d6a826b5f5..42efd6e60fc0 100644 --- a/services/core/java/com/android/server/pm/InstantAppResolver.java +++ b/services/core/java/com/android/server/pm/InstantAppResolver.java @@ -28,6 +28,7 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityManager; +import android.app.ActivityOptions; import android.app.PendingIntent; import android.content.ComponentName; import android.content.Context; @@ -296,6 +297,9 @@ public abstract class InstantAppResolver { if (needsPhaseTwo) { intent.setAction(Intent.ACTION_RESOLVE_INSTANT_APP_PACKAGE); } else { + ActivityOptions options = ActivityOptions.makeBasic() + .setPendingIntentCreatorBackgroundActivityStartMode( + ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED); // We have all of the data we need; just start the installer without a second phase if (failureIntent != null || installFailureActivity != null) { // Intent that is launched if the package couldn't be installed for any reason. @@ -322,7 +326,7 @@ public abstract class InstantAppResolver { PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_IMMUTABLE, - null /*bOptions*/, userId); + options.toBundle(), userId); IntentSender failureSender = new IntentSender(failureIntentTarget); // TODO(b/72700831): remove populating old extra intent.putExtra(Intent.EXTRA_INSTANT_APP_FAILURE, failureSender); @@ -342,7 +346,7 @@ public abstract class InstantAppResolver { new String[] { resolvedType }, PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_IMMUTABLE, - null /*bOptions*/, userId); + options.toBundle(), userId); IntentSender successSender = new IntentSender(successIntentTarget); intent.putExtra(Intent.EXTRA_INSTANT_APP_SUCCESS, successSender); } catch (RemoteException ignore) { /* ignore; same process */ } diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java index 57f6d2789dc5..a90473865ce5 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerSession.java +++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java @@ -5153,6 +5153,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } // Okay to proceed synchronized (mLock) { + assertCallerIsOwnerOrRoot(); + assertPreparedAndNotSealedLocked("setPreVerifiedDomains"); mPreVerifiedDomains = preVerifiedDomains; } } diff --git a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java index 1b6af7170756..efaa7a8598c0 100644 --- a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java +++ b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java @@ -3698,7 +3698,7 @@ public class BatteryStatsImpl extends BatteryStats { } return mTotalTimeUs + (mNesting > 0 ? (curBatteryRealtimeUs - mUpdateTimeUs) - / (mTimerPool != null ? mTimerPool.size() : 1) + / (mTimerPool != null && mTimerPool.size() > 0 ? mTimerPool.size() : 1) : 0); } diff --git a/services/core/java/com/android/server/search/SearchManagerService.java b/services/core/java/com/android/server/search/SearchManagerService.java index ecfc040ae29c..9b39fa1e177c 100644 --- a/services/core/java/com/android/server/search/SearchManagerService.java +++ b/services/core/java/com/android/server/search/SearchManagerService.java @@ -61,6 +61,8 @@ public class SearchManagerService extends ISearchManager.Stub { private static final String TAG = "SearchManagerService"; final Handler mHandler; + private final MyPackageMonitor mMyPackageMonitor; + public static class Lifecycle extends SystemService { private SearchManagerService mService; @@ -95,7 +97,8 @@ public class SearchManagerService extends ISearchManager.Stub { */ public SearchManagerService(Context context) { mContext = context; - new MyPackageMonitor().register(context, null, UserHandle.ALL, true); + mMyPackageMonitor = new MyPackageMonitor(); + mMyPackageMonitor.register(context, null, UserHandle.ALL, true); new GlobalSearchProviderObserver(context.getContentResolver()); mHandler = BackgroundThread.getHandler(); } @@ -230,7 +233,6 @@ public class SearchManagerService extends ISearchManager.Stub { if (!shouldRebuildSearchableList(changingUserId)) { return; } - synchronized (mSearchables) { // Invalidate the searchable list. Searchables searchables = mSearchables.get(changingUserId); diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 434e92f7978a..86115990f746 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -3964,7 +3964,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } if (isCurrentVisible) { - if (isNextNotYetVisible || delayRemoval) { + if (isNextNotYetVisible || delayRemoval || isInTransition()) { // Add this activity to the list of stopping activities. It will be processed and // destroyed when the next activity reports idle. addToStopping(false /* scheduleIdle */, false /* idleDelayed */, @@ -8551,7 +8551,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // If activity in fullscreen mode is letterboxed because of fixed orientation then bounds // are already calculated in resolveFixedOrientationConfiguration. // Don't apply aspect ratio if app is overridden to fullscreen by device user/manufacturer. - if (!isLetterboxedForFixedOrientationAndAspectRatio() + if (Flags.immersiveAppRepositioning() && !isLetterboxedForFixedOrientationAndAspectRatio() && !mLetterboxUiController.hasFullscreenOverride()) { resolveAspectRatioRestriction(newParentConfiguration); } @@ -8568,6 +8568,14 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A computeConfigByResolveHint(resolvedConfig, newParentConfiguration); } } + // If activity in fullscreen mode is letterboxed because of fixed orientation then bounds + // are already calculated in resolveFixedOrientationConfiguration, or if in size compat + // mode, it should already be calculated in resolveSizeCompatModeConfiguration. + // Don't apply aspect ratio if app is overridden to fullscreen by device user/manufacturer. + if (!Flags.immersiveAppRepositioning() && !isLetterboxedForFixedOrientationAndAspectRatio() + && !mInSizeCompatModeForBounds && !mLetterboxUiController.hasFullscreenOverride()) { + resolveAspectRatioRestriction(newParentConfiguration); + } if (isFixedOrientationLetterboxAllowed || compatDisplayInsets != null // In fullscreen, can be letterboxed for aspect ratio. @@ -8903,7 +8911,11 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } boolean isImmersiveMode(@NonNull Rect parentBounds) { - if (!mResolveConfigHint.mUseOverrideInsetsForConfig) { + if (!Flags.immersiveAppRepositioning()) { + return false; + } + if (!mResolveConfigHint.mUseOverrideInsetsForConfig + && mWmService.mFlags.mInsetsDecoupledConfiguration) { return false; } final Insets navBarInsets = mDisplayContent.getInsetsStateController() @@ -9252,10 +9264,12 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // orientation bounds (stored in resolved bounds) instead of parent bounds since the // activity will be displayed within them even if it is in size compat mode. They should be // saved here before resolved bounds are overridden below. - final Rect containerBounds = isAspectRatioApplied() + final boolean useResolvedBounds = Flags.immersiveAppRepositioning() + ? isAspectRatioApplied() : isLetterboxedForFixedOrientationAndAspectRatio(); + final Rect containerBounds = useResolvedBounds ? new Rect(resolvedBounds) : newParentConfiguration.windowConfiguration.getBounds(); - final Rect containerAppBounds = isAspectRatioApplied() + final Rect containerAppBounds = useResolvedBounds ? new Rect(resolvedConfig.windowConfiguration.getAppBounds()) : newParentConfiguration.windowConfiguration.getAppBounds(); diff --git a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java index 207707efb51d..ac2c886d1b66 100644 --- a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java +++ b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java @@ -28,6 +28,7 @@ import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE; import static android.os.Process.INVALID_PID; import static android.os.Process.INVALID_UID; +import static android.os.Process.ROOT_UID; import static android.os.Process.SYSTEM_UID; import static android.provider.DeviceConfig.NAMESPACE_WINDOW_MANAGER; @@ -385,6 +386,10 @@ public class BackgroundActivityStartController { return BackgroundStartPrivileges.NONE; case MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED: // no explicit choice by the app - let us decide what to do + if (callingUid == ROOT_UID || callingUid == SYSTEM_UID) { + // root and system must always opt in explicitly + return BackgroundStartPrivileges.NONE; + } if (callingPackage != null) { // determine based on the calling/creating package boolean changeEnabled = CompatChanges.isChangeEnabled( diff --git a/services/core/java/com/android/server/wm/RecentTasks.java b/services/core/java/com/android/server/wm/RecentTasks.java index 72f592b577da..e07b72a05123 100644 --- a/services/core/java/com/android/server/wm/RecentTasks.java +++ b/services/core/java/com/android/server/wm/RecentTasks.java @@ -33,6 +33,7 @@ import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT; import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; import static android.os.Process.SYSTEM_UID; import static android.view.MotionEvent.CLASSIFICATION_MULTI_FINGER_SWIPE; +import static android.view.WindowInsets.Type.mandatorySystemGestures; import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW; import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW; @@ -60,6 +61,8 @@ import android.content.pm.ParceledListSlice; import android.content.pm.UserInfo; import android.content.res.Resources; import android.graphics.Bitmap; +import android.graphics.Insets; +import android.graphics.Rect; import android.os.Environment; import android.os.IBinder; import android.os.RemoteException; @@ -71,7 +74,9 @@ import android.util.IntArray; import android.util.Slog; import android.util.SparseArray; import android.util.SparseBooleanArray; +import android.view.InsetsState; import android.view.MotionEvent; +import android.view.WindowInsets; import android.view.WindowManagerPolicyConstants.PointerEventListener; import com.android.internal.annotations.VisibleForTesting; @@ -208,6 +213,7 @@ class RecentTasks { private final HashMap<ComponentName, ActivityInfo> mTmpAvailActCache = new HashMap<>(); private final HashMap<String, ApplicationInfo> mTmpAvailAppCache = new HashMap<>(); private final SparseBooleanArray mTmpQuietProfileUserIds = new SparseBooleanArray(); + private final Rect mTmpRect = new Rect(); // TODO(b/127498985): This is currently a rough heuristic for interaction inside an app private final PointerEventListener mListener = new PointerEventListener() { @@ -229,12 +235,27 @@ class RecentTasks { if (win == null) { return; } + + // Verify the touch is within the mandatory system gesture inset bounds of the + // window, use the raw insets state to ignore window z-order + final InsetsState insetsState = dc.getInsetsStateController() + .getRawInsetsState(); + mTmpRect.set(win.getFrame()); + mTmpRect.inset(insetsState.calculateInsets(win.getFrame(), + mandatorySystemGestures(), false /* ignoreVisibility */)); + if (!mTmpRect.contains(x, y)) { + return; + } + // Unfreeze the task list once we touch down in a task final boolean isAppWindowTouch = FIRST_APPLICATION_WINDOW <= win.mAttrs.type && win.mAttrs.type <= LAST_APPLICATION_WINDOW; if (isAppWindowTouch) { final Task stack = mService.getTopDisplayFocusedRootTask(); final Task topTask = stack != null ? stack.getTopMostTask() : null; + ProtoLog.i(WM_DEBUG_TASKS, "Resetting frozen recents task list" + + " reason=app touch win=%s x=%d y=%d insetFrame=%s", win, x, y, + mTmpRect); resetFreezeTaskListReordering(topTask); } } @@ -301,6 +322,8 @@ class RecentTasks { mFreezeTaskListReordering = true; } + ProtoLog.i(WM_DEBUG_TASKS, "Setting frozen recents task list"); + // Always update the reordering time when this is called to ensure that the timeout // is reset mService.mH.removeCallbacks(mResetFreezeTaskListOnTimeoutRunnable); @@ -344,6 +367,7 @@ class RecentTasks { final Task focusedStack = mService.getTopDisplayFocusedRootTask(); final Task topTask = focusedStack != null ? focusedStack.getTopMostTask() : null; final Task reorderToEndTask = topTask != null && topTask.hasChild() ? topTask : null; + ProtoLog.i(WM_DEBUG_TASKS, "Resetting frozen recents task list reason=timeout"); resetFreezeTaskListReordering(reorderToEndTask); } } diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index cd22591d3629..6c48e9586fd9 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -1931,6 +1931,9 @@ class Task extends TaskFragment { if (td.getSystemBarsAppearance() == 0) { td.setSystemBarsAppearance(atd.getSystemBarsAppearance()); } + if (td.getTopOpaqueSystemBarsAppearance() == 0 && r.fillsParent()) { + td.setTopOpaqueSystemBarsAppearance(atd.getSystemBarsAppearance()); + } if (td.getNavigationBarColor() == 0) { td.setNavigationBarColor(atd.getNavigationBarColor()); td.setEnsureNavigationBarContrastWhenTransparent( @@ -6880,7 +6883,7 @@ class Task extends TaskFragment { private void assignLayer(@NonNull SurfaceControl.Transaction t, int layer) { t.setLayer(mContainerSurface, layer); - t.setVisibility(mContainerSurface, mOwnerTaskFragment.isVisible()); + t.setVisibility(mContainerSurface, mOwnerTaskFragment.isVisible() || mIsBoosted); for (int i = 0; i < mPendingClientTransactions.size(); i++) { t.merge(mPendingClientTransactions.get(i)); } diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java index 28369fa74527..c972eee84ea3 100644 --- a/services/core/java/com/android/server/wm/Transition.java +++ b/services/core/java/com/android/server/wm/Transition.java @@ -236,6 +236,8 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { @VisibleForTesting ArrayList<Runnable> mTransactionCompletedListeners = null; + private ArrayList<Runnable> mTransitionEndedListeners = null; + /** Custom activity-level animation options and callbacks. */ private TransitionInfo.AnimationOptions mOverrideOptions; private IRemoteCallback mClientAnimationStartCallback = null; @@ -1473,6 +1475,18 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { mController.mSnapshotController.onTransitionFinish(mType, mTargets); // Resume snapshot persist thread after snapshot controller analysis this transition. mController.updateAnimatingState(); + + invokeTransitionEndedListeners(); + } + + private void invokeTransitionEndedListeners() { + if (mTransitionEndedListeners == null) { + return; + } + for (int i = 0; i < mTransitionEndedListeners.size(); i++) { + mTransitionEndedListeners.get(i).run(); + } + mTransitionEndedListeners = null; } private void commitConfigAtEndActivities() { @@ -1584,6 +1598,7 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { // Syncengine abort will call through to onTransactionReady() mSyncEngine.abort(mSyncId); mController.dispatchLegacyAppTransitionCancelled(); + invokeTransitionEndedListeners(); } /** Immediately moves this to playing even if it isn't started yet. */ @@ -1902,6 +1917,20 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { } /** + * Adds a listener that will be executed after the transition is finished or aborted. + */ + void addTransitionEndedListener(Runnable listener) { + if (mState != STATE_COLLECTING && mState != STATE_STARTED) { + throw new IllegalStateException( + "Can't register listeners if the transition isn't collecting. state=" + mState); + } + if (mTransitionEndedListeners == null) { + mTransitionEndedListeners = new ArrayList<>(); + } + mTransitionEndedListeners.add(listener); + } + + /** * Checks if the transition contains order changes. * * This is a shallow check that doesn't account for collection in parallel, unlike @@ -3758,7 +3787,7 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { if (changeInfo.mRotation != wc.mDisplayContent.getRotation()) { // This isn't cheap, so only do it for rotation change. changeInfo.mSnapshotLuma = TransitionAnimation.getBorderLuma( - buffer, screenshotBuffer.getColorSpace()); + buffer, screenshotBuffer.getColorSpace(), wc.mSurfaceControl); } SurfaceControl.Transaction t = wc.mWmService.mTransactionFactory.get(); TransitionAnimation.configureScreenshotLayer(t, snapshotSurface, screenshotBuffer); diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 0bf1c88d5b4f..94a22394cf41 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -1525,7 +1525,7 @@ public class WindowManagerService extends IWindowManager.Stub InputChannel outInputChannel, InsetsState outInsetsState, InsetsSourceControl.Array outActiveControls, Rect outAttachedFrame, float[] outSizeCompatScale) { - outActiveControls.set(null); + outActiveControls.set(null, false /* copyControls */); int[] appOp = new int[1]; final boolean isRoundedCornerOverlay = (attrs.privateFlags & PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY) != 0; @@ -2317,7 +2317,7 @@ public class WindowManagerService extends IWindowManager.Stub InsetsState outInsetsState, InsetsSourceControl.Array outActiveControls, Bundle outBundle, WindowRelayoutResult outRelayoutResult) { if (outActiveControls != null) { - outActiveControls.set(null); + outActiveControls.set(null, false /* copyControls */); } int result = 0; boolean configChanged = false; @@ -2745,23 +2745,14 @@ public class WindowManagerService extends IWindowManager.Stub private void getInsetsSourceControls(WindowState win, InsetsSourceControl.Array outArray) { final InsetsSourceControl[] controls = win.getDisplayContent().getInsetsStateController().getControlsForDispatch(win); - if (controls != null) { - final int length = controls.length; - final InsetsSourceControl[] outControls = new InsetsSourceControl[length]; - for (int i = 0; i < length; i++) { - // We will leave the critical section before returning the leash to the client, - // so we need to copy the leash to prevent others release the one that we are - // about to return. - if (controls[i] != null) { - // This source control is an extra copy if the client is not local. By setting - // PARCELABLE_WRITE_RETURN_VALUE, the leash will be released at the end of - // SurfaceControl.writeToParcel. - outControls[i] = new InsetsSourceControl(controls[i]); - outControls[i].setParcelableFlags(PARCELABLE_WRITE_RETURN_VALUE); - } - } - outArray.set(outControls); - } + // We will leave the critical section before returning the leash to the client, + // so we need to copy the leash to prevent others release the one that we are + // about to return. + outArray.set(controls, true /* copyControls */); + // This source control is an extra copy if the client is not local. By setting + // PARCELABLE_WRITE_RETURN_VALUE, the leash will be released at the end of + // SurfaceControl.writeToParcel. + outArray.setParcelableFlags(PARCELABLE_WRITE_RETURN_VALUE); } private void tryStartExitingAnimation(WindowState win, WindowStateAnimator winAnimator) { diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java index 5e932d45f3b6..6221d9656962 100644 --- a/services/core/java/com/android/server/wm/WindowOrganizerController.java +++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java @@ -1613,6 +1613,10 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub } case OP_TYPE_SET_DECOR_SURFACE_BOOSTED: { if (Flags.activityEmbeddingInteractiveDividerFlag()) { + final Task task = taskFragment.getTask(); + if (task == null) { + break; + } final SurfaceControl.Transaction clientTransaction = operation.getSurfaceTransaction(); if (clientTransaction != null) { @@ -1621,10 +1625,22 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub // any invalid operations. clientTransaction.sanitize(caller.mPid, caller.mUid); } - taskFragment.getTask().setDecorSurfaceBoosted( - taskFragment, - operation.getBooleanValue() /* isBoosted */, - clientTransaction); + + if (transition != null) { + // The decor surface boost/unboost must happen after the transition is + // completed. Otherwise, the decor surface could be moved before Shell + // completes the transition, causing flicker. + transition.addTransitionEndedListener(() -> + task.setDecorSurfaceBoosted( + taskFragment, + operation.getBooleanValue() /* isBoosted */, + clientTransaction)); + } else { + task.setDecorSurfaceBoosted( + taskFragment, + operation.getBooleanValue() /* isBoosted */, + clientTransaction); + } } break; } diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 6953c60d0d74..d7c49ac81a6c 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -3820,7 +3820,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP final InsetsStateController stateController = getDisplayContent().getInsetsStateController(); final InsetsState insetsState = getCompatInsetsState(); - mLastReportedActiveControls.set(stateController.getControlsForDispatch(this)); + mLastReportedActiveControls.set(stateController.getControlsForDispatch(this), + false /* copyControls */); if (Flags.insetsControlChangedItem()) { getProcess().scheduleClientTransactionItem(WindowStateInsetsControlChangeItem.obtain( mClient, insetsState, mLastReportedActiveControls)); diff --git a/services/core/jni/BroadcastRadio/convert.cpp b/services/core/jni/BroadcastRadio/convert.cpp index ddbc5354358c..e42f7f8be0ca 100644 --- a/services/core/jni/BroadcastRadio/convert.cpp +++ b/services/core/jni/BroadcastRadio/convert.cpp @@ -433,7 +433,7 @@ static JavaRef<jobject> BandDescriptorFromHal(JNIEnv *env, const V1_0::BandConfi gjni.AmBandDescriptor.clazz, gjni.AmBandDescriptor.cstor, region, config.type, config.lowerLimit, config.upperLimit, spacing, am.stereo)); } else { - ALOGE("Unsupported band type: %d", config.type); + ALOGE("Unsupported band type: %d", static_cast<int>(config.type)); return nullptr; } } @@ -451,7 +451,7 @@ JavaRef<jobject> BandConfigFromHal(JNIEnv *env, const V1_0::BandConfig &config, return make_javaref(env, env->NewObject( gjni.AmBandConfig.clazz, gjni.AmBandConfig.cstor, descriptor.get())); } else { - ALOGE("Unsupported band type: %d", config.type); + ALOGE("Unsupported band type: %d", static_cast<int>(config.type)); return nullptr; } } @@ -539,9 +539,9 @@ JavaRef<jobject> MetadataFromHal(JNIEnv *env, const hidl_vec<V1_0::MetaData> &me item.clockValue.timezoneOffsetInMinutes); break; default: - ALOGW("invalid metadata type %d", item.type); + ALOGW("invalid metadata type %d", static_cast<int>(item.type)); } - ALOGE_IF(status != 0, "Failed inserting metadata %d (of type %d)", key, item.type); + ALOGE_IF(status != 0, "Failed inserting metadata %d (of type %d)", key, static_cast<int>(item.type)); } return jMetadata; diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java index e763c9eccceb..669a999c921e 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java @@ -1588,7 +1588,7 @@ final class DevicePolicyEngine { private Set<EnforcingAdmin> getEnforcingAdminsOnUser(int userId) { synchronized (mLock) { return mEnforcingAdmins.contains(userId) - ? mEnforcingAdmins.get(userId) : Collections.emptySet(); + ? new HashSet<>(mEnforcingAdmins.get(userId)) : Collections.emptySet(); } } diff --git a/services/midi/java/com/android/server/midi/MidiService.java b/services/midi/java/com/android/server/midi/MidiService.java index c16c61271280..cc340c0a5f79 100644 --- a/services/midi/java/com/android/server/midi/MidiService.java +++ b/services/midi/java/com/android/server/midi/MidiService.java @@ -287,6 +287,7 @@ public class MidiService extends IMidiManager.Stub { } public void deviceAdded(Device device) { + Log.d(TAG, "deviceAdded() " + device.getUserId() + " userId:" + getUserId()); // ignore devices that our client cannot access if (!device.isUidAllowed(mUid) || !device.isUserIdAllowed(getUserId())) return; @@ -301,6 +302,7 @@ public class MidiService extends IMidiManager.Stub { } public void deviceRemoved(Device device) { + Log.d(TAG, "deviceRemoved() " + device.getUserId() + " userId:" + getUserId()); // ignore devices that our client cannot access if (!device.isUidAllowed(mUid) || !device.isUserIdAllowed(getUserId())) return; @@ -315,6 +317,7 @@ public class MidiService extends IMidiManager.Stub { } public void deviceStatusChanged(Device device, MidiDeviceStatus status) { + Log.d(TAG, "deviceStatusChanged() " + device.getUserId() + " userId:" + getUserId()); // ignore devices that our client cannot access if (!device.isUidAllowed(mUid) || !device.isUserIdAllowed(getUserId())) return; @@ -1303,7 +1306,7 @@ public class MidiService extends IMidiManager.Stub { String[] inputPortNames, String[] outputPortNames, Bundle properties, IMidiDeviceServer server, ServiceInfo serviceInfo, boolean isPrivate, int uid, int defaultProtocol, int userId) { - Log.d(TAG, "addDeviceLocked()" + uid + " type:" + type); + Log.d(TAG, "addDeviceLocked() " + uid + " type:" + type + " userId:" + userId); // Limit the number of devices per app. int deviceCountForApp = 0; diff --git a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java index 488fe57cf6f8..9f9764853bef 100644 --- a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java +++ b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java @@ -370,18 +370,18 @@ public final class ProfcollectForwardingService extends SystemService { } private static void createAndUploadReport(ProfcollectForwardingService pfs) { - String reportName; - try { - reportName = pfs.mIProfcollect.report(pfs.mUsageSetting) + ".zip"; - } catch (RemoteException e) { - Log.e(LOG_TAG, "Failed to create report: " + e.getMessage()); - return; - } - if (!pfs.mUploadEnabled) { - Log.i(LOG_TAG, "Upload is not enabled."); - return; - } BackgroundThread.get().getThreadHandler().post(() -> { + String reportName; + try { + reportName = pfs.mIProfcollect.report(pfs.mUsageSetting) + ".zip"; + } catch (RemoteException e) { + Log.e(LOG_TAG, "Failed to create report: " + e.getMessage()); + return; + } + if (!pfs.mUploadEnabled) { + Log.i(LOG_TAG, "Upload is not enabled."); + return; + } Intent intent = new Intent() .setPackage("com.android.shell") .setAction("com.android.shell.action.PROFCOLLECT_UPLOAD") diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java index 98f572d81adf..8fd1e6baf522 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java @@ -1538,6 +1538,8 @@ public final class DisplayPowerControllerTest { @Test public void testDozeScreenStateOverride_toSupportedOffloadStateFromDoze_DisplayStateChanges() { + when(mDisplayManagerFlagsMock.isOffloadDozeOverrideHoldsWakelockEnabled()).thenReturn(true); + // set up. int initState = Display.STATE_DOZE; int supportedTargetState = Display.STATE_DOZE_SUSPEND; @@ -1556,10 +1558,15 @@ public final class DisplayPowerControllerTest { mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); advanceTime(1); // Run updatePowerState + reset(mHolder.wakelockController); mHolder.dpc.overrideDozeScreenState( supportedTargetState, Display.STATE_REASON_DEFAULT_POLICY); advanceTime(1); // Run updatePowerState + // Should get a wakelock to notify powermanager + verify(mHolder.wakelockController, atLeastOnce()).acquireWakelock( + eq(WakelockController.WAKE_LOCK_UNFINISHED_BUSINESS)); + verify(mHolder.displayPowerState) .setScreenState(supportedTargetState, Display.STATE_REASON_DEFAULT_POLICY); } diff --git a/services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java b/services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java index 12050e1beaed..01ff35fc088c 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java @@ -1142,6 +1142,20 @@ public class LocalDisplayAdapterTest { } @Test + public void test_createLocalExternalDisplay_displayManagementEnabled_doesNotCrash() + throws Exception { + FakeDisplay display = new FakeDisplay(PORT_A); + display.info.isInternal = false; + setUpDisplay(display); + updateAvailableDisplays(); + mAdapter.registerLocked(); + when(mSurfaceControlProxy.getDesiredDisplayModeSpecs(display.token)).thenReturn(null); + mInjector.getTransmitter().sendHotplug(display, /* connected */ true); + waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS); + assertThat(mListener.addedDisplays.size()).isEqualTo(1); + } + + @Test public void test_createLocalExternalDisplay_displayManagementEnabled_shouldHaveDefaultGroup() throws Exception { FakeDisplay display = new FakeDisplay(PORT_A); diff --git a/services/tests/mockingservicestests/src/com/android/server/power/batterysaver/BatterySaverStateMachineTest.java b/services/tests/mockingservicestests/src/com/android/server/power/batterysaver/BatterySaverStateMachineTest.java index ad68de84eace..9d8d520fcb53 100644 --- a/services/tests/mockingservicestests/src/com/android/server/power/batterysaver/BatterySaverStateMachineTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/power/batterysaver/BatterySaverStateMachineTest.java @@ -15,6 +15,8 @@ */ package com.android.server.power.batterysaver; +import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.NULL_DEFAULT; + import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; import static com.android.dx.mockito.inline.extended.ExtendedMockito.inOrder; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; @@ -35,12 +37,14 @@ import android.content.ContentResolver; import android.content.Context; import android.content.res.Resources; import android.os.PowerManager; +import android.platform.test.flag.junit.SetFlagsRule; import android.provider.Settings.Global; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InOrder; @@ -65,6 +69,9 @@ public class BatterySaverStateMachineTest { private DevicePersistedState mPersistedState; + @Rule + public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(NULL_DEFAULT); + private class DevicePersistedState { // Current battery level. public int batteryLevel = 100; @@ -171,6 +178,11 @@ public class BatterySaverStateMachineTest { void triggerDynamicModeNotification() { // Do nothing } + + @Override + void triggerDynamicModeNotificationV2() { + // Do nothing + } } @Before diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java index f971f0e6d4fb..4e8c75559f3b 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java @@ -923,6 +923,8 @@ public class AccessibilityManagerServiceTest { ResolveInfo resolveInfo1 = installedService1.getResolveInfo(); AccessibilityServiceInfo installedService2 = mA11yms.getCurrentUserState().mInstalledServices.getLast(); + // Invokes client change to trigger onUserStateChanged. + mA11yms.onClientChangeLocked(false); // Disables `installedService2` when(mMockPackageManager.queryIntentServicesAsUser(any(), anyInt(), anyInt())) diff --git a/services/tests/servicestests/src/com/android/server/audio/ServiceHolderTest.java b/services/tests/servicestests/src/com/android/server/audio/ServiceHolderTest.java new file mode 100644 index 000000000000..39f19ae1b382 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/audio/ServiceHolderTest.java @@ -0,0 +1,284 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.server.audio; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.media.IAudioPolicyService; +import android.os.Binder; +import android.os.IBinder; +import android.os.IServiceCallback; +import android.os.RemoteException; +import android.platform.test.annotations.Presubmit; + +import androidx.test.ext.junit.runners.AndroidJUnit4; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.ArgumentMatchers; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +import java.util.concurrent.Executor; +import java.util.function.Consumer; +import java.util.function.Function; + +@RunWith(AndroidJUnit4.class) +@Presubmit +public class ServiceHolderTest { + + private static final String AUDIO_POLICY_SERVICE_NAME = "media.audio_policy"; + + @Rule + public final MockitoRule mockito = MockitoJUnit.rule(); + + // the actual class under test + private ServiceHolder<IAudioPolicyService> mServiceHolder; + + @Mock private ServiceHolder.ServiceProviderFacade mServiceProviderFacade; + + @Mock private IAudioPolicyService mAudioPolicyService; + + @Mock private IBinder mBinder; + + @Mock private Consumer<IAudioPolicyService> mTaskOne; + @Mock private Consumer<IAudioPolicyService> mTaskTwo; + + @Before + public void setUp() throws Exception { + mServiceHolder = + new ServiceHolder( + AUDIO_POLICY_SERVICE_NAME, + (Function<IBinder, IAudioPolicyService>) + binder -> { + if (binder == mBinder) { + return mAudioPolicyService; + } else { + return mock(IAudioPolicyService.class); + } + }, + r -> r.run(), + mServiceProviderFacade); + when(mAudioPolicyService.asBinder()).thenReturn(mBinder); + } + + @Test + public void testListenerRegistered_whenConstructed() { + verify(mServiceProviderFacade) + .registerForNotifications(eq(AUDIO_POLICY_SERVICE_NAME), ArgumentMatchers.any()); + } + + @Test + public void testServiceSuccessfullyPopulated_whenCallback() throws RemoteException { + initializeViaCallback(); + verify(mBinder).linkToDeath(any(), anyInt()); + assertThat(mServiceHolder.checkService()).isEqualTo(mAudioPolicyService); + } + + @Test + public void testCheckServiceCalled_whenUncached() { + when(mServiceProviderFacade.checkService(eq(AUDIO_POLICY_SERVICE_NAME))) + .thenReturn(mBinder); + assertThat(mServiceHolder.checkService()).isEqualTo(mAudioPolicyService); + } + + @Test + public void testCheckServiceTransmitsNull() { + assertThat(mServiceHolder.checkService()).isEqualTo(null); + } + + @Test + public void testWaitForServiceCalled_whenUncached() { + when(mServiceProviderFacade.waitForService(eq(AUDIO_POLICY_SERVICE_NAME))) + .thenReturn(mBinder); + assertThat(mServiceHolder.waitForService()).isEqualTo(mAudioPolicyService); + } + + @Test + public void testCheckServiceNotCalled_whenCached() { + initializeViaCallback(); + mServiceHolder.checkService(); + verify(mServiceProviderFacade, never()).checkService(any()); + } + + @Test + public void testWaitForServiceNotCalled_whenCached() { + initializeViaCallback(); + mServiceHolder.waitForService(); + verify(mServiceProviderFacade, never()).waitForService(any()); + } + + @Test + public void testStartTaskCalled_onStart() { + mServiceHolder.registerOnStartTask(mTaskOne); + mServiceHolder.registerOnStartTask(mTaskTwo); + mServiceHolder.unregisterOnStartTask(mTaskOne); + when(mServiceProviderFacade.checkService(eq(AUDIO_POLICY_SERVICE_NAME))) + .thenReturn(mBinder); + + assertThat(mServiceHolder.checkService()).isEqualTo(mAudioPolicyService); + + verify(mTaskTwo).accept(eq(mAudioPolicyService)); + verify(mTaskOne, never()).accept(any()); + } + + @Test + public void testStartTaskCalled_onStartFromCallback() { + mServiceHolder.registerOnStartTask(mTaskOne); + mServiceHolder.registerOnStartTask(mTaskTwo); + mServiceHolder.unregisterOnStartTask(mTaskOne); + + initializeViaCallback(); + + assertThat(mServiceHolder.checkService()).isEqualTo(mAudioPolicyService); + verify(mTaskTwo).accept(eq(mAudioPolicyService)); + verify(mTaskOne, never()).accept(any()); + } + + @Test + public void testStartTaskCalled_onRegisterAfterStarted() { + initializeViaCallback(); + mServiceHolder.registerOnStartTask(mTaskOne); + verify(mTaskOne).accept(eq(mAudioPolicyService)); + } + + @Test + public void testBinderDied_clearsServiceAndUnlinks() { + initializeViaCallback(); + assertThat(mServiceHolder.checkService()).isEqualTo(mAudioPolicyService); + + mServiceHolder.binderDied(mBinder); + + verify(mBinder).unlinkToDeath(any(), anyInt()); + assertThat(mServiceHolder.checkService()).isEqualTo(null); + verify(mServiceProviderFacade).checkService(eq(AUDIO_POLICY_SERVICE_NAME)); + } + + @Test + public void testBinderDied_callsDeathTasks() { + mServiceHolder.registerOnDeathTask(mTaskOne); + mServiceHolder.registerOnDeathTask(mTaskTwo); + initializeViaCallback(); + assertThat(mServiceHolder.checkService()).isEqualTo(mAudioPolicyService); + mServiceHolder.unregisterOnDeathTask(mTaskOne); + + mServiceHolder.binderDied(mBinder); + + verify(mTaskTwo).accept(eq(mAudioPolicyService)); + verify(mTaskOne, never()).accept(any()); + } + + @Test + public void testAttemptClear_clearsServiceAndUnlinks() { + initializeViaCallback(); + assertThat(mServiceHolder.checkService()).isEqualTo(mAudioPolicyService); + + mServiceHolder.attemptClear(mBinder); + + verify(mBinder).unlinkToDeath(any(), anyInt()); + assertThat(mServiceHolder.checkService()).isEqualTo(null); + verify(mServiceProviderFacade).checkService(eq(AUDIO_POLICY_SERVICE_NAME)); + } + + @Test + public void testAttemptClear_callsDeathTasks() { + mServiceHolder.registerOnDeathTask(mTaskOne); + mServiceHolder.registerOnDeathTask(mTaskTwo); + initializeViaCallback(); + assertThat(mServiceHolder.checkService()).isEqualTo(mAudioPolicyService); + mServiceHolder.unregisterOnDeathTask(mTaskOne); + + mServiceHolder.attemptClear(mBinder); + + verify(mTaskTwo).accept(eq(mAudioPolicyService)); + verify(mTaskOne, never()).accept(any()); + } + + @Test + public void testSet_whenServiceSet_isIgnored() { + mServiceHolder.registerOnStartTask(mTaskOne); + when(mServiceProviderFacade.checkService(eq(AUDIO_POLICY_SERVICE_NAME))) + .thenReturn(mBinder); + assertThat(mServiceHolder.checkService()).isEqualTo(mAudioPolicyService); + + verify(mTaskOne).accept(eq(mAudioPolicyService)); + + // get the callback + ArgumentCaptor<IServiceCallback> cb = ArgumentCaptor.forClass(IServiceCallback.class); + verify(mServiceProviderFacade) + .registerForNotifications(eq(AUDIO_POLICY_SERVICE_NAME), cb.capture()); + + // Simulate a service callback with a different instance + try { + cb.getValue().onRegistration(AUDIO_POLICY_SERVICE_NAME, new Binder()); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + + // No additional start task call (i.e. only the first verify) + verify(mTaskOne).accept(any()); + // Same instance + assertThat(mServiceHolder.checkService()).isEqualTo(mAudioPolicyService); + + } + + @Test + public void testClear_whenServiceCleared_isIgnored() { + mServiceHolder.registerOnDeathTask(mTaskOne); + mServiceHolder.attemptClear(mBinder); + verify(mTaskOne, never()).accept(any()); + } + + @Test + public void testClear_withDifferentCookie_isIgnored() { + mServiceHolder.registerOnDeathTask(mTaskOne); + initializeViaCallback(); + assertThat(mServiceHolder.checkService()).isEqualTo(mAudioPolicyService); + + // Notif for stale cookie + mServiceHolder.attemptClear(new Binder()); + + // Service shouldn't be cleared + assertThat(mServiceHolder.checkService()).isEqualTo(mAudioPolicyService); + // No death tasks should fire + verify(mTaskOne, never()).accept(any()); + } + + private void initializeViaCallback() { + ArgumentCaptor<IServiceCallback> cb = ArgumentCaptor.forClass(IServiceCallback.class); + verify(mServiceProviderFacade) + .registerForNotifications(eq(AUDIO_POLICY_SERVICE_NAME), cb.capture()); + + try { + cb.getValue().onRegistration(AUDIO_POLICY_SERVICE_NAME, mBinder); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + } +} diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java index 98e119cf0dad..473d1dc22d7a 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java @@ -139,12 +139,13 @@ public class HdmiCecMessageValidatorTest { @Test public void isValid_setSystemAudioMode() { - assertMessageValidity("40:72:00").isEqualTo(OK); - assertMessageValidity("4F:72:01:03").isEqualTo(OK); + assertMessageValidity("50:72:00").isEqualTo(OK); + assertMessageValidity("50:72:01").isEqualTo(OK); + assertMessageValidity("5F:72:01:03").isEqualTo(ERROR_PARAMETER_LONG); - assertMessageValidity("F0:72").isEqualTo(ERROR_SOURCE); - assertMessageValidity("40:72").isEqualTo(ERROR_PARAMETER_SHORT); - assertMessageValidity("40:72:02").isEqualTo(ERROR_PARAMETER); + assertMessageValidity("40:72:00").isEqualTo(ERROR_SOURCE); + assertMessageValidity("50:72").isEqualTo(ERROR_PARAMETER_SHORT); + assertMessageValidity("50:72:02").isEqualTo(ERROR_PARAMETER); } @Test diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiUtilsTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiUtilsTest.java index c89c32a03553..74583dd619c7 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiUtilsTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiUtilsTest.java @@ -709,4 +709,18 @@ public class HdmiUtilsTest { assertThat(HdmiUtils.buildMessage("40:32:65:6E:67").getParams()).isEqualTo( new byte[]{0x65, 0x6E, 0x67}); } + + @Test + public void testVerifyAddressType() { + assertTrue(HdmiUtils.verifyAddressType(Constants.ADDR_TV, + HdmiDeviceInfo.DEVICE_TV)); + assertTrue(HdmiUtils.verifyAddressType(Constants.ADDR_AUDIO_SYSTEM, + HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM)); + assertTrue(HdmiUtils.verifyAddressType(Constants.ADDR_PLAYBACK_1, + HdmiDeviceInfo.DEVICE_PLAYBACK)); + assertFalse(HdmiUtils.verifyAddressType(Constants.ADDR_SPECIFIC_USE, + HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM)); + assertFalse(HdmiUtils.verifyAddressType(Constants.ADDR_PLAYBACK_2, + HdmiDeviceInfo.DEVICE_VIDEO_PROCESSOR)); + } } diff --git a/services/tests/servicestests/src/com/android/server/locales/LocaleManagerBackupRestoreTest.java b/services/tests/servicestests/src/com/android/server/locales/LocaleManagerBackupRestoreTest.java index 7dd1847114c8..50cfa753ebdb 100644 --- a/services/tests/servicestests/src/com/android/server/locales/LocaleManagerBackupRestoreTest.java +++ b/services/tests/servicestests/src/com/android/server/locales/LocaleManagerBackupRestoreTest.java @@ -20,7 +20,6 @@ import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertNull; import static junit.framework.Assert.assertTrue; -import static org.junit.Assert.assertNotNull; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; @@ -52,6 +51,7 @@ import android.util.ArraySet; import android.util.SparseArray; import android.util.Xml; +import androidx.test.core.app.ApplicationProvider; import androidx.test.ext.junit.runners.AndroidJUnit4; import com.android.internal.content.PackageMonitor; @@ -70,6 +70,7 @@ import org.xmlpull.v1.XmlPullParserException; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; +import java.io.File; import java.io.IOException; import java.io.OutputStream; import java.nio.charset.StandardCharsets; @@ -95,21 +96,21 @@ public class LocaleManagerBackupRestoreTest { private static final int DEFAULT_USER_ID = 0; private static final int WORK_PROFILE_USER_ID = 10; private static final int DEFAULT_UID = Binder.getCallingUid() + 100; + private static final int WORK_PROFILE_UID = Binder.getCallingUid() + 1000100; private static final long DEFAULT_CREATION_TIME_MILLIS = 1000; private static final Duration RETENTION_PERIOD = Duration.ofDays(3); private static final LocaleList DEFAULT_LOCALES = LocaleList.forLanguageTags(DEFAULT_LOCALE_TAGS); private static final Map<String, LocalesInfo> DEFAULT_PACKAGE_LOCALES_INFO_MAP = Map.of( DEFAULT_PACKAGE_NAME, new LocalesInfo(DEFAULT_LOCALE_TAGS, false)); - private static final SparseArray<LocaleManagerBackupHelper.StagedData> STAGE_DATA = - new SparseArray<>(); + private final SparseArray<File> mStagedDataFiles = new SparseArray<>(); + private File mArchivedPackageFile; private LocaleManagerBackupHelper mBackupHelper; private long mCurrentTimeMillis; + private Context mContext = spy(ApplicationProvider.getApplicationContext()); @Mock - private Context mMockContext; - @Mock private PackageManager mMockPackageManager; @Mock private LocaleManagerService mMockLocaleManagerService; @@ -138,23 +139,28 @@ public class LocaleManagerBackupRestoreTest { @Before public void setUp() throws Exception { - mMockContext = mock(Context.class); mMockPackageManager = mock(PackageManager.class); mMockLocaleManagerService = mock(LocaleManagerService.class); mMockDelegateAppLocalePackages = mock(SharedPreferences.class); mMockSpEditor = mock(SharedPreferences.Editor.class); SystemAppUpdateTracker systemAppUpdateTracker = mock(SystemAppUpdateTracker.class); - doReturn(mMockPackageManager).when(mMockContext).getPackageManager(); + doReturn(mMockPackageManager).when(mContext).getPackageManager(); doReturn(mMockSpEditor).when(mMockDelegateAppLocalePackages).edit(); HandlerThread broadcastHandlerThread = new HandlerThread(TAG, Process.THREAD_PRIORITY_BACKGROUND); broadcastHandlerThread.start(); - mBackupHelper = spy(new ShadowLocaleManagerBackupHelper(mMockContext, - mMockLocaleManagerService, mMockPackageManager, mClock, STAGE_DATA, - broadcastHandlerThread, mMockDelegateAppLocalePackages)); + File file0 = new File(mContext.getCacheDir(), "file_user_0.txt"); + File file10 = new File(mContext.getCacheDir(), "file_user_10.txt"); + mStagedDataFiles.put(DEFAULT_USER_ID, file0); + mStagedDataFiles.put(WORK_PROFILE_USER_ID, file10); + mArchivedPackageFile = new File(mContext.getCacheDir(), "file_archived.txt"); + + mBackupHelper = spy(new ShadowLocaleManagerBackupHelper(mContext, + mMockLocaleManagerService, mMockPackageManager, mClock, broadcastHandlerThread, + mStagedDataFiles, mArchivedPackageFile, mMockDelegateAppLocalePackages)); doNothing().when(mBackupHelper).notifyBackupManager(); mUserMonitor = mBackupHelper.getUserMonitor(); @@ -165,7 +171,16 @@ public class LocaleManagerBackupRestoreTest { @After public void tearDown() throws Exception { - STAGE_DATA.clear(); + for (int i = 0; i < mStagedDataFiles.size(); i++) { + int userId = mStagedDataFiles.keyAt(i); + File file = mStagedDataFiles.get(userId); + SharedPreferences sp = mContext.getSharedPreferences(file, Context.MODE_PRIVATE); + sp.edit().clear().commit(); + if (file.exists()) { + file.delete(); + } + } + mStagedDataFiles.clear(); } @Test @@ -543,17 +558,21 @@ public class LocaleManagerBackupRestoreTest { mPackageMonitor.onPackageAddedWithExtras(pkgNameA, DEFAULT_UID, bundle); mPackageMonitor.onPackageAddedWithExtras(pkgNameB, DEFAULT_UID, bundle); + checkArchivedFileExists(); + mBackupHelper.stageAndApplyRestoredPayload(out.toByteArray(), DEFAULT_USER_ID); verifyNothingRestored(); setUpPackageInstalled(pkgNameA); - mPackageMonitor.onPackageUpdateFinished(pkgNameA, DEFAULT_UID); + mBackupHelper.onPackageUpdateFinished(pkgNameA, DEFAULT_UID); verify(mMockLocaleManagerService, times(1)).setApplicationLocales(pkgNameA, DEFAULT_USER_ID, LocaleList.forLanguageTags(langTagsA), false, FrameworkStatsLog .APPLICATION_LOCALES_CHANGED__CALLER__CALLER_BACKUP_RESTORE); + checkArchivedFileExists(); + mBackupHelper.persistLocalesModificationInfo(DEFAULT_USER_ID, pkgNameA, false, false); @@ -565,11 +584,12 @@ public class LocaleManagerBackupRestoreTest { setUpPackageInstalled(pkgNameB); - mPackageMonitor.onPackageUpdateFinished(pkgNameB, DEFAULT_UID); + mBackupHelper.onPackageUpdateFinished(pkgNameB, DEFAULT_UID); verify(mMockLocaleManagerService, times(1)).setApplicationLocales(pkgNameB, DEFAULT_USER_ID, LocaleList.forLanguageTags(langTagsB), true, FrameworkStatsLog .APPLICATION_LOCALES_CHANGED__CALLER__CALLER_BACKUP_RESTORE); + checkArchivedFileDoesNotExist(); mBackupHelper.persistLocalesModificationInfo(DEFAULT_USER_ID, pkgNameB, true, false); @@ -723,7 +743,7 @@ public class LocaleManagerBackupRestoreTest { Intent intent = new Intent(); intent.setAction(Intent.ACTION_USER_REMOVED); intent.putExtra(Intent.EXTRA_USER_HANDLE, DEFAULT_USER_ID); - mUserMonitor.onReceive(mMockContext, intent); + mUserMonitor.onReceive(mContext, intent); // Stage data should be removed only for DEFAULT_USER_ID. checkStageDataDoesNotExist(DEFAULT_USER_ID); @@ -732,6 +752,72 @@ public class LocaleManagerBackupRestoreTest { } @Test + public void testRestore_multipleProfile_restoresFromStage_ArchiveEnabled() throws Exception { + final ByteArrayOutputStream outDefault = new ByteArrayOutputStream(); + writeTestPayload(outDefault, DEFAULT_PACKAGE_LOCALES_INFO_MAP); + final ByteArrayOutputStream outWorkProfile = new ByteArrayOutputStream(); + String anotherPackage = "com.android.anotherapp"; + String anotherLangTags = "mr,zh"; + LocalesInfo localesInfo = new LocalesInfo(anotherLangTags, true); + HashMap<String, LocalesInfo> pkgLocalesMapWorkProfile = new HashMap<>(); + pkgLocalesMapWorkProfile.put(anotherPackage, localesInfo); + writeTestPayload(outWorkProfile, pkgLocalesMapWorkProfile); + // DEFAULT_PACKAGE_NAME is NOT installed on the device. + setUpPackageNotInstalled(DEFAULT_PACKAGE_NAME); + setUpPackageNotInstalled(anotherPackage); + setUpLocalesForPackage(DEFAULT_PACKAGE_NAME, LocaleList.getEmptyLocaleList()); + setUpLocalesForPackage(anotherPackage, LocaleList.getEmptyLocaleList()); + setUpPackageNamesForSp(new ArraySet<>()); + + Bundle bundle = new Bundle(); + bundle.putBoolean(Intent.EXTRA_ARCHIVAL, true); + mPackageMonitor.onPackageAddedWithExtras(DEFAULT_PACKAGE_NAME, DEFAULT_UID, bundle); + mPackageMonitor.onPackageAddedWithExtras(anotherPackage, WORK_PROFILE_UID, bundle); + + checkArchivedFileExists(); + + mBackupHelper.stageAndApplyRestoredPayload(outDefault.toByteArray(), DEFAULT_USER_ID); + mBackupHelper.stageAndApplyRestoredPayload(outWorkProfile.toByteArray(), + WORK_PROFILE_USER_ID); + + verifyNothingRestored(); + verifyStageDataForUser(DEFAULT_PACKAGE_LOCALES_INFO_MAP, + DEFAULT_CREATION_TIME_MILLIS, DEFAULT_USER_ID); + verifyStageDataForUser(pkgLocalesMapWorkProfile, + DEFAULT_CREATION_TIME_MILLIS, WORK_PROFILE_USER_ID); + + setUpPackageInstalled(DEFAULT_PACKAGE_NAME); + mBackupHelper.onPackageUpdateFinished(DEFAULT_PACKAGE_NAME, DEFAULT_UID); + + verify(mMockLocaleManagerService, times(1)).setApplicationLocales(DEFAULT_PACKAGE_NAME, + DEFAULT_USER_ID, + LocaleList.forLanguageTags(DEFAULT_LOCALE_TAGS), false, FrameworkStatsLog + .APPLICATION_LOCALES_CHANGED__CALLER__CALLER_BACKUP_RESTORE); + checkArchivedFileExists(); + checkStageDataDoesNotExist(DEFAULT_USER_ID); + + mBackupHelper.persistLocalesModificationInfo(DEFAULT_USER_ID, DEFAULT_PACKAGE_NAME, false, + false); + + verify(mMockSpEditor, times(0)).putStringSet(anyString(), any()); + + setUpPackageInstalled(anotherPackage); + mBackupHelper.onPackageUpdateFinished(anotherPackage, WORK_PROFILE_UID); + + verify(mMockLocaleManagerService, times(1)).setApplicationLocales(anotherPackage, + WORK_PROFILE_USER_ID, + LocaleList.forLanguageTags(anotherLangTags), true, FrameworkStatsLog + .APPLICATION_LOCALES_CHANGED__CALLER__CALLER_BACKUP_RESTORE); + checkArchivedFileDoesNotExist(); + + mBackupHelper.persistLocalesModificationInfo(DEFAULT_USER_ID, anotherPackage, true, false); + + verify(mMockSpEditor, times(1)).putStringSet(Integer.toString(DEFAULT_USER_ID), + new ArraySet<>(Arrays.asList(anotherPackage))); + checkStageDataDoesNotExist(WORK_PROFILE_USER_ID); + } + + @Test public void testPackageRemoved_noInfoInSp() throws Exception { String pkgNameA = "com.android.myAppA"; String pkgNameB = "com.android.myAppB"; @@ -858,10 +944,22 @@ public class LocaleManagerBackupRestoreTest { private void verifyStageDataForUser(Map<String, LocalesInfo> expectedPkgLocalesMap, long expectedCreationTimeMillis, int userId) { - LocaleManagerBackupHelper.StagedData stagedDataForUser = STAGE_DATA.get(userId); - assertNotNull(stagedDataForUser); - assertEquals(expectedCreationTimeMillis, stagedDataForUser.mCreationTimeMillis); - verifyStageData(expectedPkgLocalesMap, stagedDataForUser.mPackageStates); + SharedPreferences sp = mContext.getSharedPreferences(mStagedDataFiles.get(userId), + Context.MODE_PRIVATE); + assertTrue(sp.getAll().size() > 0); + assertEquals(expectedCreationTimeMillis, sp.getLong("staged_data_time", -1)); + verifyStageData(expectedPkgLocalesMap, sp); + } + + private static void verifyStageData(Map<String, LocalesInfo> expectedPkgLocalesMap, + SharedPreferences sp) { + for (String pkg : expectedPkgLocalesMap.keySet()) { + assertTrue(!sp.getString(pkg, "").isEmpty()); + String[] info = sp.getString(pkg, "").split(" s:"); + assertEquals(expectedPkgLocalesMap.get(pkg).mLocales, info[0]); + assertEquals(expectedPkgLocalesMap.get(pkg).mSetFromDelegate, + Boolean.parseBoolean(info[1])); + } } private static void verifyStageData(Map<String, LocalesInfo> expectedPkgLocalesMap, @@ -875,11 +973,19 @@ public class LocaleManagerBackupRestoreTest { } } - private static void checkStageDataExists(int userId) { - assertNotNull(STAGE_DATA.get(userId)); + private void checkStageDataExists(int userId) { + assertTrue(mStagedDataFiles.get(userId) != null && mStagedDataFiles.get(userId).exists()); + } + + private void checkStageDataDoesNotExist(int userId) { + assertTrue(mStagedDataFiles.get(userId) == null || !mStagedDataFiles.get(userId).exists()); + } + + private void checkArchivedFileExists() { + assertTrue(mArchivedPackageFile.exists()); } - private static void checkStageDataDoesNotExist(int userId) { - assertNull(STAGE_DATA.get(userId)); + private void checkArchivedFileDoesNotExist() { + assertTrue(!mArchivedPackageFile.exists()); } -} +}
\ No newline at end of file diff --git a/services/tests/servicestests/src/com/android/server/locales/ShadowLocaleManagerBackupHelper.java b/services/tests/servicestests/src/com/android/server/locales/ShadowLocaleManagerBackupHelper.java index 9f7cbe3170f0..b46902d9904a 100644 --- a/services/tests/servicestests/src/com/android/server/locales/ShadowLocaleManagerBackupHelper.java +++ b/services/tests/servicestests/src/com/android/server/locales/ShadowLocaleManagerBackupHelper.java @@ -22,6 +22,7 @@ import android.content.pm.PackageManager; import android.os.HandlerThread; import android.util.SparseArray; +import java.io.File; import java.time.Clock; /** @@ -33,9 +34,9 @@ public class ShadowLocaleManagerBackupHelper extends LocaleManagerBackupHelper { ShadowLocaleManagerBackupHelper(Context context, LocaleManagerService localeManagerService, PackageManager packageManager, Clock clock, - SparseArray<LocaleManagerBackupHelper.StagedData> stagedData, - HandlerThread broadcastHandlerThread, SharedPreferences delegateAppLocalePackages) { - super(context, localeManagerService, packageManager, clock, stagedData, - broadcastHandlerThread, delegateAppLocalePackages); + HandlerThread broadcastHandlerThread, SparseArray<File> stagedDataFiles, + File archivedPackagesFile, SharedPreferences delegateAppLocalePackages) { + super(context, localeManagerService, packageManager, clock, broadcastHandlerThread, + stagedDataFiles, archivedPackagesFile, delegateAppLocalePackages); } } diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java index 4b22652a3f21..601a01624189 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java @@ -43,6 +43,8 @@ import android.app.PropertyInvalidatedCache; import android.content.Intent; import android.os.RemoteException; import android.os.UserHandle; +import android.platform.test.annotations.DisableFlags; +import android.platform.test.annotations.EnableFlags; import android.platform.test.annotations.Presubmit; import android.platform.test.flag.junit.SetFlagsRule; import android.service.gatekeeper.GateKeeperResponse; @@ -483,18 +485,31 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests { setSecureFrpMode(true); try { mService.setLockCredential(newPassword("1234"), nonePassword(), PRIMARY_USER_ID); - fail("Password shouldn't be changeable before FRP unlock"); + fail("Password shouldn't be changeable while FRP is active"); } catch (SecurityException e) { } } @Test - public void testSetCredentialPossibleInSecureFrpModeAfterSuw() throws RemoteException { + @DisableFlags(android.security.Flags.FLAG_FRP_ENFORCEMENT) + public void testSetCredentialPossibleInSecureFrpModeAfterSuw_FlagOff() throws RemoteException { setUserSetupComplete(true); setSecureFrpMode(true); setCredential(PRIMARY_USER_ID, newPassword("1234")); } @Test + @EnableFlags(android.security.Flags.FLAG_FRP_ENFORCEMENT) + public void testSetCredentialNotPossibleInSecureFrpModeAfterSuw_FlagOn() + throws RemoteException { + setUserSetupComplete(true); + setSecureFrpMode(true); + try { + mService.setLockCredential(newPassword("1234"), nonePassword(), PRIMARY_USER_ID); + fail("Password shouldn't be changeable after SUW while FRP is active"); + } catch (SecurityException e) { } + } + + @Test public void testPasswordHistoryDisabledByDefault() throws Exception { final int userId = PRIMARY_USER_ID; checkPasswordHistoryLength(userId, 0); diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java index 96ddfe8d5ac9..7ced9d50ab3f 100644 --- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java @@ -107,6 +107,7 @@ import android.graphics.Rect; import android.os.Binder; import android.os.RemoteException; import android.os.UserHandle; +import android.platform.test.annotations.EnableFlags; import android.platform.test.annotations.Presubmit; import android.platform.test.annotations.RequiresFlagsDisabled; import android.provider.DeviceConfig; @@ -401,6 +402,7 @@ public class SizeCompatTests extends WindowTestsBase { // TODO(b/333663877): Enable test after fix @Test @RequiresFlagsDisabled({Flags.FLAG_INSETS_DECOUPLED_CONFIGURATION}) + @EnableFlags(Flags.FLAG_IMMERSIVE_APP_REPOSITIONING) public void testRepositionLandscapeImmersiveAppWithDisplayCutout() { final int dw = 2100; final int dh = 2000; @@ -4059,6 +4061,7 @@ public class SizeCompatTests extends WindowTestsBase { } @Test + @EnableFlags(Flags.FLAG_IMMERSIVE_APP_REPOSITIONING) public void testImmersiveLetterboxAlignedToBottom_OverlappingNavbar() { assertLandscapeActivityAlignedToBottomWithNavbar(true /* immersive */); } diff --git a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java index 698afaa8e8ab..69f2d684fd77 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java @@ -1446,6 +1446,33 @@ public class TransitionTests extends WindowTestsBase { } @Test + public void testTransitionEndedListeners() { + final TransitionController controller = new TestTransitionController(mAtm); + controller.setSyncEngine(mWm.mSyncEngine); + final ITransitionPlayer player = new ITransitionPlayer.Default(); + controller.registerTransitionPlayer(player, null /* playerProc */); + final Runnable transitionEndedListener = mock(Runnable.class); + + final Transition transition1 = controller.createTransition(TRANSIT_OPEN); + transition1.addTransitionEndedListener(transitionEndedListener); + + // Using abort to force-finish the sync (since we can't wait for drawing in unit test). + // We didn't call abort on the transition itself, so it will still run onTransactionReady + // normally. + mWm.mSyncEngine.abort(transition1.getSyncId()); + transition1.finishTransition(); + + verify(transitionEndedListener).run(); + + clearInvocations(transitionEndedListener); + + final Transition transition2 = controller.createTransition(TRANSIT_OPEN); + transition2.addTransitionEndedListener(transitionEndedListener); + transition2.abort(); + verify(transitionEndedListener).run(); + } + + @Test public void testTransientLaunch() { spyOn(mWm.mSnapshotController.mTaskSnapshotController); final ArrayList<ActivityRecord> enteringAnimReports = new ArrayList<>(); diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java index 1d014201cf46..a7dbecbb5255 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java @@ -794,9 +794,8 @@ public class VoiceInteractionManagerService extends SystemService { if (curService != null && !curService.isEmpty()) { try { serviceComponent = ComponentName.unflattenFromString(curService); - serviceInfo = AppGlobals.getPackageManager() - .getServiceInfo(serviceComponent, 0, mCurUser); - } catch (RuntimeException | RemoteException e) { + serviceInfo = getValidVoiceInteractionServiceInfo(serviceComponent); + } catch (RuntimeException e) { Slog.wtf(TAG, "Bad voice interaction service name " + curService, e); serviceComponent = null; serviceInfo = null; @@ -834,6 +833,27 @@ public class VoiceInteractionManagerService extends SystemService { } } + @Nullable + private ServiceInfo getValidVoiceInteractionServiceInfo( + @Nullable ComponentName serviceComponent) { + if (serviceComponent == null) { + return null; + } + List<ResolveInfo> services = queryInteractorServices( + mCurUser, serviceComponent.getPackageName()); + for (int i = 0; i < services.size(); i++) { + ResolveInfo service = services.get(i); + VoiceInteractionServiceInfo info = new VoiceInteractionServiceInfo( + mContext.getPackageManager(), service.serviceInfo); + ServiceInfo candidateInfo = info.getServiceInfo(); + if (candidateInfo != null + && candidateInfo.getComponentName().equals(serviceComponent)) { + return candidateInfo; + } + } + return null; + } + private List<ResolveInfo> queryInteractorServices( @UserIdInt int user, @Nullable String packageName) { diff --git a/tests/FlickerTests/ActivityEmbedding/Android.bp b/tests/FlickerTests/ActivityEmbedding/Android.bp index 2cdf54248ebc..e09fbf6adc02 100644 --- a/tests/FlickerTests/ActivityEmbedding/Android.bp +++ b/tests/FlickerTests/ActivityEmbedding/Android.bp @@ -20,17 +20,65 @@ package { // all of the 'license_kinds' from "frameworks_base_license" // to get the below license kinds: // SPDX-license-identifier-Apache-2.0 + default_team: "trendy_team_windowing_sdk", default_applicable_licenses: ["frameworks_base_license"], } -android_test { - name: "FlickerTestsOther", +filegroup { + name: "FlickerTestsOtherCommon-src", + srcs: ["src/**/ActivityEmbeddingTestBase.kt"], +} + +filegroup { + name: "FlickerTestsOtherOpen-src", + srcs: ["src/**/open/*"], +} + +filegroup { + name: "FlickerTestsOtherRotation-src", + srcs: ["src/**/rotation/*"], +} + +java_library { + name: "FlickerTestsOtherCommon", + defaults: ["FlickerTestsDefault"], + srcs: [":FlickerTestsOtherCommon-src"], + static_libs: ["FlickerTestsBase"], +} + +java_defaults { + name: "FlickerTestsOtherDefaults", defaults: ["FlickerTestsDefault"], manifest: "AndroidManifest.xml", package_name: "com.android.server.wm.flicker", instrumentation_target_package: "com.android.server.wm.flicker", test_config_template: "AndroidTestTemplate.xml", - srcs: ["src/**/*"], - static_libs: ["FlickerTestsBase"], + static_libs: [ + "FlickerTestsBase", + "FlickerTestsOtherCommon", + ], data: ["trace_config/*"], } + +android_test { + name: "FlickerTestsOtherOpen", + defaults: ["FlickerTestsOtherDefaults"], + srcs: [":FlickerTestsOtherOpen-src"], +} + +android_test { + name: "FlickerTestsOtherRotation", + defaults: ["FlickerTestsOtherDefaults"], + srcs: [":FlickerTestsOtherRotation-src"], +} + +android_test { + name: "FlickerTestsOther", + defaults: ["FlickerTestsOtherDefaults"], + srcs: ["src/**/*"], + exclude_srcs: [ + ":FlickerTestsOtherOpen-src", + ":FlickerTestsOtherRotation-src", + ":FlickerTestsOtherCommon-src", + ], +} diff --git a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java index 093923f3ed53..a8b383cd4274 100644 --- a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java +++ b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java @@ -101,8 +101,8 @@ public class PackageWatchdogTest { private static final String OBSERVER_NAME_2 = "observer2"; private static final String OBSERVER_NAME_3 = "observer3"; private static final String OBSERVER_NAME_4 = "observer4"; - private static final long SHORT_DURATION = TimeUnit.SECONDS.toMillis(1); - private static final long LONG_DURATION = TimeUnit.SECONDS.toMillis(5); + private static final long SHORT_DURATION = TimeUnit.SECONDS.toMillis(10); + private static final long LONG_DURATION = TimeUnit.SECONDS.toMillis(50); @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); @@ -1453,7 +1453,8 @@ public class PackageWatchdogTest { raiseFatalFailureAndDispatch(watchdog, Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)), PackageWatchdog.FAILURE_REASON_UNKNOWN); - moveTimeForwardAndDispatch(PackageWatchdog.DEFAULT_DEESCALATION_WINDOW_MS); + moveTimeForwardAndDispatch(PackageWatchdog.DEFAULT_DEESCALATION_WINDOW_MS + - TimeUnit.MINUTES.toMillis(1)); // The first failure will be outside the threshold. raiseFatalFailureAndDispatch(watchdog, Arrays.asList(new VersionedPackage(APP_A, @@ -1712,6 +1713,9 @@ public class PackageWatchdogTest { watchdog.onPackageFailure(packages, failureReason); } mTestLooper.dispatchAll(); + if (Flags.recoverabilityDetection()) { + moveTimeForwardAndDispatch(watchdog.DEFAULT_MITIGATION_WINDOW_MS); + } } private PackageWatchdog createWatchdog() { |