diff options
1196 files changed, 30670 insertions, 13648 deletions
diff --git a/apct-tests/perftests/surfaceflinger/AndroidTest.xml b/apct-tests/perftests/surfaceflinger/AndroidTest.xml index 11d110670b7a..58cf58b89782 100644 --- a/apct-tests/perftests/surfaceflinger/AndroidTest.xml +++ b/apct-tests/perftests/surfaceflinger/AndroidTest.xml @@ -58,8 +58,8 @@ <option name="instrumentation-arg" key="report" value="true" /> <option name="instrumentation-arg" key="arguments" value="-g" /> <option name="instrumentation-arg" key="events_to_record" value="instructions,cpu-cycles,raw-l3d-cache-refill,sched:sched_waking" /> - <option name="instrumentation-arg" key="processes_to_record" value="surfaceflinger" /> - <option name="instrumentation-arg" key="symbols_to_report" value=""commit;android::SurfaceFlinger::commit(;composite;android::SurfaceFlinger::composite("" /> + <option name="instrumentation-arg" key="processes_to_record" value="surfaceflinger,android.perftests.surfaceflinger" /> + <option name="instrumentation-arg" key="symbols_to_report" value=""commit;android::SurfaceFlinger::commit(;composite;android::SurfaceFlinger::composite(;outbound;android::SurfaceComposerClient::Transaction::apply(;inbound;android::BnTransactionCompletedListener::onTransact(""/> <!-- should match profiling-iterations --> <option name="instrumentation-arg" key="test_iterations" value="525" /> diff --git a/core/api/test-current.txt b/core/api/test-current.txt index 0c9569f730cd..250a1d1f86c3 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -2958,6 +2958,10 @@ package android.service.notification { method @Deprecated public boolean isBound(); } + public class NotificationRankingUpdate implements android.os.Parcelable { + method public final boolean isFdNotNullAndClosed(); + } + } package android.service.quickaccesswallet { diff --git a/core/java/Android.bp b/core/java/Android.bp index 02b14ad7a757..f8f4cc3523f2 100644 --- a/core/java/Android.bp +++ b/core/java/Android.bp @@ -510,6 +510,17 @@ filegroup { ], } +// common protolog sources without classes that rely on Android SDK +filegroup { + name: "protolog-common-no-android-src", + srcs: [ + ":protolog-common-src", + ], + exclude_srcs: [ + "com/android/internal/protolog/common/ProtoLog.java", + ], +} + java_library { name: "protolog-lib", platform_apis: true, diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 8021ce0dc20d..6e0fc4f53a23 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -1084,6 +1084,16 @@ public class Activity extends ContextThemeWrapper } /** + * Update the forced status bar appearance. + * @hide + */ + @Override + public void updateStatusBarAppearance(int appearance) { + mTaskDescription.setStatusBarAppearance(appearance); + setTaskDescription(mTaskDescription); + } + + /** * Update the forced navigation bar color. * @hide */ @@ -3882,7 +3892,9 @@ public class Activity extends ContextThemeWrapper * it will set up the dispatch to call {@link #onKeyUp} where the action * will be performed; for earlier applications, it will perform the * action immediately in on-down, as those versions of the platform - * behaved. + * behaved. This implementation will also take care of {@link KeyEvent#KEYCODE_ESCAPE} + * by finishing the activity if it would be closed by touching outside + * of it. * * <p>Other additional default key handling may be performed * if configured with {@link #setDefaultKeyMode}. @@ -3904,6 +3916,11 @@ public class Activity extends ContextThemeWrapper return true; } + if (keyCode == KeyEvent.KEYCODE_ESCAPE && mWindow.shouldCloseOnTouchOutside()) { + event.startTracking(); + return true; + } + if (mDefaultKeyMode == DEFAULT_KEYS_DISABLE) { return false; } else if (mDefaultKeyMode == DEFAULT_KEYS_SHORTCUT) { @@ -3999,6 +4016,15 @@ public class Activity extends ContextThemeWrapper return true; } } + + if (keyCode == KeyEvent.KEYCODE_ESCAPE + && mWindow.shouldCloseOnTouchOutside() + && event.isTracking() + && !event.isCanceled()) { + finish(); + return true; + } + return false; } diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index b5ee895a5a01..ff0f437ea8a1 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -81,6 +81,7 @@ import android.util.ArrayMap; import android.util.DisplayMetrics; import android.util.Singleton; import android.util.Size; +import android.view.WindowInsetsController.Appearance; import android.window.TaskSnapshot; import com.android.internal.app.LocalePicker; @@ -1553,6 +1554,8 @@ public class ActivityManager { private int mColorBackgroundFloating; private int mStatusBarColor; private int mNavigationBarColor; + @Appearance + private int mStatusBarAppearance; private boolean mEnsureStatusBarContrastWhenTransparent; private boolean mEnsureNavigationBarContrastWhenTransparent; private int mResizeMode; @@ -1653,8 +1656,8 @@ 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, false, false, RESIZE_MODE_RESIZEABLE, - -1, -1, 0); + mStatusBarColor, mNavigationBarColor, 0, false, false, + RESIZE_MODE_RESIZEABLE, -1, -1, 0); } } @@ -1672,7 +1675,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, false, false, RESIZE_MODE_RESIZEABLE, -1, -1, 0); + colorPrimary, 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"); } @@ -1690,7 +1693,7 @@ public class ActivityManager { @Deprecated public TaskDescription(String label, @DrawableRes int iconRes) { this(label, Icon.createWithResource(ActivityThread.currentPackageName(), iconRes), - 0, 0, 0, 0, false, false, RESIZE_MODE_RESIZEABLE, -1, -1, 0); + 0, 0, 0, 0, 0, false, false, RESIZE_MODE_RESIZEABLE, -1, -1, 0); } /** @@ -1702,7 +1705,7 @@ public class ActivityManager { */ @Deprecated public TaskDescription(String label) { - this(label, null, 0, 0, 0, 0, false, false, RESIZE_MODE_RESIZEABLE, -1, -1, 0); + this(label, null, 0, 0, 0, 0, 0, false, false, RESIZE_MODE_RESIZEABLE, -1, -1, 0); } /** @@ -1712,7 +1715,7 @@ public class ActivityManager { */ @Deprecated public TaskDescription() { - this(null, null, 0, 0, 0, 0, false, false, RESIZE_MODE_RESIZEABLE, -1, -1, 0); + this(null, null, 0, 0, 0, 0, 0, false, false, RESIZE_MODE_RESIZEABLE, -1, -1, 0); } /** @@ -1728,7 +1731,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, - false, false, RESIZE_MODE_RESIZEABLE, -1, -1, 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"); } @@ -1744,14 +1747,15 @@ public class ActivityManager { */ @Deprecated public TaskDescription(String label, Bitmap icon) { - this(label, icon != null ? Icon.createWithBitmap(icon) : null, 0, 0, 0, 0, false, false, - RESIZE_MODE_RESIZEABLE, -1, -1, 0); + this(label, icon != null ? Icon.createWithBitmap(icon) : null, 0, 0, 0, 0, 0, false, + false, RESIZE_MODE_RESIZEABLE, -1, -1, 0); } /** @hide */ public TaskDescription(@Nullable String label, @Nullable Icon icon, int colorPrimary, int colorBackground, int statusBarColor, int navigationBarColor, + @Appearance int statusBarAppearance, boolean ensureStatusBarContrastWhenTransparent, boolean ensureNavigationBarContrastWhenTransparent, int resizeMode, int minWidth, int minHeight, int colorBackgroundFloating) { @@ -1761,6 +1765,7 @@ public class ActivityManager { mColorBackground = colorBackground; mStatusBarColor = statusBarColor; mNavigationBarColor = navigationBarColor; + mStatusBarAppearance = statusBarAppearance; mEnsureStatusBarContrastWhenTransparent = ensureStatusBarContrastWhenTransparent; mEnsureNavigationBarContrastWhenTransparent = ensureNavigationBarContrastWhenTransparent; @@ -1789,6 +1794,7 @@ public class ActivityManager { mColorBackground = other.mColorBackground; mStatusBarColor = other.mStatusBarColor; mNavigationBarColor = other.mNavigationBarColor; + mStatusBarAppearance = other.mStatusBarAppearance; mEnsureStatusBarContrastWhenTransparent = other.mEnsureStatusBarContrastWhenTransparent; mEnsureNavigationBarContrastWhenTransparent = other.mEnsureNavigationBarContrastWhenTransparent; @@ -1818,6 +1824,9 @@ public class ActivityManager { if (other.mNavigationBarColor != 0) { mNavigationBarColor = other.mNavigationBarColor; } + if (other.mStatusBarAppearance != 0) { + mStatusBarAppearance = other.mStatusBarAppearance; + } mEnsureStatusBarContrastWhenTransparent = other.mEnsureStatusBarContrastWhenTransparent; mEnsureNavigationBarContrastWhenTransparent = @@ -2089,6 +2098,14 @@ public class ActivityManager { /** * @hide */ + @Appearance + public int getStatusBarAppearance() { + return mStatusBarAppearance; + } + + /** + * @hide + */ public void setEnsureStatusBarContrastWhenTransparent( boolean ensureStatusBarContrastWhenTransparent) { mEnsureStatusBarContrastWhenTransparent = ensureStatusBarContrastWhenTransparent; @@ -2097,6 +2114,13 @@ public class ActivityManager { /** * @hide */ + public void setStatusBarAppearance(@Appearance int statusBarAppearance) { + mStatusBarAppearance = statusBarAppearance; + } + + /** + * @hide + */ public boolean getEnsureNavigationBarContrastWhenTransparent() { return mEnsureNavigationBarContrastWhenTransparent; } @@ -2218,6 +2242,7 @@ public class ActivityManager { dest.writeInt(mColorBackground); dest.writeInt(mStatusBarColor); dest.writeInt(mNavigationBarColor); + dest.writeInt(mStatusBarAppearance); dest.writeBoolean(mEnsureStatusBarContrastWhenTransparent); dest.writeBoolean(mEnsureNavigationBarContrastWhenTransparent); dest.writeInt(mResizeMode); @@ -2241,6 +2266,7 @@ public class ActivityManager { mColorBackground = source.readInt(); mStatusBarColor = source.readInt(); mNavigationBarColor = source.readInt(); + mStatusBarAppearance = source.readInt(); mEnsureStatusBarContrastWhenTransparent = source.readBoolean(); mEnsureNavigationBarContrastWhenTransparent = source.readBoolean(); mResizeMode = source.readInt(); @@ -2289,6 +2315,7 @@ public class ActivityManager { && mColorBackground == other.mColorBackground && mStatusBarColor == other.mStatusBarColor && mNavigationBarColor == other.mNavigationBarColor + && mStatusBarAppearance == other.mStatusBarAppearance && mEnsureStatusBarContrastWhenTransparent == other.mEnsureStatusBarContrastWhenTransparent && mEnsureNavigationBarContrastWhenTransparent diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java index 411d157fa927..4851279eea97 100644 --- a/core/java/android/app/Dialog.java +++ b/core/java/android/app/Dialog.java @@ -694,12 +694,22 @@ public class Dialog implements DialogInterface, Window.Callback, */ @Override public boolean onKeyUp(int keyCode, @NonNull KeyEvent event) { - if ((keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_ESCAPE) - && event.isTracking() - && !event.isCanceled() - && !WindowOnBackInvokedDispatcher.isOnBackInvokedCallbackEnabled(mContext)) { - onBackPressed(); - return true; + if (event.isTracking() && !event.isCanceled()) { + switch (keyCode) { + case KeyEvent.KEYCODE_BACK: + if (!WindowOnBackInvokedDispatcher.isOnBackInvokedCallbackEnabled(mContext)) { + onBackPressed(); + return true; + } + break; + case KeyEvent.KEYCODE_ESCAPE: + if (mCancelable) { + cancel(); + } else { + dismiss(); + } + return true; + } } return false; } diff --git a/core/java/android/app/KeyguardManager.java b/core/java/android/app/KeyguardManager.java index 09450f59ed3d..e7a2b61ecaf9 100644 --- a/core/java/android/app/KeyguardManager.java +++ b/core/java/android/app/KeyguardManager.java @@ -117,6 +117,26 @@ public class KeyguardManager { "android.app.action.CONFIRM_REMOTE_DEVICE_CREDENTIAL"; /** + * Intent used to prompt user for device credential for entering repair + * mode. If the credential is verified successfully, then the information + * needed to verify the credential again will be written to a location that + * is available to repair mode. This makes it possible for repair mode to + * require that the same credential be provided to exit repair mode. + * @hide + */ + public static final String ACTION_PREPARE_REPAIR_MODE_DEVICE_CREDENTIAL = + "android.app.action.PREPARE_REPAIR_MODE_DEVICE_CREDENTIAL"; + + /** + * Intent used to prompt user for device credential that is written by + * {@link #ACTION_PREPARE_REPAIR_MODE_DEVICE_CREDENTIAL} for exiting + * repair mode. + * @hide + */ + public static final String ACTION_CONFIRM_REPAIR_MODE_DEVICE_CREDENTIAL = + "android.app.action.CONFIRM_REPAIR_MODE_DEVICE_CREDENTIAL"; + + /** * A CharSequence dialog title to show to the user when used with a * {@link #ACTION_CONFIRM_DEVICE_CREDENTIAL}. * @hide diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index 2eb6ca758970..8d2394b20438 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -2833,12 +2833,14 @@ public class Notification implements Parcelable } /** - * Note all {@link Uri} that are referenced internally, with the expectation - * that Uri permission grants will need to be issued to ensure the recipient - * of this object is able to render its contents. - * - * @hide - */ + * Note all {@link Uri} that are referenced internally, with the expectation that Uri permission + * grants will need to be issued to ensure the recipient of this object is able to render its + * contents. + * See b/281044385 for more context and examples about what happens when this isn't done + * correctly. + * + * @hide + */ public void visitUris(@NonNull Consumer<Uri> visitor) { if (publicVersion != null) { publicVersion.visitUris(visitor); @@ -2882,13 +2884,13 @@ public class Notification implements Parcelable ArrayList<Person> people = extras.getParcelableArrayList(EXTRA_PEOPLE_LIST, android.app.Person.class); if (people != null && !people.isEmpty()) { for (Person p : people) { - visitor.accept(p.getIconUri()); + p.visitUris(visitor); } } final Person person = extras.getParcelable(EXTRA_MESSAGING_PERSON, Person.class); if (person != null) { - visitor.accept(person.getIconUri()); + person.visitUris(visitor); } final RemoteInputHistoryItem[] history = extras.getParcelableArray( @@ -2910,12 +2912,7 @@ public class Notification implements Parcelable if (!ArrayUtils.isEmpty(messages)) { for (MessagingStyle.Message message : MessagingStyle.Message .getMessagesFromBundleArray(messages)) { - visitor.accept(message.getDataUri()); - - Person senderPerson = message.getSenderPerson(); - if (senderPerson != null) { - visitor.accept(senderPerson.getIconUri()); - } + message.visitUris(visitor); } } @@ -2924,12 +2921,7 @@ public class Notification implements Parcelable if (!ArrayUtils.isEmpty(historic)) { for (MessagingStyle.Message message : MessagingStyle.Message .getMessagesFromBundleArray(historic)) { - visitor.accept(message.getDataUri()); - - Person senderPerson = message.getSenderPerson(); - if (senderPerson != null) { - visitor.accept(senderPerson.getIconUri()); - } + message.visitUris(visitor); } } @@ -2939,7 +2931,7 @@ public class Notification implements Parcelable if (isStyle(CallStyle.class) & extras != null) { Person callPerson = extras.getParcelable(EXTRA_CALL_PERSON, Person.class); if (callPerson != null) { - visitor.accept(callPerson.getIconUri()); + callPerson.visitUris(visitor); } visitIconUri(visitor, extras.getParcelable(EXTRA_VERIFICATION_ICON, Icon.class)); } @@ -8833,6 +8825,18 @@ public class Notification implements Parcelable } /** + * See {@link Notification#visitUris(Consumer)}. + * + * @hide + */ + public void visitUris(@NonNull Consumer<Uri> visitor) { + visitor.accept(getDataUri()); + if (mSender != null) { + mSender.visitUris(visitor); + } + } + + /** * Returns a list of messages read from the given bundle list, e.g. * {@link #EXTRA_MESSAGES} or {@link #EXTRA_HISTORIC_MESSAGES}. */ diff --git a/core/java/android/app/Person.java b/core/java/android/app/Person.java index 97a794d4e4ea..18fc0ce6af15 100644 --- a/core/java/android/app/Person.java +++ b/core/java/android/app/Person.java @@ -24,6 +24,7 @@ import android.os.Parcel; import android.os.Parcelable; import java.util.Objects; +import java.util.function.Consumer; /** * Provides an immutable reference to an entity that appears repeatedly on different surfaces of the @@ -177,6 +178,19 @@ public final class Person implements Parcelable { dest.writeBoolean(mIsBot); } + /** + * Note all {@link Uri} that are referenced internally, with the expectation that Uri permission + * grants will need to be issued to ensure the recipient of this object is able to render its + * contents. + * See b/281044385 for more context and examples about what happens when this isn't done + * correctly. + * + * @hide + */ + public void visitUris(@NonNull Consumer<Uri> visitor) { + visitor.accept(getIconUri()); + } + /** Builder for the immutable {@link Person} class. */ public static class Builder { @Nullable private CharSequence mName; diff --git a/core/java/android/app/WallpaperColors.java b/core/java/android/app/WallpaperColors.java index b710644a308c..5f5a7dfe24c6 100644 --- a/core/java/android/app/WallpaperColors.java +++ b/core/java/android/app/WallpaperColors.java @@ -28,6 +28,7 @@ import android.graphics.drawable.Drawable; import android.os.Parcel; import android.os.Parcelable; import android.os.SystemProperties; +import android.os.Trace; import android.util.Log; import android.util.MathUtils; import android.util.Size; @@ -144,6 +145,7 @@ public final class WallpaperColors implements Parcelable { throw new IllegalArgumentException("Drawable cannot be null"); } + Trace.beginSection("WallpaperColors#fromDrawable"); Rect initialBounds = drawable.copyBounds(); int width = drawable.getIntrinsicWidth(); int height = drawable.getIntrinsicHeight(); @@ -165,6 +167,7 @@ public final class WallpaperColors implements Parcelable { bitmap.recycle(); drawable.setBounds(initialBounds); + Trace.endSection(); return colors; } @@ -195,7 +198,7 @@ public final class WallpaperColors implements Parcelable { public static WallpaperColors fromBitmap(@NonNull Bitmap bitmap, @FloatRange (from = 0f, to = 1f) float dimAmount) { Objects.requireNonNull(bitmap, "Bitmap can't be null"); - + Trace.beginSection("WallpaperColors#fromBitmap"); final int bitmapArea = bitmap.getWidth() * bitmap.getHeight(); boolean shouldRecycle = false; if (bitmapArea > MAX_WALLPAPER_EXTRACTION_AREA) { @@ -247,6 +250,7 @@ public final class WallpaperColors implements Parcelable { bitmap.recycle(); } + Trace.endSection(); return new WallpaperColors(populationByColor, HINT_FROM_BITMAP | hints); } @@ -462,7 +466,7 @@ public final class WallpaperColors implements Parcelable { * Gets the most visually representative color of the wallpaper. * "Visually representative" means easily noticeable in the image, * probably happening at high frequency. - * + *fromBitmap * @return A color. */ public @NonNull Color getPrimaryColor() { @@ -545,6 +549,7 @@ public final class WallpaperColors implements Parcelable { return 0; } + Trace.beginSection("WallpaperColors#calculateDarkHints"); dimAmount = MathUtils.saturate(dimAmount); int[] pixels = new int[source.getWidth() * source.getHeight()]; double totalLuminance = 0; @@ -607,6 +612,7 @@ public final class WallpaperColors implements Parcelable { " maxD: " + maxDarkPixels + " numPixels: " + pixels.length); } + Trace.endSection(); return hints; } diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java index ee888ed7d872..1c5332a3c8bd 100644 --- a/core/java/android/app/WallpaperManager.java +++ b/core/java/android/app/WallpaperManager.java @@ -853,7 +853,7 @@ public class WallpaperManager { private static boolean isLockscreenLiveWallpaperEnabledHelper() { if (sGlobals == null) { sIsLockscreenLiveWallpaperEnabled = SystemProperties.getBoolean( - "persist.wm.debug.lockscreen_live_wallpaper", false); + "persist.wm.debug.lockscreen_live_wallpaper", true); } if (sIsLockscreenLiveWallpaperEnabled == null) { try { @@ -2430,19 +2430,38 @@ public class WallpaperManager { } /** - * Reset all wallpaper to the factory default. + * Reset all wallpaper to the factory default. As opposed to {@link #clear()}, if the device + * is configured to have a live wallpaper by default, apply it. * * <p>This method requires the caller to hold the permission * {@link android.Manifest.permission#SET_WALLPAPER}. */ @RequiresPermission(android.Manifest.permission.SET_WALLPAPER) public void clearWallpaper() { + if (isLockscreenLiveWallpaperEnabled()) { + clearWallpaper(FLAG_LOCK | FLAG_SYSTEM, mContext.getUserId()); + return; + } clearWallpaper(FLAG_LOCK, mContext.getUserId()); clearWallpaper(FLAG_SYSTEM, mContext.getUserId()); } /** - * Clear the wallpaper for a specific user. The caller must hold the + * Clear the wallpaper for a specific user. + * <ul> + * <li> When called with {@code which=}{@link #FLAG_LOCK}, clear the lockscreen wallpaper. + * The home screen wallpaper will become visible on the lock screen. </li> + * + * <li> When called with {@code which=}{@link #FLAG_SYSTEM}, revert the home screen + * wallpaper to default. The lockscreen wallpaper will be unchanged: if the previous + * wallpaper was shared between home and lock screen, it will become lock screen only. </li> + * + * <li> When called with {@code which=}({@link #FLAG_LOCK} | {@link #FLAG_SYSTEM}), put the + * default wallpaper on both home and lock screen, removing any user defined wallpaper.</li> + * </ul> + * </p> + * + * The caller must hold the * INTERACT_ACROSS_USERS_FULL permission to clear another user's * wallpaper, and must hold the SET_WALLPAPER permission in all * circumstances. @@ -2747,8 +2766,9 @@ public class WallpaperManager { /** * Remove any currently set system wallpaper, reverting to the system's built-in - * wallpaper. On success, the intent {@link Intent#ACTION_WALLPAPER_CHANGED} - * is broadcast. + * wallpaper. As opposed to {@link #clearWallpaper()}, this method always set a static wallpaper + * with the default image, even if the device is configured to have a live wallpaper by default. + * On success, the intent {@link Intent#ACTION_WALLPAPER_CHANGED} is broadcast. * * <p>This method requires the caller to hold the permission * {@link android.Manifest.permission#SET_WALLPAPER}. @@ -2763,9 +2783,14 @@ public class WallpaperManager { /** * Remove one or more currently set wallpapers, reverting to the system default - * display for each one. If {@link #FLAG_SYSTEM} is set in the {@code which} - * parameter, the intent {@link Intent#ACTION_WALLPAPER_CHANGED} will be broadcast - * upon success. + * display for each one. On success, the intent {@link Intent#ACTION_WALLPAPER_CHANGED} + * is broadcast. + * <ul> + * <li> If {@link #FLAG_SYSTEM} is set in the {@code which} parameter, put the default + * wallpaper on both home and lock screen, removing any user defined wallpaper. </li> + * <li> When called with {@code which=}{@link #FLAG_LOCK}, clear the lockscreen wallpaper. + * The home screen wallpaper will become visible on the lock screen. </li> + * </ul> * * @param which A bitwise combination of {@link #FLAG_SYSTEM} or * {@link #FLAG_LOCK} @@ -2775,6 +2800,7 @@ public class WallpaperManager { public void clear(@SetWallpaperFlags int which) throws IOException { if ((which & FLAG_SYSTEM) != 0) { clear(); + if (isLockscreenLiveWallpaperEnabled()) return; } if ((which & FLAG_LOCK) != 0) { clearWallpaper(FLAG_LOCK, mContext.getUserId()); @@ -2887,21 +2913,62 @@ public class WallpaperManager { } } - // Check if the package exists - if (cn != null) { - try { - final PackageManager packageManager = context.getPackageManager(); - packageManager.getPackageInfo(cn.getPackageName(), - PackageManager.MATCH_DIRECT_BOOT_AWARE - | PackageManager.MATCH_DIRECT_BOOT_UNAWARE); - } catch (PackageManager.NameNotFoundException e) { - cn = null; + if (!isComponentExist(context, cn)) { + cn = null; + } + + return cn; + } + + /** + * Return {@link ComponentName} of the CMF default wallpaper, or + * {@link #getDefaultWallpaperComponent(Context)} if none is defined. + * + * @hide + */ + public static ComponentName getCmfDefaultWallpaperComponent(Context context) { + ComponentName cn = null; + String[] cmfWallpaperMap = context.getResources().getStringArray( + com.android.internal.R.array.default_wallpaper_component_per_device_color); + if (cmfWallpaperMap == null || cmfWallpaperMap.length == 0) { + Log.d(TAG, "No CMF wallpaper config"); + return getDefaultWallpaperComponent(context); + } + + for (String entry : cmfWallpaperMap) { + String[] cmfWallpaper; + if (!TextUtils.isEmpty(entry)) { + cmfWallpaper = entry.split(","); + if (cmfWallpaper != null && cmfWallpaper.length == 2 && VALUE_CMF_COLOR.equals( + cmfWallpaper[0]) && !TextUtils.isEmpty(cmfWallpaper[1])) { + cn = ComponentName.unflattenFromString(cmfWallpaper[1]); + break; + } } } + if (!isComponentExist(context, cn)) { + cn = null; + } + return cn; } + private static boolean isComponentExist(Context context, ComponentName cn) { + if (cn == null) { + return false; + } + try { + final PackageManager packageManager = context.getPackageManager(); + packageManager.getPackageInfo(cn.getPackageName(), + PackageManager.MATCH_DIRECT_BOOT_AWARE + | PackageManager.MATCH_DIRECT_BOOT_UNAWARE); + } catch (PackageManager.NameNotFoundException e) { + return false; + } + return true; + } + /** * Register a callback for lock wallpaper observation. Only the OS may use this. * diff --git a/core/java/android/app/servertransaction/TransactionExecutorHelper.java b/core/java/android/app/servertransaction/TransactionExecutorHelper.java index 5311b09e609d..baf2a4722dff 100644 --- a/core/java/android/app/servertransaction/TransactionExecutorHelper.java +++ b/core/java/android/app/servertransaction/TransactionExecutorHelper.java @@ -193,8 +193,8 @@ public class TransactionExecutorHelper { switch (prevState) { // TODO(lifecycler): Extend to support all possible states. case ON_START: - lifecycleItem = StartActivityItem.obtain(null /* activityOptions */); - break; + // Fall through to return the PAUSE item to ensure the activity is properly + // resumed while relaunching. case ON_PAUSE: lifecycleItem = PauseActivityItem.obtain(); break; diff --git a/core/java/android/content/ContentCaptureOptions.java b/core/java/android/content/ContentCaptureOptions.java index 856bde870bcf..36e0529e3566 100644 --- a/core/java/android/content/ContentCaptureOptions.java +++ b/core/java/android/content/ContentCaptureOptions.java @@ -76,6 +76,20 @@ public final class ContentCaptureOptions implements Parcelable { public final boolean disableFlushForViewTreeAppearing; /** + * Is the content capture receiver enabled. + * + * @hide + */ + public final boolean enableReceiver; + + /** + * Options for the content protection flow. + * + * @hide + */ + @NonNull public final ContentProtectionOptions contentProtectionOptions; + + /** * List of activities explicitly allowlisted for content capture (or {@code null} if allowlisted * for all acitivites in the package). */ @@ -94,52 +108,99 @@ public final class ContentCaptureOptions implements Parcelable { * for contexts belonging to the content capture service app. */ public ContentCaptureOptions(int loggingLevel) { - this(/* lite= */ true, loggingLevel, /* maxBufferSize= */ 0, - /* idleFlushingFrequencyMs= */ 0, /* textChangeFlushingFrequencyMs= */ 0, - /* logHistorySize= */ 0, /* disableFlushForViewTreeAppearing= */ false, + this( + /* lite= */ true, + loggingLevel, + /* maxBufferSize= */ 0, + /* idleFlushingFrequencyMs= */ 0, + /* textChangeFlushingFrequencyMs= */ 0, + /* logHistorySize= */ 0, + /* disableFlushForViewTreeAppearing= */ false, + /* enableReceiver= */ false, + new ContentProtectionOptions( + /* enableReceiver= */ false, + /* bufferSize= */ 0), /* whitelistedComponents= */ null); } - /** - * Default constructor. - */ - public ContentCaptureOptions(int loggingLevel, int maxBufferSize, int idleFlushingFrequencyMs, - int textChangeFlushingFrequencyMs, int logHistorySize, - @SuppressLint({"ConcreteCollection", "NullableCollection"}) - @Nullable ArraySet<ComponentName> whitelistedComponents) { - this(/* lite= */ false, loggingLevel, maxBufferSize, idleFlushingFrequencyMs, - textChangeFlushingFrequencyMs, logHistorySize, + /** Default constructor. */ + public ContentCaptureOptions( + int loggingLevel, + int maxBufferSize, + int idleFlushingFrequencyMs, + int textChangeFlushingFrequencyMs, + int logHistorySize, + @SuppressLint({"ConcreteCollection", "NullableCollection"}) @Nullable + ArraySet<ComponentName> whitelistedComponents) { + this( + /* lite= */ false, + loggingLevel, + maxBufferSize, + idleFlushingFrequencyMs, + textChangeFlushingFrequencyMs, + logHistorySize, ContentCaptureManager.DEFAULT_DISABLE_FLUSH_FOR_VIEW_TREE_APPEARING, + ContentCaptureManager.DEFAULT_ENABLE_CONTENT_CAPTURE_RECEIVER, + new ContentProtectionOptions( + ContentCaptureManager.DEFAULT_ENABLE_CONTENT_PROTECTION_RECEIVER, + ContentCaptureManager.DEFAULT_CONTENT_PROTECTION_BUFFER_SIZE), whitelistedComponents); } /** @hide */ - public ContentCaptureOptions(int loggingLevel, int maxBufferSize, int idleFlushingFrequencyMs, - int textChangeFlushingFrequencyMs, int logHistorySize, + public ContentCaptureOptions( + int loggingLevel, + int maxBufferSize, + int idleFlushingFrequencyMs, + int textChangeFlushingFrequencyMs, + int logHistorySize, boolean disableFlushForViewTreeAppearing, - @SuppressLint({"ConcreteCollection", "NullableCollection"}) - @Nullable ArraySet<ComponentName> whitelistedComponents) { - this(/* lite= */ false, loggingLevel, maxBufferSize, idleFlushingFrequencyMs, - textChangeFlushingFrequencyMs, logHistorySize, disableFlushForViewTreeAppearing, + boolean enableReceiver, + @NonNull ContentProtectionOptions contentProtectionOptions, + @SuppressLint({"ConcreteCollection", "NullableCollection"}) @Nullable + ArraySet<ComponentName> whitelistedComponents) { + this( + /* lite= */ false, + loggingLevel, + maxBufferSize, + idleFlushingFrequencyMs, + textChangeFlushingFrequencyMs, + logHistorySize, + disableFlushForViewTreeAppearing, + enableReceiver, + contentProtectionOptions, whitelistedComponents); } /** @hide */ @VisibleForTesting public ContentCaptureOptions(@Nullable ArraySet<ComponentName> whitelistedComponents) { - this(ContentCaptureManager.LOGGING_LEVEL_VERBOSE, + this( + ContentCaptureManager.LOGGING_LEVEL_VERBOSE, ContentCaptureManager.DEFAULT_MAX_BUFFER_SIZE, ContentCaptureManager.DEFAULT_IDLE_FLUSHING_FREQUENCY_MS, ContentCaptureManager.DEFAULT_TEXT_CHANGE_FLUSHING_FREQUENCY_MS, ContentCaptureManager.DEFAULT_LOG_HISTORY_SIZE, ContentCaptureManager.DEFAULT_DISABLE_FLUSH_FOR_VIEW_TREE_APPEARING, + ContentCaptureManager.DEFAULT_ENABLE_CONTENT_CAPTURE_RECEIVER, + new ContentProtectionOptions( + ContentCaptureManager.DEFAULT_ENABLE_CONTENT_PROTECTION_RECEIVER, + ContentCaptureManager.DEFAULT_CONTENT_PROTECTION_BUFFER_SIZE), whitelistedComponents); } - private ContentCaptureOptions(boolean lite, int loggingLevel, int maxBufferSize, - int idleFlushingFrequencyMs, int textChangeFlushingFrequencyMs, int logHistorySize, + private ContentCaptureOptions( + boolean lite, + int loggingLevel, + int maxBufferSize, + int idleFlushingFrequencyMs, + int textChangeFlushingFrequencyMs, + int logHistorySize, boolean disableFlushForViewTreeAppearing, - @Nullable ArraySet<ComponentName> whitelistedComponents) { + boolean enableReceiver, + @NonNull ContentProtectionOptions contentProtectionOptions, + @SuppressLint({"ConcreteCollection", "NullableCollection"}) @Nullable + ArraySet<ComponentName> whitelistedComponents) { this.lite = lite; this.loggingLevel = loggingLevel; this.maxBufferSize = maxBufferSize; @@ -147,6 +208,8 @@ public final class ContentCaptureOptions implements Parcelable { this.textChangeFlushingFrequencyMs = textChangeFlushingFrequencyMs; this.logHistorySize = logHistorySize; this.disableFlushForViewTreeAppearing = disableFlushForViewTreeAppearing; + this.enableReceiver = enableReceiver; + this.contentProtectionOptions = contentProtectionOptions; this.whitelistedComponents = whitelistedComponents; } @@ -191,12 +254,22 @@ public final class ContentCaptureOptions implements Parcelable { return "ContentCaptureOptions [loggingLevel=" + loggingLevel + " (lite)]"; } final StringBuilder string = new StringBuilder("ContentCaptureOptions ["); - string.append("loggingLevel=").append(loggingLevel) - .append(", maxBufferSize=").append(maxBufferSize) - .append(", idleFlushingFrequencyMs=").append(idleFlushingFrequencyMs) - .append(", textChangeFlushingFrequencyMs=").append(textChangeFlushingFrequencyMs) - .append(", logHistorySize=").append(logHistorySize) - .append(", disableFlushForViewTreeAppearing=").append(disableFlushForViewTreeAppearing); + string.append("loggingLevel=") + .append(loggingLevel) + .append(", maxBufferSize=") + .append(maxBufferSize) + .append(", idleFlushingFrequencyMs=") + .append(idleFlushingFrequencyMs) + .append(", textChangeFlushingFrequencyMs=") + .append(textChangeFlushingFrequencyMs) + .append(", logHistorySize=") + .append(logHistorySize) + .append(", disableFlushForViewTreeAppearing=") + .append(disableFlushForViewTreeAppearing) + .append(", enableReceiver=") + .append(enableReceiver) + .append(", contentProtectionOptions=") + .append(contentProtectionOptions); if (whitelistedComponents != null) { string.append(", whitelisted=").append(whitelistedComponents); } @@ -210,11 +283,21 @@ public final class ContentCaptureOptions implements Parcelable { pw.print(", lite"); return; } - pw.print(", bufferSize="); pw.print(maxBufferSize); - pw.print(", idle="); pw.print(idleFlushingFrequencyMs); - pw.print(", textIdle="); pw.print(textChangeFlushingFrequencyMs); - pw.print(", logSize="); pw.print(logHistorySize); - pw.print(", disableFlushForViewTreeAppearing="); pw.print(disableFlushForViewTreeAppearing); + pw.print(", bufferSize="); + pw.print(maxBufferSize); + pw.print(", idle="); + pw.print(idleFlushingFrequencyMs); + pw.print(", textIdle="); + pw.print(textChangeFlushingFrequencyMs); + pw.print(", logSize="); + pw.print(logHistorySize); + pw.print(", disableFlushForViewTreeAppearing="); + pw.print(disableFlushForViewTreeAppearing); + pw.print(", enableReceiver="); + pw.print(enableReceiver); + pw.print(", contentProtectionOptions=["); + contentProtectionOptions.dumpShort(pw); + pw.print("]"); if (whitelistedComponents != null) { pw.print(", whitelisted="); pw.print(whitelistedComponents); } @@ -236,6 +319,8 @@ public final class ContentCaptureOptions implements Parcelable { parcel.writeInt(textChangeFlushingFrequencyMs); parcel.writeInt(logHistorySize); parcel.writeBoolean(disableFlushForViewTreeAppearing); + parcel.writeBoolean(enableReceiver); + contentProtectionOptions.writeToParcel(parcel); parcel.writeArraySet(whitelistedComponents); } @@ -254,12 +339,22 @@ public final class ContentCaptureOptions implements Parcelable { final int textChangeFlushingFrequencyMs = parcel.readInt(); final int logHistorySize = parcel.readInt(); final boolean disableFlushForViewTreeAppearing = parcel.readBoolean(); + final boolean enableReceiver = parcel.readBoolean(); + final ContentProtectionOptions contentProtectionOptions = + ContentProtectionOptions.createFromParcel(parcel); @SuppressWarnings("unchecked") final ArraySet<ComponentName> whitelistedComponents = (ArraySet<ComponentName>) parcel.readArraySet(null); - return new ContentCaptureOptions(loggingLevel, maxBufferSize, - idleFlushingFrequencyMs, textChangeFlushingFrequencyMs, logHistorySize, - disableFlushForViewTreeAppearing, whitelistedComponents); + return new ContentCaptureOptions( + loggingLevel, + maxBufferSize, + idleFlushingFrequencyMs, + textChangeFlushingFrequencyMs, + logHistorySize, + disableFlushForViewTreeAppearing, + enableReceiver, + contentProtectionOptions, + whitelistedComponents); } @Override @@ -267,4 +362,62 @@ public final class ContentCaptureOptions implements Parcelable { return new ContentCaptureOptions[size]; } }; + + /** + * Content protection options for a given package. + * + * <p>Does not implement {@code Parcelable} since it is an inner class without a matching AIDL. + * + * @hide + */ + public static class ContentProtectionOptions { + + /** + * Is the content protection receiver enabled. + * + * @hide + */ + public final boolean enableReceiver; + + /** + * Size of the in-memory ring buffer for the content protection flow. + * + * @hide + */ + public final int bufferSize; + + public ContentProtectionOptions(boolean enableReceiver, int bufferSize) { + this.enableReceiver = enableReceiver; + this.bufferSize = bufferSize; + } + + @Override + public String toString() { + StringBuilder stringBuilder = new StringBuilder("ContentProtectionOptions ["); + stringBuilder + .append("enableReceiver=") + .append(enableReceiver) + .append(", bufferSize=") + .append(bufferSize); + return stringBuilder.append(']').toString(); + } + + private void dumpShort(@NonNull PrintWriter pw) { + pw.print("enableReceiver="); + pw.print(enableReceiver); + pw.print(", bufferSize="); + pw.print(bufferSize); + } + + private void writeToParcel(Parcel parcel) { + parcel.writeBoolean(enableReceiver); + parcel.writeInt(bufferSize); + } + + private static ContentProtectionOptions createFromParcel(Parcel parcel) { + boolean enableReceiver = parcel.readBoolean(); + int bufferSize = parcel.readInt(); + return new ContentProtectionOptions(enableReceiver, bufferSize); + } + } } diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java index d6df03321298..94bff893b5a8 100644 --- a/core/java/android/hardware/display/DisplayManagerInternal.java +++ b/core/java/android/hardware/display/DisplayManagerInternal.java @@ -413,6 +413,15 @@ public abstract class DisplayManagerInternal { public abstract HostUsiVersion getHostUsiVersion(int displayId); /** + * Get the ALS data for a particular display. + * + * @param displayId The id of the display. + * @return {@link AmbientLightSensorData} + */ + @Nullable + public abstract AmbientLightSensorData getAmbientLightSensorData(int displayId); + + /** * Get all available DisplayGroupIds. */ public abstract IntArray getDisplayGroupIds(); @@ -669,4 +678,23 @@ public abstract class DisplayManagerInternal { return "RefreshRateLimitation(" + type + ": " + range + ")"; } } + + /** + * Class to provide Ambient sensor data using the API + * {@link DisplayManagerInternal#getAmbientLightSensorData(int)} + */ + public static final class AmbientLightSensorData { + public String sensorName; + public String sensorType; + + public AmbientLightSensorData(String name, String type) { + sensorName = name; + sensorType = type; + } + + @Override + public String toString() { + return "AmbientLightSensorData(" + sensorName + ", " + sensorType + ")"; + } + } } diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java index 01977f6195ff..619544366b02 100644 --- a/core/java/android/hardware/fingerprint/FingerprintManager.java +++ b/core/java/android/hardware/fingerprint/FingerprintManager.java @@ -103,6 +103,7 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing private static final int MSG_UDFPS_POINTER_DOWN = 108; private static final int MSG_UDFPS_POINTER_UP = 109; private static final int MSG_POWER_BUTTON_PRESSED = 110; + private static final int MSG_UDFPS_OVERLAY_SHOWN = 111; /** * @hide @@ -121,6 +122,24 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing public @interface EnrollReason {} /** + * Udfps ui event of overlay is shown on the screen. + * @hide + */ + public static final int UDFPS_UI_OVERLAY_SHOWN = 1; + /** + * Udfps ui event of the udfps UI being ready (e.g. HBM illumination is enabled). + * @hide + */ + public static final int UDFPS_UI_READY = 2; + + /** + * @hide + */ + @IntDef({UDFPS_UI_OVERLAY_SHOWN, UDFPS_UI_READY}) + @Retention(RetentionPolicy.SOURCE) + public @interface UdfpsUiEvent{} + + /** * Request authentication with any single sensor. * @hide */ @@ -475,12 +494,17 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing /** * Called when a pointer down event has occurred. */ - public void onPointerDown(int sensorId){ } + public void onUdfpsPointerDown(int sensorId){ } /** * Called when a pointer up event has occurred. */ - public void onPointerUp(int sensorId){ } + public void onUdfpsPointerUp(int sensorId){ } + + /** + * Called when udfps overlay is shown. + */ + public void onUdfpsOverlayShown() { } } /** @@ -1112,14 +1136,14 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing * @hide */ @RequiresPermission(USE_BIOMETRIC_INTERNAL) - public void onUiReady(long requestId, int sensorId) { + public void onUdfpsUiEvent(@UdfpsUiEvent int event, long requestId, int sensorId) { if (mService == null) { - Slog.w(TAG, "onUiReady: no fingerprint service"); + Slog.w(TAG, "onUdfpsUiEvent: no fingerprint service"); return; } try { - mService.onUiReady(requestId, sensorId); + mService.onUdfpsUiEvent(event, requestId, sensorId); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1365,6 +1389,8 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing case MSG_POWER_BUTTON_PRESSED: sendPowerPressed(); break; + case MSG_UDFPS_OVERLAY_SHOWN: + sendUdfpsOverlayShown(); default: Slog.w(TAG, "Unknown message: " + msg.what); @@ -1489,7 +1515,7 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing } if (mEnrollmentCallback != null) { - mEnrollmentCallback.onPointerDown(sensorId); + mEnrollmentCallback.onUdfpsPointerDown(sensorId); } } @@ -1500,7 +1526,7 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing mAuthenticationCallback.onUdfpsPointerUp(sensorId); } if (mEnrollmentCallback != null) { - mEnrollmentCallback.onPointerUp(sensorId); + mEnrollmentCallback.onUdfpsPointerUp(sensorId); } } @@ -1512,6 +1538,12 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing } } + private void sendUdfpsOverlayShown() { + if (mEnrollmentCallback != null) { + mEnrollmentCallback.onUdfpsOverlayShown(); + } + } + /** * @hide */ @@ -1787,6 +1819,11 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing public void onUdfpsPointerUp(int sensorId) { mHandler.obtainMessage(MSG_UDFPS_POINTER_UP, sensorId, 0).sendToTarget(); } + + @Override + public void onUdfpsOverlayShown() { + mHandler.obtainMessage(MSG_UDFPS_OVERLAY_SHOWN).sendToTarget(); + } }; } diff --git a/core/java/android/hardware/fingerprint/FingerprintServiceReceiver.java b/core/java/android/hardware/fingerprint/FingerprintServiceReceiver.java index a9779b51321b..89d710d4adfe 100644 --- a/core/java/android/hardware/fingerprint/FingerprintServiceReceiver.java +++ b/core/java/android/hardware/fingerprint/FingerprintServiceReceiver.java @@ -75,4 +75,9 @@ public class FingerprintServiceReceiver extends IFingerprintServiceReceiver.Stub public void onUdfpsPointerUp(int sensorId) throws RemoteException { } + + @Override + public void onUdfpsOverlayShown() throws RemoteException { + + } } diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl index ec5749ed4f05..ff2f313ac3df 100644 --- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl +++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl @@ -193,7 +193,7 @@ interface IFingerprintService { // Notifies about the fingerprint UI being ready (e.g. HBM illumination is enabled). @EnforcePermission("USE_BIOMETRIC_INTERNAL") - void onUiReady(long requestId, int sensorId); + void onUdfpsUiEvent(int event, long requestId, int sensorId); // Sets the controller for managing the UDFPS overlay. @EnforcePermission("USE_BIOMETRIC_INTERNAL") diff --git a/core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl b/core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl index 9cea1fed629d..91a32d78314c 100644 --- a/core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl +++ b/core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl @@ -32,4 +32,5 @@ oneway interface IFingerprintServiceReceiver { void onChallengeGenerated(int sensorId, int userId, long challenge); void onUdfpsPointerDown(int sensorId); void onUdfpsPointerUp(int sensorId); + void onUdfpsOverlayShown(); } diff --git a/core/java/android/hardware/input/InputSettings.java b/core/java/android/hardware/input/InputSettings.java index 17bbe1459d03..33960c058baa 100644 --- a/core/java/android/hardware/input/InputSettings.java +++ b/core/java/android/hardware/input/InputSettings.java @@ -266,7 +266,7 @@ public class InputSettings { */ public static boolean useTouchpadTapToClick(@NonNull Context context) { return Settings.System.getIntForUser(context.getContentResolver(), - Settings.System.TOUCHPAD_TAP_TO_CLICK, 0, UserHandle.USER_CURRENT) == 1; + Settings.System.TOUCHPAD_TAP_TO_CLICK, 1, UserHandle.USER_CURRENT) == 1; } /** diff --git a/core/java/android/hardware/input/KeyboardLayout.java b/core/java/android/hardware/input/KeyboardLayout.java index 0311da4b645f..4403251e0488 100644 --- a/core/java/android/hardware/input/KeyboardLayout.java +++ b/core/java/android/hardware/input/KeyboardLayout.java @@ -73,7 +73,7 @@ public final class KeyboardLayout implements Parcelable, Comparable<KeyboardLayo private final int mProductId; /** Currently supported Layout types in the KCM files */ - private enum LayoutType { + public enum LayoutType { UNDEFINED(0, LAYOUT_TYPE_UNDEFINED), QWERTY(1, LAYOUT_TYPE_QWERTY), QWERTZ(2, LAYOUT_TYPE_QWERTZ), @@ -88,9 +88,11 @@ public final class KeyboardLayout implements Parcelable, Comparable<KeyboardLayo private final int mValue; private final String mName; private static final Map<Integer, LayoutType> VALUE_TO_ENUM_MAP = new HashMap<>(); + private static final Map<String, LayoutType> NAME_TO_ENUM_MAP = new HashMap<>(); static { for (LayoutType type : LayoutType.values()) { VALUE_TO_ENUM_MAP.put(type.mValue, type); + NAME_TO_ENUM_MAP.put(type.mName, type); } } @@ -110,6 +112,25 @@ public final class KeyboardLayout implements Parcelable, Comparable<KeyboardLayo private String getName() { return mName; } + + /** + * Returns enum value for provided layout type + * @param layoutName name of the layout type + * @return int value corresponding to the LayoutType enum that matches the layout name. + * (LayoutType.UNDEFINED if no match found) + */ + public static int getLayoutTypeEnumValue(String layoutName) { + return NAME_TO_ENUM_MAP.getOrDefault(layoutName, UNDEFINED).getValue(); + } + + /** + * Returns name for provided layout type enum value + * @param enumValue value representation for LayoutType enum + * @return Layout name corresponding to the enum value (LAYOUT_TYPE_UNDEFINED if not found) + */ + public static String getLayoutNameFromValue(int enumValue) { + return VALUE_TO_ENUM_MAP.getOrDefault(enumValue, UNDEFINED).getName(); + } } @NonNull diff --git a/core/java/android/hardware/lights/Light.java b/core/java/android/hardware/lights/Light.java index 1df9b75f0b09..18d0b09faa14 100644 --- a/core/java/android/hardware/lights/Light.java +++ b/core/java/android/hardware/lights/Light.java @@ -110,6 +110,8 @@ public final class Light implements Parcelable { private final int mOrdinal; private final int mType; private final int mCapabilities; + @Nullable + private final int[] mPreferredBrightnessLevels; /** * Creates a new light with the given data. @@ -117,7 +119,7 @@ public final class Light implements Parcelable { * @hide */ public Light(int id, int ordinal, int type) { - this(id, "Light", ordinal, type, 0); + this(id, "Light", ordinal, type, 0, null); } /** @@ -126,11 +128,22 @@ public final class Light implements Parcelable { * @hide */ public Light(int id, String name, int ordinal, int type, int capabilities) { + this(id, name, ordinal, type, capabilities, null); + } + + /** + * Creates a new light with the given data. + * + * @hide + */ + public Light(int id, String name, int ordinal, int type, int capabilities, + @Nullable int[] preferredBrightnessLevels) { mId = id; mName = name; mOrdinal = ordinal; mType = type; mCapabilities = capabilities; + mPreferredBrightnessLevels = preferredBrightnessLevels; } private Light(@NonNull Parcel in) { @@ -139,6 +152,7 @@ public final class Light implements Parcelable { mOrdinal = in.readInt(); mType = in.readInt(); mCapabilities = in.readInt(); + mPreferredBrightnessLevels = in.createIntArray(); } /** Implement the Parcelable interface */ @@ -149,6 +163,7 @@ public final class Light implements Parcelable { dest.writeInt(mOrdinal); dest.writeInt(mType); dest.writeInt(mCapabilities); + dest.writeIntArray(mPreferredBrightnessLevels); } /** Implement the Parcelable interface */ @@ -252,4 +267,17 @@ public final class Light implements Parcelable { return (mCapabilities & LIGHT_CAPABILITY_COLOR_RGB) == LIGHT_CAPABILITY_COLOR_RGB; } + /** + * Returns preferred brightness levels for the light which will be used when user + * increase/decrease brightness levels for the light (currently only used for Keyboard + * backlight control using backlight up/down keys). + * + * The values in the preferred brightness level array are in the range [0, 255]. + * + * @hide + */ + @Nullable + public int[] getPreferredBrightnessLevels() { + return mPreferredBrightnessLevels; + } } diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java index a9c4818393a8..2f9c2073cd38 100644 --- a/core/java/android/inputmethodservice/InputMethodService.java +++ b/core/java/android/inputmethodservice/InputMethodService.java @@ -52,8 +52,6 @@ import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; import static android.view.WindowInsets.Type.navigationBars; import static android.view.WindowInsets.Type.statusBars; -import static java.lang.annotation.RetentionPolicy.SOURCE; - import android.annotation.AnyThread; import android.annotation.CallSuper; import android.annotation.DrawableRes; @@ -160,6 +158,7 @@ import com.android.internal.inputmethod.InputMethodNavButtonFlags; import com.android.internal.inputmethod.InputMethodPrivilegedOperations; import com.android.internal.inputmethod.InputMethodPrivilegedOperationsRegistry; import com.android.internal.inputmethod.SoftInputShowHideReason; +import com.android.internal.util.Preconditions; import com.android.internal.util.RingBuffer; import org.xmlpull.v1.XmlPullParserException; @@ -482,43 +481,53 @@ public class InputMethodService extends AbstractInputMethodService { public static final int BACK_DISPOSITION_ADJUST_NOTHING = 3; /** - * Enum flag to be used for {@link #setBackDisposition(int)}. + * Enum values to be used for {@link #setBackDisposition(int)}. * * @hide */ - @Retention(SOURCE) - @IntDef(value = {BACK_DISPOSITION_DEFAULT, BACK_DISPOSITION_WILL_NOT_DISMISS, - BACK_DISPOSITION_WILL_DISMISS, BACK_DISPOSITION_ADJUST_NOTHING}, - prefix = "BACK_DISPOSITION_") + @IntDef(prefix = { "BACK_DISPOSITION_" }, value = { + BACK_DISPOSITION_DEFAULT, + BACK_DISPOSITION_WILL_NOT_DISMISS, + BACK_DISPOSITION_WILL_DISMISS, + BACK_DISPOSITION_ADJUST_NOTHING, + }) + @Retention(RetentionPolicy.SOURCE) public @interface BackDispositionMode {} /** + * Enum flags to be used for {@link #setImeWindowStatus}, representing the current state of the + * IME window visibility. + * * @hide - * The IME is active. It may or may not be visible. */ - public static final int IME_ACTIVE = 0x1; + @IntDef(flag = true, prefix = { "IME_" }, value = { + IME_ACTIVE, + IME_VISIBLE, + IME_VISIBLE_IMPERCEPTIBLE, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface ImeWindowVisibility {} /** + * The IME is active. It may or may not be visible. * @hide - * The IME is perceptibly visible to the user. */ - public static final int IME_VISIBLE = 0x2; + public static final int IME_ACTIVE = 0x1; /** + * The IME is perceptibly visible to the user. * @hide - * The IME is active and ready with views but set invisible. - * This flag cannot be combined with {@link #IME_VISIBLE}. */ - public static final int IME_INVISIBLE = 0x4; + public static final int IME_VISIBLE = 0x2; /** - * @hide * The IME is visible, but not yet perceptible to the user (e.g. fading in) * by {@link android.view.WindowInsetsController}. * * @see InputMethodManager#reportPerceptible + * @hide */ - public static final int IME_VISIBLE_IMPERCEPTIBLE = 0x8; + public static final int IME_VISIBLE_IMPERCEPTIBLE = 0x4; // Min and max values for back disposition. private static final int BACK_DISPOSITION_MIN = BACK_DISPOSITION_DEFAULT; @@ -631,9 +640,18 @@ public class InputMethodService extends AbstractInputMethodService { int mStatusIcon; + /** + * Latest value reported of back disposition mode. + */ @BackDispositionMode int mBackDisposition; + /** + * Latest value reported of IME window visibility flags. + */ + @ImeWindowVisibility + private int mImeWindowVisibility; + private Object mLock = new Object(); @GuardedBy("mLock") private boolean mNotifyUserActionSent; @@ -1210,8 +1228,14 @@ public class InputMethodService extends AbstractInputMethodService { mImeSurfaceRemoverRunnable = null; } - private void setImeWindowStatus(int visibilityFlags, int backDisposition) { - mPrivOps.setImeWindowStatusAsync(visibilityFlags, backDisposition); + private void setImeWindowStatus(@ImeWindowVisibility int vis, + @BackDispositionMode int backDisposition) { + if (vis == mImeWindowVisibility && backDisposition == mBackDisposition) { + return; + } + mImeWindowVisibility = Preconditions.checkFlagsArgument(vis, IME_ACTIVE | IME_VISIBLE); + mBackDisposition = backDisposition; + mPrivOps.setImeWindowStatusAsync(mImeWindowVisibility, mBackDisposition); } /** Set region of the keyboard to be avoided from back gesture */ @@ -1885,15 +1909,11 @@ public class InputMethodService extends AbstractInputMethodService { * @param disposition disposition mode to be set */ public void setBackDisposition(@BackDispositionMode int disposition) { - if (disposition == mBackDisposition) { - return; - } - if (disposition > BACK_DISPOSITION_MAX || disposition < BACK_DISPOSITION_MIN) { + if (disposition < BACK_DISPOSITION_MIN || disposition > BACK_DISPOSITION_MAX) { Log.e(TAG, "Invalid back disposition value (" + disposition + ") specified."); return; } - mBackDisposition = disposition; - setImeWindowStatus(mapToImeWindowStatus(), mBackDisposition); + setImeWindowStatus(mImeWindowVisibility, disposition); } /** @@ -2867,14 +2887,8 @@ public class InputMethodService extends AbstractInputMethodService { Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.showWindow"); mDecorViewWasVisible = mDecorViewVisible; mInShowWindow = true; - final int previousImeWindowStatus = - (mDecorViewVisible ? IME_ACTIVE : 0) | (isInputViewShown() - ? (!mWindowVisible ? IME_INVISIBLE : IME_VISIBLE) : 0); startViews(prepareWindow(showInput)); - final int nextImeWindowStatus = mapToImeWindowStatus(); - if (previousImeWindowStatus != nextImeWindowStatus) { - setImeWindowStatus(nextImeWindowStatus, mBackDisposition); - } + setImeWindowStatus(mapToImeWindowStatus(), mBackDisposition); mNavigationBarController.onWindowShown(); // compute visibility @@ -4085,9 +4099,9 @@ public class InputMethodService extends AbstractInputMethodService { }; } + @ImeWindowVisibility private int mapToImeWindowStatus() { - return IME_ACTIVE - | (isInputViewShown() ? IME_VISIBLE : 0); + return IME_ACTIVE | (mDecorViewVisible ? IME_VISIBLE : 0); } private boolean isAutomotive() { diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java index 94971b8654ee..c9073fa4b72c 100644 --- a/core/java/android/os/GraphicsEnvironment.java +++ b/core/java/android/os/GraphicsEnvironment.java @@ -123,6 +123,7 @@ public class GraphicsEnvironment { private int mAngleOptInIndex = -1; private boolean mEnabledByGameMode = false; + private boolean mShouldUseAngle = false; /** * Set up GraphicsEnvironment @@ -141,19 +142,16 @@ public class GraphicsEnvironment { // Setup ANGLE and pass down ANGLE details to the C++ code Trace.traceBegin(Trace.TRACE_TAG_GRAPHICS, "setupAngle"); - boolean useAngle = false; if (setupAngle(context, coreSettings, pm, packageName)) { - if (shouldUseAngle(context, coreSettings, packageName)) { - useAngle = true; - setGpuStats(ANGLE_DRIVER_NAME, ANGLE_DRIVER_VERSION_NAME, ANGLE_DRIVER_VERSION_CODE, - 0, packageName, getVulkanVersion(pm)); - } + mShouldUseAngle = true; + setGpuStats(ANGLE_DRIVER_NAME, ANGLE_DRIVER_VERSION_NAME, ANGLE_DRIVER_VERSION_CODE, + 0, packageName, getVulkanVersion(pm)); } Trace.traceEnd(Trace.TRACE_TAG_GRAPHICS); Trace.traceBegin(Trace.TRACE_TAG_GRAPHICS, "chooseDriver"); if (!chooseDriver(context, coreSettings, pm, packageName, appInfoWithMetaData)) { - if (!useAngle) { + if (!mShouldUseAngle) { setGpuStats(SYSTEM_DRIVER_NAME, SYSTEM_DRIVER_VERSION_NAME, SYSTEM_DRIVER_VERSION_CODE, SystemProperties.getLong(PROPERTY_GFX_DRIVER_BUILD_TIME, 0), @@ -636,44 +634,35 @@ public class GraphicsEnvironment { } /** - * Determine if ANGLE will be used and setup the environment - */ - private boolean setupAndUseAngle(Context context, String packageName) { - // Need to make sure we are evaluating ANGLE usage for the correct circumstances - if (!setupAngle(context, null, context.getPackageManager(), packageName)) { - Log.v(TAG, "Package '" + packageName + "' should not use ANGLE"); - return false; - } - - final boolean useAngle = getShouldUseAngle(packageName); - Log.v(TAG, "Package '" + packageName + "' should use ANGLE = '" + useAngle + "'"); - - return useAngle; - } - - /** - * Show the ANGLE in Use Dialog Box + * Show the ANGLE in use dialog box. + * The ANGLE in use dialog box will show up as long as the application + * should use ANGLE. It does not mean the application has successfully + * loaded ANGLE because this check happens before the loading completes. * @param context */ public void showAngleInUseDialogBox(Context context) { - final String packageName = context.getPackageName(); + if (!shouldShowAngleInUseDialogBox(context)) { + return; + } + + if (!mShouldUseAngle) { + return; + } - if (shouldShowAngleInUseDialogBox(context) && setupAndUseAngle(context, packageName)) { - final Intent intent = new Intent(ACTION_ANGLE_FOR_ANDROID_TOAST_MESSAGE); - String anglePkg = getAnglePackageName(context.getPackageManager()); - intent.setPackage(anglePkg); + final Intent intent = new Intent(ACTION_ANGLE_FOR_ANDROID_TOAST_MESSAGE); + final String anglePkg = getAnglePackageName(context.getPackageManager()); + intent.setPackage(anglePkg); - context.sendOrderedBroadcast(intent, null, new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - Bundle results = getResultExtras(true); + context.sendOrderedBroadcast(intent, null, new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + final Bundle results = getResultExtras(true); - String toastMsg = results.getString(INTENT_KEY_A4A_TOAST_MESSAGE); - final Toast toast = Toast.makeText(context, toastMsg, Toast.LENGTH_LONG); - toast.show(); - } - }, null, Activity.RESULT_OK, null, null); - } + final String toastMsg = results.getString(INTENT_KEY_A4A_TOAST_MESSAGE); + final Toast toast = Toast.makeText(context, toastMsg, Toast.LENGTH_LONG); + toast.show(); + } + }, null, Activity.RESULT_OK, null, null); } private String[] getAngleEglFeatures(Context context, Bundle coreSettings) { @@ -901,9 +890,8 @@ public class GraphicsEnvironment { private static native void setDriverPathAndSphalLibraries(String path, String sphalLibraries); private static native void setGpuStats(String driverPackageName, String driverVersionName, long driverVersionCode, long driverBuildTime, String appPackageName, int vulkanVersion); - private static native void setAngleInfo(String path, String appPackage, + private static native void setAngleInfo(String path, String packageName, String devOptIn, String[] features); - private static native boolean getShouldUseAngle(String packageName); private static native boolean setInjectLayersPrSetDumpable(); private static native void nativeToggleAngleAsSystemDriver(boolean enabled); diff --git a/core/java/android/os/image/DynamicSystemClient.java b/core/java/android/os/image/DynamicSystemClient.java index 218ecc8c5571..88096ab91261 100644 --- a/core/java/android/os/image/DynamicSystemClient.java +++ b/core/java/android/os/image/DynamicSystemClient.java @@ -209,6 +209,13 @@ public class DynamicSystemClient { public static final String ACTION_HIDE_NOTIFICATION = "android.os.image.action.HIDE_NOTIFICATION"; + /** + * Intent action: notify the service to post a status update when keyguard is dismissed. + * @hide + */ + public static final String ACTION_NOTIFY_KEYGUARD_DISMISSED = + "android.os.image.action.NOTIFY_KEYGUARD_DISMISSED"; + /* * Intent Keys */ diff --git a/core/java/android/os/image/DynamicSystemManager.java b/core/java/android/os/image/DynamicSystemManager.java index 9610b16a312c..536795bafb1c 100644 --- a/core/java/android/os/image/DynamicSystemManager.java +++ b/core/java/android/os/image/DynamicSystemManager.java @@ -285,4 +285,16 @@ public class DynamicSystemManager { throw new RuntimeException(e.toString()); } } + + /** + * Returns the active DSU slot + */ + @RequiresPermission(android.Manifest.permission.MANAGE_DYNAMIC_SYSTEM) + public String getActiveDsuSlot() { + try { + return mService.getActiveDsuSlot(); + } catch (RemoteException e) { + throw new RuntimeException(e.toString()); + } + } } diff --git a/core/java/android/os/image/IDynamicSystemService.aidl b/core/java/android/os/image/IDynamicSystemService.aidl index 755368a85c40..0280ebbd87ab 100644 --- a/core/java/android/os/image/IDynamicSystemService.aidl +++ b/core/java/android/os/image/IDynamicSystemService.aidl @@ -145,4 +145,10 @@ interface IDynamicSystemService */ @EnforcePermission("MANAGE_DYNAMIC_SYSTEM") long suggestScratchSize(); + + /** + * Get the active DSU slot + */ + @EnforcePermission("MANAGE_DYNAMIC_SYSTEM") + String getActiveDsuSlot(); } diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index d695c0cb3760..d425bf8ae557 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -7486,6 +7486,14 @@ public final class Settings { public static final String STYLUS_BUTTONS_ENABLED = "stylus_buttons_enabled"; /** + * Preferred default user profile to use with the notes task button shortcut. + * + * @hide + */ + @SuppressLint("NoSettingsProvider") + public static final String DEFAULT_NOTE_TASK_PROFILE = "default_note_task_profile"; + + /** * Host name and port for global http proxy. Uses ':' seperator for * between host and port. * @@ -17979,6 +17987,15 @@ public final class Settings { "review_permissions_notification_state"; /** + * Whether repair mode is active on the device. + * <p> + * Set to 1 for true and 0 for false. + * + * @hide + */ + public static final String REPAIR_MODE_ACTIVE = "repair_mode_active"; + + /** * Settings migrated from Wear OS settings provider. * @hide */ diff --git a/core/java/android/service/contentcapture/ContentCaptureService.java b/core/java/android/service/contentcapture/ContentCaptureService.java index 737c95fadccd..7fa0ac846d60 100644 --- a/core/java/android/service/contentcapture/ContentCaptureService.java +++ b/core/java/android/service/contentcapture/ContentCaptureService.java @@ -37,6 +37,7 @@ import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.os.ParcelFileDescriptor; +import android.os.Process; import android.os.RemoteException; import android.util.Log; import android.util.Slog; @@ -88,6 +89,18 @@ public abstract class ContentCaptureService extends Service { "android.service.contentcapture.ContentCaptureService"; /** + * The {@link Intent} that must be declared as handled by the protection service. + * + * <p>To be supported, the service must also require the {@link + * android.Manifest.permission#BIND_CONTENT_CAPTURE_SERVICE} permission so that other + * applications can not abuse it. + * + * @hide + */ + public static final String PROTECTION_SERVICE_INTERFACE = + "android.service.contentcapture.ContentProtectionService"; + + /** * Name under which a ContentCaptureService component publishes information about itself. * * <p>This meta-data should reference an XML resource containing a @@ -128,10 +141,9 @@ public abstract class ContentCaptureService extends Service { private long mCallerMismatchTimeout = 1000; private long mLastCallerMismatchLog; - /** - * Binder that receives calls from the system server. - */ - private final IContentCaptureService mServerInterface = new IContentCaptureService.Stub() { + /** Binder that receives calls from the system server in the content capture flow. */ + private final IContentCaptureService mContentCaptureServerInterface = + new IContentCaptureService.Stub() { @Override public void onConnected(IBinder callback, boolean verbose, boolean debug) { @@ -187,10 +199,24 @@ public abstract class ContentCaptureService extends Service { } }; - /** - * Binder that receives calls from the app. - */ - private final IContentCaptureDirectManager mClientInterface = + /** Binder that receives calls from the system server in the content protection flow. */ + private final IContentProtectionService mContentProtectionServerInterface = + new IContentProtectionService.Stub() { + + @Override + public void onLoginDetected( + @SuppressWarnings("rawtypes") ParceledListSlice events) { + mHandler.sendMessage( + obtainMessage( + ContentCaptureService::handleOnLoginDetected, + ContentCaptureService.this, + Binder.getCallingUid(), + events)); + } + }; + + /** Binder that receives calls from the app in the content capture flow. */ + private final IContentCaptureDirectManager mContentCaptureClientInterface = new IContentCaptureDirectManager.Stub() { @Override @@ -220,9 +246,19 @@ public abstract class ContentCaptureService extends Service { @Override public final IBinder onBind(Intent intent) { if (SERVICE_INTERFACE.equals(intent.getAction())) { - return mServerInterface.asBinder(); - } - Log.w(TAG, "Tried to bind to wrong intent (should be " + SERVICE_INTERFACE + ": " + intent); + return mContentCaptureServerInterface.asBinder(); + } + if (PROTECTION_SERVICE_INTERFACE.equals(intent.getAction())) { + return mContentProtectionServerInterface.asBinder(); + } + Log.w( + TAG, + "Tried to bind to wrong intent (should be " + + SERVICE_INTERFACE + + " or " + + PROTECTION_SERVICE_INTERFACE + + "): " + + intent); return null; } @@ -456,7 +492,7 @@ public abstract class ContentCaptureService extends Service { } else { stateFlags |= ContentCaptureSession.STATE_DISABLED; } - setClientState(clientReceiver, stateFlags, mClientInterface.asBinder()); + setClientState(clientReceiver, stateFlags, mContentCaptureClientInterface.asBinder()); } private void handleSendEvents(int uid, @@ -524,6 +560,18 @@ public abstract class ContentCaptureService extends Service { writeFlushMetrics(lastSessionId, activityComponent, metrics, options, reason); } + private void handleOnLoginDetected( + int uid, @NonNull ParceledListSlice<ContentCaptureEvent> parceledEvents) { + if (uid != Process.SYSTEM_UID) { + Log.e(TAG, "handleOnLoginDetected() not allowed for uid: " + uid); + return; + } + List<ContentCaptureEvent> events = parceledEvents.getList(); + int sessionIdInt = events.isEmpty() ? NO_SESSION_ID : events.get(0).getSessionId(); + ContentCaptureSessionId sessionId = new ContentCaptureSessionId(sessionIdInt); + events.forEach(event -> onContentCaptureEvent(sessionId, event)); + } + private void handleOnActivitySnapshot(int sessionId, @NonNull SnapshotData snapshotData) { onActivitySnapshot(new ContentCaptureSessionId(sessionId), snapshotData); } diff --git a/core/java/android/service/contentcapture/ContentCaptureServiceInfo.java b/core/java/android/service/contentcapture/ContentCaptureServiceInfo.java index fb6061916d99..4aa5c00a6a52 100644 --- a/core/java/android/service/contentcapture/ContentCaptureServiceInfo.java +++ b/core/java/android/service/contentcapture/ContentCaptureServiceInfo.java @@ -48,7 +48,7 @@ import java.io.PrintWriter; * * @hide */ -public final class ContentCaptureServiceInfo { +public class ContentCaptureServiceInfo { private static final String TAG = ContentCaptureServiceInfo.class.getSimpleName(); private static final String XML_TAG_SERVICE = "content-capture-service"; diff --git a/packages/SystemUI/src/com/android/keyguard/clock/ClockInfoModule.java b/core/java/android/service/contentcapture/IContentProtectionService.aidl index 72a44bd198f2..4a13c3f63a33 100644 --- a/packages/SystemUI/src/com/android/keyguard/clock/ClockInfoModule.java +++ b/core/java/android/service/contentcapture/IContentProtectionService.aidl @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 The Android Open Source Project + * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,25 +14,17 @@ * limitations under the License. */ -package com.android.keyguard.clock; +package android.service.contentcapture; -import java.util.List; - -import dagger.Module; -import dagger.Provides; +import android.content.pm.ParceledListSlice; +import android.view.contentcapture.ContentCaptureEvent; /** - * Dagger Module for clock package. + * Interface from the system server to the content protection service. * - * @deprecated Migrate to ClockRegistry + * @hide */ -@Module -@Deprecated -public abstract class ClockInfoModule { +oneway interface IContentProtectionService { - /** */ - @Provides - public static List<ClockInfo> provideClockInfoList(ClockManager clockManager) { - return clockManager.getClockInfos(); - } + void onLoginDetected(in ParceledListSlice events); } diff --git a/core/java/android/service/notification/NotificationRankingUpdate.java b/core/java/android/service/notification/NotificationRankingUpdate.java index a853714c0e9d..75640bd47ab9 100644 --- a/core/java/android/service/notification/NotificationRankingUpdate.java +++ b/core/java/android/service/notification/NotificationRankingUpdate.java @@ -16,32 +16,117 @@ package android.service.notification; import android.annotation.Nullable; +import android.annotation.SuppressLint; +import android.annotation.TestApi; import android.os.Parcel; import android.os.Parcelable; +import android.os.SharedMemory; +import android.system.ErrnoException; +import android.system.OsConstants; + +import androidx.annotation.NonNull; + +import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags; + +import java.nio.ByteBuffer; /** + * Represents an update to notification rankings. * @hide */ +@SuppressLint({"ParcelNotFinal", "ParcelCreator"}) +@TestApi public class NotificationRankingUpdate implements Parcelable { private final NotificationListenerService.RankingMap mRankingMap; + // The ranking map is stored in shared memory when parceled, for sending across the binder. + // This is done because the ranking map can grow large if there are many notifications. + private SharedMemory mRankingMapFd = null; + private final String mSharedMemoryName = "NotificationRankingUpdatedSharedMemory"; + + /** + * @hide + */ public NotificationRankingUpdate(NotificationListenerService.Ranking[] rankings) { mRankingMap = new NotificationListenerService.RankingMap(rankings); } + /** + * @hide + */ public NotificationRankingUpdate(Parcel in) { - mRankingMap = in.readParcelable(getClass().getClassLoader(), android.service.notification.NotificationListenerService.RankingMap.class); + if (SystemUiSystemPropertiesFlags.getResolver().isEnabled( + SystemUiSystemPropertiesFlags.NotificationFlags.RANKING_UPDATE_ASHMEM)) { + // Recover the ranking map from the SharedMemory and store it in mapParcel. + final Parcel mapParcel = Parcel.obtain(); + ByteBuffer buffer = null; + try { + // The ranking map should be stored in shared memory when it is parceled, so we + // unwrap the SharedMemory object. + mRankingMapFd = in.readParcelable(getClass().getClassLoader(), SharedMemory.class); + + // In the case that the ranking map can't be read, readParcelable may return null. + // In this case, we set mRankingMap to null; + if (mRankingMapFd == null) { + mRankingMap = null; + return; + } + // We only need read-only access to the shared memory region. + buffer = mRankingMapFd.mapReadOnly(); + if (buffer == null) { + mRankingMap = null; + return; + } + byte[] payload = new byte[buffer.remaining()]; + buffer.get(payload); + mapParcel.unmarshall(payload, 0, payload.length); + mapParcel.setDataPosition(0); + + mRankingMap = mapParcel.readParcelable(getClass().getClassLoader(), + android.service.notification.NotificationListenerService.RankingMap.class); + } catch (ErrnoException e) { + // TODO(b/284297289): remove throw when associated flag is moved to droidfood, to + // avoid crashes; change to Log.wtf. + throw new RuntimeException(e); + } finally { + mapParcel.recycle(); + if (buffer != null) { + mRankingMapFd.unmap(buffer); + } + } + } else { + mRankingMap = in.readParcelable(getClass().getClassLoader(), + android.service.notification.NotificationListenerService.RankingMap.class); + } } + /** + * Confirms that the SharedMemory file descriptor is closed. Should only be used for testing. + * @hide + */ + @TestApi + public final boolean isFdNotNullAndClosed() { + return mRankingMapFd != null && mRankingMapFd.getFd() == -1; + } + + /** + * @hide + */ public NotificationListenerService.RankingMap getRankingMap() { return mRankingMap; } + /** + * @hide + */ @Override public int describeContents() { return 0; } + /** + * @hide + */ @Override public boolean equals(@Nullable Object o) { if (this == o) return true; @@ -51,11 +136,51 @@ public class NotificationRankingUpdate implements Parcelable { return mRankingMap.equals(other.mRankingMap); } + /** + * @hide + */ @Override - public void writeToParcel(Parcel out, int flags) { - out.writeParcelable(mRankingMap, flags); + public void writeToParcel(@NonNull Parcel out, int flags) { + if (SystemUiSystemPropertiesFlags.getResolver().isEnabled( + SystemUiSystemPropertiesFlags.NotificationFlags.RANKING_UPDATE_ASHMEM)) { + final Parcel mapParcel = Parcel.obtain(); + try { + // Parcels the ranking map and measures its size. + mapParcel.writeParcelable(mRankingMap, flags); + int mapSize = mapParcel.dataSize(); + + // Creates a new SharedMemory object with enough space to hold the ranking map. + SharedMemory mRankingMapFd = SharedMemory.create(mSharedMemoryName, mapSize); + if (mRankingMapFd == null) { + return; + } + + // Gets a read/write buffer mapping the entire shared memory region. + final ByteBuffer buffer = mRankingMapFd.mapReadWrite(); + + // Puts the ranking map into the shared memory region buffer. + buffer.put(mapParcel.marshall(), 0, mapSize); + + // Protects the region from being written to, by setting it to be read-only. + mRankingMapFd.setProtect(OsConstants.PROT_READ); + + // Puts the SharedMemory object in the parcel. + out.writeParcelable(mRankingMapFd, flags); + } catch (ErrnoException e) { + // TODO(b/284297289): remove throw when associated flag is moved to droidfood, to + // avoid crashes; change to Log.wtf. + throw new RuntimeException(e); + } finally { + mapParcel.recycle(); + } + } else { + out.writeParcelable(mRankingMap, flags); + } } + /** + * @hide + */ public static final @android.annotation.NonNull Parcelable.Creator<NotificationRankingUpdate> CREATOR = new Parcelable.Creator<NotificationRankingUpdate>() { public NotificationRankingUpdate createFromParcel(Parcel parcel) { diff --git a/core/java/android/view/AttachedSurfaceControl.java b/core/java/android/view/AttachedSurfaceControl.java index dc066710dfe8..1ed5d3fb6268 100644 --- a/core/java/android/view/AttachedSurfaceControl.java +++ b/core/java/android/view/AttachedSurfaceControl.java @@ -23,6 +23,9 @@ import android.graphics.Region; import android.hardware.HardwareBuffer; import android.window.SurfaceSyncGroup; +import java.util.concurrent.Executor; +import java.util.function.Consumer; + /** * Provides an interface to the root-Surface of a View Hierarchy or Window. This * is used in combination with the {@link android.view.SurfaceControl} API to enable @@ -167,4 +170,40 @@ public interface AttachedSurfaceControl { */ default void setChildBoundingInsets(@NonNull Rect insets) { } + + /** + * Add a trusted presentation listener on the SurfaceControl associated with this window. + * + * @param t Transaction that the trusted presentation listener is added on. This should + * be applied by the caller. + * @param thresholds The {@link SurfaceControl.TrustedPresentationThresholds} that will specify + * when the to invoke the callback. + * @param executor The {@link Executor} where the callback will be invoked on. + * @param listener The {@link Consumer} that will receive the callbacks when entered or + * exited the threshold. + * + * @see SurfaceControl.Transaction#setTrustedPresentationCallback(SurfaceControl, + * SurfaceControl.TrustedPresentationThresholds, Executor, Consumer) + * + * @hide b/287076178 un-hide with API bump + */ + default void addTrustedPresentationCallback(@NonNull SurfaceControl.Transaction t, + @NonNull SurfaceControl.TrustedPresentationThresholds thresholds, + @NonNull Executor executor, @NonNull Consumer<Boolean> listener) { + } + + /** + * Remove a trusted presentation listener on the SurfaceControl associated with this window. + * + * @param t Transaction that the trusted presentation listener removed on. This should + * be applied by the caller. + * @param listener The {@link Consumer} that was previously registered with + * addTrustedPresentationCallback that should be removed. + * + * @see SurfaceControl.Transaction#clearTrustedPresentationCallback(SurfaceControl) + * @hide b/287076178 un-hide with API bump + */ + default void removeTrustedPresentationCallback(@NonNull SurfaceControl.Transaction t, + @NonNull Consumer<Boolean> listener) { + } } diff --git a/core/java/android/view/ISurfaceControlViewHost.aidl b/core/java/android/view/ISurfaceControlViewHost.aidl index 15008ae18618..fd4b329570d9 100644 --- a/core/java/android/view/ISurfaceControlViewHost.aidl +++ b/core/java/android/view/ISurfaceControlViewHost.aidl @@ -19,6 +19,7 @@ package android.view; import android.content.res.Configuration; import android.graphics.Rect; import android.view.InsetsState; +import android.view.ISurfaceControlViewHostParent; import android.window.ISurfaceSyncGroup; /** @@ -34,4 +35,9 @@ interface ISurfaceControlViewHost { oneway void onDispatchDetachedFromWindow(); oneway void onInsetsChanged(in InsetsState state, in Rect insetFrame); ISurfaceSyncGroup getSurfaceSyncGroup(); + /** + * Attaches the parent interface so the embedded content can communicate back to the parent. + * If null is passed in, it will remove the parent interface and no more updates will be sent. + */ + oneway void attachParentInterface(in @nullable ISurfaceControlViewHostParent parentInterface); } diff --git a/core/java/android/view/ISurfaceControlViewHostParent.aidl b/core/java/android/view/ISurfaceControlViewHostParent.aidl new file mode 100644 index 000000000000..f42e00148587 --- /dev/null +++ b/core/java/android/view/ISurfaceControlViewHostParent.aidl @@ -0,0 +1,27 @@ +/* +** Copyright 2023, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +package android.view; + +import android.view.WindowManager; + +/** + * API from embedded content in SurfaceControlViewHost to parent containing the embedded. + * {@hide} + */ +oneway interface ISurfaceControlViewHostParent { + void updateParams(in WindowManager.LayoutParams[] childAttrs); +} diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl index 209729b2a38b..6d96bb9423c5 100644 --- a/core/java/android/view/IWindowManager.aidl +++ b/core/java/android/view/IWindowManager.aidl @@ -471,10 +471,23 @@ interface IWindowManager * Requests Keyboard Shortcuts from the displayed window. * * @param receiver The receiver to deliver the results to. + * @param deviceId The deviceId of KeyEvent by which this request is triggered, or -1 if it's + * not triggered by a KeyEvent. + * @see #requestImeKeyboardShortcuts(IResultReceiver, int) */ void requestAppKeyboardShortcuts(IResultReceiver receiver, int deviceId); /** + * Requests Keyboard Shortcuts from currently selected IME. + * + * @param receiver The receiver to deliver the results to. + * @param deviceId The deviceId of KeyEvent by which this request is triggered, or -1 if it's + * not triggered by a KeyEvent. + * @see #requestAppKeyboardShortcuts(IResultReceiver, int) + */ + void requestImeKeyboardShortcuts(IResultReceiver receiver, int deviceId); + + /** * Retrieves the current stable insets from the primary display. */ @UnsupportedAppUsage diff --git a/core/java/android/view/InsetsFrameProvider.java b/core/java/android/view/InsetsFrameProvider.java index 37b6c77e3c74..83bdb087a539 100644 --- a/core/java/android/view/InsetsFrameProvider.java +++ b/core/java/android/view/InsetsFrameProvider.java @@ -164,6 +164,10 @@ public class InsetsFrameProvider implements Parcelable { return mFlags; } + public boolean hasFlags(@Flags int mask) { + return (mFlags & mask) == mask; + } + public InsetsFrameProvider setInsetsSize(Insets insetsSize) { mInsetsSize = insetsSize; return this; diff --git a/core/java/android/view/InsetsSource.java b/core/java/android/view/InsetsSource.java index 9fc42fff7084..e10184976abe 100644 --- a/core/java/android/view/InsetsSource.java +++ b/core/java/android/view/InsetsSource.java @@ -58,9 +58,20 @@ public class InsetsSource implements Parcelable { */ public static final int FLAG_SUPPRESS_SCRIM = 1; + /** + * Controls whether the insets frame will be used to move {@link RoundedCorner} inward with the + * insets frame size when calculating the rounded corner insets to other windows. + * + * For example, task bar will draw fake rounded corners above itself, so we need to move the + * rounded corner up by the task bar insets size to make other windows see a rounded corner + * above the task bar. + */ + public static final int FLAG_INSETS_ROUNDED_CORNER = 1 << 1; + @Retention(RetentionPolicy.SOURCE) @IntDef(flag = true, prefix = "FLAG_", value = { FLAG_SUPPRESS_SCRIM, + FLAG_INSETS_ROUNDED_CORNER, }) public @interface Flags {} @@ -78,7 +89,6 @@ public class InsetsSource implements Parcelable { private @Nullable Rect mVisibleFrame; private boolean mVisible; - private boolean mInsetsRoundedCornerFrame; private final Rect mTmpFrame = new Rect(); @@ -98,7 +108,6 @@ public class InsetsSource implements Parcelable { ? new Rect(other.mVisibleFrame) : null; mFlags = other.mFlags; - mInsetsRoundedCornerFrame = other.mInsetsRoundedCornerFrame; } public void set(InsetsSource other) { @@ -108,7 +117,6 @@ public class InsetsSource implements Parcelable { ? new Rect(other.mVisibleFrame) : null; mFlags = other.mFlags; - mInsetsRoundedCornerFrame = other.mInsetsRoundedCornerFrame; } public InsetsSource setFrame(int left, int top, int right, int bottom) { @@ -136,6 +144,11 @@ public class InsetsSource implements Parcelable { return this; } + public InsetsSource setFlags(@Flags int flags, @Flags int mask) { + mFlags = (mFlags & ~mask) | (flags & mask); + return this; + } + public int getId() { return mId; } @@ -160,20 +173,15 @@ public class InsetsSource implements Parcelable { return mFlags; } + public boolean hasFlags(int flags) { + return (mFlags & flags) == flags; + } + boolean isUserControllable() { // If mVisibleFrame is null, it will be the same area as mFrame. return mVisibleFrame == null || !mVisibleFrame.isEmpty(); } - public boolean insetsRoundedCornerFrame() { - return mInsetsRoundedCornerFrame; - } - - public InsetsSource setInsetsRoundedCornerFrame(boolean insetsRoundedCornerFrame) { - mInsetsRoundedCornerFrame = insetsRoundedCornerFrame; - return this; - } - /** * Calculates the insets this source will cause to a client window. * @@ -317,6 +325,9 @@ public class InsetsSource implements Parcelable { if ((flags & FLAG_SUPPRESS_SCRIM) != 0) { joiner.add("SUPPRESS_SCRIM"); } + if ((flags & FLAG_INSETS_ROUNDED_CORNER) != 0) { + joiner.add("INSETS_ROUNDED_CORNER"); + } return joiner.toString(); } @@ -347,7 +358,6 @@ public class InsetsSource implements Parcelable { } pw.print(" visible="); pw.print(mVisible); pw.print(" flags="); pw.print(flagsToString(mFlags)); - pw.print(" insetsRoundedCornerFrame="); pw.print(mInsetsRoundedCornerFrame); pw.println(); } @@ -372,14 +382,12 @@ public class InsetsSource implements Parcelable { if (mFlags != that.mFlags) return false; if (excludeInvisibleImeFrames && !mVisible && mType == WindowInsets.Type.ime()) return true; if (!Objects.equals(mVisibleFrame, that.mVisibleFrame)) return false; - if (mInsetsRoundedCornerFrame != that.mInsetsRoundedCornerFrame) return false; return mFrame.equals(that.mFrame); } @Override public int hashCode() { - return Objects.hash(mId, mType, mFrame, mVisibleFrame, mVisible, mFlags, - mInsetsRoundedCornerFrame); + return Objects.hash(mId, mType, mFrame, mVisibleFrame, mVisible, mFlags); } public InsetsSource(Parcel in) { @@ -393,7 +401,6 @@ public class InsetsSource implements Parcelable { } mVisible = in.readBoolean(); mFlags = in.readInt(); - mInsetsRoundedCornerFrame = in.readBoolean(); } @Override @@ -414,7 +421,6 @@ public class InsetsSource implements Parcelable { } dest.writeBoolean(mVisible); dest.writeInt(mFlags); - dest.writeBoolean(mInsetsRoundedCornerFrame); } @Override @@ -424,7 +430,6 @@ public class InsetsSource implements Parcelable { + " mFrame=" + mFrame.toShortString() + " mVisible=" + mVisible + " mFlags=[" + flagsToString(mFlags) + "]" - + (mInsetsRoundedCornerFrame ? " insetsRoundedCornerFrame" : "") + "}"; } diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java index b55d25aa7013..dceae90822b8 100644 --- a/core/java/android/view/InsetsState.java +++ b/core/java/android/view/InsetsState.java @@ -16,6 +16,7 @@ package android.view; +import static android.view.InsetsSource.FLAG_INSETS_ROUNDED_CORNER; import static android.view.InsetsStateProto.DISPLAY_CUTOUT; import static android.view.InsetsStateProto.DISPLAY_FRAME; import static android.view.InsetsStateProto.SOURCES; @@ -220,7 +221,7 @@ public class InsetsState implements Parcelable { final Rect roundedCornerFrame = new Rect(mRoundedCornerFrame); for (int i = mSources.size() - 1; i >= 0; i--) { final InsetsSource source = mSources.valueAt(i); - if (source.insetsRoundedCornerFrame()) { + if (source.hasFlags(FLAG_INSETS_ROUNDED_CORNER)) { final Insets insets = source.calculateInsets(roundedCornerFrame, false); roundedCornerFrame.inset(insets); } diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java index 1af8ca2efe11..c703af5af4e6 100644 --- a/core/java/android/view/MotionEvent.java +++ b/core/java/android/view/MotionEvent.java @@ -1285,6 +1285,8 @@ public final class MotionEvent extends InputEvent implements Parcelable { * </ul> * These values are relative to the state from the last event, not accumulated, so developers * should make sure to process this axis value for all batched historical events. + * <p> + * This axis is only set on the first pointer in a motion event. */ public static final int AXIS_GESTURE_X_OFFSET = 48; @@ -1304,6 +1306,8 @@ public final class MotionEvent extends InputEvent implements Parcelable { * </ul> * These values are relative to the state from the last event, not accumulated, so developers * should make sure to process this axis value for all batched historical events. + * <p> + * This axis is only set on the first pointer in a motion event. */ public static final int AXIS_GESTURE_SCROLL_X_DISTANCE = 50; @@ -1324,14 +1328,29 @@ public final class MotionEvent extends InputEvent implements Parcelable { * </ul> * These values are relative to the state from the last event, not accumulated, so developers * should make sure to process this axis value for all batched historical events. + * <p> + * This axis is only set on the first pointer in a motion event. */ public static final int AXIS_GESTURE_PINCH_SCALE_FACTOR = 52; + /** + * Axis constant: the number of fingers being used in a multi-finger swipe gesture. + * <p> + * <ul> + * <li>For a touch pad, reports the number of fingers being used in a multi-finger swipe gesture + * (with CLASSIFICATION_MULTI_FINGER_SWIPE). + * </ul> + * <p> + * Since CLASSIFICATION_MULTI_FINGER_SWIPE is a hidden API, so is this axis. It is only set on + * the first pointer in a motion event. + * @hide + */ + public static final int AXIS_GESTURE_SWIPE_FINGER_COUNT = 53; + // NOTE: If you add a new axis here you must also add it to: // frameworks/native/include/android/input.h // frameworks/native/libs/input/InputEventLabels.cpp - // platform/cts/tests/tests/view/src/android/view/cts/MotionEventTest.java - // (testAxisFromToString) + // cts/tests/tests/view/src/android/view/cts/MotionEventTest.java (testAxisFromToString) // Symbolic names of all axes. private static final SparseArray<String> AXIS_SYMBOLIC_NAMES = new SparseArray<String>(); @@ -1387,6 +1406,7 @@ public final class MotionEvent extends InputEvent implements Parcelable { names.append(AXIS_GESTURE_SCROLL_X_DISTANCE, "AXIS_GESTURE_SCROLL_X_DISTANCE"); names.append(AXIS_GESTURE_SCROLL_Y_DISTANCE, "AXIS_GESTURE_SCROLL_Y_DISTANCE"); names.append(AXIS_GESTURE_PINCH_SCALE_FACTOR, "AXIS_GESTURE_PINCH_SCALE_FACTOR"); + names.append(AXIS_GESTURE_SWIPE_FINGER_COUNT, "AXIS_GESTURE_SWIPE_FINGER_COUNT"); } /** diff --git a/core/java/android/view/SurfaceControlViewHost.java b/core/java/android/view/SurfaceControlViewHost.java index c8cf7d9a5194..effc127dabd2 100644 --- a/core/java/android/view/SurfaceControlViewHost.java +++ b/core/java/android/view/SurfaceControlViewHost.java @@ -54,7 +54,7 @@ public class SurfaceControlViewHost { private final static String TAG = "SurfaceControlViewHost"; private final ViewRootImpl mViewRoot; private final CloseGuard mCloseGuard = CloseGuard.get(); - private WindowlessWindowManager mWm; + private final WindowlessWindowManager mWm; private SurfaceControl mSurfaceControl; private IAccessibilityEmbeddedConnection mAccessibilityEmbeddedConnection; @@ -67,9 +67,7 @@ public class SurfaceControlViewHost { return; } mViewRoot.mHandler.post(() -> { - if (mWm != null) { - mWm.setConfiguration(configuration); - } + mWm.setConfiguration(configuration); if (mViewRoot != null) { mViewRoot.forceWmRelayout(); } @@ -116,6 +114,11 @@ public class SurfaceControlViewHost { } return null; } + + @Override + public void attachParentInterface(@Nullable ISurfaceControlViewHostParent parentInterface) { + mViewRoot.mHandler.post(() -> mWm.setParentInterface(parentInterface)); + } } private ISurfaceControlViewHost mRemoteInterface = new ISurfaceControlViewHostImpl(); @@ -148,10 +151,11 @@ public class SurfaceControlViewHost { private SurfaceControl mSurfaceControl; private final IAccessibilityEmbeddedConnection mAccessibilityEmbeddedConnection; private final IBinder mInputToken; + @NonNull private final ISurfaceControlViewHost mRemoteInterface; SurfacePackage(SurfaceControl sc, IAccessibilityEmbeddedConnection connection, - IBinder inputToken, ISurfaceControlViewHost ri) { + IBinder inputToken, @NonNull ISurfaceControlViewHost ri) { mSurfaceControl = sc; mAccessibilityEmbeddedConnection = connection; mInputToken = inputToken; @@ -213,6 +217,7 @@ public class SurfaceControlViewHost { /** * @hide */ + @NonNull public ISurfaceControlViewHost getRemoteInterface() { return mRemoteInterface; } diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java index 0e4cf89e7772..1e268bed3b17 100644 --- a/core/java/android/view/SurfaceView.java +++ b/core/java/android/view/SurfaceView.java @@ -16,6 +16,7 @@ package android.view; +import static android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON; import static android.view.WindowManagerPolicyConstants.APPLICATION_MEDIA_OVERLAY_SUBLAYER; import static android.view.WindowManagerPolicyConstants.APPLICATION_MEDIA_SUBLAYER; import static android.view.WindowManagerPolicyConstants.APPLICATION_PANEL_SUBLAYER; @@ -40,6 +41,7 @@ import android.os.Build; import android.os.Handler; import android.os.IBinder; import android.os.Looper; +import android.os.RemoteException; import android.os.SystemClock; import android.util.ArraySet; import android.util.AttributeSet; @@ -54,6 +56,8 @@ import com.android.internal.view.SurfaceCallbackHelper; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; +import java.util.Arrays; +import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.CountDownLatch; import java.util.concurrent.locks.ReentrantLock; import java.util.function.Consumer; @@ -302,6 +306,26 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall private SurfaceControl mBlastSurfaceControl; private BLASTBufferQueue mBlastBufferQueue; + private final ConcurrentLinkedQueue<WindowManager.LayoutParams> mEmbeddedWindowParams = + new ConcurrentLinkedQueue<>(); + + private final ISurfaceControlViewHostParent mSurfaceControlViewHostParent = + new ISurfaceControlViewHostParent.Stub() { + @Override + public void updateParams(WindowManager.LayoutParams[] childAttrs) { + mEmbeddedWindowParams.clear(); + mEmbeddedWindowParams.addAll(Arrays.asList(childAttrs)); + + if (isAttachedToWindow()) { + runOnUiThread(() -> { + if (mParent != null) { + mParent.recomputeViewAttributes(SurfaceView.this); + } + }); + } + } + }; + public SurfaceView(Context context) { this(context, null); } @@ -801,9 +825,18 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall mBlastSurfaceControl = null; } - if (releaseSurfacePackage && mSurfacePackage != null) { - mSurfacePackage.release(); - mSurfacePackage = null; + if (mSurfacePackage != null) { + try { + mSurfacePackage.getRemoteInterface().attachParentInterface(null); + mEmbeddedWindowParams.clear(); + } catch (RemoteException e) { + Log.d(TAG, "Failed to remove parent interface from SCVH. Likely SCVH is " + + "already dead"); + } + if (releaseSurfacePackage) { + mSurfacePackage.release(); + mSurfacePackage = null; + } } applyTransactionOnVriDraw(transaction); @@ -1854,6 +1887,12 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall applyTransactionOnVriDraw(transaction); } mSurfacePackage = p; + try { + mSurfacePackage.getRemoteInterface().attachParentInterface( + mSurfaceControlViewHostParent); + } catch (RemoteException e) { + Log.d(TAG, "Failed to attach parent interface to SCVH. Likely SCVH is already dead."); + } if (isFocused()) { requestEmbeddedFocus(true); @@ -2014,4 +2053,19 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall mBlastBufferQueue.mergeWithNextTransaction(transaction, frameNumber); } } + + @Override + void performCollectViewAttributes(AttachInfo attachInfo, int visibility) { + super.performCollectViewAttributes(attachInfo, visibility); + if (mEmbeddedWindowParams.isEmpty()) { + return; + } + + for (WindowManager.LayoutParams embeddedWindowAttr : mEmbeddedWindowParams) { + if ((embeddedWindowAttr.flags & FLAG_KEEP_SCREEN_ON) == FLAG_KEEP_SCREEN_ON) { + attachInfo.mKeepScreenOn = true; + break; + } + } + } } diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index d4578475e9c3..01a99b92a055 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -2043,7 +2043,10 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager final float x = event.getXDispatchLocation(pointerIndex); final float y = event.getYDispatchLocation(pointerIndex); if (isOnScrollbarThumb(x, y) || isDraggingScrollBar()) { - return PointerIcon.getSystemIcon(mContext, PointerIcon.TYPE_ARROW); + // Return null here so that it fallbacks to the default PointerIcon for the source + // device. For mouse, the default PointerIcon is PointerIcon.TYPE_ARROW. + // For stylus, the default PointerIcon is PointerIcon.TYPE_NULL. + return null; } // Check what the child under the pointer says about the pointer. final int childrenCount = mChildrenCount; diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index a28be4be24ad..583b76a4d8d4 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -1900,9 +1900,10 @@ public final class ViewRootImpl implements ViewParent, && !Objects.equals(mTmpFrames.attachedFrame, attachedFrame); final boolean displayChanged = mDisplay.getDisplayId() != displayId; final boolean compatScaleChanged = mTmpFrames.compatScale != compatScale; + final boolean dragResizingChanged = mPendingDragResizing != dragResizing; if (msg == MSG_RESIZED && !frameChanged && !configChanged && !attachedFrameChanged && !displayChanged && !forceNextWindowRelayout - && !compatScaleChanged) { + && !compatScaleChanged && !dragResizingChanged) { return; } @@ -8308,10 +8309,20 @@ public final class ViewRootImpl implements ViewParent, } } - if (mSurfaceControl.isValid() && !HardwareRenderer.isDrawingEnabled()) { - // When drawing is disabled the window layer won't have a valid buffer. - // Set a window crop so input can get delivered to the window. - mTransaction.setWindowCrop(mSurfaceControl, mSurfaceSize.x, mSurfaceSize.y).apply(); + if (mSurfaceControl.isValid()) { + if (mPendingDragResizing && !mSurfaceSize.equals( + mWinFrameInScreen.width(), mWinFrameInScreen.height())) { + // During drag-resize, a single fullscreen-sized surface is reused for optimization. + // Crop to the content size instead of the surface size to avoid exposing garbage + // content that is still on the surface from previous re-layouts (e.g. when + // resizing to a larger size). + mTransaction.setWindowCrop(mSurfaceControl, + mWinFrameInScreen.width(), mWinFrameInScreen.height()); + } else if (!HardwareRenderer.isDrawingEnabled()) { + // When drawing is disabled the window layer won't have a valid buffer. + // Set a window crop so input can get delivered to the window. + mTransaction.setWindowCrop(mSurfaceControl, mSurfaceSize.x, mSurfaceSize.y).apply(); + } } mLastTransformHint = transformHint; @@ -11553,4 +11564,17 @@ public final class ViewRootImpl implements ViewParent, mChildBoundingInsetsChanged = true; scheduleTraversals(); } + + @Override + public void addTrustedPresentationCallback(@NonNull SurfaceControl.Transaction t, + @NonNull SurfaceControl.TrustedPresentationThresholds thresholds, + @NonNull Executor executor, @NonNull Consumer<Boolean> listener) { + t.setTrustedPresentationCallback(getSurfaceControl(), thresholds, executor, listener); + } + + @Override + public void removeTrustedPresentationCallback(@NonNull SurfaceControl.Transaction t, + @NonNull Consumer<Boolean> listener) { + t.clearTrustedPresentationCallback(getSurfaceControl()); + } } diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java index 21fe87f42e1b..2f04b0c695da 100644 --- a/core/java/android/view/Window.java +++ b/core/java/android/view/Window.java @@ -658,6 +658,12 @@ public abstract class Window { void updateStatusBarColor(int color); /** + * Update the status bar appearance. + */ + + void updateStatusBarAppearance(int appearance); + + /** * Update the navigation bar color to a forced one. */ void updateNavigationBarColor(int color); @@ -1039,6 +1045,9 @@ public abstract class Window { if (mDecorCallback != null) { mDecorCallback.onSystemBarAppearanceChanged(appearance); } + if (mWindowControllerCallback != null) { + mWindowControllerCallback.updateStatusBarAppearance(appearance); + } } /** @hide */ @@ -1482,6 +1491,11 @@ public abstract class Window { } /** @hide */ + public boolean shouldCloseOnTouchOutside() { + return mCloseOnTouchOutside; + } + + /** @hide */ @SuppressWarnings("HiddenAbstractMethod") @UnsupportedAppUsage public abstract void alwaysReadCloseOnTouchAttr(); diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index a91781580ac4..d702367965a1 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -1384,15 +1384,28 @@ public interface WindowManager extends ViewManager { "android.window.PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED"; /** - * Request for keyboard shortcuts to be retrieved asynchronously. + * Request for app's keyboard shortcuts to be retrieved asynchronously. * * @param receiver The callback to be triggered when the result is ready. + * @param deviceId The deviceId of KeyEvent by which this request is triggered, or -1 if it's + * not triggered by a KeyEvent. * * @hide */ public void requestAppKeyboardShortcuts(final KeyboardShortcutsReceiver receiver, int deviceId); /** + * Request for ime's keyboard shortcuts to be retrieved asynchronously. + * + * @param receiver The callback to be triggered when the result is ready. + * @param deviceId The deviceId of KeyEvent by which this request is triggered, or -1 if it's + * not triggered by a KeyEvent. + * + * @hide + */ + default void requestImeKeyboardShortcuts(KeyboardShortcutsReceiver receiver, int deviceId) {}; + + /** * Return the touch region for the current IME window, or an empty region if there is none. * * @return The region of the IME that is accepting touch inputs, or null if there is no IME, no @@ -4294,17 +4307,6 @@ public interface WindowManager extends ViewManager { public InsetsFrameProvider[] providedInsets; /** - * If specified, the frame that used to calculate relative {@link RoundedCorner} will be - * the window frame of this window minus the insets that this window provides. - * - * Task bar will draw fake rounded corners above itself, so we need this insets to calculate - * correct rounded corners for this window. - * - * @hide - */ - public boolean insetsRoundedCornerFrame = false; - - /** * {@link LayoutParams} to be applied to the window when layout with a assigned rotation. * This will make layout during rotation change smoothly. * @@ -4760,7 +4762,6 @@ public interface WindowManager extends ViewManager { out.writeBoolean(mFitInsetsIgnoringVisibility); out.writeBoolean(preferMinimalPostProcessing); out.writeInt(mBlurBehindRadius); - out.writeBoolean(insetsRoundedCornerFrame); out.writeBoolean(mWallpaperTouchEventsEnabled); out.writeTypedArray(providedInsets, 0 /* parcelableFlags */); checkNonRecursiveParams(); @@ -4832,7 +4833,6 @@ public interface WindowManager extends ViewManager { mFitInsetsIgnoringVisibility = in.readBoolean(); preferMinimalPostProcessing = in.readBoolean(); mBlurBehindRadius = in.readInt(); - insetsRoundedCornerFrame = in.readBoolean(); mWallpaperTouchEventsEnabled = in.readBoolean(); providedInsets = in.createTypedArray(InsetsFrameProvider.CREATOR); paramsForRotation = in.createTypedArray(LayoutParams.CREATOR); @@ -5140,11 +5140,6 @@ public interface WindowManager extends ViewManager { changes |= LAYOUT_CHANGED; } - if (insetsRoundedCornerFrame != o.insetsRoundedCornerFrame) { - insetsRoundedCornerFrame = o.insetsRoundedCornerFrame; - changes |= LAYOUT_CHANGED; - } - if (paramsForRotation != o.paramsForRotation) { if ((changes & LAYOUT_CHANGED) == 0) { if (paramsForRotation != null && o.paramsForRotation != null @@ -5382,10 +5377,6 @@ public interface WindowManager extends ViewManager { sb.append(prefix).append(" ").append(providedInsets[i]); } } - if (insetsRoundedCornerFrame) { - sb.append(" insetsRoundedCornerFrame="); - sb.append(insetsRoundedCornerFrame); - } if (paramsForRotation != null && paramsForRotation.length != 0) { sb.append(System.lineSeparator()); sb.append(prefix).append(" paramsForRotation:"); diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java index df3e0bb74292..b57163c4e435 100644 --- a/core/java/android/view/WindowManagerImpl.java +++ b/core/java/android/view/WindowManagerImpl.java @@ -215,14 +215,36 @@ public final class WindowManagerImpl implements WindowManager { @Override public void send(int resultCode, Bundle resultData) throws RemoteException { List<KeyboardShortcutGroup> result = - resultData.getParcelableArrayList(PARCEL_KEY_SHORTCUTS_ARRAY, android.view.KeyboardShortcutGroup.class); + resultData.getParcelableArrayList(PARCEL_KEY_SHORTCUTS_ARRAY, + android.view.KeyboardShortcutGroup.class); receiver.onKeyboardShortcutsReceived(result); } }; try { WindowManagerGlobal.getWindowManagerService() - .requestAppKeyboardShortcuts(resultReceiver, deviceId); + .requestAppKeyboardShortcuts(resultReceiver, deviceId); } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + @Override + public void requestImeKeyboardShortcuts( + final KeyboardShortcutsReceiver receiver, int deviceId) { + IResultReceiver resultReceiver = new IResultReceiver.Stub() { + @Override + public void send(int resultCode, Bundle resultData) throws RemoteException { + List<KeyboardShortcutGroup> result = + resultData.getParcelableArrayList(PARCEL_KEY_SHORTCUTS_ARRAY, + android.view.KeyboardShortcutGroup.class); + receiver.onKeyboardShortcutsReceived(result); + } + }; + try { + WindowManagerGlobal.getWindowManagerService() + .requestImeKeyboardShortcuts(resultReceiver, deviceId); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); } } diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java index 96bfb2d9e1e6..7d3d283a45f2 100644 --- a/core/java/android/view/WindowlessWindowManager.java +++ b/core/java/android/view/WindowlessWindowManager.java @@ -49,7 +49,8 @@ public class WindowlessWindowManager implements IWindowSession { private class State { SurfaceControl mSurfaceControl; - WindowManager.LayoutParams mParams = new WindowManager.LayoutParams(); + final WindowManager.LayoutParams mParams = new WindowManager.LayoutParams(); + final WindowManager.LayoutParams mLastReportedParams = new WindowManager.LayoutParams(); int mDisplayId; IBinder mInputChannelToken; Region mInputRegion; @@ -94,6 +95,8 @@ public class WindowlessWindowManager implements IWindowSession { private final MergedConfiguration mTmpConfig = new MergedConfiguration(); private final WindowlessWindowLayout mLayout = new WindowlessWindowLayout(); + private ISurfaceControlViewHostParent mParentInterface; + public WindowlessWindowManager(Configuration c, SurfaceControl rootSurface, IBinder hostInputToken) { mRootSurface = rootSurface; @@ -244,6 +247,7 @@ public class WindowlessWindowManager implements IWindowSession { final int res = WindowManagerGlobal.ADD_OKAY | WindowManagerGlobal.ADD_FLAG_APP_VISIBLE | WindowManagerGlobal.ADD_FLAG_USE_BLAST; + sendLayoutParamsToParent(); // Include whether the window is in touch mode. return isInTouchModeInternal(displayId) ? res | WindowManagerGlobal.ADD_FLAG_IN_TOUCH_MODE : res; @@ -425,6 +429,7 @@ public class WindowlessWindowManager implements IWindowSession { outInsetsState.set(mInsetsState); } + sendLayoutParamsToParent(); return 0; } @@ -645,4 +650,45 @@ public class WindowlessWindowManager implements IWindowSession { " we shouldn't get here!"); return false; } + + void setParentInterface(@Nullable ISurfaceControlViewHostParent parentInterface) { + IBinder oldInterface = mParentInterface == null ? null : mParentInterface.asBinder(); + IBinder newInterface = parentInterface == null ? null : parentInterface.asBinder(); + // If the parent interface has changed, it needs to clear the last reported params so it + // will update the new interface with the params. + if (oldInterface != newInterface) { + clearLastReportedParams(); + } + mParentInterface = parentInterface; + sendLayoutParamsToParent(); + } + + private void clearLastReportedParams() { + WindowManager.LayoutParams emptyParam = new WindowManager.LayoutParams(); + for (State windowInfo : mStateForWindow.values()) { + windowInfo.mLastReportedParams.copyFrom(emptyParam); + } + } + + private void sendLayoutParamsToParent() { + if (mParentInterface == null) { + return; + } + WindowManager.LayoutParams[] params = + new WindowManager.LayoutParams[mStateForWindow.size()]; + int index = 0; + boolean hasChanges = false; + for (State windowInfo : mStateForWindow.values()) { + int changes = windowInfo.mLastReportedParams.copyFrom(windowInfo.mParams); + hasChanges |= (changes != 0); + params[index++] = windowInfo.mParams; + } + + if (hasChanges) { + try { + mParentInterface.updateParams(params); + } catch (RemoteException e) { + } + } + } } diff --git a/core/java/android/view/contentcapture/ContentCaptureManager.java b/core/java/android/view/contentcapture/ContentCaptureManager.java index 668351b949c1..c9afdc0ad074 100644 --- a/core/java/android/view/contentcapture/ContentCaptureManager.java +++ b/core/java/android/view/contentcapture/ContentCaptureManager.java @@ -52,6 +52,7 @@ import android.view.contentcapture.ContentCaptureSession.FlushReason; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.RingBuffer; import com.android.internal.util.SyncResultReceiver; import java.io.PrintWriter; @@ -352,6 +353,30 @@ public final class ContentCaptureManager { public static final String DEVICE_CONFIG_PROPERTY_DISABLE_FLUSH_FOR_VIEW_TREE_APPEARING = "disable_flush_for_view_tree_appearing"; + /** + * Enables the content protection receiver. + * + * @hide + */ + public static final String DEVICE_CONFIG_PROPERTY_ENABLE_CONTENT_PROTECTION_RECEIVER = + "enable_content_protection_receiver"; + + /** + * Sets the size of the app blocklist for the content protection flow. + * + * @hide + */ + public static final String DEVICE_CONFIG_PROPERTY_CONTENT_PROTECTION_APPS_BLOCKLIST_SIZE = + "content_protection_apps_blocklist_size"; + + /** + * Sets the size of the in-memory ring buffer for the content protection flow. + * + * @hide + */ + public static final String DEVICE_CONFIG_PROPERTY_CONTENT_PROTECTION_BUFFER_SIZE = + "content_protection_buffer_size"; + /** @hide */ @TestApi public static final int LOGGING_LEVEL_OFF = 0; @@ -384,6 +409,14 @@ public final class ContentCaptureManager { public static final int DEFAULT_LOG_HISTORY_SIZE = 10; /** @hide */ public static final boolean DEFAULT_DISABLE_FLUSH_FOR_VIEW_TREE_APPEARING = false; + /** @hide */ + public static final boolean DEFAULT_ENABLE_CONTENT_CAPTURE_RECEIVER = true; + /** @hide */ + public static final boolean DEFAULT_ENABLE_CONTENT_PROTECTION_RECEIVER = false; + /** @hide */ + public static final int DEFAULT_CONTENT_PROTECTION_APPS_BLOCKLIST_SIZE = 1000; + /** @hide */ + public static final int DEFAULT_CONTENT_PROTECTION_BUFFER_SIZE = 150; private final Object mLock = new Object(); @@ -414,6 +447,9 @@ public final class ContentCaptureManager { @Nullable // set on-demand by addDumpable() private Dumper mDumpable; + // Created here in order to live across activity and session changes + @Nullable private final RingBuffer<ContentCaptureEvent> mContentProtectionEventBuffer; + /** @hide */ public interface ContentCaptureClient { /** @@ -424,12 +460,15 @@ public final class ContentCaptureManager { } /** @hide */ - static class StrippedContext { - final String mPackageName; - final String mContext; + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) + public static class StrippedContext { + @NonNull final String mPackageName; + @NonNull final String mContext; final @UserIdInt int mUserId; - private StrippedContext(Context context) { + /** @hide */ + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) + public StrippedContext(@NonNull Context context) { mPackageName = context.getPackageName(); mContext = context.toString(); mUserId = context.getUserId(); @@ -440,6 +479,7 @@ public final class ContentCaptureManager { return mContext; } + @NonNull public String getPackageName() { return mPackageName; } @@ -469,6 +509,16 @@ public final class ContentCaptureManager { mHandler = Handler.createAsync(Looper.getMainLooper()); mDataShareAdapterResourceManager = new LocalDataShareAdapterResourceManager(); + + if (mOptions.contentProtectionOptions.enableReceiver + && mOptions.contentProtectionOptions.bufferSize > 0) { + mContentProtectionEventBuffer = + new RingBuffer( + ContentCaptureEvent.class, + mOptions.contentProtectionOptions.bufferSize); + } else { + mContentProtectionEventBuffer = null; + } } /** @@ -837,6 +887,13 @@ public final class ContentCaptureManager { activity.addDumpable(mDumpable); } + /** @hide */ + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) + @Nullable + public RingBuffer<ContentCaptureEvent> getContentProtectionEventBuffer() { + return mContentProtectionEventBuffer; + } + // NOTE: ContentCaptureManager cannot implement it directly as it would be exposed as public API private final class Dumper implements Dumpable { @Override diff --git a/core/java/android/view/contentcapture/ContentCaptureSession.java b/core/java/android/view/contentcapture/ContentCaptureSession.java index 62044aa78213..dc3d32317ded 100644 --- a/core/java/android/view/contentcapture/ContentCaptureSession.java +++ b/core/java/android/view/contentcapture/ContentCaptureSession.java @@ -191,20 +191,22 @@ public abstract class ContentCaptureSession implements AutoCloseable { static final long NOTIFY_NODES_DISAPPEAR_NOW_SENDS_TREE_EVENTS = 258825825L; /** @hide */ - @IntDef(prefix = { "FLUSH_REASON_" }, value = { - FLUSH_REASON_FULL, - FLUSH_REASON_VIEW_ROOT_ENTERED, - FLUSH_REASON_SESSION_STARTED, - FLUSH_REASON_SESSION_FINISHED, - FLUSH_REASON_IDLE_TIMEOUT, - FLUSH_REASON_TEXT_CHANGE_TIMEOUT, - FLUSH_REASON_SESSION_CONNECTED, - FLUSH_REASON_FORCE_FLUSH, - FLUSH_REASON_VIEW_TREE_APPEARING, - FLUSH_REASON_VIEW_TREE_APPEARED - }) + @IntDef( + prefix = {"FLUSH_REASON_"}, + value = { + FLUSH_REASON_FULL, + FLUSH_REASON_VIEW_ROOT_ENTERED, + FLUSH_REASON_SESSION_STARTED, + FLUSH_REASON_SESSION_FINISHED, + FLUSH_REASON_IDLE_TIMEOUT, + FLUSH_REASON_TEXT_CHANGE_TIMEOUT, + FLUSH_REASON_SESSION_CONNECTED, + FLUSH_REASON_FORCE_FLUSH, + FLUSH_REASON_VIEW_TREE_APPEARING, + FLUSH_REASON_VIEW_TREE_APPEARED + }) @Retention(RetentionPolicy.SOURCE) - public @interface FlushReason{} + public @interface FlushReason {} private final Object mLock = new Object(); @@ -686,7 +688,7 @@ public abstract class ContentCaptureSession implements AutoCloseable { case FLUSH_REASON_VIEW_TREE_APPEARED: return "VIEW_TREE_APPEARED"; default: - return "UNKOWN-" + reason; + return "UNKNOWN-" + reason; } } diff --git a/core/java/android/view/contentcapture/IContentCaptureManager.aidl b/core/java/android/view/contentcapture/IContentCaptureManager.aidl index a64111069c9b..14879977d2a5 100644 --- a/core/java/android/view/contentcapture/IContentCaptureManager.aidl +++ b/core/java/android/view/contentcapture/IContentCaptureManager.aidl @@ -17,6 +17,7 @@ package android.view.contentcapture; import android.content.ComponentName; +import android.content.pm.ParceledListSlice; import android.view.contentcapture.ContentCaptureContext; import android.view.contentcapture.ContentCaptureEvent; import android.view.contentcapture.DataRemovalRequest; @@ -108,4 +109,9 @@ oneway interface IContentCaptureManager { */ void registerContentCaptureOptionsCallback(String packageName, in IContentCaptureOptionsCallback callback); + + /** + * Notifies the system server that a login was detected. + */ + void onLoginDetected(in ParceledListSlice<ContentCaptureEvent> events); } diff --git a/core/java/android/view/contentcapture/MainContentCaptureSession.java b/core/java/android/view/contentcapture/MainContentCaptureSession.java index efd50e7d2343..2241fd5dc37a 100644 --- a/core/java/android/view/contentcapture/MainContentCaptureSession.java +++ b/core/java/android/view/contentcapture/MainContentCaptureSession.java @@ -52,8 +52,10 @@ import android.util.Log; import android.util.TimeUtils; import android.view.autofill.AutofillId; import android.view.contentcapture.ViewNode.ViewStructureImpl; +import android.view.contentprotection.ContentProtectionEventProcessor; import android.view.inputmethod.BaseInputConnection; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.os.IResultReceiver; import java.io.PrintWriter; @@ -118,9 +120,13 @@ public final class MainContentCaptureSession extends ContentCaptureSession { /** * Direct interface to the service binder object - it's used to send the events, including the * last ones (when the session is finished) + * + * @hide */ - @NonNull - private IContentCaptureDirectManager mDirectServiceInterface; + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) + @Nullable + public IContentCaptureDirectManager mDirectServiceInterface; + @Nullable private DeathRecipient mDirectServiceVulture; @@ -131,14 +137,19 @@ public final class MainContentCaptureSession extends ContentCaptureSession { @Nullable private IBinder mShareableActivityToken; + /** @hide */ + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) @Nullable - private ComponentName mComponentName; + public ComponentName mComponentName; /** * List of events held to be sent as a batch. + * + * @hide */ + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) @Nullable - private ArrayList<ContentCaptureEvent> mEvents; + public ArrayList<ContentCaptureEvent> mEvents; // Used just for debugging purposes (on dump) private long mNextFlush; @@ -157,6 +168,11 @@ public final class MainContentCaptureSession extends ContentCaptureSession { @NonNull private final SessionStateReceiver mSessionStateReceiver; + /** @hide */ + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) + @Nullable + public ContentProtectionEventProcessor mContentProtectionEventProcessor; + private static class SessionStateReceiver extends IResultReceiver.Stub { private final WeakReference<MainContentCaptureSession> mMainSession; @@ -194,8 +210,12 @@ public final class MainContentCaptureSession extends ContentCaptureSession { } } - protected MainContentCaptureSession(@NonNull ContentCaptureManager.StrippedContext context, - @NonNull ContentCaptureManager manager, @NonNull Handler handler, + /** @hide */ + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PROTECTED) + public MainContentCaptureSession( + @NonNull ContentCaptureManager.StrippedContext context, + @NonNull ContentCaptureManager manager, + @NonNull Handler handler, @NonNull IContentCaptureManager systemServerInterface) { mContext = context; mManager = manager; @@ -273,15 +293,16 @@ public final class MainContentCaptureSession extends ContentCaptureSession { } /** - * Callback from {@code system_server} after call to - * {@link IContentCaptureManager#startSession(IBinder, ComponentName, String, int, - * IResultReceiver)}. + * Callback from {@code system_server} after call to {@link + * IContentCaptureManager#startSession(IBinder, ComponentName, String, int, IResultReceiver)}. * * @param resultCode session state * @param binder handle to {@code IContentCaptureDirectManager} + * @hide */ + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) @UiThread - private void onSessionStarted(int resultCode, @Nullable IBinder binder) { + public void onSessionStarted(int resultCode, @Nullable IBinder binder) { if (binder != null) { mDirectServiceInterface = IContentCaptureDirectManager.Stub.asInterface(binder); mDirectServiceVulture = () -> { @@ -296,6 +317,20 @@ public final class MainContentCaptureSession extends ContentCaptureSession { } } + // Should not be possible for mComponentName to be null here but check anyway + if (mManager.mOptions.contentProtectionOptions.enableReceiver + && mManager.getContentProtectionEventBuffer() != null + && mComponentName != null) { + mContentProtectionEventProcessor = + new ContentProtectionEventProcessor( + mManager.getContentProtectionEventBuffer(), + mHandler, + mSystemServerInterface, + mComponentName.getPackageName()); + } else { + mContentProtectionEventProcessor = null; + } + if ((resultCode & STATE_DISABLED) != 0) { resetSession(resultCode); } else { @@ -311,8 +346,10 @@ public final class MainContentCaptureSession extends ContentCaptureSession { } } + /** @hide */ + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) @UiThread - private void sendEvent(@NonNull ContentCaptureEvent event) { + public void sendEvent(@NonNull ContentCaptureEvent event) { sendEvent(event, /* forceFlush= */ false); } @@ -337,6 +374,25 @@ public final class MainContentCaptureSession extends ContentCaptureSession { if (sVerbose) Log.v(TAG, "handleSendEvent(): ignoring when disabled"); return; } + + if (isContentProtectionReceiverEnabled()) { + sendContentProtectionEvent(event); + } + if (isContentCaptureReceiverEnabled()) { + sendContentCaptureEvent(event, forceFlush); + } + } + + @UiThread + private void sendContentProtectionEvent(@NonNull ContentCaptureEvent event) { + if (mContentProtectionEventProcessor != null) { + mContentProtectionEventProcessor.processEvent(event); + } + } + + @UiThread + private void sendContentCaptureEvent(@NonNull ContentCaptureEvent event, boolean forceFlush) { + final int eventType = event.getType(); final int maxBufferSize = mManager.mOptions.maxBufferSize; if (mEvents == null) { if (sVerbose) { @@ -528,9 +584,11 @@ public final class MainContentCaptureSession extends ContentCaptureSession { flush(reason); } + /** @hide */ + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) @Override @UiThread - void flush(@FlushReason int reason) { + public void flush(@FlushReason int reason) { if (mEvents == null || mEvents.size() == 0) { if (sVerbose) { Log.v(TAG, "Don't flush for empty event buffer."); @@ -544,6 +602,10 @@ public final class MainContentCaptureSession extends ContentCaptureSession { return; } + if (!isContentCaptureReceiverEnabled()) { + return; + } + if (mDirectServiceInterface == null) { if (sVerbose) { Log.v(TAG, "handleForceFlush(" + getDebugState(reason) + "): hold your horses, " @@ -607,8 +669,10 @@ public final class MainContentCaptureSession extends ContentCaptureSession { return new ParceledListSlice<>(events); } + /** hide */ + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) @UiThread - private void destroySession() { + public void destroySession() { if (sDebug) { Log.d(TAG, "Destroying session (ctx=" + mContext + ", id=" + mId + ") with " + (mEvents == null ? 0 : mEvents.size()) + " event(s) for " @@ -626,12 +690,15 @@ public final class MainContentCaptureSession extends ContentCaptureSession { mDirectServiceInterface.asBinder().unlinkToDeath(mDirectServiceVulture, 0); } mDirectServiceInterface = null; + mContentProtectionEventProcessor = null; } // TODO(b/122454205): once we support multiple sessions, we might need to move some of these // clearings out. + /** @hide */ + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) @UiThread - private void resetSession(int newState) { + public void resetSession(int newState) { if (sVerbose) { Log.v(TAG, "handleResetSession(" + getActivityName() + "): from " + getStateAsString(mState) + " to " + getStateAsString(newState)); @@ -651,6 +718,7 @@ public final class MainContentCaptureSession extends ContentCaptureSession { } } mDirectServiceInterface = null; + mContentProtectionEventProcessor = null; mHandler.removeMessages(MSG_FLUSH); } @@ -878,4 +946,14 @@ public final class MainContentCaptureSession extends ContentCaptureSession { private String getDebugState(@FlushReason int reason) { return getDebugState() + ", reason=" + getFlushReasonAsString(reason); } + + @UiThread + private boolean isContentProtectionReceiverEnabled() { + return mManager.mOptions.contentProtectionOptions.enableReceiver; + } + + @UiThread + private boolean isContentCaptureReceiverEnabled() { + return mManager.mOptions.enableReceiver; + } } diff --git a/core/java/android/view/contentcapture/ViewNode.java b/core/java/android/view/contentcapture/ViewNode.java index 044a31f3b297..f218995e55ad 100644 --- a/core/java/android/view/contentcapture/ViewNode.java +++ b/core/java/android/view/contentcapture/ViewNode.java @@ -480,6 +480,11 @@ public final class ViewNode extends AssistStructure.ViewNode { return mLocaleList; } + /** @hide */ + public void setTextIdEntry(@NonNull String textIdEntry) { + mTextIdEntry = textIdEntry; + } + private void writeSelfToParcel(@NonNull Parcel parcel, int parcelFlags) { long nodeFlags = mFlags; diff --git a/core/java/android/view/contentprotection/ContentProtectionEventProcessor.java b/core/java/android/view/contentprotection/ContentProtectionEventProcessor.java new file mode 100644 index 000000000000..b44abf3eb04d --- /dev/null +++ b/core/java/android/view/contentprotection/ContentProtectionEventProcessor.java @@ -0,0 +1,254 @@ +/* + * 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 android.view.contentprotection; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.UiThread; +import android.content.pm.ParceledListSlice; +import android.os.Handler; +import android.text.InputType; +import android.util.Log; +import android.view.contentcapture.ContentCaptureEvent; +import android.view.contentcapture.IContentCaptureManager; +import android.view.contentcapture.ViewNode; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.RingBuffer; + +import java.time.Duration; +import java.time.Instant; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * Main entry point for processing {@link ContentCaptureEvent} for the content protection flow. + * + * @hide + */ +public class ContentProtectionEventProcessor { + + private static final String TAG = "ContentProtectionEventProcessor"; + + private static final List<Integer> PASSWORD_FIELD_INPUT_TYPES = + Collections.unmodifiableList( + Arrays.asList( + InputType.TYPE_NUMBER_VARIATION_PASSWORD, + InputType.TYPE_TEXT_VARIATION_PASSWORD, + InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD, + InputType.TYPE_TEXT_VARIATION_WEB_PASSWORD)); + + private static final List<String> PASSWORD_TEXTS = + Collections.unmodifiableList( + Arrays.asList("password", "pass word", "code", "pin", "credential")); + + private static final List<String> ADDITIONAL_SUSPICIOUS_TEXTS = + Collections.unmodifiableList( + Arrays.asList("user", "mail", "phone", "number", "login", "log in", "sign in")); + + private static final Duration MIN_DURATION_BETWEEN_FLUSHING = Duration.ofSeconds(3); + + private static final String ANDROID_CLASS_NAME_PREFIX = "android."; + + private static final Set<Integer> EVENT_TYPES_TO_STORE = + Collections.unmodifiableSet( + new HashSet<>( + Arrays.asList( + ContentCaptureEvent.TYPE_VIEW_APPEARED, + ContentCaptureEvent.TYPE_VIEW_DISAPPEARED, + ContentCaptureEvent.TYPE_VIEW_TEXT_CHANGED))); + + private static final int RESET_LOGIN_TOTAL_EVENTS_TO_PROCESS = 150; + + @NonNull private final RingBuffer<ContentCaptureEvent> mEventBuffer; + + @NonNull private final Handler mHandler; + + @NonNull private final IContentCaptureManager mContentCaptureManager; + + @NonNull private final String mPackageName; + + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) + public boolean mPasswordFieldDetected = false; + + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) + public boolean mSuspiciousTextDetected = false; + + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) + @Nullable + public Instant mLastFlushTime; + + private int mResetLoginRemainingEventsToProcess; + + public ContentProtectionEventProcessor( + @NonNull RingBuffer<ContentCaptureEvent> eventBuffer, + @NonNull Handler handler, + @NonNull IContentCaptureManager contentCaptureManager, + @NonNull String packageName) { + mEventBuffer = eventBuffer; + mHandler = handler; + mContentCaptureManager = contentCaptureManager; + mPackageName = packageName; + } + + /** Main entry point for {@link ContentCaptureEvent} processing. */ + @UiThread + public void processEvent(@NonNull ContentCaptureEvent event) { + if (EVENT_TYPES_TO_STORE.contains(event.getType())) { + storeEvent(event); + } + if (event.getType() == ContentCaptureEvent.TYPE_VIEW_APPEARED) { + processViewAppearedEvent(event); + } + } + + @UiThread + private void storeEvent(@NonNull ContentCaptureEvent event) { + // Ensure receiver gets the package name which might not be set + ViewNode viewNode = (event.getViewNode() != null) ? event.getViewNode() : new ViewNode(); + viewNode.setTextIdEntry(mPackageName); + event.setViewNode(viewNode); + mEventBuffer.append(event); + } + + @UiThread + private void processViewAppearedEvent(@NonNull ContentCaptureEvent event) { + mPasswordFieldDetected |= isPasswordField(event); + mSuspiciousTextDetected |= isSuspiciousText(event); + if (mPasswordFieldDetected && mSuspiciousTextDetected) { + loginDetected(); + } else { + maybeResetLoginFlags(); + } + } + + @UiThread + private void loginDetected() { + if (mLastFlushTime == null + || Instant.now().isAfter(mLastFlushTime.plus(MIN_DURATION_BETWEEN_FLUSHING))) { + flush(); + } + resetLoginFlags(); + } + + @UiThread + private void resetLoginFlags() { + mPasswordFieldDetected = false; + mSuspiciousTextDetected = false; + mResetLoginRemainingEventsToProcess = 0; + } + + @UiThread + private void maybeResetLoginFlags() { + if (mPasswordFieldDetected || mSuspiciousTextDetected) { + if (mResetLoginRemainingEventsToProcess <= 0) { + mResetLoginRemainingEventsToProcess = RESET_LOGIN_TOTAL_EVENTS_TO_PROCESS; + } else { + mResetLoginRemainingEventsToProcess--; + if (mResetLoginRemainingEventsToProcess <= 0) { + resetLoginFlags(); + } + } + } + } + + @UiThread + private void flush() { + mLastFlushTime = Instant.now(); + + // Note the thread annotations, do not move clearEvents to mHandler + ParceledListSlice<ContentCaptureEvent> events = clearEvents(); + mHandler.post(() -> handlerOnLoginDetected(events)); + } + + @UiThread + @NonNull + private ParceledListSlice<ContentCaptureEvent> clearEvents() { + List<ContentCaptureEvent> events = Arrays.asList(mEventBuffer.toArray()); + mEventBuffer.clear(); + return new ParceledListSlice<>(events); + } + + private void handlerOnLoginDetected(@NonNull ParceledListSlice<ContentCaptureEvent> events) { + try { + mContentCaptureManager.onLoginDetected(events); + } catch (Exception ex) { + Log.e(TAG, "Failed to flush events for: " + mPackageName, ex); + } + } + + private boolean isPasswordField(@NonNull ContentCaptureEvent event) { + return isPasswordField(event.getViewNode()); + } + + private boolean isPasswordField(@Nullable ViewNode viewNode) { + if (viewNode == null) { + return false; + } + return isAndroidPasswordField(viewNode) || isWebViewPasswordField(viewNode); + } + + private boolean isAndroidPasswordField(@NonNull ViewNode viewNode) { + if (!isAndroidViewNode(viewNode)) { + return false; + } + int inputType = viewNode.getInputType(); + return PASSWORD_FIELD_INPUT_TYPES.stream() + .anyMatch(passwordInputType -> (inputType & passwordInputType) != 0); + } + + private boolean isWebViewPasswordField(@NonNull ViewNode viewNode) { + if (viewNode.getClassName() != null) { + return false; + } + return isPasswordText(ContentProtectionUtils.getViewNodeText(viewNode)); + } + + private boolean isAndroidViewNode(@NonNull ViewNode viewNode) { + String className = viewNode.getClassName(); + return className != null && className.startsWith(ANDROID_CLASS_NAME_PREFIX); + } + + private boolean isSuspiciousText(@NonNull ContentCaptureEvent event) { + return isSuspiciousText(ContentProtectionUtils.getEventText(event)) + || isSuspiciousText(ContentProtectionUtils.getViewNodeText(event)); + } + + private boolean isSuspiciousText(@Nullable String text) { + if (text == null) { + return false; + } + if (isPasswordText(text)) { + return true; + } + String lowerCaseText = text.toLowerCase(); + return ADDITIONAL_SUSPICIOUS_TEXTS.stream() + .anyMatch(suspiciousText -> lowerCaseText.contains(suspiciousText)); + } + + private boolean isPasswordText(@Nullable String text) { + if (text == null) { + return false; + } + String lowerCaseText = text.toLowerCase(); + return PASSWORD_TEXTS.stream() + .anyMatch(passwordText -> lowerCaseText.contains(passwordText)); + } +} diff --git a/core/java/android/view/contentprotection/ContentProtectionUtils.java b/core/java/android/view/contentprotection/ContentProtectionUtils.java new file mode 100644 index 000000000000..9abf6f10d05d --- /dev/null +++ b/core/java/android/view/contentprotection/ContentProtectionUtils.java @@ -0,0 +1,60 @@ +/* + * 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 android.view.contentprotection; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.view.contentcapture.ContentCaptureEvent; +import android.view.contentcapture.ViewNode; + +/** + * Utilities for reading data from {@link ContentCaptureEvent} and {@link ViewNode}. + * + * @hide + */ +public final class ContentProtectionUtils { + + /** Returns the text extracted directly from the {@link ContentCaptureEvent}, if set. */ + @Nullable + public static String getEventText(@NonNull ContentCaptureEvent event) { + CharSequence text = event.getText(); + if (text == null) { + return null; + } + return text.toString(); + } + + /** Returns the text extracted from the event's {@link ViewNode}, if set. */ + @Nullable + public static String getViewNodeText(@NonNull ContentCaptureEvent event) { + ViewNode viewNode = event.getViewNode(); + if (viewNode == null) { + return null; + } + return getViewNodeText(viewNode); + } + + /** Returns the text extracted directly from the {@link ViewNode}, if set. */ + @Nullable + public static String getViewNodeText(@NonNull ViewNode viewNode) { + CharSequence text = viewNode.getText(); + if (text == null) { + return null; + } + return text.toString(); + } +} diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index 41ef44e1ac1f..40b060ad0bbf 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -4361,15 +4361,14 @@ public final class InputMethodManager { * @param icProto {@link InputConnection} call data in proto format. * @hide */ - @GuardedBy("mH") public void dumpDebug(ProtoOutputStream proto, @Nullable byte[] icProto) { - if (!isImeSessionAvailableLocked()) { - return; - } - - proto.write(DISPLAY_ID, mDisplayId); - final long token = proto.start(INPUT_METHOD_MANAGER); synchronized (mH) { + if (!isImeSessionAvailableLocked()) { + return; + } + + proto.write(DISPLAY_ID, mDisplayId); + final long token = proto.start(INPUT_METHOD_MANAGER); proto.write(CUR_ID, mCurBindState.mImeId); proto.write(FULLSCREEN_MODE, mFullscreenMode); proto.write(ACTIVE, mActive); diff --git a/core/java/android/view/inputmethod/TextAppearanceInfo.java b/core/java/android/view/inputmethod/TextAppearanceInfo.java index 05717dd5d930..7eee33f7f617 100644 --- a/core/java/android/view/inputmethod/TextAppearanceInfo.java +++ b/core/java/android/view/inputmethod/TextAppearanceInfo.java @@ -238,7 +238,10 @@ public final class TextAppearanceInfo implements Parcelable { .setFontFeatureSettings(textPaint.getFontFeatureSettings()) .setFontVariationSettings(textPaint.getFontVariationSettings()) .setTextScaleX(textPaint.getTextScaleX()) - .setTextColor(textPaint.getColor()) + // When there is a hint text (text length is 0), the text color should be the normal + // text color rather than hint text color. + .setTextColor(text.length() == 0 + ? textView.getCurrentTextColor() : textPaint.getColor()) .setLinkTextColor(textPaint.linkColor) .setAllCaps(textView.isAllCaps()) .setFallbackLineSpacing(textView.isFallbackLineSpacing()) diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java index adeb88909d38..6ad1960cbda9 100644 --- a/core/java/android/widget/AbsListView.java +++ b/core/java/android/widget/AbsListView.java @@ -4703,7 +4703,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te @Override public PointerIcon onResolvePointerIcon(MotionEvent event, int pointerIndex) { - if (mFastScroll != null) { + if (mFastScroll != null && event.isFromSource(InputDevice.SOURCE_MOUSE)) { PointerIcon pointerIcon = mFastScroll.onResolvePointerIcon(event, pointerIndex); if (pointerIcon != null) { return pointerIcon; diff --git a/core/java/android/widget/Button.java b/core/java/android/widget/Button.java index 634cbe323d86..405099d6a260 100644 --- a/core/java/android/widget/Button.java +++ b/core/java/android/widget/Button.java @@ -18,6 +18,7 @@ package android.widget; import android.content.Context; import android.util.AttributeSet; +import android.view.InputDevice; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.PointerIcon; @@ -173,7 +174,8 @@ public class Button extends TextView { @Override public PointerIcon onResolvePointerIcon(MotionEvent event, int pointerIndex) { - if (getPointerIcon() == null && isClickable() && isEnabled()) { + if (getPointerIcon() == null && isClickable() && isEnabled() + && event.isFromSource(InputDevice.SOURCE_MOUSE)) { return PointerIcon.getSystemIcon(getContext(), PointerIcon.TYPE_HAND); } return super.onResolvePointerIcon(event, pointerIndex); diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java index 2dbff581fe84..d37c37a392a5 100644 --- a/core/java/android/widget/Editor.java +++ b/core/java/android/widget/Editor.java @@ -8112,16 +8112,10 @@ public class Editor { mHighlightPaint = new Paint(); mHighlightPath = new Path(); - // The highlight color is supposed to be 12% of the color primary40. We can't - // directly access Material 3 theme. But because Material 3 sets the colorPrimary to - // be primary40, here we hardcoded it to be 12% of colorPrimary. - final TypedValue typedValue = new TypedValue(); - mTextView.getContext().getTheme() - .resolveAttribute(R.attr.colorPrimary, typedValue, true); - final int colorPrimary = typedValue.data; - final int highlightColor = ColorUtils.setAlphaComponent(colorPrimary, - (int) (0.12f * Color.alpha(colorPrimary))); - mHighlightPaint.setColor(highlightColor); + // Insert mode highlight color is 20% opacity of the default text color. + int color = mTextView.getTextColors().getDefaultColor(); + color = ColorUtils.setAlphaComponent(color, (int) (0.2f * Color.alpha(color))); + mHighlightPaint.setColor(color); } /** diff --git a/core/java/android/widget/ImageButton.java b/core/java/android/widget/ImageButton.java index e1b0c915c684..b6c5396ca176 100644 --- a/core/java/android/widget/ImageButton.java +++ b/core/java/android/widget/ImageButton.java @@ -18,6 +18,7 @@ package android.widget; import android.content.Context; import android.util.AttributeSet; +import android.view.InputDevice; import android.view.MotionEvent; import android.view.PointerIcon; import android.widget.RemoteViews.RemoteView; @@ -99,7 +100,8 @@ public class ImageButton extends ImageView { @Override public PointerIcon onResolvePointerIcon(MotionEvent event, int pointerIndex) { - if (getPointerIcon() == null && isClickable() && isEnabled()) { + if (getPointerIcon() == null && isClickable() && isEnabled() + && event.isFromSource(InputDevice.SOURCE_MOUSE)) { return PointerIcon.getSystemIcon(getContext(), PointerIcon.TYPE_HAND); } return super.onResolvePointerIcon(event, pointerIndex); diff --git a/core/java/android/widget/RadialTimePickerView.java b/core/java/android/widget/RadialTimePickerView.java index f3600b0de22b..edf0f48c3577 100644 --- a/core/java/android/widget/RadialTimePickerView.java +++ b/core/java/android/widget/RadialTimePickerView.java @@ -38,6 +38,7 @@ import android.util.MathUtils; import android.util.StateSet; import android.util.TypedValue; import android.view.HapticFeedbackConstants; +import android.view.InputDevice; import android.view.MotionEvent; import android.view.PointerIcon; import android.view.View; @@ -1060,9 +1061,11 @@ public class RadialTimePickerView extends View { if (!isEnabled()) { return null; } - final int degrees = getDegreesFromXY(event.getX(), event.getY(), false); - if (degrees != -1) { - return PointerIcon.getSystemIcon(getContext(), PointerIcon.TYPE_HAND); + if (event.isFromSource(InputDevice.SOURCE_MOUSE)) { + final int degrees = getDegreesFromXY(event.getX(), event.getY(), false); + if (degrees != -1) { + return PointerIcon.getSystemIcon(getContext(), PointerIcon.TYPE_HAND); + } } return super.onResolvePointerIcon(event, pointerIndex); } diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java index 7f96266a1f69..3be8c3d6b502 100644 --- a/core/java/android/widget/RemoteViews.java +++ b/core/java/android/widget/RemoteViews.java @@ -232,6 +232,7 @@ public class RemoteViews implements Parcelable, Filter { private static final int NIGHT_MODE_REFLECTION_ACTION_TAG = 30; private static final int SET_REMOTE_COLLECTION_ITEMS_ADAPTER_TAG = 31; private static final int ATTRIBUTE_REFLECTION_ACTION_TAG = 32; + private static final int SET_REMOTE_ADAPTER_TAG = 33; /** @hide **/ @IntDef(prefix = "MARGIN_", value = { @@ -960,6 +961,11 @@ public class RemoteViews implements Parcelable, Filter { return SET_REMOTE_VIEW_ADAPTER_LIST_TAG; } + @Override + public String getUniqueKey() { + return (SET_REMOTE_ADAPTER_TAG + "_" + viewId); + } + int viewTypeCount; ArrayList<RemoteViews> list; } @@ -1082,6 +1088,11 @@ public class RemoteViews implements Parcelable, Filter { public int getActionTag() { return SET_REMOTE_COLLECTION_ITEMS_ADAPTER_TAG; } + + @Override + public String getUniqueKey() { + return (SET_REMOTE_ADAPTER_TAG + "_" + viewId); + } } private class SetRemoteViewsAdapterIntent extends Action { diff --git a/core/java/android/widget/SimpleMonthView.java b/core/java/android/widget/SimpleMonthView.java index 6c53a44c79fa..1317b51f7bfa 100644 --- a/core/java/android/widget/SimpleMonthView.java +++ b/core/java/android/widget/SimpleMonthView.java @@ -39,6 +39,7 @@ import android.util.AttributeSet; import android.util.IntArray; import android.util.MathUtils; import android.util.StateSet; +import android.view.InputDevice; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.PointerIcon; @@ -1041,12 +1042,15 @@ class SimpleMonthView extends View { if (!isEnabled()) { return null; } - // Add 0.5f to event coordinates to match the logic in onTouchEvent. - final int x = (int) (event.getX() + 0.5f); - final int y = (int) (event.getY() + 0.5f); - final int dayUnderPointer = getDayAtLocation(x, y); - if (dayUnderPointer >= 0) { - return PointerIcon.getSystemIcon(getContext(), PointerIcon.TYPE_HAND); + + if (event.isFromSource(InputDevice.SOURCE_MOUSE)) { + // Add 0.5f to event coordinates to match the logic in onTouchEvent. + final int x = (int) (event.getX() + 0.5f); + final int y = (int) (event.getY() + 0.5f); + final int dayUnderPointer = getDayAtLocation(x, y); + if (dayUnderPointer >= 0) { + return PointerIcon.getSystemIcon(getContext(), PointerIcon.TYPE_HAND); + } } return super.onResolvePointerIcon(event, pointerIndex); } diff --git a/core/java/android/widget/Spinner.java b/core/java/android/widget/Spinner.java index ad431efc0bd2..ecc41a5ec6c9 100644 --- a/core/java/android/widget/Spinner.java +++ b/core/java/android/widget/Spinner.java @@ -38,6 +38,7 @@ import android.util.AttributeSet; import android.util.Log; import android.view.ContextThemeWrapper; import android.view.Gravity; +import android.view.InputDevice; import android.view.MotionEvent; import android.view.PointerIcon; import android.view.View; @@ -935,7 +936,8 @@ public class Spinner extends AbsSpinner implements OnClickListener { @Override public PointerIcon onResolvePointerIcon(MotionEvent event, int pointerIndex) { - if (getPointerIcon() == null && isClickable() && isEnabled()) { + if (getPointerIcon() == null && isClickable() && isEnabled() + && event.isFromSource(InputDevice.SOURCE_MOUSE)) { return PointerIcon.getSystemIcon(getContext(), PointerIcon.TYPE_HAND); } return super.onResolvePointerIcon(event, pointerIndex); diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index db7d48471d9d..7e1e52dd0707 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -9223,18 +9223,20 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener @Override public PointerIcon onResolvePointerIcon(MotionEvent event, int pointerIndex) { - if (mSpannable != null && mLinksClickable) { - final float x = event.getX(pointerIndex); - final float y = event.getY(pointerIndex); - final int offset = getOffsetForPosition(x, y); - final ClickableSpan[] clickables = mSpannable.getSpans(offset, offset, - ClickableSpan.class); - if (clickables.length > 0) { - return PointerIcon.getSystemIcon(mContext, PointerIcon.TYPE_HAND); - } - } - if (isTextSelectable() || isTextEditable()) { - return PointerIcon.getSystemIcon(mContext, PointerIcon.TYPE_TEXT); + if (event.isFromSource(InputDevice.SOURCE_MOUSE)) { + if (mSpannable != null && mLinksClickable) { + final float x = event.getX(pointerIndex); + final float y = event.getY(pointerIndex); + final int offset = getOffsetForPosition(x, y); + final ClickableSpan[] clickables = mSpannable.getSpans(offset, offset, + ClickableSpan.class); + if (clickables.length > 0) { + return PointerIcon.getSystemIcon(mContext, PointerIcon.TYPE_HAND); + } + } + if (isTextSelectable() || isTextEditable()) { + return PointerIcon.getSystemIcon(mContext, PointerIcon.TYPE_TEXT); + } } return super.onResolvePointerIcon(event, pointerIndex); } diff --git a/core/java/android/window/WindowInfosListenerForTest.java b/core/java/android/window/WindowInfosListenerForTest.java index 25bf85cfaa58..7fc55a7f581b 100644 --- a/core/java/android/window/WindowInfosListenerForTest.java +++ b/core/java/android/window/WindowInfosListenerForTest.java @@ -87,6 +87,14 @@ public class WindowInfosListenerForTest { this.isTrustedOverlay = (inputConfig & InputConfig.TRUSTED_OVERLAY) != 0; this.isVisible = (inputConfig & InputConfig.NOT_VISIBLE) == 0; } + + @Override + public String toString() { + return name + ", frame=" + bounds + + ", isVisible=" + isVisible + + ", isTrustedOverlay=" + isTrustedOverlay + + ", token=" + windowToken; + } } private static final String TAG = "WindowInfosListenerForTest"; diff --git a/core/java/com/android/internal/accessibility/common/MagnificationConstants.java b/core/java/com/android/internal/accessibility/common/MagnificationConstants.java index 94c230bc94fb..95c3419180a9 100644 --- a/core/java/com/android/internal/accessibility/common/MagnificationConstants.java +++ b/core/java/com/android/internal/accessibility/common/MagnificationConstants.java @@ -27,4 +27,10 @@ public final class MagnificationConstants { * the min value, there will be no obvious magnification effect. */ public static final float PERSISTED_SCALE_MIN_VALUE = 1.3f; + + /** Minimum supported value for magnification scale. */ + public static final float SCALE_MIN_VALUE = 1.0f; + + /** Maximum supported value for magnification scale. */ + public static final float SCALE_MAX_VALUE = 8.0f; } diff --git a/core/java/com/android/internal/app/AssistUtils.java b/core/java/com/android/internal/app/AssistUtils.java index 57cc38cc6dfd..0ea80144a798 100644 --- a/core/java/com/android/internal/app/AssistUtils.java +++ b/core/java/com/android/internal/app/AssistUtils.java @@ -59,6 +59,8 @@ public class AssistUtils { public static final int INVOCATION_TYPE_POWER_BUTTON_LONG_PRESS = 6; /** value for INVOCATION_TYPE_KEY: press on physcial assistant button */ public static final int INVOCATION_TYPE_ASSIST_BUTTON = 7; + /** value for INVOCATION_TYPE_KEY: long press on nav handle */ + public static final int INVOCATION_TYPE_NAV_HANDLE_LONG_PRESS = 8; private final Context mContext; private final IVoiceInteractionManagerService mVoiceInteractionManagerService; diff --git a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java index dd52de4f84c7..9ffccb34f44d 100644 --- a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java +++ b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java @@ -574,6 +574,11 @@ public final class SystemUiDeviceConfigFlags { */ public static final String COMBINED_BROADCAST_ENABLED = "combined_broadcast_enabled"; + /** + * (boolean) Whether to allow cursor hover states for certain elements. + */ + public static final String CURSOR_HOVER_STATES_ENABLED = "cursor_hover_states_enabled"; + private SystemUiDeviceConfigFlags() { } } diff --git a/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java b/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java index c9e76009136a..6b8ae946e68c 100644 --- a/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java +++ b/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java @@ -85,6 +85,10 @@ public class SystemUiSystemPropertiesFlags { /** Gating the holding of WakeLocks until NLSes are told about a new notification. */ public static final Flag WAKE_LOCK_FOR_POSTING_NOTIFICATION = devFlag("persist.sysui.notification.wake_lock_for_posting_notification"); + + /** Gating storing NotificationRankingUpdate ranking map in shared memory. */ + public static final Flag RANKING_UPDATE_ASHMEM = devFlag( + "persist.sysui.notification.ranking_update_ashmem"); } //// == End of flags. Everything below this line is the implementation. == //// diff --git a/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java b/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java index 66e3333acf7c..1a3804900665 100644 --- a/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java +++ b/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java @@ -20,6 +20,7 @@ import android.annotation.AnyThread; import android.annotation.DrawableRes; import android.annotation.NonNull; import android.annotation.Nullable; +import android.inputmethodservice.InputMethodService; import android.net.Uri; import android.os.IBinder; import android.os.RemoteException; @@ -105,14 +106,10 @@ public final class InputMethodPrivilegedOperations { * * @param vis visibility flags * @param backDisposition disposition flags - * @see android.inputmethodservice.InputMethodService#IME_ACTIVE - * @see android.inputmethodservice.InputMethodService#IME_VISIBLE - * @see android.inputmethodservice.InputMethodService#IME_INVISIBLE - * @see android.inputmethodservice.InputMethodService#BACK_DISPOSITION_DEFAULT - * @see android.inputmethodservice.InputMethodService#BACK_DISPOSITION_ADJUST_NOTHING */ @AnyThread - public void setImeWindowStatusAsync(int vis, int backDisposition) { + public void setImeWindowStatusAsync(@InputMethodService.ImeWindowVisibility int vis, + @InputMethodService.BackDispositionMode int backDisposition) { final IInputMethodPrivilegedOperations ops = mOps.getAndWarnIfNull(); if (ops == null) { return; diff --git a/core/java/com/android/internal/jank/InteractionJankMonitor.java b/core/java/com/android/internal/jank/InteractionJankMonitor.java index e530aec2119a..869b69611eba 100644 --- a/core/java/com/android/internal/jank/InteractionJankMonitor.java +++ b/core/java/com/android/internal/jank/InteractionJankMonitor.java @@ -28,6 +28,7 @@ import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_IN import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__IME_INSETS_ANIMATION; import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_ALL_APPS_SCROLL; import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_CLOSE_TO_HOME; +import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_CLOSE_TO_HOME_FALLBACK; import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_CLOSE_TO_PIP; import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_LAUNCH_FROM_ICON; import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_LAUNCH_FROM_RECENTS; @@ -258,8 +259,16 @@ public class InteractionJankMonitor { public static final int CUJ_IME_INSETS_ANIMATION = 69; public static final int CUJ_LOCKSCREEN_CLOCK_MOVE_ANIMATION = 70; public static final int CUJ_LAUNCHER_OPEN_SEARCH_RESULT = 71; + // 72 - 77 are reserved for b/281564325. - private static final int LAST_CUJ = CUJ_LAUNCHER_OPEN_SEARCH_RESULT; + /** + * In some cases when we do not have any end-target, we play a simple slide-down animation. + * eg: Open an app from Overview/Task switcher such that there is no home-screen icon. + * eg: Exit the app using back gesture. + */ + public static final int CUJ_LAUNCHER_APP_CLOSE_TO_HOME_FALLBACK = 78; + + private static final int LAST_CUJ = CUJ_LAUNCHER_APP_CLOSE_TO_HOME_FALLBACK; private static final int NO_STATSD_LOGGING = -1; // Used to convert CujType to InteractionType enum value for statsd logging. @@ -340,6 +349,14 @@ public class InteractionJankMonitor { CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_IME_INSETS_ANIMATION] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__IME_INSETS_ANIMATION; CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LOCKSCREEN_CLOCK_MOVE_ANIMATION] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_CLOCK_MOVE_ANIMATION; CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LAUNCHER_OPEN_SEARCH_RESULT] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_OPEN_SEARCH_RESULT; + // 72 - 77 are reserved for b/281564325. + CUJ_TO_STATSD_INTERACTION_TYPE[72] = NO_STATSD_LOGGING; + CUJ_TO_STATSD_INTERACTION_TYPE[73] = NO_STATSD_LOGGING; + CUJ_TO_STATSD_INTERACTION_TYPE[74] = NO_STATSD_LOGGING; + CUJ_TO_STATSD_INTERACTION_TYPE[75] = NO_STATSD_LOGGING; + CUJ_TO_STATSD_INTERACTION_TYPE[76] = NO_STATSD_LOGGING; + CUJ_TO_STATSD_INTERACTION_TYPE[77] = NO_STATSD_LOGGING; + CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LAUNCHER_APP_CLOSE_TO_HOME_FALLBACK] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_CLOSE_TO_HOME_FALLBACK; } private static class InstanceHolder { @@ -439,6 +456,7 @@ public class InteractionJankMonitor { CUJ_IME_INSETS_ANIMATION, CUJ_LOCKSCREEN_CLOCK_MOVE_ANIMATION, CUJ_LAUNCHER_OPEN_SEARCH_RESULT, + CUJ_LAUNCHER_APP_CLOSE_TO_HOME_FALLBACK, }) @Retention(RetentionPolicy.SOURCE) public @interface CujType { @@ -1050,6 +1068,8 @@ public class InteractionJankMonitor { return "LOCKSCREEN_CLOCK_MOVE_ANIMATION"; case CUJ_LAUNCHER_OPEN_SEARCH_RESULT: return "LAUNCHER_OPEN_SEARCH_RESULT"; + case CUJ_LAUNCHER_APP_CLOSE_TO_HOME_FALLBACK: + return "LAUNCHER_APP_CLOSE_TO_HOME_FALLBACK"; } return "UNKNOWN"; } diff --git a/core/java/com/android/internal/protolog/common/ProtoLog.java b/core/java/com/android/internal/protolog/common/ProtoLog.java index 93765cdf0890..8870096f3db7 100644 --- a/core/java/com/android/internal/protolog/common/ProtoLog.java +++ b/core/java/com/android/internal/protolog/common/ProtoLog.java @@ -16,6 +16,8 @@ package com.android.internal.protolog.common; +import android.util.Log; + /** * ProtoLog API - exposes static logging methods. Usage of this API is similar * to {@code android.utils.Log} class. Instead of plain text log messages each call consists of @@ -53,6 +55,9 @@ public class ProtoLog { throw new UnsupportedOperationException( "ProtoLog calls MUST be processed with ProtoLogTool"); } + if (group.isLogToLogcat()) { + Log.d(group.getTag(), String.format(messageString, args)); + } } /** @@ -68,6 +73,9 @@ public class ProtoLog { throw new UnsupportedOperationException( "ProtoLog calls MUST be processed with ProtoLogTool"); } + if (group.isLogToLogcat()) { + Log.v(group.getTag(), String.format(messageString, args)); + } } /** @@ -83,6 +91,9 @@ public class ProtoLog { throw new UnsupportedOperationException( "ProtoLog calls MUST be processed with ProtoLogTool"); } + if (group.isLogToLogcat()) { + Log.i(group.getTag(), String.format(messageString, args)); + } } /** @@ -98,6 +109,9 @@ public class ProtoLog { throw new UnsupportedOperationException( "ProtoLog calls MUST be processed with ProtoLogTool"); } + if (group.isLogToLogcat()) { + Log.w(group.getTag(), String.format(messageString, args)); + } } /** @@ -113,6 +127,9 @@ public class ProtoLog { throw new UnsupportedOperationException( "ProtoLog calls MUST be processed with ProtoLogTool"); } + if (group.isLogToLogcat()) { + Log.e(group.getTag(), String.format(messageString, args)); + } } /** @@ -128,5 +145,8 @@ public class ProtoLog { throw new UnsupportedOperationException( "ProtoLog calls MUST be processed with ProtoLogTool"); } + if (group.isLogToLogcat()) { + Log.wtf(group.getTag(), String.format(messageString, args)); + } } } diff --git a/core/java/com/android/internal/statusbar/IAppClipsService.aidl b/core/java/com/android/internal/statusbar/IAppClipsService.aidl index 013d0d32e7a2..d6ab8bcdde20 100644 --- a/core/java/com/android/internal/statusbar/IAppClipsService.aidl +++ b/core/java/com/android/internal/statusbar/IAppClipsService.aidl @@ -23,4 +23,6 @@ package com.android.internal.statusbar; */ interface IAppClipsService { boolean canLaunchCaptureContentActivityForNote(in int taskId); -}
\ No newline at end of file + + int canLaunchCaptureContentActivityForNoteInternal(in int taskId); +} diff --git a/core/java/com/android/internal/statusbar/RegisterStatusBarResult.java b/core/java/com/android/internal/statusbar/RegisterStatusBarResult.java index 4f827cda6afa..8b9a9913183d 100644 --- a/core/java/com/android/internal/statusbar/RegisterStatusBarResult.java +++ b/core/java/com/android/internal/statusbar/RegisterStatusBarResult.java @@ -16,6 +16,7 @@ package com.android.internal.statusbar; +import android.inputmethodservice.InputMethodService; import android.os.IBinder; import android.os.Parcel; import android.os.Parcelable; @@ -31,7 +32,9 @@ public final class RegisterStatusBarResult implements Parcelable { public final int mDisabledFlags1; // switch[0] public final int mAppearance; // switch[1] public final AppearanceRegion[] mAppearanceRegions; // switch[2] + @InputMethodService.ImeWindowVisibility public final int mImeWindowVis; // switch[3] + @InputMethodService.BackDispositionMode public final int mImeBackDisposition; // switch[4] public final boolean mShowImeSwitcher; // switch[5] public final int mDisabledFlags2; // switch[6] @@ -44,10 +47,12 @@ public final class RegisterStatusBarResult implements Parcelable { public final LetterboxDetails[] mLetterboxDetails; public RegisterStatusBarResult(ArrayMap<String, StatusBarIcon> icons, int disabledFlags1, - int appearance, AppearanceRegion[] appearanceRegions, int imeWindowVis, - int imeBackDisposition, boolean showImeSwitcher, int disabledFlags2, IBinder imeToken, - boolean navbarColorManagedByIme, int behavior, int requestedVisibleTypes, - String packageName, int transientBarTypes, LetterboxDetails[] letterboxDetails) { + int appearance, AppearanceRegion[] appearanceRegions, + @InputMethodService.ImeWindowVisibility int imeWindowVis, + @InputMethodService.BackDispositionMode int imeBackDisposition, boolean showImeSwitcher, + int disabledFlags2, IBinder imeToken, boolean navbarColorManagedByIme, int behavior, + int requestedVisibleTypes, String packageName, int transientBarTypes, + LetterboxDetails[] letterboxDetails) { mIcons = new ArrayMap<>(icons); mDisabledFlags1 = disabledFlags1; mAppearance = appearance; diff --git a/core/java/com/android/internal/util/TraceBuffer.java b/core/java/com/android/internal/util/TraceBuffer.java index fcc77bd4f043..c23e90254179 100644 --- a/core/java/com/android/internal/util/TraceBuffer.java +++ b/core/java/com/android/internal/util/TraceBuffer.java @@ -18,6 +18,7 @@ package com.android.internal.util; import android.util.proto.ProtoOutputStream; +import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import java.io.File; @@ -39,12 +40,13 @@ import java.util.function.Consumer; * {@hide} */ public class TraceBuffer<P, S extends P, T extends P> { - private final Object mBufferLock = new Object(); - private final ProtoProvider<P, S, T> mProtoProvider; + @GuardedBy("this") private final Queue<T> mBuffer = new ArrayDeque<>(); private final Consumer mProtoDequeuedCallback; + @GuardedBy("this") private int mBufferUsedSize; + @GuardedBy("this") private int mBufferCapacity; /** @@ -115,18 +117,18 @@ public class TraceBuffer<P, S extends P, T extends P> { resetBuffer(); } - public int getAvailableSpace() { + public synchronized int getAvailableSpace() { return mBufferCapacity - mBufferUsedSize; } /** * Returns buffer size. */ - public int size() { + public synchronized int size() { return mBuffer.size(); } - public void setCapacity(int capacity) { + public synchronized void setCapacity(int capacity) { mBufferCapacity = capacity; } @@ -137,22 +139,19 @@ public class TraceBuffer<P, S extends P, T extends P> { * @throws IllegalStateException if the element cannot be added because it is larger * than the buffer size. */ - public void add(T proto) { + public synchronized void add(T proto) { int protoLength = mProtoProvider.getItemSize(proto); if (protoLength > mBufferCapacity) { throw new IllegalStateException("Trace object too large for the buffer. Buffer size:" + mBufferCapacity + " Object size: " + protoLength); } - synchronized (mBufferLock) { - discardOldest(protoLength); - mBuffer.add(proto); - mBufferUsedSize += protoLength; - mBufferLock.notify(); - } + discardOldest(protoLength); + mBuffer.add(proto); + mBufferUsedSize += protoLength; } @VisibleForTesting - public boolean contains(byte[] other) { + public synchronized boolean contains(byte[] other) { return mBuffer.stream() .anyMatch(p -> Arrays.equals(mProtoProvider.getBytes(p), other)); } @@ -160,15 +159,13 @@ public class TraceBuffer<P, S extends P, T extends P> { /** * Writes the trace buffer to disk inside the encapsulatingProto. */ - public void writeTraceToFile(File traceFile, S encapsulatingProto) + public synchronized void writeTraceToFile(File traceFile, S encapsulatingProto) throws IOException { - synchronized (mBufferLock) { - traceFile.delete(); - try (OutputStream os = new FileOutputStream(traceFile)) { - traceFile.setReadable(true /* readable */, false /* ownerOnly */); - mProtoProvider.write(encapsulatingProto, mBuffer, os); - os.flush(); - } + traceFile.delete(); + try (OutputStream os = new FileOutputStream(traceFile)) { + traceFile.setReadable(true /* readable */, false /* ownerOnly */); + mProtoProvider.write(encapsulatingProto, mBuffer, os); + os.flush(); } } @@ -199,31 +196,27 @@ public class TraceBuffer<P, S extends P, T extends P> { /** * Removes all elements from the buffer */ - public void resetBuffer() { - synchronized (mBufferLock) { - if (mProtoDequeuedCallback != null) { - for (T item : mBuffer) { - mProtoDequeuedCallback.accept(item); - } + public synchronized void resetBuffer() { + if (mProtoDequeuedCallback != null) { + for (T item : mBuffer) { + mProtoDequeuedCallback.accept(item); } - mBuffer.clear(); - mBufferUsedSize = 0; } + mBuffer.clear(); + mBufferUsedSize = 0; } @VisibleForTesting - public int getBufferSize() { + public synchronized int getBufferSize() { return mBufferUsedSize; } /** * Returns the buffer status in human-readable form. */ - public String getStatus() { - synchronized (mBufferLock) { - return "Buffer size: " + mBufferCapacity + " bytes" + "\n" - + "Buffer usage: " + mBufferUsedSize + " bytes" + "\n" - + "Elements in the buffer: " + mBuffer.size(); - } + public synchronized String getStatus() { + return "Buffer size: " + mBufferCapacity + " bytes" + "\n" + + "Buffer usage: " + mBufferUsedSize + " bytes" + "\n" + + "Elements in the buffer: " + mBuffer.size(); } } diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java index 92cfa670045e..361bdce715de 100644 --- a/core/java/com/android/internal/widget/LockPatternUtils.java +++ b/core/java/com/android/internal/widget/LockPatternUtils.java @@ -47,6 +47,7 @@ import android.os.Message; import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemClock; +import android.os.SystemProperties; import android.os.UserHandle; import android.os.UserManager; import android.os.storage.StorageManager; @@ -160,9 +161,17 @@ public class LockPatternUtils { */ public static final int VERIFY_FLAG_REQUEST_GK_PW_HANDLE = 1 << 0; + /** + * Flag provided to {@link #verifyCredential(LockscreenCredential, int, int)} . If set, the + * method writes the password data to the repair mode file after the credential is verified + * successfully. + */ + public static final int VERIFY_FLAG_WRITE_REPAIR_MODE_PW = 1 << 1; + @Retention(RetentionPolicy.SOURCE) @IntDef(flag = true, value = { - VERIFY_FLAG_REQUEST_GK_PW_HANDLE + VERIFY_FLAG_REQUEST_GK_PW_HANDLE, + VERIFY_FLAG_WRITE_REPAIR_MODE_PW }) public @interface VerifyFlag {} @@ -171,6 +180,11 @@ public class LockPatternUtils { */ public static final int USER_FRP = UserHandle.USER_NULL + 1; + /** + * Special user id for triggering the exiting repair mode verification flow. + */ + public static final int USER_REPAIR_MODE = UserHandle.USER_NULL + 2; + public final static String PATTERN_EVER_CHOSEN_KEY = "lockscreen.patterneverchosen"; public final static String PASSWORD_TYPE_KEY = "lockscreen.password_type"; @Deprecated @@ -201,6 +215,8 @@ public class LockPatternUtils { public static final String CURRENT_LSKF_BASED_PROTECTOR_ID_KEY = "sp-handle"; public static final String PASSWORD_HISTORY_DELIMITER = ","; + private static final String GSI_RUNNING_PROP = "ro.gsid.image_running"; + /** * drives the pin auto confirmation feature availability in code logic. */ @@ -389,7 +405,7 @@ public class LockPatternUtils { @UnsupportedAppUsage public void reportFailedPasswordAttempt(int userId) { - if (userId == USER_FRP && frpCredentialEnabled(mContext)) { + if (isSpecialUserId(mContext, userId, /* checkDeviceSupported= */ true)) { return; } getDevicePolicyManager().reportFailedPasswordAttempt(userId); @@ -398,7 +414,7 @@ public class LockPatternUtils { @UnsupportedAppUsage public void reportSuccessfulPasswordAttempt(int userId) { - if (userId == USER_FRP && frpCredentialEnabled(mContext)) { + if (isSpecialUserId(mContext, userId, /* checkDeviceSupported= */ true)) { return; } getDevicePolicyManager().reportSuccessfulPasswordAttempt(userId); @@ -406,21 +422,21 @@ public class LockPatternUtils { } public void reportPasswordLockout(int timeoutMs, int userId) { - if (userId == USER_FRP && frpCredentialEnabled(mContext)) { + if (isSpecialUserId(mContext, userId, /* checkDeviceSupported= */ true)) { return; } getTrustManager().reportUnlockLockout(timeoutMs, userId); } public int getCurrentFailedPasswordAttempts(int userId) { - if (userId == USER_FRP && frpCredentialEnabled(mContext)) { + if (isSpecialUserId(mContext, userId, /* checkDeviceSupported= */ true)) { return 0; } return getDevicePolicyManager().getCurrentFailedPasswordAttempts(userId); } public int getMaximumFailedPasswordsForWipe(int userId) { - if (userId == USER_FRP && frpCredentialEnabled(mContext)) { + if (isSpecialUserId(mContext, userId, /* checkDeviceSupported= */ true)) { return 0; } return getDevicePolicyManager().getMaximumFailedPasswordsForWipe( @@ -757,6 +773,17 @@ public class LockPatternUtils { } } + /** Returns the credential type corresponding to the given PIN or password quality. */ + public static int pinOrPasswordQualityToCredentialType(int quality) { + if (isQualityAlphabeticPassword(quality)) { + return CREDENTIAL_TYPE_PASSWORD; + } + if (isQualityNumericPin(quality)) { + return CREDENTIAL_TYPE_PIN; + } + throw new IllegalArgumentException("Quality is neither Pin nor password: " + quality); + } + /** * Save a new lockscreen credential. * @@ -991,7 +1018,7 @@ public class LockPatternUtils { } @Override public boolean shouldBypassCache(Integer userHandle) { - return userHandle == USER_FRP; + return isSpecialUserId(userHandle); } }; @@ -1105,9 +1132,10 @@ public class LockPatternUtils { @UnsupportedAppUsage public long setLockoutAttemptDeadline(int userId, int timeoutMs) { final long deadline = SystemClock.elapsedRealtime() + timeoutMs; - if (userId == USER_FRP) { - // For secure password storage (that is required for FRP), the underlying storage also - // enforces the deadline. Since we cannot store settings for the FRP user, don't. + if (isSpecialUserId(userId)) { + // For secure password storage (that is required for special users such as FRP), the + // underlying storage also enforces the deadline. Since we cannot store settings + // for special users, don't. return deadline; } mLockoutDeadlines.put(userId, deadline); @@ -1847,6 +1875,64 @@ public class LockPatternUtils { } /** + * Return {@code true} if repair mode is supported by the device. + */ + public static boolean isRepairModeSupported(Context context) { + return context.getResources().getBoolean( + com.android.internal.R.bool.config_repairModeSupported); + } + + /** + * Return {@code true} if repair mode is active on the device. + */ + public static boolean isRepairModeActive(Context context) { + return Settings.Global.getInt(context.getContentResolver(), + Settings.Global.REPAIR_MODE_ACTIVE, /* def= */ 0) > 0; + } + + /** + * Return {@code true} if repair mode is supported by the device and the user has been granted + * admin privileges. + */ + public static boolean canUserEnterRepairMode(Context context, UserInfo info) { + return info != null && info.isAdmin() && isRepairModeSupported(context); + } + + /** + * Return {@code true} if GSI is running on the device. + */ + public static boolean isGsiRunning() { + return SystemProperties.getInt(GSI_RUNNING_PROP, 0) > 0; + } + + /** + * Return {@code true} if the given user id is a special user such as {@link #USER_FRP}. + */ + public static boolean isSpecialUserId(int userId) { + return isSpecialUserId(/* context= */ null, userId, /* checkDeviceSupported= */ false); + } + + /** + * Return {@code true} if the given user id is a special user for the verification flow. + * + * @param checkDeviceSupported {@code true} to check the specified user is supported + * by the device. + */ + private static boolean isSpecialUserId(@Nullable Context context, int userId, + boolean checkDeviceSupported) { + switch (userId) { + case USER_FRP: + if (checkDeviceSupported) return frpCredentialEnabled(context); + return true; + + case USER_REPAIR_MODE: + if (checkDeviceSupported) return isRepairModeSupported(context); + return true; + } + return false; + } + + /** * Attempt to rederive the unified work challenge for the specified profile user and unlock the * user. If successful, this would allow the user to leave quiet mode automatically without * additional user authentication. diff --git a/core/java/com/android/internal/widget/PointerLocationView.java b/core/java/com/android/internal/widget/PointerLocationView.java index f55d15de1d51..e65b4b65945f 100644 --- a/core/java/com/android/internal/widget/PointerLocationView.java +++ b/core/java/com/android/internal/widget/PointerLocationView.java @@ -163,6 +163,8 @@ public class PointerLocationView extends View implements InputDeviceListener, @UnsupportedAppUsage private boolean mPrintCoords = true; + private float mDensity; + public PointerLocationView(Context c) { super(c); setFocusableInTouchMode(true); @@ -365,11 +367,8 @@ public class PointerLocationView extends View implements InputDeviceListener, drawOval(canvas, ps.mCoords.x, ps.mCoords.y, ps.mCoords.toolMajor, ps.mCoords.toolMinor, ps.mCoords.orientation, mPaint); - // Draw the orientation arrow. - float arrowSize = ps.mCoords.toolMajor * 0.7f; - if (arrowSize < 20) { - arrowSize = 20; - } + // Draw the orientation arrow, and ensure it has a minimum size of 24dp. + final float arrowSize = Math.max(ps.mCoords.toolMajor * 0.7f, 24 * mDensity); mPaint.setARGB(255, pressureLevel, 255, 0); float orientationVectorX = (float) (Math.sin(ps.mCoords.orientation) * arrowSize); @@ -398,7 +397,7 @@ public class PointerLocationView extends View implements InputDeviceListener, canvas.drawCircle( ps.mCoords.x + orientationVectorX * tiltScale, ps.mCoords.y + orientationVectorY * tiltScale, - 3.0f, mPaint); + 3.0f * mDensity, mPaint); // Draw the current bounding box if (ps.mHasBoundingBox) { @@ -1003,10 +1002,10 @@ public class PointerLocationView extends View implements InputDeviceListener, // Compute size by display density. private void configureDensityDependentFactors() { - final float density = getResources().getDisplayMetrics().density; - mTextPaint.setTextSize(10 * density); - mPaint.setStrokeWidth(1 * density); - mCurrentPointPaint.setStrokeWidth(1 * density); - mPathPaint.setStrokeWidth(1 * density); + mDensity = getResources().getDisplayMetrics().density; + mTextPaint.setTextSize(10 * mDensity); + mPaint.setStrokeWidth(1 * mDensity); + mCurrentPointPaint.setStrokeWidth(1 * mDensity); + mPathPaint.setStrokeWidth(1 * mDensity); } } diff --git a/core/jni/android_os_GraphicsEnvironment.cpp b/core/jni/android_os_GraphicsEnvironment.cpp index 01dbceb38d3a..d94b9828808b 100644 --- a/core/jni/android_os_GraphicsEnvironment.cpp +++ b/core/jni/android_os_GraphicsEnvironment.cpp @@ -49,10 +49,10 @@ void setGpuStats_native(JNIEnv* env, jobject clazz, jstring driverPackageName, appPackageNameChars.c_str(), vulkanVersion); } -void setAngleInfo_native(JNIEnv* env, jobject clazz, jstring path, jstring appName, +void setAngleInfo_native(JNIEnv* env, jobject clazz, jstring path, jstring packageName, jstring devOptIn, jobjectArray featuresObj) { ScopedUtfChars pathChars(env, path); - ScopedUtfChars appNameChars(env, appName); + ScopedUtfChars packageNameChars(env, packageName); ScopedUtfChars devOptInChars(env, devOptIn); std::vector<std::string> features; @@ -73,15 +73,10 @@ void setAngleInfo_native(JNIEnv* env, jobject clazz, jstring path, jstring appNa } } - android::GraphicsEnv::getInstance().setAngleInfo(pathChars.c_str(), appNameChars.c_str(), + android::GraphicsEnv::getInstance().setAngleInfo(pathChars.c_str(), packageNameChars.c_str(), devOptInChars.c_str(), features); } -bool shouldUseAngle_native(JNIEnv* env, jobject clazz, jstring appName) { - ScopedUtfChars appNameChars(env, appName); - return android::GraphicsEnv::getInstance().shouldUseAngle(appNameChars.c_str()); -} - void setLayerPaths_native(JNIEnv* env, jobject clazz, jobject classLoader, jstring layerPaths) { android::NativeLoaderNamespace* appNamespace = android::FindNativeLoaderNamespaceByClassLoader( env, classLoader); @@ -126,8 +121,6 @@ const JNINativeMethod g_methods[] = { {"setAngleInfo", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;)V", reinterpret_cast<void*>(setAngleInfo_native)}, - {"getShouldUseAngle", "(Ljava/lang/String;)Z", - reinterpret_cast<void*>(shouldUseAngle_native)}, {"setLayerPaths", "(Ljava/lang/ClassLoader;Ljava/lang/String;)V", reinterpret_cast<void*>(setLayerPaths_native)}, {"setDebugLayers", "(Ljava/lang/String;)V", reinterpret_cast<void*>(setDebugLayers_native)}, diff --git a/core/proto/android/input/keyboard_configured.proto b/core/proto/android/input/keyboard_configured.proto new file mode 100644 index 000000000000..16990087b319 --- /dev/null +++ b/core/proto/android/input/keyboard_configured.proto @@ -0,0 +1,50 @@ +/* + * Copyright 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +syntax = "proto2"; + +package com.android.internal.os; + +option java_outer_classname = "KeyboardConfiguredProto"; + +/** + * RepeatedKeyboardLayout proto from input_extension_atoms.proto, + * duplicated here so that it's accessible in the build. + * Must be kept in sync with the version in input_extension_atoms.proto. + */ + +// Message containing the repeated field for KeyboardLayoutConfig +message RepeatedKeyboardLayoutConfig { + repeated KeyboardLayoutConfig keyboard_layout_config = 1; +} + +// Keyboard layout configured when the device is connected +// used in KeyboardConfigured atom +message KeyboardLayoutConfig { + // Keyboard configuration details + // Layout type mappings found at: + // frameworks/base/core/res/res/values/attrs.xml + optional int32 keyboard_layout_type = 1; + // PK language language tag (e.g. en-US, ru-Cyrl, etc). This will follow + // BCP-47 language tag standards. + optional string keyboard_language_tag = 2; + // Selected keyboard layout name (e.g. English(US), English(Dvorak), etc.) + optional string keyboard_layout_name = 3; + // Criteria for layout selection (such as user, device, virtual keyboard based) + // IntDef annotation at: + // services/core/java/com/android/server/input/KeyboardMetricsCollector.java + optional int32 layout_selection_criteria = 4; +} diff --git a/core/res/res/drawable/ic_bluetooth_share_icon.xml b/core/res/res/drawable/ic_bluetooth_share_icon.xml deleted file mode 100644 index 6acfd57e669f..000000000000 --- a/core/res/res/drawable/ic_bluetooth_share_icon.xml +++ /dev/null @@ -1,27 +0,0 @@ -<!-- - Copyright (C) 2019 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> -<!-- This drawable should only be used by the Bluetooth application for its share target icon. --> -<vector xmlns:android="http://schemas.android.com/apk/res/android" - android:width="24dp" - android:height="24dp" - android:viewportWidth="24" - android:viewportHeight="24" - android:tint="@*android:color/accent_device_default_light"> - - <path - android:fillColor="@android:color/white" - android:pathData="M17.71,7.71L12,2h-1v7.59L6.41,5L5,6.41L10.59,12L5,17.59L6.41,19L11,14.41V22h1l5.71-5.71L13.41,12L17.71,7.71z M13,5.83 l1.88,1.88L13,9.59V5.83z M14.88,16.29L13,18.17v-3.76L14.88,16.29z" /> -</vector> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index ee8c0f83304e..e643240932f1 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -1675,16 +1675,56 @@ in the config_autoBrightnessLevels array. This array should have size one greater than the size of the config_autoBrightnessLevels array. The brightness values must be between 0 and 255 and be non-decreasing. + This must be overridden in platform specific overlays --> <integer-array name="config_autoBrightnessButtonBacklightValues"> </integer-array> + <!-- Smoothing constant for Ambient keyboard backlight change. It should contain value + in the range (0.0, 1.0] that will be used to calculate smoothed lux values using + simple exponential smoothing. This value indicated how quickly we transition to + the lux values provided by the Ambient light sensor. + Simple formula for newLuxValue = (1-constant)*currLuxValue + constant*rawLuxValue, where + rawLuxValue is the value provided by the Ambient light sensor. (e.g. value of 1.0 means we + immediately start using the value provided by the Ambient light sensor) + This must be overridden in platform specific overlays --> + <item name="config_autoKeyboardBrightnessSmoothingConstant" format="float" type="dimen"> + 1.0 + </item> + <!-- Array of output values for keyboard backlight corresponding to the lux values - in the config_autoBrightnessLevels array. This array should have size one greater - than the size of the config_autoBrightnessLevels array. + in the config_autoKeyboardBacklight(Increase/Decrease)LuxThreshold arrays. The brightness values must be between 0 and 255 and be non-decreasing. - This must be overridden in platform specific overlays --> - <integer-array name="config_autoBrightnessKeyboardBacklightValues"> + + This can be overridden in platform specific overlays --> + <integer-array name="config_autoKeyboardBacklightBrightnessValues"> + <item>102</item> + <item>153</item> + <item>0</item> + </integer-array> + + <!-- Array of threshold values for keyboard backlight corresponding to the values + in the config_autoKeyboardBacklightBrightnessValues array. + These lux values indicate when to move to a lower keyboard backlight value, + as defined in config_autoKeyboardBacklightBrightnessValues array. + + This can be overridden in platform specific overlays --> + <integer-array name="config_autoKeyboardBacklightDecreaseLuxThreshold"> + <item>-1</item> + <item>40</item> + <item>150</item> + </integer-array> + + <!-- Array of threshold values for keyboard backlight corresponding to the values + in the config_autoKeyboardBacklightBrightnessValues array. + These lux values indicate when to move to a higher keyboard backlight value, + as defined in config_autoKeyboardBacklightBrightnessValues array. + + This can be overridden in platform specific overlays --> + <integer-array name="config_autoKeyboardBacklightIncreaseLuxThreshold"> + <item>55</item> + <item>200</item> + <item>-1</item> </integer-array> <!-- An array describing the screen's backlight values corresponding to the brightness @@ -1817,6 +1857,17 @@ specified --> <string name="default_wallpaper_component" translatable="false">@null</string> + <!-- Default wallpaper component per device color map, each item is a comma separated key-value + pair with key being a device color and value being the corresponding wallpaper component. + The component with its key matching the device color will be the default wallpaper, the + default wallpaper component will be the default if this config is not specified. + + E.g. for SLV color, and com.android.example/com.android.example.SlVDefaultWallpaper + <item>SLV,com.android.example/com.android.example.SlVDefaultWallpaper</item> --> + <string-array name="default_wallpaper_component_per_device_color" translatable="false"> + <!-- Add packages here --> + </string-array> + <!-- By default a product has no distinct default lock wallpaper --> <item name="default_lock_wallpaper" type="drawable">@null</item> @@ -4419,6 +4470,14 @@ --> <string name="config_defaultContentCaptureService" translatable="false"></string> + <!-- The package name for the system's content protection service. + This service must be trusted, as it can be activated without explicit consent of the user. + If no service with the specified name exists on the device, content protection will be + disabled. + Example: "com.android.contentprotection/.ContentProtectionService" + --> + <string name="config_defaultContentProtectionService" translatable="false"></string> + <!-- The package name for the system's augmented autofill service. This service must be trusted, as it can be activated without explicit consent of the user. If no service with the specified name exists on the device, augmented autofill wil be @@ -6185,7 +6244,7 @@ <!-- Flag indicating whether the show Stylus pointer icon. If set, a pointer icon will be shown over the location of a stylus pointer.--> - <bool name="config_enableStylusPointerIcon">false</bool> + <bool name="config_enableStylusPointerIcon">true</bool> <!-- Determines whether SafetyCenter feature is enabled. --> <bool name="config_enableSafetyCenter">true</bool> @@ -6446,4 +6505,9 @@ <!-- Whether the AOSP support for app cloning building blocks is to be enabled for the device. --> <bool name="config_enableAppCloningBuildingBlocks">true</bool> + + <!-- Enables or disables support for repair mode. The feature creates a secure + environment to protect the user's privacy when the device is being repaired. + Off by default, since OEMs may have had a similar feature on their devices. --> + <bool name="config_repairModeSupported">false</bool> </resources> diff --git a/core/res/res/values/config_telephony.xml b/core/res/res/values/config_telephony.xml index 4ae54a052859..08c40ba0e823 100644 --- a/core/res/res/values/config_telephony.xml +++ b/core/res/res/values/config_telephony.xml @@ -67,6 +67,15 @@ <bool name="auto_data_switch_ping_test_before_switch">true</bool> <java-symbol type="bool" name="auto_data_switch_ping_test_before_switch" /> + <!-- Define the tolerated gap of score for auto data switch decision, larger than which the + device will switch to the SIM with higher score. The score is used in conjunction with the + score table defined in + CarrierConfigManager#KEY_AUTO_DATA_SWITCH_RAT_SIGNAL_SCORE_STRING_ARRAY. + If 0, the device always switch to the higher score SIM. + If < 0, the network type and signal strength based auto switch is disabled. --> + <integer name="auto_data_switch_score_tolerance">3000</integer> + <java-symbol type="integer" name="auto_data_switch_score_tolerance" /> + <!-- Boolean indicating whether the Iwlan data service supports persistence of iwlan ipsec tunnels across service restart. If iwlan tunnels are not persisted across restart, Framework will clean up dangling data connections when service restarts --> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 80a997717cc3..11f50dfdf84b 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -520,6 +520,7 @@ <java-symbol type="dimen" name="config_viewConfigurationHandwritingSlop" /> <java-symbol type="dimen" name="config_viewConfigurationHoverSlop" /> <java-symbol type="dimen" name="config_ambiguousGestureMultiplier" /> + <java-symbol type="dimen" name="config_autoKeyboardBrightnessSmoothingConstant" /> <java-symbol type="dimen" name="config_viewMinFlingVelocity" /> <java-symbol type="dimen" name="config_viewMaxFlingVelocity" /> <java-symbol type="dimen" name="config_viewMinRotaryEncoderFlingVelocity" /> @@ -1888,11 +1889,13 @@ <java-symbol type="anim" name="dream_activity_open_enter" /> <java-symbol type="anim" name="dream_activity_close_exit" /> <java-symbol type="array" name="config_autoBrightnessButtonBacklightValues" /> - <java-symbol type="array" name="config_autoBrightnessKeyboardBacklightValues" /> <java-symbol type="array" name="config_autoBrightnessLcdBacklightValues" /> <java-symbol type="array" name="config_autoBrightnessLcdBacklightValues_doze" /> <java-symbol type="array" name="config_autoBrightnessLevels" /> <java-symbol type="array" name="config_autoBrightnessLevelsIdle" /> + <java-symbol type="array" name="config_autoKeyboardBacklightBrightnessValues" /> + <java-symbol type="array" name="config_autoKeyboardBacklightDecreaseLuxThreshold" /> + <java-symbol type="array" name="config_autoKeyboardBacklightIncreaseLuxThreshold" /> <java-symbol type="array" name="config_ambientThresholdLevels" /> <java-symbol type="array" name="config_ambientBrighteningThresholds" /> <java-symbol type="array" name="config_ambientDarkeningThresholds" /> @@ -2116,6 +2119,7 @@ <java-symbol type="string" name="data_usage_rapid_body" /> <java-symbol type="string" name="data_usage_rapid_app_body" /> <java-symbol type="string" name="default_wallpaper_component" /> + <java-symbol type="array" name="default_wallpaper_component_per_device_color" /> <java-symbol type="string" name="device_storage_monitor_notification_channel" /> <java-symbol type="string" name="dlg_ok" /> <java-symbol type="string" name="dump_heap_notification" /> @@ -3763,6 +3767,7 @@ <java-symbol type="string" name="config_defaultTextClassifierPackage" /> <java-symbol type="string" name="config_defaultWellbeingPackage" /> <java-symbol type="string" name="config_defaultContentCaptureService" /> + <java-symbol type="string" name="config_defaultContentProtectionService" /> <java-symbol type="string" name="config_defaultAugmentedAutofillService" /> <java-symbol type="string" name="config_defaultTranslationService" /> <java-symbol type="string" name="config_defaultAppPredictionService" /> @@ -4913,6 +4918,8 @@ <java-symbol type="bool" name="config_safetyProtectionEnabled" /> + <java-symbol type="bool" name="config_repairModeSupported" /> + <java-symbol type="string" name="config_devicePolicyManagementUpdater" /> <java-symbol type="string" name="config_deviceSpecificDeviceStatePolicyProvider" /> diff --git a/core/tests/BroadcastRadioTests/Android.bp b/core/tests/BroadcastRadioTests/Android.bp index 436f058f3cbc..85d54e02d318 100644 --- a/core/tests/BroadcastRadioTests/Android.bp +++ b/core/tests/BroadcastRadioTests/Android.bp @@ -45,6 +45,7 @@ android_test { libs: ["android.test.base"], test_suites: [ "general-tests", + "automotive-general-tests", ], // mockito-target-inline dependency jni_libs: [ diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml index c1deba3288e5..129de649a4b5 100644 --- a/core/tests/coretests/AndroidManifest.xml +++ b/core/tests/coretests/AndroidManifest.xml @@ -1716,6 +1716,17 @@ <meta-data android:name="android.view.im" android:resource="@xml/ime_meta_handwriting"/> </service> + + <activity android:name="android.widget.PointerIconTestActivity" + android:label="PointerIconTestActivity" + android:screenOrientation="portrait" + android:exported="true" + android:theme="@android:style/Theme.Material.Light"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" /> + </intent-filter> + </activity> </application> <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" diff --git a/core/tests/coretests/res/layout/pointer_icon_test.xml b/core/tests/coretests/res/layout/pointer_icon_test.xml new file mode 100644 index 000000000000..a2a64473ae23 --- /dev/null +++ b/core/tests/coretests/res/layout/pointer_icon_test.xml @@ -0,0 +1,59 @@ +<?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. + --> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent"> + <TextView + android:id="@+id/textview" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="Test"/> + + <EditText + android:id="@+id/edittext" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="Test"/> + + <Button + android:id="@+id/button" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="Test"/> + + <ImageButton + android:id="@+id/imagebutton" + android:layout_width="50dp" + android:layout_height="50dp"/> + + <Spinner + android:id="@+id/spinner" + android:layout_width="50dp" + android:layout_height="50dp"/> + + <RadialTimePickerView + android:id="@+id/timepicker" + android:layout_width="200dp" + android:layout_height="200dp"/> + + <CalendarView + android:id="@+id/calendar" + android:layout_width="wrap_content" + android:layout_height="wrap_content"/> +</LinearLayout>
\ No newline at end of file diff --git a/core/tests/coretests/res/values/overlayable_icons_test.xml b/core/tests/coretests/res/values/overlayable_icons_test.xml index 7ea1848a723e..6a50e9ae58f7 100644 --- a/core/tests/coretests/res/values/overlayable_icons_test.xml +++ b/core/tests/coretests/res/values/overlayable_icons_test.xml @@ -21,7 +21,6 @@ <item>@*android:drawable/ic_audio_alarm</item> <item>@*android:drawable/ic_audio_alarm_mute</item> <item>@*android:drawable/ic_battery_80_24dp</item> - <item>@*android:drawable/ic_bluetooth_share_icon</item> <item>@*android:drawable/ic_bt_headphones_a2dp</item> <item>@*android:drawable/ic_bt_headset_hfp</item> <item>@*android:drawable/ic_bt_hearing_aid</item> diff --git a/core/tests/coretests/src/android/app/activity/ActivityManagerTest.java b/core/tests/coretests/src/android/app/activity/ActivityManagerTest.java index 81eb213ce78a..5ac99db3aea5 100644 --- a/core/tests/coretests/src/android/app/activity/ActivityManagerTest.java +++ b/core/tests/coretests/src/android/app/activity/ActivityManagerTest.java @@ -128,6 +128,7 @@ public class ActivityManagerTest extends AndroidTestCase { 0x222222, // colorBackground 0x333333, // statusBarColor 0x444444, // navigationBarColor + 0, // statusBarAppearance true, // ensureStatusBarContrastWhenTransparent true, // ensureNavigationBarContrastWhenTransparent RESIZE_MODE_RESIZEABLE, // resizeMode @@ -152,6 +153,7 @@ public class ActivityManagerTest extends AndroidTestCase { 0x222222, // colorBackground 0x333333, // statusBarColor 0x444444, // navigationBarColor + 0, // statusBarAppearance false, // ensureStatusBarContrastWhenTransparent false, // ensureNavigationBarContrastWhenTransparent RESIZE_MODE_UNRESIZEABLE, // resizeMode @@ -167,6 +169,7 @@ public class ActivityManagerTest extends AndroidTestCase { 0x2222222, // colorBackground 0x3333332, // statusBarColor 0x4444442, // navigationBarColor + 0, // statusBarAppearance true, // ensureStatusBarContrastWhenTransparent true, // ensureNavigationBarContrastWhenTransparent RESIZE_MODE_RESIZEABLE, // resizeMode @@ -197,6 +200,7 @@ public class ActivityManagerTest extends AndroidTestCase { 0x222222, // colorBackground 0x333333, // statusBarColor 0x444444, // navigationBarColor + 0, // statusBarAppearance false, // ensureStatusBarContrastWhenTransparent false, // ensureNavigationBarContrastWhenTransparent RESIZE_MODE_UNRESIZEABLE, // resizeMode @@ -219,6 +223,7 @@ public class ActivityManagerTest extends AndroidTestCase { 0x222222, // colorBackground 0x333333, // statusBarColor 0x444444, // navigationBarColor + 0, // statusBarAppearance false, // ensureStatusBarContrastWhenTransparent false, // ensureNavigationBarContrastWhenTransparent RESIZE_MODE_UNRESIZEABLE, // resizeMode @@ -250,6 +255,7 @@ public class ActivityManagerTest extends AndroidTestCase { assertEquals(td1.getBackgroundColor(), td2.getBackgroundColor()); assertEquals(td1.getStatusBarColor(), td2.getStatusBarColor()); assertEquals(td1.getNavigationBarColor(), td2.getNavigationBarColor()); + assertEquals(td1.getStatusBarAppearance(), td2.getStatusBarAppearance()); assertEquals(td1.getResizeMode(), td2.getResizeMode()); assertEquals(td1.getMinWidth(), td2.getMinWidth()); assertEquals(td1.getMinHeight(), td2.getMinHeight()); diff --git a/core/tests/coretests/src/android/content/ContentCaptureOptionsTest.java b/core/tests/coretests/src/android/content/ContentCaptureOptionsTest.java index c6f4fa27dc83..f8348d28b7fe 100644 --- a/core/tests/coretests/src/android/content/ContentCaptureOptionsTest.java +++ b/core/tests/coretests/src/android/content/ContentCaptureOptionsTest.java @@ -22,6 +22,7 @@ import static org.mockito.Mockito.when; import android.annotation.NonNull; import android.annotation.Nullable; +import android.os.Parcel; import android.util.ArraySet; import android.view.contentcapture.ContentCaptureManager.ContentCaptureClient; @@ -40,16 +41,29 @@ import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class ContentCaptureOptionsTest { - private final ComponentName mContextComponent = new ComponentName("marco", "polo"); - private final ComponentName mComp1 = new ComponentName("comp", "one"); - private final ComponentName mComp2 = new ComponentName("two", "comp"); + private static final ComponentName CONTEXT_COMPONENT = new ComponentName("marco", "polo"); + private static final ComponentName COMPONENT1 = new ComponentName("comp", "one"); + private static final ComponentName COMPONENT2 = new ComponentName("two", "comp"); + private static final ContentCaptureOptions CONTENT_CAPTURE_OPTIONS = + new ContentCaptureOptions( + /* loggingLevel= */ 1000, + /* maxBufferSize= */ 1001, + /* idleFlushingFrequencyMs= */ 1002, + /* textChangeFlushingFrequencyMs= */ 1003, + /* logHistorySize= */ 1004, + /* disableFlushForViewTreeAppearing= */ true, + /* enableReceiver= */ false, + new ContentCaptureOptions.ContentProtectionOptions( + /* enableReceiver= */ true, + /* bufferSize= */ 2001), + /* whitelistedComponents= */ toSet(COMPONENT1, COMPONENT2)); @Mock private Context mContext; @Mock private ContentCaptureClient mClient; @Before public void setExpectation() { - when(mClient.contentCaptureClientGetComponentName()).thenReturn(mContextComponent); + when(mClient.contentCaptureClientGetComponentName()).thenReturn(CONTEXT_COMPONENT); when(mContext.getContentCaptureClient()).thenReturn(mClient); } @@ -67,26 +81,27 @@ public class ContentCaptureOptionsTest { @Test public void testIsWhitelisted_notWhitelisted() { - ContentCaptureOptions options = new ContentCaptureOptions(toSet(mComp1, mComp2)); + ContentCaptureOptions options = new ContentCaptureOptions(toSet(COMPONENT1, COMPONENT2)); assertThat(options.isWhitelisted(mContext)).isFalse(); } @Test public void testIsWhitelisted_whitelisted() { - ContentCaptureOptions options = new ContentCaptureOptions(toSet(mComp1, mContextComponent)); + ContentCaptureOptions options = + new ContentCaptureOptions(toSet(COMPONENT1, CONTEXT_COMPONENT)); assertThat(options.isWhitelisted(mContext)).isTrue(); } @Test public void testIsWhitelisted_invalidContext() { - ContentCaptureOptions options = new ContentCaptureOptions(toSet(mContextComponent)); + ContentCaptureOptions options = new ContentCaptureOptions(toSet(CONTEXT_COMPONENT)); Context invalidContext = mock(Context.class); // has no client assertThat(options.isWhitelisted(invalidContext)).isFalse(); } @Test public void testIsWhitelisted_clientWithNullComponentName() { - ContentCaptureOptions options = new ContentCaptureOptions(toSet(mContextComponent)); + ContentCaptureOptions options = new ContentCaptureOptions(toSet(CONTEXT_COMPONENT)); ContentCaptureClient client = mock(ContentCaptureClient.class); Context context = mock(Context.class); when(context.getContentCaptureClient()).thenReturn(client); @@ -94,8 +109,69 @@ public class ContentCaptureOptionsTest { assertThat(options.isWhitelisted(context)).isFalse(); } + @Test + public void testToString() { + String actual = CONTENT_CAPTURE_OPTIONS.toString(); + + String expected = + new StringBuilder("ContentCaptureOptions [") + .append("loggingLevel=") + .append(CONTENT_CAPTURE_OPTIONS.loggingLevel) + .append(", maxBufferSize=") + .append(CONTENT_CAPTURE_OPTIONS.maxBufferSize) + .append(", idleFlushingFrequencyMs=") + .append(CONTENT_CAPTURE_OPTIONS.idleFlushingFrequencyMs) + .append(", textChangeFlushingFrequencyMs=") + .append(CONTENT_CAPTURE_OPTIONS.textChangeFlushingFrequencyMs) + .append(", logHistorySize=") + .append(CONTENT_CAPTURE_OPTIONS.logHistorySize) + .append(", disableFlushForViewTreeAppearing=") + .append(CONTENT_CAPTURE_OPTIONS.disableFlushForViewTreeAppearing) + .append(", enableReceiver=") + .append(CONTENT_CAPTURE_OPTIONS.enableReceiver) + .append(", contentProtectionOptions=ContentProtectionOptions [") + .append("enableReceiver=") + .append(CONTENT_CAPTURE_OPTIONS.contentProtectionOptions.enableReceiver) + .append(", bufferSize=") + .append(CONTENT_CAPTURE_OPTIONS.contentProtectionOptions.bufferSize) + .append("], whitelisted=") + .append(CONTENT_CAPTURE_OPTIONS.whitelistedComponents) + .append(']') + .toString(); + assertThat(actual).isEqualTo(expected); + } + + @Test + public void testParcelSerializationDeserialization() { + Parcel parcel = Parcel.obtain(); + CONTENT_CAPTURE_OPTIONS.writeToParcel(parcel, /* flags= */ 0); + parcel.setDataPosition(0); + + ContentCaptureOptions actual = ContentCaptureOptions.CREATOR.createFromParcel(parcel); + parcel.recycle(); + + assertThat(actual).isNotNull(); + assertThat(actual.loggingLevel).isEqualTo(CONTENT_CAPTURE_OPTIONS.loggingLevel); + assertThat(actual.maxBufferSize).isEqualTo(CONTENT_CAPTURE_OPTIONS.maxBufferSize); + assertThat(actual.idleFlushingFrequencyMs) + .isEqualTo(CONTENT_CAPTURE_OPTIONS.idleFlushingFrequencyMs); + assertThat(actual.textChangeFlushingFrequencyMs) + .isEqualTo(CONTENT_CAPTURE_OPTIONS.textChangeFlushingFrequencyMs); + assertThat(actual.logHistorySize).isEqualTo(CONTENT_CAPTURE_OPTIONS.logHistorySize); + assertThat(actual.disableFlushForViewTreeAppearing) + .isEqualTo(CONTENT_CAPTURE_OPTIONS.disableFlushForViewTreeAppearing); + assertThat(actual.enableReceiver).isEqualTo(CONTENT_CAPTURE_OPTIONS.enableReceiver); + assertThat(actual.contentProtectionOptions).isNotNull(); + assertThat(actual.contentProtectionOptions.enableReceiver) + .isEqualTo(CONTENT_CAPTURE_OPTIONS.contentProtectionOptions.enableReceiver); + assertThat(actual.contentProtectionOptions.bufferSize) + .isEqualTo(CONTENT_CAPTURE_OPTIONS.contentProtectionOptions.bufferSize); + assertThat(actual.whitelistedComponents) + .containsExactlyElementsIn(CONTENT_CAPTURE_OPTIONS.whitelistedComponents); + } + @NonNull - private ArraySet<ComponentName> toSet(@Nullable ComponentName... comps) { + private static ArraySet<ComponentName> toSet(@Nullable ComponentName... comps) { ArraySet<ComponentName> set = new ArraySet<>(); if (comps != null) { for (int i = 0; i < comps.length; i++) { diff --git a/core/tests/coretests/src/android/service/notification/NotificationRankingUpdateTest.java b/core/tests/coretests/src/android/service/notification/NotificationRankingUpdateTest.java new file mode 100644 index 000000000000..a84ac55f0d5a --- /dev/null +++ b/core/tests/coretests/src/android/service/notification/NotificationRankingUpdateTest.java @@ -0,0 +1,195 @@ +/* + * 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 android.service.notification; + +import static com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags.RANKING_UPDATE_ASHMEM; + +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertFalse; +import static junit.framework.Assert.assertNotNull; +import static junit.framework.Assert.assertNull; +import static junit.framework.Assert.assertTrue; + +import android.app.NotificationChannel; +import android.app.NotificationManager; +import android.os.Parcel; + +import androidx.test.filters.SmallTest; + +import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +@SmallTest +@RunWith(Parameterized.class) +public class NotificationRankingUpdateTest { + + private static final String NOTIFICATION_CHANNEL_ID = "test_channel_id"; + private static final String TEST_KEY = "key"; + + private NotificationChannel mNotificationChannel; + + // TODO(b/284297289): remove this flag set once resolved. + @Parameterized.Parameters(name = "rankingUpdateAshmem={0}") + public static Boolean[] getRankingUpdateAshmem() { + return new Boolean[] { true, false }; + } + + @Parameterized.Parameter + public boolean mRankingUpdateAshmem; + + @Before + public void setUp() { + mNotificationChannel = new NotificationChannel(NOTIFICATION_CHANNEL_ID, "test channel", + NotificationManager.IMPORTANCE_DEFAULT); + + SystemUiSystemPropertiesFlags.TEST_RESOLVER = flag -> { + if (flag.mSysPropKey.equals(RANKING_UPDATE_ASHMEM.mSysPropKey)) { + return mRankingUpdateAshmem; + } + return new SystemUiSystemPropertiesFlags.DebugResolver().isEnabled(flag); + }; + } + + @After + public void tearDown() { + SystemUiSystemPropertiesFlags.TEST_RESOLVER = null; + } + + public NotificationListenerService.Ranking createTestRanking(String key, int rank) { + NotificationListenerService.Ranking ranking = new NotificationListenerService.Ranking(); + ranking.populate( + /* key= */ key, + /* rank= */ rank, + /* matchesInterruptionFilter= */ false, + /* visibilityOverride= */ 0, + /* suppressedVisualEffects= */ 0, + mNotificationChannel.getImportance(), + /* explanation= */ null, + /* overrideGroupKey= */ null, + mNotificationChannel, + /* overridePeople= */ null, + /* snoozeCriteria= */ null, + /* showBadge= */ true, + /* userSentiment= */ 0, + /* hidden= */ false, + /* lastAudiblyAlertedMs= */ -1, + /* noisy= */ false, + /* smartActions= */ null, + /* smartReplies= */ null, + /* canBubble= */ false, + /* isTextChanged= */ false, + /* isConversation= */ false, + /* shortcutInfo= */ null, + /* rankingAdjustment= */ 0, + /* isBubble= */ false, + /* proposedImportance= */ 0, + /* sensitiveContent= */ false + ); + return ranking; + } + + @Test + public void testRankingUpdate_rankingConstructor() { + NotificationListenerService.Ranking ranking = createTestRanking(TEST_KEY, 123); + NotificationRankingUpdate rankingUpdate = new NotificationRankingUpdate( + new NotificationListenerService.Ranking[]{ranking}); + + NotificationListenerService.RankingMap retrievedRankings = rankingUpdate.getRankingMap(); + NotificationListenerService.Ranking retrievedRanking = + new NotificationListenerService.Ranking(); + assertTrue(retrievedRankings.getRanking(TEST_KEY, retrievedRanking)); + assertEquals(123, retrievedRanking.getRank()); + } + + @Test + public void testRankingUpdate_parcelConstructor() { + NotificationListenerService.Ranking ranking = createTestRanking(TEST_KEY, 123); + NotificationRankingUpdate rankingUpdate = new NotificationRankingUpdate( + new NotificationListenerService.Ranking[]{ranking}); + + Parcel parceledRankingUpdate = Parcel.obtain(); + rankingUpdate.writeToParcel(parceledRankingUpdate, 0); + parceledRankingUpdate.setDataPosition(0); + + NotificationRankingUpdate retrievedRankingUpdate = new NotificationRankingUpdate( + parceledRankingUpdate); + + NotificationListenerService.RankingMap retrievedRankings = + retrievedRankingUpdate.getRankingMap(); + assertNotNull(retrievedRankings); + assertTrue(retrievedRankingUpdate.isFdNotNullAndClosed()); + NotificationListenerService.Ranking retrievedRanking = + new NotificationListenerService.Ranking(); + assertTrue(retrievedRankings.getRanking(TEST_KEY, retrievedRanking)); + assertEquals(123, retrievedRanking.getRank()); + assertTrue(retrievedRankingUpdate.equals(rankingUpdate)); + parceledRankingUpdate.recycle(); + } + + @Test + public void testRankingUpdate_emptyParcelInCheck() { + NotificationListenerService.Ranking ranking = createTestRanking(TEST_KEY, 123); + NotificationRankingUpdate rankingUpdate = new NotificationRankingUpdate( + new NotificationListenerService.Ranking[]{ranking}); + + Parcel parceledRankingUpdate = Parcel.obtain(); + rankingUpdate.writeToParcel(parceledRankingUpdate, 0); + + // This will fail to read the parceledRankingUpdate, because the data position hasn't + // been reset, so it'll find no data to read. + NotificationRankingUpdate retrievedRankingUpdate = new NotificationRankingUpdate( + parceledRankingUpdate); + assertNull(retrievedRankingUpdate.getRankingMap()); + } + + @Test + public void testRankingUpdate_describeContents() { + NotificationListenerService.Ranking ranking = createTestRanking(TEST_KEY, 123); + NotificationRankingUpdate rankingUpdate = new NotificationRankingUpdate( + new NotificationListenerService.Ranking[]{ranking}); + assertEquals(0, rankingUpdate.describeContents()); + } + + @Test + public void testRankingUpdate_equals() { + NotificationListenerService.Ranking ranking = createTestRanking(TEST_KEY, 123); + NotificationRankingUpdate rankingUpdate = new NotificationRankingUpdate( + new NotificationListenerService.Ranking[]{ranking}); + // Reflexive equality. + assertTrue(rankingUpdate.equals(rankingUpdate)); + // Null or wrong class inequality. + assertFalse(rankingUpdate.equals(null)); + assertFalse(rankingUpdate.equals(ranking)); + + // Different ranking contents inequality. + NotificationListenerService.Ranking ranking2 = createTestRanking(TEST_KEY, 456); + NotificationRankingUpdate rankingUpdate2 = new NotificationRankingUpdate( + new NotificationListenerService.Ranking[]{ranking2}); + assertFalse(rankingUpdate.equals(rankingUpdate2)); + + // Same ranking contents equality. + ranking2 = createTestRanking(TEST_KEY, 123); + rankingUpdate2 = new NotificationRankingUpdate( + new NotificationListenerService.Ranking[]{ranking2}); + assertTrue(rankingUpdate.equals(rankingUpdate2)); + } +} diff --git a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureManagerTest.java b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureManagerTest.java index 17ed4c478350..101f7c21fa19 100644 --- a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureManagerTest.java +++ b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureManagerTest.java @@ -15,14 +15,17 @@ */ package android.view.contentcapture; +import static android.view.contentcapture.ContentCaptureEvent.TYPE_SESSION_STARTED; + import static com.google.common.truth.Truth.assertThat; -import static org.mockito.Mockito.mock; import static org.testng.Assert.assertThrows; import android.content.ContentCaptureOptions; import android.content.Context; +import com.android.internal.util.RingBuffer; + import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; @@ -37,9 +40,15 @@ import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class ContentCaptureManagerTest { + private static final int BUFFER_SIZE = 100; + + private static final ContentCaptureOptions EMPTY_OPTIONS = new ContentCaptureOptions(null); + @Mock private Context mMockContext; + @Mock private IContentCaptureManager mMockContentCaptureManager; + @Test public void testConstructor_invalidParametersThrowsException() { assertThrows(NullPointerException.class, @@ -48,11 +57,65 @@ public class ContentCaptureManagerTest { } @Test + public void testConstructor_contentProtection_default_bufferNotCreated() { + ContentCaptureManager manager = + new ContentCaptureManager(mMockContext, mMockContentCaptureManager, EMPTY_OPTIONS); + + assertThat(manager.getContentProtectionEventBuffer()).isNull(); + } + + @Test + public void testConstructor_contentProtection_disabled_bufferNotCreated() { + ContentCaptureOptions options = + createOptions( + new ContentCaptureOptions.ContentProtectionOptions( + /* enableReceiver= */ false, BUFFER_SIZE)); + + ContentCaptureManager manager = + new ContentCaptureManager(mMockContext, mMockContentCaptureManager, options); + + assertThat(manager.getContentProtectionEventBuffer()).isNull(); + } + + @Test + public void testConstructor_contentProtection_invalidBufferSize_bufferNotCreated() { + ContentCaptureOptions options = + createOptions( + new ContentCaptureOptions.ContentProtectionOptions( + /* enableReceiver= */ true, /* bufferSize= */ 0)); + + ContentCaptureManager manager = + new ContentCaptureManager(mMockContext, mMockContentCaptureManager, options); + + assertThat(manager.getContentProtectionEventBuffer()).isNull(); + } + + @Test + public void testConstructor_contentProtection_enabled_bufferCreated() { + ContentCaptureOptions options = + createOptions( + new ContentCaptureOptions.ContentProtectionOptions( + /* enableReceiver= */ true, BUFFER_SIZE)); + + ContentCaptureManager manager = + new ContentCaptureManager(mMockContext, mMockContentCaptureManager, options); + RingBuffer<ContentCaptureEvent> buffer = manager.getContentProtectionEventBuffer(); + + assertThat(buffer).isNotNull(); + ContentCaptureEvent[] expected = new ContentCaptureEvent[BUFFER_SIZE]; + int offset = 3; + for (int i = 0; i < BUFFER_SIZE + offset; i++) { + ContentCaptureEvent event = new ContentCaptureEvent(i, TYPE_SESSION_STARTED); + buffer.append(event); + expected[(i + BUFFER_SIZE - offset) % BUFFER_SIZE] = event; + } + assertThat(buffer.toArray()).isEqualTo(expected); + } + + @Test public void testRemoveData_invalidParametersThrowsException() { - final IContentCaptureManager mockService = mock(IContentCaptureManager.class); - final ContentCaptureOptions options = new ContentCaptureOptions(null); final ContentCaptureManager manager = - new ContentCaptureManager(mMockContext, mockService, options); + new ContentCaptureManager(mMockContext, mMockContentCaptureManager, EMPTY_OPTIONS); assertThrows(NullPointerException.class, () -> manager.removeData(null)); } @@ -60,10 +123,8 @@ public class ContentCaptureManagerTest { @Test @SuppressWarnings("GuardedBy") public void testFlushViewTreeAppearingEventDisabled_setAndGet() { - final IContentCaptureManager mockService = mock(IContentCaptureManager.class); - final ContentCaptureOptions options = new ContentCaptureOptions(null); final ContentCaptureManager manager = - new ContentCaptureManager(mMockContext, mockService, options); + new ContentCaptureManager(mMockContext, mMockContentCaptureManager, EMPTY_OPTIONS); assertThat(manager.getFlushViewTreeAppearingEventDisabled()).isFalse(); manager.setFlushViewTreeAppearingEventDisabled(true); @@ -71,4 +132,18 @@ public class ContentCaptureManagerTest { manager.setFlushViewTreeAppearingEventDisabled(false); assertThat(manager.getFlushViewTreeAppearingEventDisabled()).isFalse(); } + + private ContentCaptureOptions createOptions( + ContentCaptureOptions.ContentProtectionOptions contentProtectionOptions) { + return new ContentCaptureOptions( + /* loggingLevel= */ 0, + /* maxBufferSize= */ 0, + /* idleFlushingFrequencyMs= */ 0, + /* textChangeFlushingFrequencyMs= */ 0, + /* logHistorySize= */ 0, + /* disableFlushForViewTreeAppearing= */ false, + /* enableReceiver= */ true, + contentProtectionOptions, + /* whitelistedComponents= */ null); + } } diff --git a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java index 27d58b8cdcb7..23b9b9bdb451 100644 --- a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java +++ b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java @@ -27,9 +27,12 @@ import android.view.ViewStructure; import android.view.autofill.AutofillId; import android.view.contentcapture.ViewNode.ViewStructureImpl; +import com.google.common.collect.ImmutableMap; + import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges; import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges; +import org.junit.Ignore; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TestRule; @@ -37,6 +40,8 @@ import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; +import java.util.Map; + /** * Unit tests for {@link ContentCaptureSession}. * @@ -126,6 +131,7 @@ public class ContentCaptureSessionTest { () -> mSession1.notifyViewsDisappeared(new AutofillId(42, 108), new long[] {666})); } + @Ignore("b/286134492") @Test public void testNotifyViewsDisappeared_noSendTreeEventBeforeU() { MyContentCaptureSession session = new MyContentCaptureSession(121); @@ -135,6 +141,7 @@ public class ContentCaptureSessionTest { assertThat(session.mInternalNotifyViewTreeEventFinishedCount).isEqualTo(0); } + @Ignore("b/286134492") @EnableCompatChanges({ContentCaptureSession.NOTIFY_NODES_DISAPPEAR_NOW_SENDS_TREE_EVENTS}) @Test public void testNotifyViewsDisappeared_sendTreeEventSinceU() { @@ -145,6 +152,34 @@ public class ContentCaptureSessionTest { assertThat(session.mInternalNotifyViewTreeEventFinishedCount).isEqualTo(1); } + @Test + public void testGetFlushReasonAsString() { + int invalidFlushReason = ContentCaptureSession.FLUSH_REASON_VIEW_TREE_APPEARED + 1; + Map<Integer, String> expectedMap = + new ImmutableMap.Builder<Integer, String>() + .put(ContentCaptureSession.FLUSH_REASON_FULL, "FULL") + .put(ContentCaptureSession.FLUSH_REASON_VIEW_ROOT_ENTERED, "VIEW_ROOT") + .put(ContentCaptureSession.FLUSH_REASON_SESSION_STARTED, "STARTED") + .put(ContentCaptureSession.FLUSH_REASON_SESSION_FINISHED, "FINISHED") + .put(ContentCaptureSession.FLUSH_REASON_IDLE_TIMEOUT, "IDLE") + .put(ContentCaptureSession.FLUSH_REASON_TEXT_CHANGE_TIMEOUT, "TEXT_CHANGE") + .put(ContentCaptureSession.FLUSH_REASON_SESSION_CONNECTED, "CONNECTED") + .put(ContentCaptureSession.FLUSH_REASON_FORCE_FLUSH, "FORCE_FLUSH") + .put( + ContentCaptureSession.FLUSH_REASON_VIEW_TREE_APPEARING, + "VIEW_TREE_APPEARING") + .put( + ContentCaptureSession.FLUSH_REASON_VIEW_TREE_APPEARED, + "VIEW_TREE_APPEARED") + .put(invalidFlushReason, "UNKNOWN-" + invalidFlushReason) + .build(); + + expectedMap.forEach( + (reason, expected) -> + assertThat(ContentCaptureSession.getFlushReasonAsString(reason)) + .isEqualTo(expected)); + } + // Cannot use @Spy because we need to pass the session id on constructor private class MyContentCaptureSession extends ContentCaptureSession { int mInternalNotifyViewTreeEventStartedCount = 0; diff --git a/core/tests/coretests/src/android/view/contentcapture/MainContentCaptureSessionTest.java b/core/tests/coretests/src/android/view/contentcapture/MainContentCaptureSessionTest.java new file mode 100644 index 000000000000..3373b8b13273 --- /dev/null +++ b/core/tests/coretests/src/android/view/contentcapture/MainContentCaptureSessionTest.java @@ -0,0 +1,360 @@ +/* + * 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 android.view.contentcapture; + +import static android.view.contentcapture.ContentCaptureEvent.TYPE_SESSION_STARTED; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.anyInt; +import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; + +import android.content.ComponentName; +import android.content.ContentCaptureOptions; +import android.content.Context; +import android.content.pm.ParceledListSlice; +import android.os.Handler; +import android.os.Looper; +import android.view.contentprotection.ContentProtectionEventProcessor; + +import androidx.test.core.app.ApplicationProvider; +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.filters.SmallTest; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * Test for {@link MainContentCaptureSession}. + * + * <p>Run with: {@code atest + * FrameworksCoreTests:android.view.contentcapture.MainContentCaptureSessionTest} + */ +@RunWith(AndroidJUnit4.class) +@SmallTest +public class MainContentCaptureSessionTest { + + private static final int BUFFER_SIZE = 100; + + private static final int REASON = 123; + + private static final ContentCaptureEvent EVENT = + new ContentCaptureEvent(/* sessionId= */ 0, TYPE_SESSION_STARTED); + + private static final ComponentName COMPONENT_NAME = + new ComponentName("com.test.package", "TestClass"); + + private static final Context sContext = ApplicationProvider.getApplicationContext(); + + private static final ContentCaptureManager.StrippedContext sStrippedContext = + new ContentCaptureManager.StrippedContext(sContext); + + @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule(); + + @Mock private IContentCaptureManager mMockSystemServerInterface; + + @Mock private ContentProtectionEventProcessor mMockContentProtectionEventProcessor; + + @Mock private IContentCaptureDirectManager mMockContentCaptureDirectManager; + + @Test + public void onSessionStarted_contentProtectionEnabled_processorCreated() { + MainContentCaptureSession session = createSession(); + assertThat(session.mContentProtectionEventProcessor).isNull(); + + session.onSessionStarted(/* resultCode= */ 0, /* binder= */ null); + + assertThat(session.mContentProtectionEventProcessor).isNotNull(); + } + + @Test + public void onSessionStarted_contentProtectionDisabled_processorNotCreated() { + MainContentCaptureSession session = + createSession( + /* enableContentCaptureReceiver= */ true, + /* enableContentProtectionReceiver= */ false); + session.mContentProtectionEventProcessor = mMockContentProtectionEventProcessor; + + session.onSessionStarted(/* resultCode= */ 0, /* binder= */ null); + + assertThat(session.mContentProtectionEventProcessor).isNull(); + verifyZeroInteractions(mMockContentProtectionEventProcessor); + } + + @Test + public void onSessionStarted_contentProtectionNoBuffer_processorNotCreated() { + ContentCaptureOptions options = + createOptions( + /* enableContentCaptureReceiver= */ true, + new ContentCaptureOptions.ContentProtectionOptions( + /* enableReceiver= */ true, -BUFFER_SIZE)); + MainContentCaptureSession session = createSession(options); + session.mContentProtectionEventProcessor = mMockContentProtectionEventProcessor; + + session.onSessionStarted(/* resultCode= */ 0, /* binder= */ null); + + assertThat(session.mContentProtectionEventProcessor).isNull(); + verifyZeroInteractions(mMockContentProtectionEventProcessor); + } + + @Test + public void onSessionStarted_noComponentName_processorNotCreated() { + MainContentCaptureSession session = createSession(); + session.mComponentName = null; + + session.onSessionStarted(/* resultCode= */ 0, /* binder= */ null); + + assertThat(session.mContentProtectionEventProcessor).isNull(); + } + + @Test + public void sendEvent_contentCaptureDisabled_contentProtectionDisabled() { + MainContentCaptureSession session = + createSession( + /* enableContentCaptureReceiver= */ false, + /* enableContentProtectionReceiver= */ false); + session.mContentProtectionEventProcessor = mMockContentProtectionEventProcessor; + + session.sendEvent(EVENT); + + verifyZeroInteractions(mMockContentProtectionEventProcessor); + assertThat(session.mEvents).isNull(); + } + + @Test + public void sendEvent_contentCaptureDisabled_contentProtectionEnabled() { + MainContentCaptureSession session = + createSession( + /* enableContentCaptureReceiver= */ false, + /* enableContentProtectionReceiver= */ true); + session.mContentProtectionEventProcessor = mMockContentProtectionEventProcessor; + + session.sendEvent(EVENT); + + verify(mMockContentProtectionEventProcessor).processEvent(EVENT); + assertThat(session.mEvents).isNull(); + } + + @Test + public void sendEvent_contentCaptureEnabled_contentProtectionDisabled() { + MainContentCaptureSession session = + createSession( + /* enableContentCaptureReceiver= */ true, + /* enableContentProtectionReceiver= */ false); + session.mContentProtectionEventProcessor = mMockContentProtectionEventProcessor; + + session.sendEvent(EVENT); + + verifyZeroInteractions(mMockContentProtectionEventProcessor); + assertThat(session.mEvents).isNotNull(); + assertThat(session.mEvents).containsExactly(EVENT); + } + + @Test + public void sendEvent_contentCaptureEnabled_contentProtectionEnabled() { + MainContentCaptureSession session = createSession(); + session.mContentProtectionEventProcessor = mMockContentProtectionEventProcessor; + + session.sendEvent(EVENT); + + verify(mMockContentProtectionEventProcessor).processEvent(EVENT); + assertThat(session.mEvents).isNotNull(); + assertThat(session.mEvents).containsExactly(EVENT); + } + + @Test + public void sendEvent_contentProtectionEnabled_processorNotCreated() { + MainContentCaptureSession session = + createSession( + /* enableContentCaptureReceiver= */ false, + /* enableContentProtectionReceiver= */ true); + + session.sendEvent(EVENT); + + verifyZeroInteractions(mMockContentProtectionEventProcessor); + assertThat(session.mEvents).isNull(); + } + + @Test + public void flush_contentCaptureDisabled_contentProtectionDisabled() throws Exception { + ContentCaptureOptions options = + createOptions( + /* enableContentCaptureReceiver= */ false, + /* enableContentProtectionReceiver= */ false); + MainContentCaptureSession session = createSession(options); + session.mEvents = new ArrayList<>(Arrays.asList(EVENT)); + session.mDirectServiceInterface = mMockContentCaptureDirectManager; + + session.flush(REASON); + + verifyZeroInteractions(mMockContentProtectionEventProcessor); + verifyZeroInteractions(mMockContentCaptureDirectManager); + assertThat(session.mEvents).containsExactly(EVENT); + } + + @Test + public void flush_contentCaptureDisabled_contentProtectionEnabled() { + MainContentCaptureSession session = + createSession( + /* enableContentCaptureReceiver= */ false, + /* enableContentProtectionReceiver= */ true); + session.mEvents = new ArrayList<>(Arrays.asList(EVENT)); + session.mDirectServiceInterface = mMockContentCaptureDirectManager; + + session.flush(REASON); + + verifyZeroInteractions(mMockContentProtectionEventProcessor); + verifyZeroInteractions(mMockContentCaptureDirectManager); + assertThat(session.mEvents).containsExactly(EVENT); + } + + @Test + public void flush_contentCaptureEnabled_contentProtectionDisabled() throws Exception { + ContentCaptureOptions options = + createOptions( + /* enableContentCaptureReceiver= */ true, + /* enableContentProtectionReceiver= */ false); + MainContentCaptureSession session = createSession(options); + session.mEvents = new ArrayList<>(Arrays.asList(EVENT)); + session.mDirectServiceInterface = mMockContentCaptureDirectManager; + + session.flush(REASON); + + verifyZeroInteractions(mMockContentProtectionEventProcessor); + assertThat(session.mEvents).isEmpty(); + assertEventFlushedContentCapture(options); + } + + @Test + public void flush_contentCaptureEnabled_contentProtectionEnabled() throws Exception { + ContentCaptureOptions options = + createOptions( + /* enableContentCaptureReceiver= */ true, + /* enableContentProtectionReceiver= */ true); + MainContentCaptureSession session = createSession(options); + session.mEvents = new ArrayList<>(Arrays.asList(EVENT)); + session.mDirectServiceInterface = mMockContentCaptureDirectManager; + + session.flush(REASON); + + verifyZeroInteractions(mMockContentProtectionEventProcessor); + assertThat(session.mEvents).isEmpty(); + assertEventFlushedContentCapture(options); + } + + @Test + public void destroySession() throws Exception { + MainContentCaptureSession session = createSession(); + session.mContentProtectionEventProcessor = mMockContentProtectionEventProcessor; + + session.destroySession(); + + verify(mMockSystemServerInterface).finishSession(anyInt()); + verifyZeroInteractions(mMockContentProtectionEventProcessor); + assertThat(session.mDirectServiceInterface).isNull(); + assertThat(session.mContentProtectionEventProcessor).isNull(); + } + + @Test + public void resetSession() { + MainContentCaptureSession session = createSession(); + session.mContentProtectionEventProcessor = mMockContentProtectionEventProcessor; + + session.resetSession(/* newState= */ 0); + + verifyZeroInteractions(mMockSystemServerInterface); + verifyZeroInteractions(mMockContentProtectionEventProcessor); + assertThat(session.mDirectServiceInterface).isNull(); + assertThat(session.mContentProtectionEventProcessor).isNull(); + } + + private static ContentCaptureOptions createOptions( + boolean enableContentCaptureReceiver, + ContentCaptureOptions.ContentProtectionOptions contentProtectionOptions) { + return new ContentCaptureOptions( + /* loggingLevel= */ 0, + BUFFER_SIZE, + /* idleFlushingFrequencyMs= */ 0, + /* textChangeFlushingFrequencyMs= */ 0, + /* logHistorySize= */ 0, + /* disableFlushForViewTreeAppearing= */ false, + enableContentCaptureReceiver, + contentProtectionOptions, + /* whitelistedComponents= */ null); + } + + private static ContentCaptureOptions createOptions( + boolean enableContentCaptureReceiver, boolean enableContentProtectionReceiver) { + return createOptions( + enableContentCaptureReceiver, + new ContentCaptureOptions.ContentProtectionOptions( + enableContentProtectionReceiver, BUFFER_SIZE)); + } + + private ContentCaptureManager createManager(ContentCaptureOptions options) { + return new ContentCaptureManager(sContext, mMockSystemServerInterface, options); + } + + private MainContentCaptureSession createSession(ContentCaptureManager manager) { + MainContentCaptureSession session = + new MainContentCaptureSession( + sStrippedContext, + manager, + new Handler(Looper.getMainLooper()), + mMockSystemServerInterface); + session.mComponentName = COMPONENT_NAME; + return session; + } + + private MainContentCaptureSession createSession(ContentCaptureOptions options) { + return createSession(createManager(options)); + } + + private MainContentCaptureSession createSession( + boolean enableContentCaptureReceiver, boolean enableContentProtectionReceiver) { + return createSession( + createOptions(enableContentCaptureReceiver, enableContentProtectionReceiver)); + } + + private MainContentCaptureSession createSession() { + return createSession( + /* enableContentCaptureReceiver= */ true, + /* enableContentProtectionReceiver= */ true); + } + + private void assertEventFlushedContentCapture(ContentCaptureOptions options) throws Exception { + ArgumentCaptor<ParceledListSlice> captor = ArgumentCaptor.forClass(ParceledListSlice.class); + verify(mMockContentCaptureDirectManager) + .sendEvents(captor.capture(), eq(REASON), eq(options)); + + assertThat(captor.getValue()).isNotNull(); + List<ContentCaptureEvent> actual = captor.getValue().getList(); + assertThat(actual).isNotNull(); + assertThat(actual).containsExactly(EVENT); + } +} diff --git a/core/tests/coretests/src/android/view/contentcapture/ViewNodeTest.java b/core/tests/coretests/src/android/view/contentcapture/ViewNodeTest.java index 93315f11d242..a4e77f5d8dc5 100644 --- a/core/tests/coretests/src/android/view/contentcapture/ViewNodeTest.java +++ b/core/tests/coretests/src/android/view/contentcapture/ViewNodeTest.java @@ -41,49 +41,60 @@ public class ViewNodeTest { private final Context mContext = InstrumentationRegistry.getTargetContext(); + private final View mView = new View(mContext); + + private final ViewStructureImpl mViewStructure = new ViewStructureImpl(mView); + + private final ViewNode mViewNode = mViewStructure.getNode(); + @Mock private HtmlInfo mHtmlInfoMock; @Test public void testUnsupportedProperties() { - View view = new View(mContext); + mViewStructure.setChildCount(1); + assertThat(mViewNode.getChildCount()).isEqualTo(0); + + mViewStructure.addChildCount(1); + assertThat(mViewNode.getChildCount()).isEqualTo(0); - ViewStructureImpl structure = new ViewStructureImpl(view); - ViewNode node = structure.getNode(); + assertThat(mViewStructure.newChild(0)).isNull(); + assertThat(mViewNode.getChildCount()).isEqualTo(0); - structure.setChildCount(1); - assertThat(node.getChildCount()).isEqualTo(0); + assertThat(mViewStructure.asyncNewChild(0)).isNull(); + assertThat(mViewNode.getChildCount()).isEqualTo(0); - structure.addChildCount(1); - assertThat(node.getChildCount()).isEqualTo(0); + mViewStructure.asyncCommit(); + assertThat(mViewNode.getChildCount()).isEqualTo(0); - assertThat(structure.newChild(0)).isNull(); - assertThat(node.getChildCount()).isEqualTo(0); + mViewStructure.setWebDomain("Y U NO SET?"); + assertThat(mViewNode.getWebDomain()).isNull(); - assertThat(structure.asyncNewChild(0)).isNull(); - assertThat(node.getChildCount()).isEqualTo(0); + assertThat(mViewStructure.newHtmlInfoBuilder("WHATEVER")).isNull(); - structure.asyncCommit(); - assertThat(node.getChildCount()).isEqualTo(0); + mViewStructure.setHtmlInfo(mHtmlInfoMock); + assertThat(mViewNode.getHtmlInfo()).isNull(); - structure.setWebDomain("Y U NO SET?"); - assertThat(node.getWebDomain()).isNull(); + mViewStructure.setDataIsSensitive(true); - assertThat(structure.newHtmlInfoBuilder("WHATEVER")).isNull(); + assertThat(mViewStructure.getTempRect()).isNull(); - structure.setHtmlInfo(mHtmlInfoMock); - assertThat(node.getHtmlInfo()).isNull(); + // Graphic properties + mViewStructure.setElevation(6.66f); + assertThat(mViewNode.getElevation()).isEqualTo(0f); + mViewStructure.setAlpha(66.6f); + assertThat(mViewNode.getAlpha()).isEqualTo(1.0f); + mViewStructure.setTransformation(Matrix.IDENTITY_MATRIX); + assertThat(mViewNode.getTransformation()).isNull(); + } - structure.setDataIsSensitive(true); + @Test + public void testGetSet_textIdEntry() { + assertThat(mViewNode.getTextIdEntry()).isNull(); - assertThat(structure.getTempRect()).isNull(); + String expected = "TEXT_ID_ENTRY"; + mViewNode.setTextIdEntry(expected); - // Graphic properties - structure.setElevation(6.66f); - assertThat(node.getElevation()).isWithin(1.0e-10f).of(0f); - structure.setAlpha(66.6f); - assertThat(node.getAlpha()).isWithin(1.0e-10f).of(1.0f); - structure.setTransformation(Matrix.IDENTITY_MATRIX); - assertThat(node.getTransformation()).isNull(); + assertThat(mViewNode.getTextIdEntry()).isEqualTo(expected); } } diff --git a/core/tests/coretests/src/android/view/contentprotection/ContentProtectionEventProcessorTest.java b/core/tests/coretests/src/android/view/contentprotection/ContentProtectionEventProcessorTest.java new file mode 100644 index 000000000000..39a2e0e048e8 --- /dev/null +++ b/core/tests/coretests/src/android/view/contentprotection/ContentProtectionEventProcessorTest.java @@ -0,0 +1,568 @@ +/* + * 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 android.view.contentprotection; + +import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_APPEARED; +import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_DISAPPEARED; +import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_TEXT_CHANGED; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.verifyZeroInteractions; +import static org.mockito.Mockito.when; + +import android.annotation.Nullable; +import android.content.Context; +import android.content.pm.ParceledListSlice; +import android.os.Handler; +import android.os.Looper; +import android.text.InputType; +import android.view.View; +import android.view.contentcapture.ContentCaptureEvent; +import android.view.contentcapture.IContentCaptureManager; +import android.view.contentcapture.ViewNode; +import android.view.contentcapture.ViewNode.ViewStructureImpl; + +import androidx.test.core.app.ApplicationProvider; +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.filters.SmallTest; + +import com.android.internal.util.RingBuffer; + +import com.google.common.collect.ImmutableSet; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +import java.time.Instant; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +/** + * Test for {@link ContentProtectionEventProcessor}. + * + * <p>Run with: {@code atest + * FrameworksCoreTests:android.view.contentprotection.ContentProtectionEventProcessorTest} + */ +@RunWith(AndroidJUnit4.class) +@SmallTest +public class ContentProtectionEventProcessorTest { + + private static final String PACKAGE_NAME = "com.test.package.name"; + + private static final String ANDROID_CLASS_NAME = "android.test.some.class.name"; + + private static final String PASSWORD_TEXT = "ENTER PASSWORD HERE"; + + private static final String SUSPICIOUS_TEXT = "PLEASE SIGN IN"; + + private static final String SAFE_TEXT = "SAFE TEXT"; + + private static final ContentCaptureEvent PROCESS_EVENT = createProcessEvent(); + + private static final ContentCaptureEvent[] BUFFERED_EVENTS = + new ContentCaptureEvent[] {PROCESS_EVENT}; + + private static final Set<Integer> EVENT_TYPES_TO_STORE = + ImmutableSet.of(TYPE_VIEW_APPEARED, TYPE_VIEW_DISAPPEARED, TYPE_VIEW_TEXT_CHANGED); + + private static final int RESET_LOGIN_TOTAL_EVENTS_TO_PROCESS = 150; + + @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule(); + + @Mock private RingBuffer<ContentCaptureEvent> mMockEventBuffer; + + @Mock private IContentCaptureManager mMockContentCaptureManager; + + private final Context mContext = ApplicationProvider.getApplicationContext(); + + private ContentProtectionEventProcessor mContentProtectionEventProcessor; + + @Before + public void setup() { + mContentProtectionEventProcessor = + new ContentProtectionEventProcessor( + mMockEventBuffer, + new Handler(Looper.getMainLooper()), + mMockContentCaptureManager, + PACKAGE_NAME); + } + + @Test + public void processEvent_buffer_storesOnlySubsetOfEventTypes() { + List<ContentCaptureEvent> expectedEvents = new ArrayList<>(); + for (int type = -100; type <= 100; type++) { + ContentCaptureEvent event = createEvent(type); + if (EVENT_TYPES_TO_STORE.contains(type)) { + expectedEvents.add(event); + } + + mContentProtectionEventProcessor.processEvent(event); + } + + assertThat(expectedEvents).hasSize(EVENT_TYPES_TO_STORE.size()); + expectedEvents.forEach((expectedEvent) -> verify(mMockEventBuffer).append(expectedEvent)); + verifyNoMoreInteractions(mMockEventBuffer); + } + + @Test + public void processEvent_buffer_setsTextIdEntry_withoutExistingViewNode() { + ContentCaptureEvent event = createStoreEvent(); + + mContentProtectionEventProcessor.processEvent(event); + + assertThat(event.getViewNode()).isNotNull(); + assertThat(event.getViewNode().getTextIdEntry()).isEqualTo(PACKAGE_NAME); + verify(mMockEventBuffer).append(event); + } + + @Test + public void processEvent_buffer_setsTextIdEntry_withExistingViewNode() { + ViewNode viewNode = new ViewNode(); + viewNode.setTextIdEntry(PACKAGE_NAME + "TO BE OVERWRITTEN"); + ContentCaptureEvent event = createStoreEvent(); + event.setViewNode(viewNode); + + mContentProtectionEventProcessor.processEvent(event); + + assertThat(event.getViewNode()).isSameInstanceAs(viewNode); + assertThat(viewNode.getTextIdEntry()).isEqualTo(PACKAGE_NAME); + verify(mMockEventBuffer).append(event); + } + + @Test + public void processEvent_loginDetected_inspectsOnlyTypeViewAppeared() { + mContentProtectionEventProcessor.mPasswordFieldDetected = true; + mContentProtectionEventProcessor.mSuspiciousTextDetected = true; + + for (int type = -100; type <= 100; type++) { + if (type == TYPE_VIEW_APPEARED) { + continue; + } + + mContentProtectionEventProcessor.processEvent(createEvent(type)); + + assertThat(mContentProtectionEventProcessor.mPasswordFieldDetected).isTrue(); + assertThat(mContentProtectionEventProcessor.mSuspiciousTextDetected).isTrue(); + } + + verify(mMockEventBuffer, never()).clear(); + verify(mMockEventBuffer, never()).toArray(); + verifyZeroInteractions(mMockContentCaptureManager); + } + + @Test + public void processEvent_loginDetected() throws Exception { + when(mMockEventBuffer.toArray()).thenReturn(BUFFERED_EVENTS); + mContentProtectionEventProcessor.mPasswordFieldDetected = true; + mContentProtectionEventProcessor.mSuspiciousTextDetected = true; + + mContentProtectionEventProcessor.processEvent(PROCESS_EVENT); + + assertThat(mContentProtectionEventProcessor.mPasswordFieldDetected).isFalse(); + assertThat(mContentProtectionEventProcessor.mSuspiciousTextDetected).isFalse(); + verify(mMockEventBuffer).clear(); + verify(mMockEventBuffer).toArray(); + assertOnLoginDetected(); + } + + @Test + public void processEvent_loginDetected_passwordFieldNotDetected() { + mContentProtectionEventProcessor.mSuspiciousTextDetected = true; + assertThat(mContentProtectionEventProcessor.mPasswordFieldDetected).isFalse(); + + mContentProtectionEventProcessor.processEvent(PROCESS_EVENT); + + assertThat(mContentProtectionEventProcessor.mPasswordFieldDetected).isFalse(); + assertThat(mContentProtectionEventProcessor.mSuspiciousTextDetected).isTrue(); + verify(mMockEventBuffer, never()).clear(); + verify(mMockEventBuffer, never()).toArray(); + verifyZeroInteractions(mMockContentCaptureManager); + } + + @Test + public void processEvent_loginDetected_suspiciousTextNotDetected() { + mContentProtectionEventProcessor.mPasswordFieldDetected = true; + assertThat(mContentProtectionEventProcessor.mSuspiciousTextDetected).isFalse(); + + mContentProtectionEventProcessor.processEvent(PROCESS_EVENT); + + assertThat(mContentProtectionEventProcessor.mPasswordFieldDetected).isTrue(); + assertThat(mContentProtectionEventProcessor.mSuspiciousTextDetected).isFalse(); + verify(mMockEventBuffer, never()).clear(); + verify(mMockEventBuffer, never()).toArray(); + verifyZeroInteractions(mMockContentCaptureManager); + } + + @Test + public void processEvent_loginDetected_withoutViewNode() { + assertThat(mContentProtectionEventProcessor.mPasswordFieldDetected).isFalse(); + assertThat(mContentProtectionEventProcessor.mSuspiciousTextDetected).isFalse(); + + mContentProtectionEventProcessor.processEvent(PROCESS_EVENT); + + assertThat(mContentProtectionEventProcessor.mPasswordFieldDetected).isFalse(); + assertThat(mContentProtectionEventProcessor.mSuspiciousTextDetected).isFalse(); + verify(mMockEventBuffer, never()).clear(); + verify(mMockEventBuffer, never()).toArray(); + verifyZeroInteractions(mMockContentCaptureManager); + } + + @Test + public void processEvent_loginDetected_belowResetLimit() throws Exception { + when(mMockEventBuffer.toArray()).thenReturn(BUFFERED_EVENTS); + mContentProtectionEventProcessor.mSuspiciousTextDetected = true; + ContentCaptureEvent event = + createAndroidPasswordFieldEvent( + ANDROID_CLASS_NAME, InputType.TYPE_TEXT_VARIATION_PASSWORD); + + for (int i = 0; i < RESET_LOGIN_TOTAL_EVENTS_TO_PROCESS; i++) { + mContentProtectionEventProcessor.processEvent(PROCESS_EVENT); + } + + assertThat(mContentProtectionEventProcessor.mPasswordFieldDetected).isFalse(); + assertThat(mContentProtectionEventProcessor.mSuspiciousTextDetected).isTrue(); + verify(mMockEventBuffer, never()).clear(); + verify(mMockEventBuffer, never()).toArray(); + + mContentProtectionEventProcessor.processEvent(event); + + assertThat(mContentProtectionEventProcessor.mPasswordFieldDetected).isFalse(); + assertThat(mContentProtectionEventProcessor.mSuspiciousTextDetected).isFalse(); + verify(mMockEventBuffer).clear(); + verify(mMockEventBuffer).toArray(); + assertOnLoginDetected(); + } + + @Test + public void processEvent_loginDetected_aboveResetLimit() throws Exception { + mContentProtectionEventProcessor.mSuspiciousTextDetected = true; + ContentCaptureEvent event = + createAndroidPasswordFieldEvent( + ANDROID_CLASS_NAME, InputType.TYPE_TEXT_VARIATION_PASSWORD); + + for (int i = 0; i < RESET_LOGIN_TOTAL_EVENTS_TO_PROCESS + 1; i++) { + mContentProtectionEventProcessor.processEvent(PROCESS_EVENT); + } + + assertThat(mContentProtectionEventProcessor.mPasswordFieldDetected).isFalse(); + assertThat(mContentProtectionEventProcessor.mSuspiciousTextDetected).isFalse(); + verify(mMockEventBuffer, never()).clear(); + verify(mMockEventBuffer, never()).toArray(); + + mContentProtectionEventProcessor.processEvent(event); + + assertThat(mContentProtectionEventProcessor.mPasswordFieldDetected).isTrue(); + assertThat(mContentProtectionEventProcessor.mSuspiciousTextDetected).isFalse(); + verify(mMockEventBuffer, never()).clear(); + verify(mMockEventBuffer, never()).toArray(); + } + + @Test + public void processEvent_multipleLoginsDetected_belowFlushThreshold() throws Exception { + when(mMockEventBuffer.toArray()).thenReturn(BUFFERED_EVENTS); + + mContentProtectionEventProcessor.mPasswordFieldDetected = true; + mContentProtectionEventProcessor.mSuspiciousTextDetected = true; + mContentProtectionEventProcessor.processEvent(PROCESS_EVENT); + + mContentProtectionEventProcessor.mPasswordFieldDetected = true; + mContentProtectionEventProcessor.mSuspiciousTextDetected = true; + mContentProtectionEventProcessor.processEvent(PROCESS_EVENT); + + assertThat(mContentProtectionEventProcessor.mPasswordFieldDetected).isFalse(); + assertThat(mContentProtectionEventProcessor.mSuspiciousTextDetected).isFalse(); + verify(mMockEventBuffer).clear(); + verify(mMockEventBuffer).toArray(); + assertOnLoginDetected(); + } + + @Test + public void processEvent_multipleLoginsDetected_aboveFlushThreshold() throws Exception { + when(mMockEventBuffer.toArray()).thenReturn(BUFFERED_EVENTS); + + mContentProtectionEventProcessor.mPasswordFieldDetected = true; + mContentProtectionEventProcessor.mSuspiciousTextDetected = true; + mContentProtectionEventProcessor.processEvent(PROCESS_EVENT); + + mContentProtectionEventProcessor.mLastFlushTime = Instant.now().minusSeconds(5); + + mContentProtectionEventProcessor.mPasswordFieldDetected = true; + mContentProtectionEventProcessor.mSuspiciousTextDetected = true; + mContentProtectionEventProcessor.processEvent(PROCESS_EVENT); + + assertThat(mContentProtectionEventProcessor.mPasswordFieldDetected).isFalse(); + assertThat(mContentProtectionEventProcessor.mSuspiciousTextDetected).isFalse(); + verify(mMockEventBuffer, times(2)).clear(); + verify(mMockEventBuffer, times(2)).toArray(); + assertOnLoginDetected(PROCESS_EVENT, /* times= */ 2); + } + + @Test + public void isPasswordField_android() { + ContentCaptureEvent event = + createAndroidPasswordFieldEvent( + ANDROID_CLASS_NAME, InputType.TYPE_TEXT_VARIATION_PASSWORD); + + mContentProtectionEventProcessor.processEvent(event); + + assertThat(mContentProtectionEventProcessor.mPasswordFieldDetected).isTrue(); + verify(mMockEventBuffer, never()).clear(); + verify(mMockEventBuffer, never()).toArray(); + verifyZeroInteractions(mMockContentCaptureManager); + } + + @Test + public void isPasswordField_android_withoutClassName() { + ContentCaptureEvent event = + createAndroidPasswordFieldEvent( + /* className= */ null, InputType.TYPE_TEXT_VARIATION_PASSWORD); + + mContentProtectionEventProcessor.processEvent(event); + + assertThat(mContentProtectionEventProcessor.mPasswordFieldDetected).isFalse(); + verify(mMockEventBuffer, never()).clear(); + verify(mMockEventBuffer, never()).toArray(); + verifyZeroInteractions(mMockContentCaptureManager); + } + + @Test + public void isPasswordField_android_wrongClassName() { + ContentCaptureEvent event = + createAndroidPasswordFieldEvent( + "wrong.prefix" + ANDROID_CLASS_NAME, + InputType.TYPE_TEXT_VARIATION_PASSWORD); + + mContentProtectionEventProcessor.processEvent(event); + + assertThat(mContentProtectionEventProcessor.mPasswordFieldDetected).isFalse(); + verify(mMockEventBuffer, never()).clear(); + verify(mMockEventBuffer, never()).toArray(); + verifyZeroInteractions(mMockContentCaptureManager); + } + + @Test + public void isPasswordField_android_wrongInputType() { + ContentCaptureEvent event = + createAndroidPasswordFieldEvent( + ANDROID_CLASS_NAME, InputType.TYPE_TEXT_VARIATION_NORMAL); + + mContentProtectionEventProcessor.processEvent(event); + + assertThat(mContentProtectionEventProcessor.mPasswordFieldDetected).isFalse(); + verify(mMockEventBuffer, never()).clear(); + verify(mMockEventBuffer, never()).toArray(); + verifyZeroInteractions(mMockContentCaptureManager); + } + + @Test + public void isPasswordField_webView() throws Exception { + ContentCaptureEvent event = + createWebViewPasswordFieldEvent( + /* className= */ null, /* eventText= */ null, PASSWORD_TEXT); + when(mMockEventBuffer.toArray()).thenReturn(new ContentCaptureEvent[] {event}); + + mContentProtectionEventProcessor.processEvent(event); + + assertThat(mContentProtectionEventProcessor.mPasswordFieldDetected).isFalse(); + verify(mMockEventBuffer).clear(); + verify(mMockEventBuffer).toArray(); + assertOnLoginDetected(event, /* times= */ 1); + } + + @Test + public void isPasswordField_webView_withClassName() { + ContentCaptureEvent event = + createWebViewPasswordFieldEvent( + /* className= */ "any.class.name", /* eventText= */ null, PASSWORD_TEXT); + + mContentProtectionEventProcessor.processEvent(event); + + assertThat(mContentProtectionEventProcessor.mPasswordFieldDetected).isFalse(); + verify(mMockEventBuffer, never()).clear(); + verify(mMockEventBuffer, never()).toArray(); + verifyZeroInteractions(mMockContentCaptureManager); + } + + @Test + public void isPasswordField_webView_withSafeViewNodeText() { + ContentCaptureEvent event = + createWebViewPasswordFieldEvent( + /* className= */ null, /* eventText= */ null, SAFE_TEXT); + + mContentProtectionEventProcessor.processEvent(event); + + assertThat(mContentProtectionEventProcessor.mPasswordFieldDetected).isFalse(); + verify(mMockEventBuffer, never()).clear(); + verify(mMockEventBuffer, never()).toArray(); + verifyZeroInteractions(mMockContentCaptureManager); + } + + @Test + public void isPasswordField_webView_withEventText() { + ContentCaptureEvent event = + createWebViewPasswordFieldEvent(/* className= */ null, PASSWORD_TEXT, SAFE_TEXT); + + mContentProtectionEventProcessor.processEvent(event); + + assertThat(mContentProtectionEventProcessor.mPasswordFieldDetected).isFalse(); + verify(mMockEventBuffer, never()).clear(); + verify(mMockEventBuffer, never()).toArray(); + verifyZeroInteractions(mMockContentCaptureManager); + } + + @Test + public void isSuspiciousText_withSafeText() { + ContentCaptureEvent event = createSuspiciousTextEvent(SAFE_TEXT, SAFE_TEXT); + + mContentProtectionEventProcessor.processEvent(event); + + assertThat(mContentProtectionEventProcessor.mSuspiciousTextDetected).isFalse(); + verify(mMockEventBuffer, never()).clear(); + verify(mMockEventBuffer, never()).toArray(); + verifyZeroInteractions(mMockContentCaptureManager); + } + + @Test + public void isSuspiciousText_eventText_suspiciousText() { + ContentCaptureEvent event = createSuspiciousTextEvent(SUSPICIOUS_TEXT, SAFE_TEXT); + + mContentProtectionEventProcessor.processEvent(event); + + assertThat(mContentProtectionEventProcessor.mSuspiciousTextDetected).isTrue(); + verify(mMockEventBuffer, never()).clear(); + verify(mMockEventBuffer, never()).toArray(); + verifyZeroInteractions(mMockContentCaptureManager); + } + + @Test + public void isSuspiciousText_viewNodeText_suspiciousText() { + ContentCaptureEvent event = createSuspiciousTextEvent(SAFE_TEXT, SUSPICIOUS_TEXT); + + mContentProtectionEventProcessor.processEvent(event); + + assertThat(mContentProtectionEventProcessor.mSuspiciousTextDetected).isTrue(); + verify(mMockEventBuffer, never()).clear(); + verify(mMockEventBuffer, never()).toArray(); + verifyZeroInteractions(mMockContentCaptureManager); + } + + @Test + public void isSuspiciousText_eventText_passwordText() { + ContentCaptureEvent event = createSuspiciousTextEvent(PASSWORD_TEXT, SAFE_TEXT); + + mContentProtectionEventProcessor.processEvent(event); + + assertThat(mContentProtectionEventProcessor.mSuspiciousTextDetected).isTrue(); + verify(mMockEventBuffer, never()).clear(); + verify(mMockEventBuffer, never()).toArray(); + verifyZeroInteractions(mMockContentCaptureManager); + } + + @Test + public void isSuspiciousText_viewNodeText_passwordText() { + // Specify the class to differ from {@link isPasswordField_webView} test in this version + ContentCaptureEvent event = + createProcessEvent( + "test.class.not.a.web.view", /* inputType= */ 0, SAFE_TEXT, PASSWORD_TEXT); + + mContentProtectionEventProcessor.processEvent(event); + + assertThat(mContentProtectionEventProcessor.mSuspiciousTextDetected).isTrue(); + verify(mMockEventBuffer, never()).clear(); + verify(mMockEventBuffer, never()).toArray(); + verifyZeroInteractions(mMockContentCaptureManager); + } + + private static ContentCaptureEvent createEvent(int type) { + return new ContentCaptureEvent(/* sessionId= */ 123, type); + } + + private static ContentCaptureEvent createStoreEvent() { + return createEvent(TYPE_VIEW_TEXT_CHANGED); + } + + private static ContentCaptureEvent createProcessEvent() { + return createEvent(TYPE_VIEW_APPEARED); + } + + private ContentCaptureEvent createProcessEvent( + @Nullable String className, + int inputType, + @Nullable String eventText, + @Nullable String viewNodeText) { + View view = new View(mContext); + ViewStructureImpl viewStructure = new ViewStructureImpl(view); + if (className != null) { + viewStructure.setClassName(className); + } + if (viewNodeText != null) { + viewStructure.setText(viewNodeText); + } + viewStructure.setInputType(inputType); + + ContentCaptureEvent event = createProcessEvent(); + event.setViewNode(viewStructure.getNode()); + if (eventText != null) { + event.setText(eventText); + } + + return event; + } + + private ContentCaptureEvent createAndroidPasswordFieldEvent( + @Nullable String className, int inputType) { + return createProcessEvent( + className, inputType, /* eventText= */ null, /* viewNodeText= */ null); + } + + private ContentCaptureEvent createWebViewPasswordFieldEvent( + @Nullable String className, @Nullable String eventText, @Nullable String viewNodeText) { + return createProcessEvent(className, /* inputType= */ 0, eventText, viewNodeText); + } + + private ContentCaptureEvent createSuspiciousTextEvent( + @Nullable String eventText, @Nullable String viewNodeText) { + return createProcessEvent( + /* className= */ null, /* inputType= */ 0, eventText, viewNodeText); + } + + private void assertOnLoginDetected() throws Exception { + assertOnLoginDetected(PROCESS_EVENT, /* times= */ 1); + } + + private void assertOnLoginDetected(ContentCaptureEvent event, int times) throws Exception { + ArgumentCaptor<ParceledListSlice> captor = ArgumentCaptor.forClass(ParceledListSlice.class); + verify(mMockContentCaptureManager, times(times)).onLoginDetected(captor.capture()); + + assertThat(captor.getValue()).isNotNull(); + List<ContentCaptureEvent> actual = captor.getValue().getList(); + assertThat(actual).isNotNull(); + assertThat(actual).containsExactly(event); + } +} diff --git a/core/tests/coretests/src/android/view/contentprotection/ContentProtectionUtilsTest.java b/core/tests/coretests/src/android/view/contentprotection/ContentProtectionUtilsTest.java new file mode 100644 index 000000000000..1459799adee5 --- /dev/null +++ b/core/tests/coretests/src/android/view/contentprotection/ContentProtectionUtilsTest.java @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view.contentprotection; + +import static android.view.contentcapture.ContentCaptureEvent.TYPE_SESSION_STARTED; + +import static com.google.common.truth.Truth.assertThat; + +import android.view.View; +import android.view.contentcapture.ContentCaptureEvent; +import android.view.contentcapture.ViewNode; +import android.view.contentcapture.ViewNode.ViewStructureImpl; + +import androidx.test.core.app.ApplicationProvider; +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.filters.SmallTest; + +import org.junit.Test; +import org.junit.runner.RunWith; + +/** + * Test for {@link ContentProtectionUtils}. + * + * <p>Run with: {@code atest + * FrameworksCoreTests:android.view.contentprotection.ContentProtectionUtilsTest} + */ +@RunWith(AndroidJUnit4.class) +@SmallTest +public class ContentProtectionUtilsTest { + + private static final String TEXT = "TEST_TEXT"; + + private static final ContentCaptureEvent EVENT = createEvent(); + + private static final ViewNode VIEW_NODE = new ViewNode(); + + private static final ViewNode VIEW_NODE_WITH_TEXT = createViewNodeWithText(); + + @Test + public void event_getEventText_null() { + String actual = ContentProtectionUtils.getEventText(EVENT); + + assertThat(actual).isNull(); + } + + @Test + public void event_getEventText_notNull() { + ContentCaptureEvent event = createEvent(); + event.setText(TEXT); + + String actual = ContentProtectionUtils.getEventText(event); + + assertThat(actual).isEqualTo(TEXT); + } + + @Test + public void event_getViewNodeText_null() { + String actual = ContentProtectionUtils.getViewNodeText(EVENT); + + assertThat(actual).isNull(); + } + + @Test + public void event_getViewNodeText_notNull() { + ContentCaptureEvent event = createEvent(); + event.setViewNode(VIEW_NODE_WITH_TEXT); + + String actual = ContentProtectionUtils.getViewNodeText(event); + + assertThat(actual).isEqualTo(TEXT); + } + + @Test + public void viewNode_getViewNodeText_null() { + String actual = ContentProtectionUtils.getViewNodeText(VIEW_NODE); + + assertThat(actual).isNull(); + } + + @Test + public void viewNode_getViewNodeText_notNull() { + String actual = ContentProtectionUtils.getViewNodeText(VIEW_NODE_WITH_TEXT); + + assertThat(actual).isEqualTo(TEXT); + } + + private static ContentCaptureEvent createEvent() { + return new ContentCaptureEvent(/* sessionId= */ 123, TYPE_SESSION_STARTED); + } + + private static ViewNode createViewNodeWithText() { + View view = new View(ApplicationProvider.getApplicationContext()); + ViewStructureImpl viewStructure = new ViewStructureImpl(view); + viewStructure.setText(TEXT); + return viewStructure.getNode(); + } +} diff --git a/core/tests/coretests/src/android/view/inputmethod/TextAppearanceInfoTest.java b/core/tests/coretests/src/android/view/inputmethod/TextAppearanceInfoTest.java index f93cd18a8521..0750cf1a64ab 100644 --- a/core/tests/coretests/src/android/view/inputmethod/TextAppearanceInfoTest.java +++ b/core/tests/coretests/src/android/view/inputmethod/TextAppearanceInfoTest.java @@ -37,6 +37,7 @@ import android.text.style.ForegroundColorSpan; import android.text.style.ScaleXSpan; import android.text.style.StyleSpan; import android.text.style.TypefaceSpan; +import android.text.util.Linkify; import android.view.ViewGroup; import android.widget.EditText; @@ -53,7 +54,7 @@ import org.junit.runner.RunWith; @RunWith(AndroidJUnit4.class) public class TextAppearanceInfoTest { private static final float EPSILON = 0.0000001f; - private static final String TEST_TEXT = "Happy birthday!"; + private static final String TEST_TEXT = "Hello: google.com"; private static final float TEXT_SIZE = 16.5f; private static final LocaleList TEXT_LOCALES = LocaleList.forLanguageTags("en,ja"); private static final String FONT_FAMILY_NAME = "sans-serif"; @@ -84,39 +85,7 @@ public class TextAppearanceInfoTest { @Before public void setUp() { - mEditText.setText(mSpannableText); - mEditText.getPaint().setTextSize(TEXT_SIZE); - mEditText.setTextLocales(TEXT_LOCALES); - Typeface family = Typeface.create(FONT_FAMILY_NAME, Typeface.NORMAL); - mEditText.setTypeface( - Typeface.create(family, TEXT_WEIGHT, (TEXT_STYLE & Typeface.ITALIC) != 0)); - mEditText.setAllCaps(ALL_CAPS); - mEditText.setShadowLayer(SHADOW_RADIUS, SHADOW_DX, SHADOW_DY, SHADOW_COLOR); - mEditText.setElegantTextHeight(ELEGANT_TEXT_HEIGHT); - mEditText.setFallbackLineSpacing(FALLBACK_LINE_SPACING); - mEditText.setLetterSpacing(LETTER_SPACING); - mEditText.setFontFeatureSettings(FONT_FEATURE_SETTINGS); - mEditText.setFontVariationSettings(FONT_VARIATION_SETTINGS); - mEditText.setLineBreakStyle(LINE_BREAK_STYLE); - mEditText.setLineBreakWordStyle(LINE_BREAK_WORD_STYLE); - mEditText.setTextScaleX(TEXT_SCALEX); - mEditText.setHighlightColor(HIGHLIGHT_TEXT_COLOR); - mEditText.setTextColor(TEXT_COLOR); - mEditText.setHintTextColor(HINT_TEXT_COLOR); - mEditText.setLinkTextColor(LINK_TEXT_COLOR); - ViewGroup.LayoutParams params = - new ViewGroup.LayoutParams( - ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); - mEditText.setLayoutParams(params); - mEditText.measure(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); - Bitmap bitmap = - Bitmap.createBitmap( - Math.max(1, mEditText.getMeasuredWidth()), - Math.max(1, mEditText.getMeasuredHeight()), - Bitmap.Config.ARGB_8888); - mEditText.layout(0, 0, mEditText.getMeasuredWidth(), mEditText.getMeasuredHeight()); - mCanvas = new Canvas(bitmap); - mEditText.draw(mCanvas); + initEditText(mSpannableText); } @Test @@ -233,6 +202,43 @@ public class TextAppearanceInfoTest { assertEquals(info1.getSystemFontFamilyName(), FONT_FAMILY_NAME); } + private void initEditText(CharSequence text) { + mEditText.setText(text); + mEditText.getPaint().setTextSize(TEXT_SIZE); + mEditText.setTextLocales(TEXT_LOCALES); + Typeface family = Typeface.create(FONT_FAMILY_NAME, Typeface.NORMAL); + mEditText.setTypeface( + Typeface.create(family, TEXT_WEIGHT, (TEXT_STYLE & Typeface.ITALIC) != 0)); + mEditText.setAllCaps(ALL_CAPS); + mEditText.setShadowLayer(SHADOW_RADIUS, SHADOW_DX, SHADOW_DY, SHADOW_COLOR); + mEditText.setElegantTextHeight(ELEGANT_TEXT_HEIGHT); + mEditText.setFallbackLineSpacing(FALLBACK_LINE_SPACING); + mEditText.setLetterSpacing(LETTER_SPACING); + mEditText.setFontFeatureSettings(FONT_FEATURE_SETTINGS); + mEditText.setFontVariationSettings(FONT_VARIATION_SETTINGS); + mEditText.setLineBreakStyle(LINE_BREAK_STYLE); + mEditText.setLineBreakWordStyle(LINE_BREAK_WORD_STYLE); + mEditText.setTextScaleX(TEXT_SCALEX); + mEditText.setHighlightColor(HIGHLIGHT_TEXT_COLOR); + mEditText.setTextColor(TEXT_COLOR); + mEditText.setHintTextColor(HINT_TEXT_COLOR); + mEditText.setHint("Hint text"); + mEditText.setLinkTextColor(LINK_TEXT_COLOR); + mEditText.setAutoLinkMask(Linkify.WEB_URLS); + ViewGroup.LayoutParams params = + new ViewGroup.LayoutParams( + ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); + mEditText.setLayoutParams(params); + mEditText.measure(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); + Bitmap bitmap = + Bitmap.createBitmap( + Math.max(1, mEditText.getMeasuredWidth()), + Math.max(1, mEditText.getMeasuredHeight()), + Bitmap.Config.ARGB_8888); + mEditText.layout(0, 0, mEditText.getMeasuredWidth(), mEditText.getMeasuredHeight()); + mCanvas = new Canvas(bitmap); + mEditText.draw(mCanvas); + } private void assertTextAppearanceInfoContentsEqual(TextAppearanceInfo textAppearanceInfo) { assertEquals(textAppearanceInfo.getTextSize(), TEXT_SIZE, EPSILON); assertEquals(textAppearanceInfo.getTextLocales(), TEXT_LOCALES); @@ -258,6 +264,15 @@ public class TextAppearanceInfoTest { assertEquals(textAppearanceInfo.getLinkTextColor(), LINK_TEXT_COLOR); } + @Test + public void testCreateFromTextView_withHintText() { + // Make hint text display + initEditText(""); + + // The text color should not be hint color + assertTextAppearanceInfoContentsEqual(TextAppearanceInfo.createFromTextView(mEditText)); + } + static class CustomForegroundColorSpan extends ForegroundColorSpan { @Nullable public TextPaint lastTextPaint = null; diff --git a/core/tests/coretests/src/android/widget/PointerIconTest.java b/core/tests/coretests/src/android/widget/PointerIconTest.java new file mode 100644 index 000000000000..8e9e1a50d8c9 --- /dev/null +++ b/core/tests/coretests/src/android/widget/PointerIconTest.java @@ -0,0 +1,306 @@ +/* + * 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 android.widget; + +import static com.google.common.truth.Truth.assertThat; + +import android.annotation.UiThread; +import android.app.Instrumentation; +import android.graphics.Rect; +import android.os.SystemClock; +import android.view.InputDevice; +import android.view.MotionEvent; +import android.view.PointerIcon; +import android.view.View; + +import androidx.test.ext.junit.rules.ActivityScenarioRule; +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.filters.SmallTest; +import androidx.test.platform.app.InstrumentationRegistry; + +import com.android.frameworks.coretests.R; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.Calendar; +import java.util.GregorianCalendar; + +@SmallTest +@RunWith(AndroidJUnit4.class) +public class PointerIconTest { + private Instrumentation mInstrumentation; + + @Before + public void setup() { + mInstrumentation = InstrumentationRegistry.getInstrumentation(); + } + + @Rule + public ActivityScenarioRule<PointerIconTestActivity> mActivityScenarioRule = + new ActivityScenarioRule<>(PointerIconTestActivity.class); + + @Test + @UiThread + public void button_mouse_onResolvePointerIcon_returnsTypeHand() { + assertOnResolvePointerIconForMouseEvent(R.id.button, PointerIcon.TYPE_HAND); + } + + @Test + @UiThread + public void button_mouse_disabled_onResolvePointerIcon_returnsNull() { + assertOnResolvePointerIconReturnNull(R.id.button, /* enabled */ false, /* clickable */ true, + /* isMouse */ true); + } + + @Test + @UiThread + public void button_mouse_unclickable_onResolvePointerIcon_returnsNull() { + assertOnResolvePointerIconReturnNull(R.id.button, /* enabled */ true, /* clickable */ false, + /* isMouse */ true); + } + + @Test + @UiThread + public void button_stylus_onResolvePointerIcon_returnsNull() { + assertOnResolvePointerIconReturnNull(R.id.button, /* enabled */ true, /* clickable */ true, + /* isMouse */ false); + } + + @Test + @UiThread + public void imageButton_mouse_onResolvePointerIconreturnsTypeHand() { + assertOnResolvePointerIconForMouseEvent(R.id.imagebutton, PointerIcon.TYPE_HAND); + } + + @Test + @UiThread + public void imageButton_mouse_diabled_onResolvePointerIcon_returnsNull() { + assertOnResolvePointerIconReturnNull(R.id.imagebutton, /* enabled */ false, + /* clickable */ true, /* isMouse */ true); + } + + @Test + @UiThread + public void imageButton_mouse_unclickable_onResolvePointerIcon_returnsNull() { + assertOnResolvePointerIconReturnNull(R.id.imagebutton, /* enabled */ true, + /* clickable */ false, /* isMouse */ true); + } + + @Test + @UiThread + public void imageButton_stylus_onResolvePointerIcon_returnsNull() { + assertOnResolvePointerIconReturnNull(R.id.imagebutton, /* enabled */ true, + /* clickable */ true, /* isMouse */ false); + } + + @Test + @UiThread + public void textView_mouse_onResolvePointerIcon_returnsNull() { + assertOnResolvePointerIconReturnNull(R.id.textview, /* enabled */ true, + /* clickable */ true, /* isMouse */ true); + } + + @Test + @UiThread + public void textView_stylus_onResolvePointerIcon_returnsNull() { + assertOnResolvePointerIconReturnNull(R.id.textview, /* enabled */ true, + /* clickable */ true, /* isMouse */ false); + } + + @Test + @UiThread + public void editText_mouse_onResolvePointerIcon_returnsTypeText() { + assertOnResolvePointerIconForMouseEvent(R.id.edittext, PointerIcon.TYPE_TEXT); + } + + @Test + @UiThread + public void editText_stylus_onResolvePointerIcon_returnsNull() { + assertOnResolvePointerIconReturnNull(R.id.edittext, /* enabled */ true, + /* clickable */ true, /* isMouse */ false); + } + + @Test + @UiThread + public void spinner_mouse_onResolvePointerIcon_returnsTypeHand() { + assertOnResolvePointerIconForMouseEvent(R.id.spinner, PointerIcon.TYPE_HAND); + } + + @Test + @UiThread + public void spinner_mouse_disabled_onResolvePointerIcon_returnsNull() { + assertOnResolvePointerIconReturnNull(R.id.spinner, /* enabled */ false, + /* clickable */ true, /* isMouse */ true); + } + + @Test + @UiThread + public void spinner_mouse_unclickable_onResolvePointerIcon_returnsNull() { + assertOnResolvePointerIconReturnNull(R.id.spinner, /* enabled */ true, + /* clickable */ false, /* isMouse */ true); + } + + @Test + @UiThread + public void spinner_stylus_onResolvePointerIcon_returnsNull() { + assertOnResolvePointerIconReturnNull(R.id.spinner, /* enabled */ true, /* clickable */ true, + /* isMouse */ false); + } + + @Test + @UiThread + public void radialTimePickerView_mouse_onResolvePointerIcon_returnsTypeHand() { + assertOnResolvePointerIconForMouseEvent(R.id.timepicker, PointerIcon.TYPE_HAND); + + } + + @Test + @UiThread + public void radialTimePickerView_mouse_disabled_onResolvePointerIcon_returnsNull() { + assertOnResolvePointerIconReturnNull(R.id.timepicker, /* enabled */ false, + /* clickable */ true, /* isMouse */ true); + } + + @Test + @UiThread + public void radialTimePickerView_stylus_onResolvePointerIcon_returnsNull() { + assertOnResolvePointerIconReturnNull(R.id.timepicker, /* enabled */ true, + /* clickable */ true, /* isMouse */ false); + } + + @Test + @UiThread + public void calendarView_mouse_onResolvePointerIcon_returnsTypeHand() { + assertPointerIconForCalendarView(/* pointerType */ PointerIcon.TYPE_HAND, + /* isMouse */ true); + } + + @Test + @UiThread + public void calendarView_stylus_onResolvePointerIcon_returnsNull() { + assertPointerIconForCalendarView(/* pointerType */ Integer.MIN_VALUE, /* isMouse */ false); + } + + /** + * Assert {@link View#onResolvePointerIcon} method for {@link CalendarView}. + * + * @param pointerType the expected type of the {@link PointerIcon}. + * When {@link Integer#MIN_VALUE} is passed, it will verify that the + * returned {@link PointerIcon} is null. + * @param isMouse if true, mouse events are used to test the given view. Otherwise, it uses + * stylus events to test the view. + */ + void assertPointerIconForCalendarView(int pointerType, boolean isMouse) { + Calendar calendar = new GregorianCalendar(); + calendar.set(2023, 0, 1); + long time = calendar.getTimeInMillis(); + mActivityScenarioRule.getScenario().onActivity(activity -> { + CalendarView calendarView = activity.findViewById(R.id.calendar); + calendarView.setDate(time, /* animate */ false, /* center */true); + }); + + // Wait for setDate to finish and then verify. + mInstrumentation.waitForIdleSync(); + mActivityScenarioRule.getScenario().onActivity(activity -> { + CalendarView calendarView = activity.findViewById(R.id.calendar); + Rect bounds = new Rect(); + calendarView.getBoundsForDate(time, bounds); + MotionEvent event = createHoverEvent(isMouse, bounds.centerX(), bounds.centerY()); + PointerIcon icon = calendarView.onResolvePointerIcon(event, /* pointerIndex */ 0); + if (pointerType != Integer.MIN_VALUE) { + assertThat(icon.getType()).isEqualTo(pointerType); + } else { + assertThat(icon).isNull(); + } + }); + } + + /** + * Assert that the given view's {@link View#onResolvePointerIcon(MotionEvent, int)} method + * returns a {@link PointerIcon} with the specified pointer type. The passed {@link MotionEvent} + * locates at the center of the view. + * + * @param resId the resource id of the view to be tested. + * @param pointerType the expected pointer type. When {@link Integer#MIN_VALUE} is passed, it + * will verify that the returned {@link PointerIcon} is null. + */ + public void assertOnResolvePointerIconForMouseEvent(int resId, int pointerType) { + mActivityScenarioRule.getScenario().onActivity(activity -> { + View view = activity.findViewById(resId); + MotionEvent event = createHoverEvent(/* isMouse */ true, /* x */ 0, /* y */ 0); + PointerIcon icon = view.onResolvePointerIcon(event, /* pointerIndex */ 0); + if (pointerType != Integer.MIN_VALUE) { + assertThat(icon.getType()).isEqualTo(pointerType); + } else { + assertThat(icon).isNull(); + } + }); + } + + /** + * Assert that the given view's {@link View#onResolvePointerIcon(MotionEvent, int)} method + * returns a {@link PointerIcon} with the specified pointer type. The passed {@link MotionEvent} + * locates at the center of the view. + * + * @param resId the resource id of the view to be tested. + * @param enabled whether the tested view is enabled. + * @param clickable whether the tested view is clickable. + * @param isMouse if true, mouse events are used to test the given view. Otherwise, it uses + * stylus events to test the view. + */ + public void assertOnResolvePointerIconReturnNull(int resId, boolean enabled, boolean clickable, + boolean isMouse) { + mActivityScenarioRule.getScenario().onActivity(activity -> { + View view = activity.findViewById(resId); + view.setEnabled(enabled); + view.setClickable(clickable); + MotionEvent event = createHoverEvent(isMouse, /* x */ 0, /* y */ 0); + PointerIcon icon = view.onResolvePointerIcon(event, /* pointerIndex */ 0); + assertThat(icon).isNull(); + }); + } + + + /** + * Create a hover {@link MotionEvent} for testing. + * + * @param isMouse if true, a {@link MotionEvent} from mouse is returned. Otherwise, + * a {@link MotionEvent} from stylus is returned. + * @param x the x coordinate of the returned {@link MotionEvent} + * @param y the y coordinate of the returned {@link MotionEvent} + */ + private MotionEvent createHoverEvent(boolean isMouse, int x, int y) { + MotionEvent.PointerProperties[] properties = MotionEvent.PointerProperties.createArray(1); + properties[0].toolType = + isMouse ? MotionEvent.TOOL_TYPE_MOUSE : MotionEvent.TOOL_TYPE_STYLUS; + + MotionEvent.PointerCoords[] coords = MotionEvent.PointerCoords.createArray(1); + coords[0].x = x; + coords[0].y = y; + + int source = isMouse ? InputDevice.SOURCE_MOUSE : InputDevice.SOURCE_STYLUS; + long eventTime = SystemClock.uptimeMillis(); + return MotionEvent.obtain(/* downTime */ 0, eventTime, MotionEvent.ACTION_HOVER_MOVE, + /* pointerCount */ 1, properties, coords, /* metaState */ 0, /* buttonState */ 0, + /* xPrecision */ 1, /* yPrecision */ 1, /* deviceId */ 0, /* edgeFlags */ 0, + source, /* flags */ 0); + } + +} diff --git a/core/tests/coretests/src/android/widget/PointerIconTestActivity.java b/core/tests/coretests/src/android/widget/PointerIconTestActivity.java new file mode 100644 index 000000000000..d491fb051e30 --- /dev/null +++ b/core/tests/coretests/src/android/widget/PointerIconTestActivity.java @@ -0,0 +1,35 @@ +/* + * 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 android.widget; + +import android.app.Activity; +import android.os.Bundle; + +import com.android.frameworks.coretests.R; + +public class PointerIconTestActivity extends Activity { + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.pointer_icon_test); + + RadialTimePickerView timePicker = findViewById(R.id.timepicker); + // Set the time of TimePicker to 0:00 so that the test is stable. + timePicker.setCurrentHour(0); + timePicker.setCurrentMinute(0); + } +} diff --git a/core/tests/coretests/src/com/android/internal/widget/LockPatternUtilsTest.java b/core/tests/coretests/src/com/android/internal/widget/LockPatternUtilsTest.java index 6167c4b80cee..1a668f7bdc8f 100644 --- a/core/tests/coretests/src/com/android/internal/widget/LockPatternUtilsTest.java +++ b/core/tests/coretests/src/com/android/internal/widget/LockPatternUtilsTest.java @@ -37,10 +37,23 @@ public class LockPatternUtilsTest { } @Test + public void testUserRepairMode_isNotRegularUser() { + assertTrue(LockPatternUtils.USER_REPAIR_MODE < 0); + } + + @Test public void testUserFrp_isNotAReservedSpecialUser() throws Exception { assertNotEquals(UserHandle.USER_NULL, LockPatternUtils.USER_FRP); assertNotEquals(UserHandle.USER_ALL, LockPatternUtils.USER_FRP); assertNotEquals(UserHandle.USER_CURRENT, LockPatternUtils.USER_FRP); assertNotEquals(UserHandle.USER_CURRENT_OR_SELF, LockPatternUtils.USER_FRP); } + + @Test + public void testUserRepairMode_isNotAReservedSpecialUser() throws Exception { + assertNotEquals(UserHandle.USER_NULL, LockPatternUtils.USER_REPAIR_MODE); + assertNotEquals(UserHandle.USER_ALL, LockPatternUtils.USER_REPAIR_MODE); + assertNotEquals(UserHandle.USER_CURRENT, LockPatternUtils.USER_REPAIR_MODE); + assertNotEquals(UserHandle.USER_CURRENT_OR_SELF, LockPatternUtils.USER_REPAIR_MODE); + } } diff --git a/core/tests/mockingcoretests/Android.bp b/core/tests/mockingcoretests/Android.bp index 96811be37f5a..741e5357bc55 100644 --- a/core/tests/mockingcoretests/Android.bp +++ b/core/tests/mockingcoretests/Android.bp @@ -56,7 +56,10 @@ android_test { ], platform_apis: true, - test_suites: ["device-tests"], + test_suites: [ + "device-tests", + "automotive-tests", + ], certificate: "platform", } diff --git a/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java b/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java index be2c27de637c..3e0e36d6f3b8 100644 --- a/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java +++ b/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java @@ -181,7 +181,7 @@ public class ActivityThreadClientTest { // Verify for ON_START state. Activity should be relaunched. getInstrumentation().runOnMainSync(() -> clientSession.startActivity(r)); - recreateAndVerifyRelaunched(activityThread, activity[0], r, ON_START); + recreateAndVerifyRelaunched(activityThread, activity[0], r, ON_PAUSE); // Verify for ON_RESUME state. Activity should be relaunched. getInstrumentation().runOnMainSync(() -> clientSession.resumeActivity(r)); diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json index 93e44f1d2f87..94e23e735d06 100644 --- a/data/etc/services.core.protolog.json +++ b/data/etc/services.core.protolog.json @@ -3385,6 +3385,12 @@ "group": "WM_DEBUG_REMOTE_ANIMATIONS", "at": "com\/android\/server\/wm\/RemoteAnimationController.java" }, + "975028389": { + "message": "unable to call receiver for empty keyboard shortcuts", + "level": "ERROR", + "group": "WM_ERROR", + "at": "com\/android\/server\/wm\/WindowManagerService.java" + }, "975275467": { "message": "Set animatingExit: reason=remove\/isAnimating win=%s", "level": "VERBOSE", diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java index 89f4890c254e..4cedd41e2d9a 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java @@ -308,7 +308,8 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen forAllTaskContainers(taskContainer -> { synchronized (mLock) { - final List<TaskFragmentContainer> containers = taskContainer.mContainers; + final List<TaskFragmentContainer> containers = + taskContainer.getTaskFragmentContainers(); // Clean up the TaskFragmentContainers by the z-order from the lowest. for (int i = 0; i < containers.size(); i++) { final TaskFragmentContainer container = containers.get(i); @@ -611,8 +612,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen @NonNull TaskContainer taskContainer) { // Update all TaskFragments in the Task. Make a copy of the list since some may be // removed on updating. - final List<TaskFragmentContainer> containers = - new ArrayList<>(taskContainer.mContainers); + final List<TaskFragmentContainer> containers = taskContainer.getTaskFragmentContainers(); for (int i = containers.size() - 1; i >= 0; i--) { final TaskFragmentContainer container = containers.get(i); // Wait until onTaskFragmentAppeared to update new container. @@ -1331,7 +1331,8 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen // Check pending appeared activity first because there can be a delay for the server // update. for (int i = mTaskContainers.size() - 1; i >= 0; i--) { - final List<TaskFragmentContainer> containers = mTaskContainers.valueAt(i).mContainers; + final List<TaskFragmentContainer> containers = mTaskContainers.valueAt(i) + .getTaskFragmentContainers(); for (int j = containers.size() - 1; j >= 0; j--) { final TaskFragmentContainer container = containers.get(j); if (container.hasPendingAppearedActivity(activityToken)) { @@ -1342,7 +1343,8 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen // Check appeared activity if there is no such pending appeared activity. for (int i = mTaskContainers.size() - 1; i >= 0; i--) { - final List<TaskFragmentContainer> containers = mTaskContainers.valueAt(i).mContainers; + final List<TaskFragmentContainer> containers = mTaskContainers.valueAt(i) + .getTaskFragmentContainers(); for (int j = containers.size() - 1; j >= 0; j--) { final TaskFragmentContainer container = containers.get(j); if (container.hasAppearedActivity(activityToken)) { @@ -1418,7 +1420,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen if (splitRule instanceof SplitPairRule && ((SplitPairRule) splitRule).shouldClearTop()) { removeExistingSecondaryContainers(wct, primaryContainer); } - primaryContainer.getTaskContainer().mSplitContainers.add(splitContainer); + primaryContainer.getTaskContainer().addSplitContainer(splitContainer); } /** Cleanups all the dependencies when the TaskFragment is entering PIP. */ @@ -1430,8 +1432,9 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen return; } final List<SplitContainer> splitsToRemove = new ArrayList<>(); + final List<SplitContainer> splitContainers = taskContainer.getSplitContainers(); final Set<TaskFragmentContainer> containersToUpdate = new ArraySet<>(); - for (SplitContainer splitContainer : taskContainer.mSplitContainers) { + for (SplitContainer splitContainer : splitContainers) { if (splitContainer.getPrimaryContainer() != container && splitContainer.getSecondaryContainer() != container) { continue; @@ -1449,7 +1452,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen } } container.resetDependencies(); - taskContainer.mSplitContainers.removeAll(splitsToRemove); + taskContainer.removeSplitContainers(splitsToRemove); // If there is any TaskFragment split with the PIP TaskFragment, update their presentations // since the split is dismissed. // We don't want to close any of them even if they are dependencies of the PIP TaskFragment. @@ -1471,7 +1474,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen void removeContainers(@NonNull TaskContainer taskContainer, @NonNull List<TaskFragmentContainer> containers) { // Remove all split containers that included this one - taskContainer.mContainers.removeAll(containers); + taskContainer.removeTaskFragmentContainers(containers); // Marked as a pending removal which will be removed after it is actually removed on the // server side (#onTaskFragmentVanished). // In this way, we can keep track of the Task bounds until we no longer have any @@ -1481,7 +1484,8 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen // Cleanup any split references. final List<SplitContainer> containersToRemove = new ArrayList<>(); - for (SplitContainer splitContainer : taskContainer.mSplitContainers) { + final List<SplitContainer> splitContainers = taskContainer.getSplitContainers(); + for (SplitContainer splitContainer : splitContainers) { if (containersToRemove.contains(splitContainer)) { // Don't need to check because it has been in the remove list. continue; @@ -1492,10 +1496,12 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen containersToRemove.add(splitContainer); } } - taskContainer.mSplitContainers.removeAll(containersToRemove); + taskContainer.removeSplitContainers(containersToRemove); // Cleanup any dependent references. - for (TaskFragmentContainer containerToUpdate : taskContainer.mContainers) { + final List<TaskFragmentContainer> taskFragmentContainers = + taskContainer.getTaskFragmentContainers(); + for (TaskFragmentContainer containerToUpdate : taskFragmentContainers) { containerToUpdate.removeContainersToFinishOnExit(containers); } } @@ -1534,8 +1540,9 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen if (taskContainer == null) { return null; } - for (int i = taskContainer.mContainers.size() - 1; i >= 0; i--) { - final TaskFragmentContainer container = taskContainer.mContainers.get(i); + final List<TaskFragmentContainer> containers = taskContainer.getTaskFragmentContainers(); + for (int i = containers.size() - 1; i >= 0; i--) { + final TaskFragmentContainer container = containers.get(i); if (!container.isFinished() && (container.getRunningActivityCount() > 0 // We may be waiting for the top TaskFragment to become non-empty after // creation. In that case, we don't want to treat the TaskFragment below it as @@ -1629,7 +1636,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen /** Whether the given split is the topmost split in the Task. */ private boolean isTopMostSplit(@NonNull SplitContainer splitContainer) { final List<SplitContainer> splitContainers = splitContainer.getPrimaryContainer() - .getTaskContainer().mSplitContainers; + .getTaskContainer().getSplitContainers(); return splitContainer == splitContainers.get(splitContainers.size() - 1); } @@ -1641,7 +1648,8 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen if (container == null) { return null; } - final List<SplitContainer> splitContainers = container.getTaskContainer().mSplitContainers; + final List<SplitContainer> splitContainers = + container.getTaskContainer().getSplitContainers(); if (splitContainers.isEmpty()) { return null; } @@ -1665,7 +1673,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen @NonNull TaskFragmentContainer firstContainer, @NonNull TaskFragmentContainer secondContainer) { final List<SplitContainer> splitContainers = firstContainer.getTaskContainer() - .mSplitContainers; + .getSplitContainers(); for (int i = splitContainers.size() - 1; i >= 0; i--) { final SplitContainer splitContainer = splitContainers.get(i); final TaskFragmentContainer primary = splitContainer.getPrimaryContainer(); @@ -1930,7 +1938,8 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen @GuardedBy("mLock") TaskFragmentContainer getContainer(@NonNull IBinder fragmentToken) { for (int i = mTaskContainers.size() - 1; i >= 0; i--) { - final List<TaskFragmentContainer> containers = mTaskContainers.valueAt(i).mContainers; + final List<TaskFragmentContainer> containers = mTaskContainers.valueAt(i) + .getTaskFragmentContainers(); for (TaskFragmentContainer container : containers) { if (container.getTaskFragmentToken().equals(fragmentToken)) { return container; @@ -1945,7 +1954,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen @GuardedBy("mLock") SplitContainer getSplitContainer(@NonNull IBinder token) { for (int i = mTaskContainers.size() - 1; i >= 0; i--) { - final List<SplitContainer> containers = mTaskContainers.valueAt(i).mSplitContainers; + final List<SplitContainer> containers = mTaskContainers.valueAt(i).getSplitContainers(); for (SplitContainer container : containers) { if (container.getToken().equals(token)) { return container; @@ -2091,7 +2100,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen } for (int i = mTaskContainers.size() - 1; i >= 0; i--) { final List<TaskFragmentContainer> containers = mTaskContainers.valueAt(i) - .mContainers; + .getTaskFragmentContainers(); for (int j = containers.size() - 1; j >= 0; j--) { final TaskFragmentContainer container = containers.get(j); if (!container.hasActivity(activityToken) 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 4b15bb187035..4580c9836168 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java @@ -51,11 +51,11 @@ class TaskContainer { /** Active TaskFragments in this Task. */ @NonNull - final List<TaskFragmentContainer> mContainers = new ArrayList<>(); + private final List<TaskFragmentContainer> mContainers = new ArrayList<>(); /** Active split pairs in this Task. */ @NonNull - final List<SplitContainer> mSplitContainers = new ArrayList<>(); + private final List<SplitContainer> mSplitContainers = new ArrayList<>(); @NonNull private final Configuration mConfiguration; @@ -207,6 +207,53 @@ class TaskContainer { return false; } + /** + * Returns a list of {@link SplitContainer}. Do not modify the containers directly on the + * returned list. Use {@link #addSplitContainer} or {@link #removeSplitContainers} instead. + */ + @NonNull + List<SplitContainer> getSplitContainers() { + return mSplitContainers; + } + + void addSplitContainer(@NonNull SplitContainer splitContainer) { + mSplitContainers.add(splitContainer); + } + + void removeSplitContainers(@NonNull List<SplitContainer> containers) { + mSplitContainers.removeAll(containers); + } + + void addTaskFragmentContainer(@NonNull TaskFragmentContainer taskFragmentContainer) { + mContainers.add(taskFragmentContainer); + } + + void addTaskFragmentContainer(int index, @NonNull TaskFragmentContainer taskFragmentContainer) { + mContainers.add(index, taskFragmentContainer); + } + + void removeTaskFragmentContainer(@NonNull TaskFragmentContainer taskFragmentContainer) { + mContainers.remove(taskFragmentContainer); + } + + void removeTaskFragmentContainers(@NonNull List<TaskFragmentContainer> taskFragmentContainer) { + mContainers.removeAll(taskFragmentContainer); + } + + void clearTaskFragmentContainer() { + mContainers.clear(); + } + + /** + * Returns a list of {@link TaskFragmentContainer}. Do not modify the containers directly on + * the returned list. Use {@link #addTaskFragmentContainer}, + * {@link #removeTaskFragmentContainer} or other related methods instead. + */ + @NonNull + List<TaskFragmentContainer> getTaskFragmentContainers() { + return mContainers; + } + /** Adds the descriptors of split states in this Task to {@code outSplitStates}. */ void getSplitStates(@NonNull List<SplitInfo> outSplitStates) { for (SplitContainer container : mSplitContainers) { diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java index 60be9d16d749..61df335515b8 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java @@ -180,23 +180,25 @@ class TaskFragmentContainer { throw new IllegalArgumentException( "pairedPrimaryContainer must be in the same Task"); } - final int primaryIndex = taskContainer.mContainers.indexOf(pairedPrimaryContainer); - taskContainer.mContainers.add(primaryIndex + 1, this); + final int primaryIndex = taskContainer.indexOf(pairedPrimaryContainer); + taskContainer.addTaskFragmentContainer(primaryIndex + 1, this); } else if (pendingAppearedActivity != null) { // The TaskFragment will be positioned right above the pending appeared Activity. If any // existing TaskFragment is empty with pending Intent, it is likely that the Activity of // the pending Intent hasn't been created yet, so the new Activity should be below the // empty TaskFragment. - int i = taskContainer.mContainers.size() - 1; + final List<TaskFragmentContainer> containers = + taskContainer.getTaskFragmentContainers(); + int i = containers.size() - 1; for (; i >= 0; i--) { - final TaskFragmentContainer container = taskContainer.mContainers.get(i); + final TaskFragmentContainer container = containers.get(i); if (!container.isEmpty() || container.getPendingAppearedIntent() == null) { break; } } - taskContainer.mContainers.add(i + 1, this); + taskContainer.addTaskFragmentContainer(i + 1, this); } else { - taskContainer.mContainers.add(this); + taskContainer.addTaskFragmentContainer(this); } if (pendingAppearedActivity != null) { addPendingAppearedActivity(pendingAppearedActivity); diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java index ff08782e8cd8..9e264726a65a 100644 --- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java +++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java @@ -183,23 +183,23 @@ public class SplitControllerTest { // tf2 has running activity so is active. final TaskFragmentContainer tf2 = mock(TaskFragmentContainer.class); doReturn(1).when(tf2).getRunningActivityCount(); - taskContainer.mContainers.add(tf2); + taskContainer.addTaskFragmentContainer(tf2); // tf3 is finished so is not active. final TaskFragmentContainer tf3 = mock(TaskFragmentContainer.class); doReturn(true).when(tf3).isFinished(); doReturn(false).when(tf3).isWaitingActivityAppear(); - taskContainer.mContainers.add(tf3); + taskContainer.addTaskFragmentContainer(tf3); mSplitController.mTaskContainers.put(TASK_ID, taskContainer); assertWithMessage("Must return tf2 because tf3 is not active.") .that(mSplitController.getTopActiveContainer(TASK_ID)).isEqualTo(tf2); - taskContainer.mContainers.remove(tf3); + taskContainer.removeTaskFragmentContainer(tf3); assertWithMessage("Must return tf2 because tf2 has running activity.") .that(mSplitController.getTopActiveContainer(TASK_ID)).isEqualTo(tf2); - taskContainer.mContainers.remove(tf2); + taskContainer.removeTaskFragmentContainer(tf2); assertWithMessage("Must return tf because we are waiting for tf1 to appear.") .that(mSplitController.getTopActiveContainer(TASK_ID)).isEqualTo(tf1); @@ -320,11 +320,11 @@ public class SplitControllerTest { doReturn(tf).when(splitContainer).getSecondaryContainer(); doReturn(createTestTaskContainer()).when(splitContainer).getTaskContainer(); doReturn(createSplitRule(mActivity, mActivity)).when(splitContainer).getSplitRule(); - final List<SplitContainer> splitContainers = - mSplitController.getTaskContainer(TASK_ID).mSplitContainers; - splitContainers.add(splitContainer); + final TaskContainer taskContainer = mSplitController.getTaskContainer(TASK_ID); + taskContainer.addSplitContainer(splitContainer); // Add a mock SplitContainer on top of splitContainer - splitContainers.add(1, mock(SplitContainer.class)); + final SplitContainer splitContainer2 = mock(SplitContainer.class); + taskContainer.addSplitContainer(splitContainer2); mSplitController.updateContainer(mTransaction, tf); @@ -332,7 +332,9 @@ public class SplitControllerTest { // Verify if one or both containers in the top SplitContainer are finished, // dismissPlaceholder() won't be called. - splitContainers.remove(1); + final ArrayList<SplitContainer> splitContainersToRemove = new ArrayList<>(); + splitContainersToRemove.add(splitContainer2); + taskContainer.removeSplitContainers(splitContainersToRemove); doReturn(true).when(tf).isFinished(); mSplitController.updateContainer(mTransaction, tf); @@ -363,7 +365,8 @@ public class SplitControllerTest { final Activity r1 = createMockActivity(); addSplitTaskFragments(r0, r1); final TaskContainer taskContainer = mSplitController.getTaskContainer(TASK_ID); - final TaskFragmentContainer taskFragmentContainer = taskContainer.mContainers.get(0); + final TaskFragmentContainer taskFragmentContainer = + taskContainer.getTaskFragmentContainers().get(0); spyOn(taskContainer); // No update when the Task is invisible. @@ -377,7 +380,7 @@ public class SplitControllerTest { doReturn(true).when(taskContainer).isVisible(); mSplitController.updateContainer(mTransaction, taskFragmentContainer); - verify(mSplitPresenter).updateSplitContainer(taskContainer.mSplitContainers.get(0), + verify(mSplitPresenter).updateSplitContainer(taskContainer.getSplitContainers().get(0), mTransaction); } @@ -1090,8 +1093,8 @@ public class SplitControllerTest { verify(mTransaction).finishActivity(mActivity.getActivityToken()); verify(mTransaction).finishActivity(secondaryActivity0.getActivityToken()); verify(mTransaction).finishActivity(secondaryActivity1.getActivityToken()); - assertTrue(taskContainer.mContainers.isEmpty()); - assertTrue(taskContainer.mSplitContainers.isEmpty()); + assertTrue(taskContainer.getTaskFragmentContainers().isEmpty()); + assertTrue(taskContainer.getSplitContainers().isEmpty()); } @Test @@ -1363,15 +1366,13 @@ public class SplitControllerTest { TaskFragmentContainer tf = mSplitController.newContainer(mActivity, TASK_ID); tf.setInfo(mTransaction, createMockTaskFragmentInfo(tf, mActivity)); - List<TaskFragmentContainer> containers = mSplitController.mTaskContainers.get(TASK_ID) - .mContainers; - - assertEquals(containers.get(0), tf); + final TaskContainer taskContainer = mSplitController.mTaskContainers.get(TASK_ID); + assertEquals(taskContainer.getTaskFragmentContainers().get(0), tf); mSplitController.finishActivityStacks(Collections.singleton(tf.getTaskFragmentToken())); verify(mSplitPresenter).deleteTaskFragment(any(), eq(tf.getTaskFragmentToken())); - assertTrue(containers.isEmpty()); + assertTrue(taskContainer.getTaskFragmentContainers().isEmpty()); } @Test @@ -1381,10 +1382,8 @@ public class SplitControllerTest { bottomTf.setInfo(mTransaction, createMockTaskFragmentInfo(bottomTf, mActivity)); topTf.setInfo(mTransaction, createMockTaskFragmentInfo(topTf, createMockActivity())); - List<TaskFragmentContainer> containers = mSplitController.mTaskContainers.get(TASK_ID) - .mContainers; - - assertEquals(containers.size(), 2); + final TaskContainer taskContainer = mSplitController.mTaskContainers.get(TASK_ID); + assertEquals(taskContainer.getTaskFragmentContainers().size(), 2); Set<IBinder> activityStackTokens = new ArraySet<>(new IBinder[]{ topTf.getTaskFragmentToken(), bottomTf.getTaskFragmentToken()}); @@ -1403,7 +1402,7 @@ public class SplitControllerTest { + "regardless of the order in ActivityStack set", topTf.getTaskFragmentToken(), fragmentTokens.get(1)); - assertTrue(containers.isEmpty()); + assertTrue(taskContainer.getTaskFragmentContainers().isEmpty()); } @Test diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java index 13e709271221..11af1d1f20e1 100644 --- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java +++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java @@ -127,7 +127,7 @@ public class TaskContainerTest { assertFalse(taskContainer.isEmpty()); taskContainer.mFinishedContainer.add(tf.getTaskFragmentToken()); - taskContainer.mContainers.clear(); + taskContainer.clearTaskFragmentContainer(); assertFalse(taskContainer.isEmpty()); } @@ -152,13 +152,13 @@ public class TaskContainerTest { assertNull(taskContainer.getTopNonFinishingActivity()); final TaskFragmentContainer tf0 = mock(TaskFragmentContainer.class); - taskContainer.mContainers.add(tf0); + taskContainer.addTaskFragmentContainer(tf0); final Activity activity0 = mock(Activity.class); doReturn(activity0).when(tf0).getTopNonFinishingActivity(); assertEquals(activity0, taskContainer.getTopNonFinishingActivity()); final TaskFragmentContainer tf1 = mock(TaskFragmentContainer.class); - taskContainer.mContainers.add(tf1); + taskContainer.addTaskFragmentContainer(tf1); assertEquals(activity0, taskContainer.getTopNonFinishingActivity()); final Activity activity1 = mock(Activity.class); diff --git a/libs/WindowManager/Shell/Android.bp b/libs/WindowManager/Shell/Android.bp index 54978bd4496d..71598938f42f 100644 --- a/libs/WindowManager/Shell/Android.bp +++ b/libs/WindowManager/Shell/Android.bp @@ -42,16 +42,19 @@ filegroup { filegroup { name: "wm_shell_util-sources", srcs: [ - "src/com/android/wm/shell/util/**/*.java", + "src/com/android/wm/shell/animation/Interpolators.java", + "src/com/android/wm/shell/animation/PhysicsAnimator.kt", + "src/com/android/wm/shell/common/bubbles/*.kt", + "src/com/android/wm/shell/common/bubbles/*.java", + "src/com/android/wm/shell/common/magnetictarget/MagnetizedObject.kt", "src/com/android/wm/shell/common/split/SplitScreenConstants.java", - "src/com/android/wm/shell/sysui/ShellSharedConstants.java", "src/com/android/wm/shell/common/TransactionPool.java", - "src/com/android/wm/shell/common/bubbles/*.java", "src/com/android/wm/shell/common/TriangleShape.java", - "src/com/android/wm/shell/animation/Interpolators.java", + "src/com/android/wm/shell/draganddrop/DragAndDropConstants.java", "src/com/android/wm/shell/pip/PipContentOverlay.java", "src/com/android/wm/shell/startingsurface/SplashScreenExitAnimationUtils.java", - "src/com/android/wm/shell/draganddrop/DragAndDropConstants.java", + "src/com/android/wm/shell/sysui/ShellSharedConstants.java", + "src/com/android/wm/shell/util/**/*.java", ], path: "src", } diff --git a/libs/WindowManager/Shell/res/drawable/bubble_manage_menu_section.xml b/libs/WindowManager/Shell/res/drawable/bubble_manage_menu_section.xml new file mode 100644 index 000000000000..d99d64d8da20 --- /dev/null +++ b/libs/WindowManager/Shell/res/drawable/bubble_manage_menu_section.xml @@ -0,0 +1,24 @@ +<?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. + --> +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:state_pressed="true"> + <ripple android:color="#99999999"> + <item android:drawable="@drawable/bubble_manage_menu_bg" /> + </ripple> + </item> + <item android:drawable="@drawable/bubble_manage_menu_bg" /> +</selector>
\ No newline at end of file diff --git a/libs/WindowManager/Shell/res/drawable/decor_handle_dark.xml b/libs/WindowManager/Shell/res/drawable/decor_handle_dark.xml index 5d7771366bec..ce242751c172 100644 --- a/libs/WindowManager/Shell/res/drawable/decor_handle_dark.xml +++ b/libs/WindowManager/Shell/res/drawable/decor_handle_dark.xml @@ -13,13 +13,20 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License. --> -<vector xmlns:android="http://schemas.android.com/apk/res/android" - android:width="24dp" - android:height="24dp" - android:viewportWidth="24" - android:viewportHeight="24"> - <group android:translateY="8.0"> +<vector + xmlns:android="http://schemas.android.com/apk/res/android" + android:width="128dp" + android:height="4dp" + android:viewportWidth="128" + android:viewportHeight="4" + > + <group> + <clip-path + android:pathData="M2 0H126C127.105 0 128 0.895431 128 2C128 3.10457 127.105 4 126 4H2C0.895431 4 0 3.10457 0 2C0 0.895431 0.895431 0 2 0Z" + /> <path - android:fillColor="@android:color/black" android:pathData="M3,5V3H21V5Z"/> + android:pathData="M0 0V4H128V0" + android:fillColor="@android:color/black" + /> </group> </vector> diff --git a/libs/WindowManager/Shell/res/drawable/ic_expand_less.xml b/libs/WindowManager/Shell/res/drawable/ic_expand_less.xml new file mode 100644 index 000000000000..f4508464883d --- /dev/null +++ b/libs/WindowManager/Shell/res/drawable/ic_expand_less.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2023 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M18.59,16.41L20,15L12,7L4,15L5.41,16.41L12,9.83" + android:fillColor="#5F6368"/> +</vector> diff --git a/libs/WindowManager/Shell/res/layout/bubble_bar_menu_item.xml b/libs/WindowManager/Shell/res/layout/bubble_bar_menu_item.xml new file mode 100644 index 000000000000..ddcd5c60d9c8 --- /dev/null +++ b/libs/WindowManager/Shell/res/layout/bubble_bar_menu_item.xml @@ -0,0 +1,41 @@ +<?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 + --> +<com.android.wm.shell.bubbles.bar.BubbleBarMenuItemView + xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="horizontal" + android:layout_width="match_parent" + android:layout_height="@dimen/bubble_bar_manage_menu_item_height" + android:gravity="center_vertical" + android:paddingStart="@dimen/bubble_menu_padding" + android:paddingEnd="@dimen/bubble_menu_padding" + android:background="@drawable/bubble_manage_menu_row"> + + <ImageView + android:id="@+id/bubble_bar_menu_item_icon" + android:layout_width="@dimen/bubble_bar_manage_menu_item_icon_size" + android:layout_height="@dimen/bubble_bar_manage_menu_item_icon_size" + android:contentDescription="@null"/> + + <TextView + android:id="@+id/bubble_bar_menu_item_title" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginStart="16dp" + android:textColor="?android:attr/textColorPrimary" + android:textAppearance="@*android:style/TextAppearance.DeviceDefault" /> + +</com.android.wm.shell.bubbles.bar.BubbleBarMenuItemView>
\ No newline at end of file diff --git a/libs/WindowManager/Shell/res/layout/bubble_bar_menu_view.xml b/libs/WindowManager/Shell/res/layout/bubble_bar_menu_view.xml new file mode 100644 index 000000000000..82e5aee41ff2 --- /dev/null +++ b/libs/WindowManager/Shell/res/layout/bubble_bar_menu_view.xml @@ -0,0 +1,77 @@ +<?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 + --> +<com.android.wm.shell.bubbles.bar.BubbleBarMenuView + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center_horizontal" + android:minWidth="@dimen/bubble_bar_manage_menu_min_width" + android:orientation="vertical" + android:elevation="@dimen/bubble_manage_menu_elevation" + android:paddingTop="@dimen/bubble_bar_manage_menu_padding_top" + android:paddingHorizontal="@dimen/bubble_bar_manage_menu_padding" + android:paddingBottom="@dimen/bubble_bar_manage_menu_padding" + android:clipToPadding="false"> + + <LinearLayout + android:id="@+id/bubble_bar_manage_menu_bubble_section" + android:layout_width="match_parent" + android:layout_height="@dimen/bubble_bar_manage_menu_item_height" + android:orientation="horizontal" + android:gravity="center_vertical" + android:paddingStart="14dp" + android:paddingEnd="12dp" + android:background="@drawable/bubble_manage_menu_section" + android:elevation="@dimen/bubble_manage_menu_elevation"> + + <ImageView + android:id="@+id/bubble_bar_manage_menu_bubble_icon" + android:layout_width="@dimen/bubble_menu_icon_size" + android:layout_height="@dimen/bubble_menu_icon_size" + android:contentDescription="@null" /> + + <TextView + android:id="@+id/bubble_bar_manage_menu_bubble_title" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginStart="8dp" + android:layout_weight="1" + android:textColor="?android:attr/textColorPrimary" + android:textAppearance="@*android:style/TextAppearance.DeviceDefault" /> + + <ImageView + android:id="@+id/bubble_bar_manage_menu_dismiss_icon" + android:layout_width="@dimen/bubble_bar_manage_menu_dismiss_icon_size" + android:layout_height="@dimen/bubble_bar_manage_menu_dismiss_icon_size" + android:layout_marginStart="8dp" + android:contentDescription="@null" + android:src="@drawable/ic_expand_less" + app:tint="?android:attr/textColorPrimary" /> + + </LinearLayout> + + <LinearLayout + android:id="@+id/bubble_bar_manage_menu_actions_section" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical" + android:layout_marginTop="@dimen/bubble_bar_manage_menu_section_spacing" + android:background="@drawable/bubble_manage_menu_bg" + android:elevation="@dimen/bubble_manage_menu_elevation" /> + +</com.android.wm.shell.bubbles.bar.BubbleBarMenuView>
\ No newline at end of file diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_focused_window_decor.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_focused_window_decor.xml index 1d6864c152c2..0ca912e20527 100644 --- a/libs/WindowManager/Shell/res/layout/desktop_mode_focused_window_decor.xml +++ b/libs/WindowManager/Shell/res/layout/desktop_mode_focused_window_decor.xml @@ -28,6 +28,7 @@ android:layout_width="176dp" android:layout_height="42dp" android:paddingHorizontal="24dp" + android:paddingVertical="19dp" android:contentDescription="@string/handle_text" android:src="@drawable/decor_handle_dark" tools:tint="@color/desktop_mode_caption_handle_bar_dark" diff --git a/libs/WindowManager/Shell/res/values/colors.xml b/libs/WindowManager/Shell/res/values/colors.xml index 171a6b2fe5fb..f2a07857cd4a 100644 --- a/libs/WindowManager/Shell/res/values/colors.xml +++ b/libs/WindowManager/Shell/res/values/colors.xml @@ -30,6 +30,9 @@ <color name="bubbles_light">#FFFFFF</color> <color name="bubbles_dark">@color/GM2_grey_800</color> <color name="bubbles_icon_tint">@color/GM2_grey_700</color> + <color name="bubble_bar_expanded_view_handle_light">#EBffffff</color> + <color name="bubble_bar_expanded_view_handle_dark">#99000000</color> + <color name="bubble_bar_expanded_view_menu_close">#DC362E</color> <!-- PiP --> <color name="pip_custom_close_bg">#D93025</color> diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml index 2be34c90a661..2e3f60441b3a 100644 --- a/libs/WindowManager/Shell/res/values/dimen.xml +++ b/libs/WindowManager/Shell/res/values/dimen.xml @@ -229,7 +229,25 @@ <!-- Size of the bubble bar (height), should match transient_taskbar_size in Launcher. --> <dimen name="bubblebar_size">72dp</dimen> <!-- The size of the drag handle / menu shown along with a bubble bar expanded view. --> - <dimen name="bubblebar_expanded_view_menu_size">16dp</dimen> + <dimen name="bubble_bar_expanded_view_handle_size">40dp</dimen> + <!-- The width of the drag handle shown along with a bubble bar expanded view. --> + <dimen name="bubble_bar_expanded_view_handle_width">128dp</dimen> + <!-- The height of the drag handle shown along with a bubble bar expanded view. --> + <dimen name="bubble_bar_expanded_view_handle_height">4dp</dimen> + <!-- Minimum width of the bubble bar manage menu. --> + <dimen name="bubble_bar_manage_menu_min_width">200dp</dimen> + <!-- Size of the dismiss icon in the bubble bar manage menu. --> + <dimen name="bubble_bar_manage_menu_dismiss_icon_size">16dp</dimen> + <!-- Padding of the bubble bar manage menu, provides space for menu shadows --> + <dimen name="bubble_bar_manage_menu_padding">8dp</dimen> + <!-- Top padding of the bubble bar manage menu --> + <dimen name="bubble_bar_manage_menu_padding_top">2dp</dimen> + <!-- Spacing between sections of the bubble bar manage menu --> + <dimen name="bubble_bar_manage_menu_section_spacing">2dp</dimen> + <!-- Height of an item in the bubble bar manage menu. --> + <dimen name="bubble_bar_manage_menu_item_height">52dp</dimen> + <!-- Size of the icons in the bubble bar manage menu. --> + <dimen name="bubble_bar_manage_menu_item_icon_size">20dp</dimen> <!-- Bottom and end margin for compat buttons. --> <dimen name="compat_button_margin">24dp</dimen> @@ -398,7 +416,7 @@ <!-- The radius of the caption menu shadow. --> <dimen name="desktop_mode_handle_menu_shadow_radius">2dp</dimen> - <dimen name="freeform_resize_handle">30dp</dimen> + <dimen name="freeform_resize_handle">15dp</dimen> <dimen name="freeform_resize_corner">44dp</dimen> </resources> diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java index 102f2cb4b8d0..504839fedf06 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java @@ -855,7 +855,8 @@ public class Bubble implements BubbleViewProvider { return mIsAppBubble; } - Intent getSettingsIntent(final Context context) { + /** Creates open app settings intent */ + public Intent getSettingsIntent(final Context context) { final Intent intent = new Intent(Settings.ACTION_APP_NOTIFICATION_BUBBLE_SETTINGS); intent.putExtra(Settings.EXTRA_APP_PACKAGE, getPackageName()); final int uid = getUid(context); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java index 0fdfbb8c0c61..c48f2fdaf42f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java @@ -719,6 +719,7 @@ public class BubbleController implements ConfigurationChangeListener, // TODO(b/273312602): consider foldables where we do need a stack view when folded if (mLayerView == null) { mLayerView = new BubbleBarLayerView(mContext, this); + mLayerView.setUnBubbleConversationCallback(mSysuiProxy::onUnbubbleConversation); } } else { if (mStackView == null) { @@ -1220,6 +1221,13 @@ public class BubbleController implements ConfigurationChangeListener, } /** + * Dismiss bubble if it exists and remove it from the stack + */ + public void dismissBubble(Bubble bubble, @Bubbles.DismissReason int reason) { + mBubbleData.dismissBubbleWithKey(bubble.getKey(), reason); + } + + /** * Performs a screenshot that may exclude the bubble layer, if one is present. The screenshot * can be access via the supplied {@link SynchronousScreenCaptureListener#getBuffer()} * asynchronously. @@ -1846,7 +1854,7 @@ public class BubbleController implements ConfigurationChangeListener, if (mStackView != null) { mStackView.setVisibility(VISIBLE); } - if (mLayerView != null && isStackExpanded()) { + if (mLayerView != null) { mLayerView.setVisibility(VISIBLE); } } @@ -2144,7 +2152,7 @@ public class BubbleController implements ConfigurationChangeListener, pw.println(" suppressing: " + key); } - pw.print("mAppBubbleTaskIds: " + mAppBubbleTaskIds.values()); + pw.println("mAppBubbleTaskIds: " + mAppBubbleTaskIds.values()); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java index 68fea41e134e..9860b076264b 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java @@ -88,6 +88,8 @@ import com.android.wm.shell.bubbles.animation.PhysicsAnimationLayout; import com.android.wm.shell.bubbles.animation.StackAnimationController; import com.android.wm.shell.common.FloatingContentCoordinator; import com.android.wm.shell.common.ShellExecutor; +import com.android.wm.shell.common.bubbles.DismissView; +import com.android.wm.shell.common.bubbles.RelativeTouchListener; import com.android.wm.shell.common.magnetictarget.MagnetizedObject; import java.io.PrintWriter; @@ -1179,6 +1181,7 @@ public class BubbleStackView extends FrameLayout removeView(mDismissView); } mDismissView = new DismissView(getContext()); + DismissViewUtils.setup(mDismissView); int elevation = getResources().getDimensionPixelSize(R.dimen.bubble_elevation); addView(mDismissView); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewHelper.java index 7a5815994dd0..da4a9898a44c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewHelper.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewHelper.java @@ -15,6 +15,7 @@ */ package com.android.wm.shell.bubbles; +import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED; import static android.app.ActivityTaskManager.INVALID_TASK_ID; import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK; import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT; @@ -110,6 +111,9 @@ public class BubbleTaskViewHelper { try { options.setTaskAlwaysOnTop(true); options.setLaunchedFromBubble(true); + options.setPendingIntentBackgroundActivityStartMode( + MODE_BACKGROUND_ACTIVITY_START_ALLOWED); + options.setPendingIntentBackgroundActivityLaunchAllowedByPermission(true); Intent fillInIntent = new Intent(); // Apply flags to make behaviour match documentLaunchMode=always. @@ -117,11 +121,19 @@ public class BubbleTaskViewHelper { fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK); if (mBubble.isAppBubble()) { - PendingIntent pi = PendingIntent.getActivity(mContext, 0, - mBubble.getAppBubbleIntent(), - PendingIntent.FLAG_MUTABLE, - null); - mTaskView.startActivity(pi, fillInIntent, options, launchBounds); + Context context = + mContext.createContextAsUser( + mBubble.getUser(), Context.CONTEXT_RESTRICTED); + PendingIntent pi = PendingIntent.getActivity( + context, + /* requestCode= */ 0, + mBubble.getAppBubbleIntent() + .addFlags(FLAG_ACTIVITY_NEW_DOCUMENT) + .addFlags(FLAG_ACTIVITY_MULTIPLE_TASK), + PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT, + /* options= */ null); + mTaskView.startActivity(pi, /* fillInIntent= */ null, options, + launchBounds); } else if (mBubble.hasMetadataShortcutId()) { options.setApplyActivityFlagsForBubbles(true); mTaskView.startShortcutActivity(mBubble.getShortcutInfo(), diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java index 8ab9841ff0c2..80e29998e8d3 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java @@ -104,7 +104,11 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask @Override protected BubbleViewInfo doInBackground(Void... voids) { - if (mController.get().isShowingAsBubbleBar()) { + if (!verifyState()) { + // If we're in an inconsistent state, then switched modes and should just bail now. + return null; + } + if (mLayerView.get() != null) { return BubbleViewInfo.populateForBubbleBar(mContext.get(), mController.get(), mLayerView.get(), mIconFactory, mBubble, mSkipInflation); } else { @@ -118,7 +122,11 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask if (isCancelled() || viewInfo == null) { return; } + mMainExecutor.execute(() -> { + if (!verifyState()) { + return; + } mBubble.setViewInfo(viewInfo); if (mCallback != null) { mCallback.onBubbleViewsReady(mBubble); @@ -126,6 +134,14 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask }); } + private boolean verifyState() { + if (mController.get().isShowingAsBubbleBar()) { + return mLayerView.get() != null; + } else { + return mStackView.get() != null; + } + } + /** * Info necessary to render a bubble. */ @@ -192,6 +208,11 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask } info.rawBadgeBitmap = iconFactory.getBadgeBitmap(badgedIcon, false).icon; + float[] bubbleBitmapScale = new float[1]; + info.bubbleBitmap = iconFactory.getBubbleBitmap( + iconFactory.getBubbleDrawable(c, info.shortcutInfo, + b.getIcon()), bubbleBitmapScale); + return info; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/DismissViewExt.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/DismissViewExt.kt new file mode 100644 index 000000000000..ed3624035757 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/DismissViewExt.kt @@ -0,0 +1,33 @@ +/* + * 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. + */ +@file:JvmName("DismissViewUtils") + +package com.android.wm.shell.bubbles + +import com.android.wm.shell.R +import com.android.wm.shell.common.bubbles.DismissView + +fun DismissView.setup() { + setup(DismissView.Config( + targetSizeResId = R.dimen.dismiss_circle_size, + iconSizeResId = R.dimen.dismiss_target_x_size, + bottomMarginResId = R.dimen.floating_dismiss_bottom_margin, + floatingGradientHeightResId = R.dimen.floating_dismiss_gradient_height, + floatingGradientColorResId = android.R.color.system_neutral1_900, + backgroundResId = R.drawable.dismiss_circle_background, + iconResId = R.drawable.pip_ic_close_white + )) +}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java index b8f049becb6f..32ed10258eee 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java @@ -16,11 +16,14 @@ package com.android.wm.shell.bubbles.bar; +import android.annotation.ColorInt; +import android.annotation.Nullable; import android.app.ActivityManager; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Color; import android.graphics.Outline; +import android.graphics.Rect; import android.util.AttributeSet; import android.view.View; import android.view.ViewOutlineProvider; @@ -31,8 +34,12 @@ import com.android.wm.shell.R; import com.android.wm.shell.bubbles.Bubble; import com.android.wm.shell.bubbles.BubbleController; import com.android.wm.shell.bubbles.BubbleTaskViewHelper; +import com.android.wm.shell.bubbles.Bubbles; import com.android.wm.shell.taskview.TaskView; +import java.util.function.Consumer; +import java.util.function.Supplier; + /** * Expanded view of a bubble when it's part of the bubble bar. * @@ -45,11 +52,14 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView private BubbleController mController; private BubbleTaskViewHelper mBubbleTaskViewHelper; + private BubbleBarMenuViewController mMenuViewController; + private @Nullable Supplier<Rect> mLayerBoundsSupplier; + private @Nullable Consumer<String> mUnBubbleConversationCallback; - private HandleView mMenuView; - private TaskView mTaskView; + private BubbleBarHandleView mHandleView = new BubbleBarHandleView(getContext()); + private @Nullable TaskView mTaskView; - private int mMenuHeight; + private int mHandleHeight; private int mBackgroundColor; private float mCornerRadius = 0f; @@ -83,11 +93,9 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView super.onFinishInflate(); Context context = getContext(); setElevation(getResources().getDimensionPixelSize(R.dimen.bubble_elevation)); - mMenuHeight = context.getResources().getDimensionPixelSize( - R.dimen.bubblebar_expanded_view_menu_size); - mMenuView = new HandleView(context); - addView(mMenuView); - + mHandleHeight = context.getResources().getDimensionPixelSize( + R.dimen.bubble_bar_expanded_view_handle_size); + addView(mHandleView); applyThemeAttrs(); setClipToOutline(true); setOutlineProvider(new ViewOutlineProvider() { @@ -98,6 +106,13 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView }); } + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + // Hide manage menu when view disappears + mMenuViewController.hideMenu(false /* animated */); + } + /** Set the BubbleController on the view, must be called before doing anything else. */ public void initialize(BubbleController controller) { mController = controller; @@ -108,13 +123,43 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView addView(mTaskView); mTaskView.setEnableSurfaceClipping(true); mTaskView.setCornerRadius(mCornerRadius); + mMenuViewController = new BubbleBarMenuViewController(mContext, this); + mMenuViewController.setListener(new BubbleBarMenuViewController.Listener() { + @Override + public void onMenuVisibilityChanged(boolean visible) { + if (mTaskView == null || mLayerBoundsSupplier == null) return; + // Updates the obscured touchable region for the task surface. + mTaskView.setObscuredTouchRect(visible ? mLayerBoundsSupplier.get() : null); + } + + @Override + public void onUnBubbleConversation(Bubble bubble) { + if (mUnBubbleConversationCallback != null) { + mUnBubbleConversationCallback.accept(bubble.getKey()); + } + } + + @Override + public void onOpenAppSettings(Bubble bubble) { + mController.collapseStack(); + mContext.startActivityAsUser(bubble.getSettingsIntent(mContext), bubble.getUser()); + } + + @Override + public void onDismissBubble(Bubble bubble) { + mController.dismissBubble(bubble, Bubbles.DISMISS_USER_REMOVED); + } + }); + mHandleView.setOnClickListener(view -> { + mMenuViewController.showMenu(true /* animated */); + }); } // TODO (b/275087636): call this when theme/config changes void applyThemeAttrs() { boolean supportsRoundedCorners = ScreenDecorationsUtils.supportsRoundedCornersOnWindows( mContext.getResources()); - final TypedArray ta = mContext.obtainStyledAttributes(new int[] { + final TypedArray ta = mContext.obtainStyledAttributes(new int[]{ android.R.attr.dialogCornerRadius, android.R.attr.colorBackgroundFloating}); mCornerRadius = supportsRoundedCorners ? ta.getDimensionPixelSize(0, 0) : 0; @@ -123,14 +168,12 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView ta.recycle(); - mMenuView.setCornerRadius(mCornerRadius); - mMenuHeight = getResources().getDimensionPixelSize( - R.dimen.bubblebar_expanded_view_menu_size); + mHandleHeight = getResources().getDimensionPixelSize( + R.dimen.bubble_bar_expanded_view_handle_size); if (mTaskView != null) { mTaskView.setCornerRadius(mCornerRadius); - mTaskView.setElevation(150); - updateMenuColor(); + updateHandleAndBackgroundColor(true /* animated */); } } @@ -138,10 +181,8 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int height = MeasureSpec.getSize(heightMeasureSpec); - - // Add corner radius here so that the menu extends behind the rounded corners of TaskView. - int menuViewHeight = Math.min((int) (mMenuHeight + mCornerRadius), height); - measureChild(mMenuView, widthMeasureSpec, MeasureSpec.makeMeasureSpec(menuViewHeight, + int menuViewHeight = Math.min(mHandleHeight, height); + measureChild(mHandleView, widthMeasureSpec, MeasureSpec.makeMeasureSpec(menuViewHeight, MeasureSpec.getMode(heightMeasureSpec))); if (mTaskView != null) { @@ -153,12 +194,12 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { + super.onLayout(changed, l, t, r, b); // Drag handle above - final int dragHandleBottom = t + mMenuView.getMeasuredHeight(); - mMenuView.layout(l, t, r, dragHandleBottom); + final int dragHandleBottom = t + mHandleView.getMeasuredHeight(); + mHandleView.layout(l, t, r, dragHandleBottom); if (mTaskView != null) { - // Subtract radius so that the menu extends behind the rounded corners of TaskView. - mTaskView.layout(l, (int) (dragHandleBottom - mCornerRadius), r, + mTaskView.layout(l, dragHandleBottom, r, dragHandleBottom + mTaskView.getMeasuredHeight()); } } @@ -166,7 +207,7 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView @Override public void onTaskCreated() { setContentVisibility(true); - updateMenuColor(); + updateHandleAndBackgroundColor(false /* animated */); } @Override @@ -187,11 +228,13 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView } mBubbleTaskViewHelper.cleanUpTaskView(); } + mMenuViewController.hideMenu(false /* animated */); } - /** Updates the bubble shown in this task view. */ + /** Updates the bubble shown in the expanded view. */ public void update(Bubble bubble) { mBubbleTaskViewHelper.update(bubble); + mMenuViewController.updateMenu(bubble); } /** The task id of the activity shown in the task view, if it exists. */ @@ -199,6 +242,17 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView return mBubbleTaskViewHelper != null ? mBubbleTaskViewHelper.getTaskId() : INVALID_TASK_ID; } + /** Sets layer bounds supplier used for obscured touchable region of task view */ + void setLayerBoundsSupplier(@Nullable Supplier<Rect> supplier) { + mLayerBoundsSupplier = supplier; + } + + /** Sets the function to call to un-bubble the given conversation. */ + public void setUnBubbleConversationCallback( + @Nullable Consumer<String> unBubbleConversationCallback) { + mUnBubbleConversationCallback = unBubbleConversationCallback; + } + /** * Call when the location or size of the view has changed to update TaskView. */ @@ -218,16 +272,33 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView } } - /** Updates the menu bar to be the status bar color specified by the app. */ - private void updateMenuColor() { + /** + * Updates the background color to match with task view status/bg color, and sets handle color + * to contrast with the background + */ + private void updateHandleAndBackgroundColor(boolean animated) { if (mTaskView == null) return; - ActivityManager.RunningTaskInfo info = mTaskView.getTaskInfo(); - final int taskBgColor = info.taskDescription.getStatusBarColor(); - final int color = Color.valueOf(taskBgColor == -1 ? Color.WHITE : taskBgColor).toArgb(); - if (color != -1) { - mMenuView.setBackgroundColor(color); + final int color = getTaskViewColor(); + final boolean isRegionDark = Color.luminance(color) <= 0.5; + mHandleView.updateHandleColor(isRegionDark, animated); + setBackgroundColor(color); + } + + /** + * Retrieves task view status/nav bar color or background if available + * + * TODO (b/283075226): Update with color sampling when + * RegionSamplingHelper or alternative is available + */ + private @ColorInt int getTaskViewColor() { + if (mTaskView == null || mTaskView.getTaskInfo() == null) return mBackgroundColor; + ActivityManager.TaskDescription taskDescription = mTaskView.getTaskInfo().taskDescription; + if (taskDescription.getStatusBarColor() != Color.TRANSPARENT) { + return taskDescription.getStatusBarColor(); + } else if (taskDescription.getBackgroundColor() != Color.TRANSPARENT) { + return taskDescription.getBackgroundColor(); } else { - mMenuView.setBackgroundColor(mBackgroundColor); + return mBackgroundColor; } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarHandleView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarHandleView.java new file mode 100644 index 000000000000..ce26bc0322b3 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarHandleView.java @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.wm.shell.bubbles.bar; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ObjectAnimator; +import android.annotation.Nullable; +import android.content.Context; +import android.graphics.Outline; +import android.graphics.Rect; +import android.util.AttributeSet; +import android.view.View; +import android.view.ViewOutlineProvider; + +import androidx.annotation.ColorInt; +import androidx.core.content.ContextCompat; + +import com.android.wm.shell.R; + +/** + * Handle view to show at the top of a bubble bar expanded view. + */ +public class BubbleBarHandleView extends View { + private static final long COLOR_CHANGE_DURATION = 120; + + private int mHandleWidth; + private int mHandleHeight; + private @ColorInt int mHandleLightColor; + private @ColorInt int mHandleDarkColor; + private @Nullable ObjectAnimator mColorChangeAnim; + + public BubbleBarHandleView(Context context) { + this(context, null /* attrs */); + } + + public BubbleBarHandleView(Context context, AttributeSet attrs) { + this(context, attrs, 0 /* defStyleAttr */); + } + + public BubbleBarHandleView(Context context, AttributeSet attrs, int defStyleAttr) { + this(context, attrs, defStyleAttr, 0 /* defStyleRes */); + } + + public BubbleBarHandleView(Context context, AttributeSet attrs, int defStyleAttr, + int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + + mHandleWidth = getResources().getDimensionPixelSize( + R.dimen.bubble_bar_expanded_view_handle_width); + mHandleHeight = getResources().getDimensionPixelSize( + R.dimen.bubble_bar_expanded_view_handle_height); + mHandleLightColor = ContextCompat.getColor(getContext(), + R.color.bubble_bar_expanded_view_handle_light); + mHandleDarkColor = ContextCompat.getColor(getContext(), + R.color.bubble_bar_expanded_view_handle_dark); + + setClipToOutline(true); + setOutlineProvider(new ViewOutlineProvider() { + @Override + public void getOutline(View view, Outline outline) { + final int handleCenterX = view.getWidth() / 2; + final int handleCenterY = view.getHeight() / 2; + final float handleRadius = mHandleHeight / 2f; + Rect handleBounds = new Rect( + handleCenterX - mHandleWidth / 2, + handleCenterY - mHandleHeight / 2, + handleCenterX + mHandleWidth / 2, + handleCenterY + mHandleHeight / 2); + outline.setRoundRect(handleBounds, handleRadius); + } + }); + } + + /** + * Updates the handle color. + * + * @param isRegionDark Whether the background behind the handle is dark, and thus the handle + * should be light (and vice versa). + * @param animated Whether to animate the change, or apply it immediately. + */ + public void updateHandleColor(boolean isRegionDark, boolean animated) { + int newColor = isRegionDark ? mHandleLightColor : mHandleDarkColor; + if (mColorChangeAnim != null) { + mColorChangeAnim.cancel(); + } + if (animated) { + mColorChangeAnim = ObjectAnimator.ofArgb(this, "backgroundColor", newColor); + mColorChangeAnim.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mColorChangeAnim = null; + } + }); + mColorChangeAnim.setDuration(COLOR_CHANGE_DURATION); + mColorChangeAnim.start(); + } else { + setBackgroundColor(newColor); + } + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java index b1a725b6e5c4..bc622e7a7a3e 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java @@ -32,6 +32,8 @@ import com.android.wm.shell.bubbles.BubbleController; import com.android.wm.shell.bubbles.BubblePositioner; import com.android.wm.shell.bubbles.BubbleViewProvider; +import java.util.function.Consumer; + /** * Similar to {@link com.android.wm.shell.bubbles.BubbleStackView}, this view is added to window * manager to display bubbles. However, it is only used when bubbles are being displayed in @@ -53,6 +55,7 @@ public class BubbleBarLayerView extends FrameLayout @Nullable private BubbleViewProvider mExpandedBubble; private BubbleBarExpandedView mExpandedView; + private @Nullable Consumer<String> mUnBubbleConversationCallback; // TODO(b/273310265) - currently the view is always on the right, need to update for RTL. /** Whether the expanded view is displaying on the left of the screen or not. */ @@ -146,6 +149,13 @@ public class BubbleBarLayerView extends FrameLayout final int width = mPositioner.getExpandedViewWidthForBubbleBar(); final int height = mPositioner.getExpandedViewHeightForBubbleBar(); mExpandedView.setVisibility(GONE); + mExpandedView.setUnBubbleConversationCallback(mUnBubbleConversationCallback); + mExpandedView.setLayerBoundsSupplier(() -> new Rect(0, 0, getWidth(), getHeight())); + mExpandedView.setUnBubbleConversationCallback(bubbleKey -> { + if (mUnBubbleConversationCallback != null) { + mUnBubbleConversationCallback.accept(bubbleKey); + } + }); addView(mExpandedView, new FrameLayout.LayoutParams(width, height)); } @@ -165,6 +175,12 @@ public class BubbleBarLayerView extends FrameLayout showScrim(false); } + /** Sets the function to call to un-bubble the given conversation. */ + public void setUnBubbleConversationCallback( + @Nullable Consumer<String> unBubbleConversationCallback) { + mUnBubbleConversationCallback = unBubbleConversationCallback; + } + /** Updates the expanded view size and position. */ private void updateExpandedView() { if (mExpandedView == null) return; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuItemView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuItemView.java new file mode 100644 index 000000000000..00b977721bea --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuItemView.java @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.wm.shell.bubbles.bar; + +import android.annotation.ColorInt; +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.Color; +import android.graphics.drawable.Icon; +import android.util.AttributeSet; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.TextView; + +import com.android.wm.shell.R; + +/** + * Bubble bar expanded view menu item view to display menu action details + */ +public class BubbleBarMenuItemView extends LinearLayout { + private ImageView mImageView; + private TextView mTextView; + + public BubbleBarMenuItemView(Context context) { + this(context, null /* attrs */); + } + + public BubbleBarMenuItemView(Context context, AttributeSet attrs) { + this(context, attrs, 0 /* defStyleAttr */); + } + + public BubbleBarMenuItemView(Context context, AttributeSet attrs, int defStyleAttr) { + this(context, attrs, defStyleAttr, 0 /* defStyleRes */); + } + + public BubbleBarMenuItemView(Context context, AttributeSet attrs, int defStyleAttr, + int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + } + + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + mImageView = findViewById(R.id.bubble_bar_menu_item_icon); + mTextView = findViewById(R.id.bubble_bar_menu_item_title); + } + + /** + * Update menu item with the details and tint color + */ + void update(Icon icon, String title, @ColorInt int tint) { + if (tint == Color.TRANSPARENT) { + final TypedArray typedArray = getContext().obtainStyledAttributes( + new int[]{android.R.attr.textColorPrimary}); + mTextView.setTextColor(typedArray.getColor(0, Color.BLACK)); + } else { + icon.setTint(tint); + mTextView.setTextColor(tint); + } + + mImageView.setImageIcon(icon); + mTextView.setText(title); + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuView.java new file mode 100644 index 000000000000..211fe0d48e43 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuView.java @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.wm.shell.bubbles.bar; + +import android.annotation.ColorInt; +import android.content.Context; +import android.graphics.Color; +import android.graphics.drawable.Icon; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.TextView; + +import com.android.wm.shell.R; +import com.android.wm.shell.bubbles.Bubble; + +import java.util.ArrayList; + +/** + * Bubble bar expanded view menu + */ +public class BubbleBarMenuView extends LinearLayout { + private ViewGroup mBubbleSectionView; + private ViewGroup mActionsSectionView; + private ImageView mBubbleIconView; + private TextView mBubbleTitleView; + + public BubbleBarMenuView(Context context) { + this(context, null /* attrs */); + } + + public BubbleBarMenuView(Context context, AttributeSet attrs) { + this(context, attrs, 0 /* defStyleAttr */); + } + + public BubbleBarMenuView(Context context, AttributeSet attrs, int defStyleAttr) { + this(context, attrs, defStyleAttr, 0 /* defStyleRes */); + } + + public BubbleBarMenuView(Context context, AttributeSet attrs, int defStyleAttr, + int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + } + + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + mBubbleSectionView = findViewById(R.id.bubble_bar_manage_menu_bubble_section); + mActionsSectionView = findViewById(R.id.bubble_bar_manage_menu_actions_section); + mBubbleIconView = findViewById(R.id.bubble_bar_manage_menu_bubble_icon); + mBubbleTitleView = findViewById(R.id.bubble_bar_manage_menu_bubble_title); + } + + /** Update menu details with bubble info */ + void updateInfo(Bubble bubble) { + if (bubble.getIcon() != null) { + mBubbleIconView.setImageIcon(bubble.getIcon()); + } else { + mBubbleIconView.setImageBitmap(bubble.getBubbleIcon()); + } + mBubbleTitleView.setText(bubble.getTitle()); + } + + /** + * Update menu action items views + * @param actions used to populate menu item views + */ + void updateActions(ArrayList<MenuAction> actions) { + mActionsSectionView.removeAllViews(); + LayoutInflater inflater = LayoutInflater.from(mContext); + + for (MenuAction action : actions) { + BubbleBarMenuItemView itemView = (BubbleBarMenuItemView) inflater.inflate( + R.layout.bubble_bar_menu_item, mActionsSectionView, false); + itemView.update(action.mIcon, action.mTitle, action.mTint); + itemView.setOnClickListener(action.mOnClick); + mActionsSectionView.addView(itemView); + } + } + + /** Sets on close menu listener */ + void setOnCloseListener(Runnable onClose) { + mBubbleSectionView.setOnClickListener(view -> { + onClose.run(); + }); + } + + /** + * Overridden to proxy to section views alpha. + * @implNote + * If animate alpha on the parent (menu container) view, section view shadows get distorted. + * To prevent distortion and artifacts alpha changes applied directly on the section views. + */ + @Override + public void setAlpha(float alpha) { + mBubbleSectionView.setAlpha(alpha); + mActionsSectionView.setAlpha(alpha); + } + + /** + * Overridden to proxy section view alpha value. + * @implNote + * The assumption is that both section views have the same alpha value + */ + @Override + public float getAlpha() { + return mBubbleSectionView.getAlpha(); + } + + /** + * Menu action details used to create menu items + */ + static class MenuAction { + private Icon mIcon; + private @ColorInt int mTint; + private String mTitle; + private OnClickListener mOnClick; + + MenuAction(Icon icon, String title, OnClickListener onClick) { + this(icon, title, Color.TRANSPARENT, onClick); + } + + MenuAction(Icon icon, String title, @ColorInt int tint, OnClickListener onClick) { + this.mIcon = icon; + this.mTitle = title; + this.mTint = tint; + this.mOnClick = onClick; + } + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuViewController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuViewController.java new file mode 100644 index 000000000000..8be140c16435 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuViewController.java @@ -0,0 +1,242 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.wm.shell.bubbles.bar; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.Context; +import android.content.res.Resources; +import android.graphics.drawable.Icon; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import androidx.core.content.ContextCompat; +import androidx.dynamicanimation.animation.DynamicAnimation; +import androidx.dynamicanimation.animation.SpringForce; + +import com.android.wm.shell.R; +import com.android.wm.shell.animation.PhysicsAnimator; +import com.android.wm.shell.bubbles.Bubble; + +import java.util.ArrayList; + +/** + * Manages bubble bar expanded view menu presentation and animations + */ +class BubbleBarMenuViewController { + private static final float MENU_INITIAL_SCALE = 0.5f; + private final Context mContext; + private final ViewGroup mRootView; + private @Nullable Listener mListener; + private @Nullable Bubble mBubble; + private @Nullable BubbleBarMenuView mMenuView; + /** A transparent view used to intercept touches to collapse menu when presented */ + private @Nullable View mScrimView; + private @Nullable PhysicsAnimator<BubbleBarMenuView> mMenuAnimator; + private PhysicsAnimator.SpringConfig mMenuSpringConfig; + + BubbleBarMenuViewController(Context context, ViewGroup rootView) { + mContext = context; + mRootView = rootView; + mMenuSpringConfig = new PhysicsAnimator.SpringConfig( + SpringForce.STIFFNESS_MEDIUM, SpringForce.DAMPING_RATIO_LOW_BOUNCY); + } + + /** Sets menu actions listener */ + void setListener(@Nullable Listener listener) { + mListener = listener; + } + + /** Update menu with bubble */ + void updateMenu(@NonNull Bubble bubble) { + mBubble = bubble; + } + + /** + * Show bubble bar expanded view menu + * @param animated if should animate transition + */ + void showMenu(boolean animated) { + if (mMenuView == null || mScrimView == null) { + setupMenu(); + } + cancelAnimations(); + mMenuView.setVisibility(View.VISIBLE); + mScrimView.setVisibility(View.VISIBLE); + Runnable endActions = () -> { + mMenuView.getChildAt(0).requestAccessibilityFocus(); + if (mListener != null) { + mListener.onMenuVisibilityChanged(true /* isShown */); + } + }; + if (animated) { + animateTransition(true /* show */, endActions); + } else { + endActions.run(); + } + } + + /** + * Hide bubble bar expanded view menu + * @param animated if should animate transition + */ + void hideMenu(boolean animated) { + if (mMenuView == null || mScrimView == null) return; + cancelAnimations(); + Runnable endActions = () -> { + mMenuView.setVisibility(View.GONE); + mScrimView.setVisibility(View.GONE); + if (mListener != null) { + mListener.onMenuVisibilityChanged(false /* isShown */); + } + }; + if (animated) { + animateTransition(false /* show */, endActions); + } else { + endActions.run(); + } + } + + /** + * Animate show/hide menu transition + * @param show if should show or hide the menu + * @param endActions will be called when animation ends + */ + private void animateTransition(boolean show, Runnable endActions) { + if (mMenuView == null) return; + mMenuAnimator = PhysicsAnimator.getInstance(mMenuView); + mMenuAnimator.setDefaultSpringConfig(mMenuSpringConfig); + mMenuAnimator + .spring(DynamicAnimation.ALPHA, show ? 1f : 0f) + .spring(DynamicAnimation.SCALE_Y, show ? 1f : MENU_INITIAL_SCALE) + .withEndActions(() -> { + mMenuAnimator = null; + endActions.run(); + }) + .start(); + } + + /** Cancel running animations */ + private void cancelAnimations() { + if (mMenuAnimator != null) { + mMenuAnimator.cancel(); + mMenuAnimator = null; + } + } + + /** Sets up and inflate menu views */ + private void setupMenu() { + // Menu view setup + mMenuView = (BubbleBarMenuView) LayoutInflater.from(mContext).inflate( + R.layout.bubble_bar_menu_view, mRootView, false); + mMenuView.setAlpha(0f); + mMenuView.setPivotY(0f); + mMenuView.setScaleY(MENU_INITIAL_SCALE); + mMenuView.setOnCloseListener(() -> hideMenu(true /* animated */)); + if (mBubble != null) { + mMenuView.updateInfo(mBubble); + mMenuView.updateActions(createMenuActions(mBubble)); + } + // Scrim view setup + mScrimView = new View(mContext); + mScrimView.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO); + mScrimView.setOnClickListener(view -> hideMenu(true /* animated */)); + // Attach to root view + mRootView.addView(mScrimView); + mRootView.addView(mMenuView); + } + + /** + * Creates menu actions to populate menu view + * @param bubble used to create actions depending on bubble type + */ + private ArrayList<BubbleBarMenuView.MenuAction> createMenuActions(Bubble bubble) { + ArrayList<BubbleBarMenuView.MenuAction> menuActions = new ArrayList<>(); + Resources resources = mContext.getResources(); + + if (bubble.isConversation()) { + // Don't bubble conversation action + menuActions.add(new BubbleBarMenuView.MenuAction( + Icon.createWithResource(mContext, R.drawable.bubble_ic_stop_bubble), + resources.getString(R.string.bubbles_dont_bubble_conversation), + view -> { + hideMenu(true /* animated */); + if (mListener != null) { + mListener.onUnBubbleConversation(bubble); + } + } + )); + // Open settings action + Icon appIcon = bubble.getRawAppBadge() != null ? Icon.createWithBitmap( + bubble.getRawAppBadge()) : null; + menuActions.add(new BubbleBarMenuView.MenuAction( + appIcon, + resources.getString(R.string.bubbles_app_settings, bubble.getAppName()), + view -> { + hideMenu(true /* animated */); + if (mListener != null) { + mListener.onOpenAppSettings(bubble); + } + } + )); + } + + // Dismiss bubble action + menuActions.add(new BubbleBarMenuView.MenuAction( + Icon.createWithResource(resources, R.drawable.ic_remove_no_shadow), + resources.getString(R.string.bubble_dismiss_text), + ContextCompat.getColor(mContext, R.color.bubble_bar_expanded_view_menu_close), + view -> { + hideMenu(true /* animated */); + if (mListener != null) { + mListener.onDismissBubble(bubble); + } + } + )); + + return menuActions; + } + + /** + * Bubble bar expanded view menu actions listener + */ + interface Listener { + /** + * Called when manage menu is shown/hidden + * If animated will be called when animation ends + */ + void onMenuVisibilityChanged(boolean visible); + + /** + * Un-bubbles conversation and removes the bubble from the stack + * This conversation will not be bubbled with new messages + * @see com.android.wm.shell.bubbles.BubbleController + */ + void onUnBubbleConversation(Bubble bubble); + + /** + * Launches app notification bubble settings for the bubble with intent created in: + * {@code Bubble.getSettingsIntent} + */ + void onOpenAppSettings(Bubble bubble); + + /** + * Dismiss bubble and remove it from the bubble stack + */ + void onDismissBubble(Bubble bubble); + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/HandleView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/HandleView.java deleted file mode 100644 index 9ee8a9d98aa1..000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/HandleView.java +++ /dev/null @@ -1,42 +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.bubbles.bar; - -import android.content.Context; -import android.view.Gravity; -import android.widget.LinearLayout; - -/** - * Handle / menu view to show at the top of a bubble bar expanded view. - */ -public class HandleView extends LinearLayout { - - // TODO(b/273307221): implement the manage menu in this view. - public HandleView(Context context) { - super(context); - setOrientation(LinearLayout.HORIZONTAL); - setGravity(Gravity.CENTER); - } - - /** - * The menu extends past the top of the TaskView because of the rounded corners. This means - * to center content in the menu we must subtract the radius (i.e. the amount of space covered - * by TaskView). - */ - public void setCornerRadius(float radius) { - setPadding(getPaddingLeft(), getPaddingTop(), getPaddingRight(), (int) radius); - } -} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/LaunchAdjacentController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/LaunchAdjacentController.kt new file mode 100644 index 000000000000..81592c35e4ac --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/LaunchAdjacentController.kt @@ -0,0 +1,90 @@ +/* + * 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.common + +import android.window.WindowContainerToken +import android.window.WindowContainerTransaction +import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_TASK_ORG +import com.android.wm.shell.util.KtProtoLog + +/** + * Controller to manage behavior of activities launched with + * [android.content.Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT]. + */ +class LaunchAdjacentController(private val syncQueue: SyncTransactionQueue) { + + /** Allows to temporarily disable launch adjacent handling */ + var launchAdjacentEnabled: Boolean = true + set(value) { + if (field != value) { + KtProtoLog.d(WM_SHELL_TASK_ORG, "set launch adjacent flag root enabled=%b", value) + field = value + container?.let { c -> + if (value) { + enableContainer(c) + } else { + disableContainer((c)) + } + } + } + } + private var container: WindowContainerToken? = null + + /** + * Set [container] as the new launch adjacent flag root container. + * + * If launch adjacent handling is disabled through [setLaunchAdjacentEnabled], won't set the + * container until after it is enabled again. + * + * @see WindowContainerTransaction.setLaunchAdjacentFlagRoot + */ + fun setLaunchAdjacentRoot(container: WindowContainerToken) { + KtProtoLog.d(WM_SHELL_TASK_ORG, "set new launch adjacent flag root container") + this.container = container + if (launchAdjacentEnabled) { + enableContainer(container) + } + } + + /** + * Clear a container previously set through [setLaunchAdjacentRoot]. + * + * Always clears the container, regardless of [launchAdjacentEnabled] value. + * + * @see WindowContainerTransaction.clearLaunchAdjacentFlagRoot + */ + fun clearLaunchAdjacentRoot() { + KtProtoLog.d(WM_SHELL_TASK_ORG, "clear launch adjacent flag root container") + container?.let { + disableContainer(it) + container = null + } + } + + private fun enableContainer(container: WindowContainerToken) { + KtProtoLog.v(WM_SHELL_TASK_ORG, "enable launch adjacent flag root container") + val wct = WindowContainerTransaction() + wct.setLaunchAdjacentFlagRoot(container) + syncQueue.queue(wct) + } + + private fun disableContainer(container: WindowContainerToken) { + KtProtoLog.v(WM_SHELL_TASK_ORG, "disable launch adjacent flag root container") + val wct = WindowContainerTransaction() + wct.clearLaunchAdjacentFlagRoot(container) + syncQueue.queue(wct) + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DismissCircleView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/DismissCircleView.java index e0c782d1675b..7c5bb211a4cc 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DismissCircleView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/DismissCircleView.java @@ -14,16 +14,17 @@ * limitations under the License. */ -package com.android.wm.shell.common; +package com.android.wm.shell.common.bubbles; import android.content.Context; import android.content.res.Configuration; -import android.content.res.Resources; import android.view.Gravity; import android.widget.FrameLayout; import android.widget.ImageView; -import com.android.wm.shell.R; +import androidx.annotation.DimenRes; +import androidx.annotation.DrawableRes; +import androidx.core.content.ContextCompat; /** * Circular view with a semitransparent, circular background with an 'X' inside it. @@ -31,33 +32,44 @@ import com.android.wm.shell.R; * This is used by both Bubbles and PIP as the dismiss target. */ public class DismissCircleView extends FrameLayout { + @DrawableRes int mBackgroundResId; + @DimenRes int mIconSizeResId; private final ImageView mIconView = new ImageView(getContext()); public DismissCircleView(Context context) { super(context); - final Resources res = getResources(); - - setBackground(res.getDrawable(R.drawable.dismiss_circle_background)); - - mIconView.setImageDrawable(res.getDrawable(R.drawable.pip_ic_close_white)); addView(mIconView); - - setViewSizes(); } @Override protected void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); - final Resources res = getResources(); - setBackground(res.getDrawable(R.drawable.dismiss_circle_background)); + setBackground(ContextCompat.getDrawable(getContext(), mBackgroundResId)); + setViewSizes(); + } + + /** + * Sets up view with the provided resource ids. + * Decouples resource dependency in order to be used externally (e.g. Launcher) + * + * @param backgroundResId drawable resource id of the circle background + * @param iconResId drawable resource id of the icon for the dismiss view + * @param iconSizeResId dimen resource id of the icon size + */ + public void setup(@DrawableRes int backgroundResId, @DrawableRes int iconResId, + @DimenRes int iconSizeResId) { + mBackgroundResId = backgroundResId; + mIconSizeResId = iconSizeResId; + + setBackground(ContextCompat.getDrawable(getContext(), backgroundResId)); + mIconView.setImageDrawable(ContextCompat.getDrawable(getContext(), iconResId)); setViewSizes(); } /** Retrieves the current dimensions for the icon and circle and applies them. */ private void setViewSizes() { - final Resources res = getResources(); - final int iconSize = res.getDimensionPixelSize(R.dimen.dismiss_target_x_size); + final int iconSize = getResources().getDimensionPixelSize(mIconSizeResId); mIconView.setLayoutParams( new FrameLayout.LayoutParams(iconSize, iconSize, Gravity.CENTER)); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/DismissView.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/DismissView.kt index 67ecb915e098..d275a0be8e93 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/DismissView.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/DismissView.kt @@ -14,41 +14,73 @@ * limitations under the License. */ -package com.android.wm.shell.bubbles +package com.android.wm.shell.common.bubbles import android.animation.ObjectAnimator import android.content.Context import android.graphics.Color import android.graphics.drawable.GradientDrawable import android.util.IntProperty +import android.util.Log import android.view.Gravity import android.view.View import android.view.ViewGroup import android.view.WindowInsets import android.view.WindowManager import android.widget.FrameLayout +import androidx.annotation.ColorRes +import androidx.annotation.DimenRes +import androidx.annotation.DrawableRes +import androidx.core.content.ContextCompat import androidx.dynamicanimation.animation.DynamicAnimation import androidx.dynamicanimation.animation.SpringForce.DAMPING_RATIO_LOW_BOUNCY import androidx.dynamicanimation.animation.SpringForce.STIFFNESS_LOW -import com.android.wm.shell.R import com.android.wm.shell.animation.PhysicsAnimator -import com.android.wm.shell.common.DismissCircleView -/* +/** * View that handles interactions between DismissCircleView and BubbleStackView. + * + * @note [setup] method should be called after initialisation */ class DismissView(context: Context) : FrameLayout(context) { + /** + * The configuration is used to provide module specific resource ids + * + * @see [setup] method + */ + data class Config( + /** dimen resource id of the dismiss target circle view size */ + @DimenRes val targetSizeResId: Int, + /** dimen resource id of the icon size in the dismiss target */ + @DimenRes val iconSizeResId: Int, + /** dimen resource id of the bottom margin for the dismiss target */ + @DimenRes var bottomMarginResId: Int, + /** dimen resource id of the height for dismiss area gradient */ + @DimenRes val floatingGradientHeightResId: Int, + /** color resource id of the dismiss area gradient color */ + @ColorRes val floatingGradientColorResId: Int, + /** drawable resource id of the dismiss target background */ + @DrawableRes val backgroundResId: Int, + /** drawable resource id of the icon for the dismiss target */ + @DrawableRes val iconResId: Int + ) + + companion object { + private const val SHOULD_SETUP = + "The view isn't ready. Should be called after `setup`" + private val TAG = DismissView::class.simpleName + } var circle = DismissCircleView(context) var isShowing = false - var targetSizeResId: Int + var config: Config? = null private val animator = PhysicsAnimator.getInstance(circle) private val spring = PhysicsAnimator.SpringConfig(STIFFNESS_LOW, DAMPING_RATIO_LOW_BOUNCY) private val DISMISS_SCRIM_FADE_MS = 200L private var wm: WindowManager = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager - private var gradientDrawable = createGradient() + private var gradientDrawable: GradientDrawable? = null private val GRADIENT_ALPHA: IntProperty<GradientDrawable> = object : IntProperty<GradientDrawable>("alpha") { @@ -61,23 +93,41 @@ class DismissView(context: Context) : FrameLayout(context) { } init { - setLayoutParams(LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, - resources.getDimensionPixelSize(R.dimen.floating_dismiss_gradient_height), - Gravity.BOTTOM)) - updatePadding() setClipToPadding(false) setClipChildren(false) setVisibility(View.INVISIBLE) + addView(circle) + } + + /** + * Sets up view with the provided resource ids. + * + * Decouples resource dependency in order to be used externally (e.g. Launcher). Usually called + * with default params in module specific extension: + * @see [DismissView.setup] in DismissViewExt.kt + */ + fun setup(config: Config) { + this.config = config + + // Setup layout + layoutParams = LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + resources.getDimensionPixelSize(config.floatingGradientHeightResId), + Gravity.BOTTOM) + updatePadding() + + // Setup gradient + gradientDrawable = createGradient(color = config.floatingGradientColorResId) setBackgroundDrawable(gradientDrawable) - targetSizeResId = R.dimen.dismiss_circle_size - val targetSize: Int = resources.getDimensionPixelSize(targetSizeResId) - addView(circle, LayoutParams(targetSize, targetSize, - Gravity.BOTTOM or Gravity.CENTER_HORIZONTAL)) - // start with circle offscreen so it's animated up - circle.setTranslationY(resources.getDimensionPixelSize( - R.dimen.floating_dismiss_gradient_height).toFloat()) + // Setup DismissCircleView + circle.setup(config.backgroundResId, config.iconResId, config.iconSizeResId) + val targetSize: Int = resources.getDimensionPixelSize(config.targetSizeResId) + circle.layoutParams = LayoutParams(targetSize, targetSize, + Gravity.BOTTOM or Gravity.CENTER_HORIZONTAL) + // Initial position with circle offscreen so it's animated up + circle.translationY = resources.getDimensionPixelSize(config.floatingGradientHeightResId) + .toFloat() } /** @@ -85,6 +135,7 @@ class DismissView(context: Context) : FrameLayout(context) { */ fun show() { if (isShowing) return + val gradientDrawable = checkExists(gradientDrawable) ?: return isShowing = true setVisibility(View.VISIBLE) val alphaAnim = ObjectAnimator.ofInt(gradientDrawable, GRADIENT_ALPHA, @@ -104,6 +155,7 @@ class DismissView(context: Context) : FrameLayout(context) { */ fun hide() { if (!isShowing) return + val gradientDrawable = checkExists(gradientDrawable) ?: return isShowing = false val alphaAnim = ObjectAnimator.ofInt(gradientDrawable, GRADIENT_ALPHA, gradientDrawable.alpha, 0) @@ -124,18 +176,17 @@ class DismissView(context: Context) : FrameLayout(context) { } fun updateResources() { + val config = checkExists(config) ?: return updatePadding() - layoutParams.height = resources.getDimensionPixelSize( - R.dimen.floating_dismiss_gradient_height) - - val targetSize = resources.getDimensionPixelSize(targetSizeResId) + layoutParams.height = resources.getDimensionPixelSize(config.floatingGradientHeightResId) + val targetSize = resources.getDimensionPixelSize(config.targetSizeResId) circle.layoutParams.width = targetSize circle.layoutParams.height = targetSize circle.requestLayout() } - private fun createGradient(): GradientDrawable { - val gradientColor = context.resources.getColor(android.R.color.system_neutral1_900) + private fun createGradient(@ColorRes color: Int): GradientDrawable { + val gradientColor = ContextCompat.getColor(context, color) val alpha = 0.7f * 255 val gradientColorWithAlpha = Color.argb(alpha.toInt(), Color.red(gradientColor), @@ -150,10 +201,22 @@ class DismissView(context: Context) : FrameLayout(context) { } private fun updatePadding() { + val config = checkExists(config) ?: return val insets: WindowInsets = wm.getCurrentWindowMetrics().getWindowInsets() val navInset = insets.getInsetsIgnoringVisibility( WindowInsets.Type.navigationBars()) setPadding(0, 0, 0, navInset.bottom + - resources.getDimensionPixelSize(R.dimen.floating_dismiss_bottom_margin)) + resources.getDimensionPixelSize(config.bottomMarginResId)) + } + + /** + * Checks if the value is set up and exists, if not logs an exception. + * Used for convenient logging in case `setup` wasn't called before + * + * @return value provided as argument + */ + private fun <T>checkExists(value: T?): T? { + if (value == null) Log.e(TAG, SHOULD_SETUP) + return value } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/RelativeTouchListener.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/RelativeTouchListener.kt index ea9d065d5f53..cc37bd3a4589 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/RelativeTouchListener.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/RelativeTouchListener.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.wm.shell.bubbles +package com.android.wm.shell.common.bubbles import android.graphics.PointF import android.view.MotionEvent diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerHandleView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerHandleView.java index c76937de6669..ec2680085fb5 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerHandleView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerHandleView.java @@ -76,6 +76,9 @@ public class DividerHandleView extends View { private int mCurrentHeight; private AnimatorSet mAnimator; private boolean mTouching; + private boolean mHovering; + private final int mHoveringWidth; + private final int mHoveringHeight; public DividerHandleView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); @@ -87,6 +90,8 @@ public class DividerHandleView extends View { mCurrentHeight = mHeight; mTouchingWidth = mWidth > mHeight ? mWidth / 2 : mWidth; mTouchingHeight = mHeight > mWidth ? mHeight / 2 : mHeight; + mHoveringWidth = mWidth > mHeight ? ((int) (mWidth * 1.5f)) : mWidth; + mHoveringHeight = mHeight > mWidth ? ((int) (mHeight * 1.5f)) : mHeight; } /** Sets touching state for this handle view. */ @@ -94,24 +99,32 @@ public class DividerHandleView extends View { if (touching == mTouching) { return; } + setInputState(touching, animate, mTouchingWidth, mTouchingHeight); + mTouching = touching; + } + + /** Sets hovering state for this handle view. */ + public void setHovering(boolean hovering, boolean animate) { + if (hovering == mHovering) { + return; + } + setInputState(hovering, animate, mHoveringWidth, mHoveringHeight); + mHovering = hovering; + } + + private void setInputState(boolean stateOn, boolean animate, int stateWidth, int stateHeight) { if (mAnimator != null) { mAnimator.cancel(); mAnimator = null; } if (!animate) { - if (touching) { - mCurrentWidth = mTouchingWidth; - mCurrentHeight = mTouchingHeight; - } else { - mCurrentWidth = mWidth; - mCurrentHeight = mHeight; - } + mCurrentWidth = stateOn ? stateWidth : mWidth; + mCurrentHeight = stateOn ? stateHeight : mHeight; invalidate(); } else { - animateToTarget(touching ? mTouchingWidth : mWidth, - touching ? mTouchingHeight : mHeight, touching); + animateToTarget(stateOn ? stateWidth : mWidth, + stateOn ? stateHeight : mHeight, stateOn); } - mTouching = touching; } private void animateToTarget(int targetWidth, int targetHeight, boolean touching) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java index 69f0bad4fb45..262d487b1d1b 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java @@ -17,14 +17,19 @@ package com.android.wm.shell.common.split; import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; +import static android.view.PointerIcon.TYPE_HORIZONTAL_DOUBLE_ARROW; +import static android.view.PointerIcon.TYPE_VERTICAL_DOUBLE_ARROW; import static android.view.WindowManager.LayoutParams.FLAG_SLIPPERY; +import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.CURSOR_HOVER_STATES_ENABLED; + import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ObjectAnimator; import android.content.Context; import android.graphics.Rect; import android.os.Bundle; +import android.provider.DeviceConfig; import android.util.AttributeSet; import android.util.Property; import android.view.GestureDetector; @@ -32,6 +37,7 @@ import android.view.InsetsController; import android.view.InsetsSource; import android.view.InsetsState; import android.view.MotionEvent; +import android.view.PointerIcon; import android.view.SurfaceControlViewHost; import android.view.VelocityTracker; import android.view.View; @@ -46,6 +52,7 @@ import android.widget.FrameLayout; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.policy.DividerSnapAlgorithm; import com.android.internal.protolog.common.ProtoLog; import com.android.wm.shell.R; @@ -222,7 +229,7 @@ public class DividerView extends FrameLayout implements View.OnTouchListener { for (int i = insetsState.sourceSize() - 1; i >= 0; i--) { final InsetsSource source = insetsState.sourceAt(i); if (source.getType() == WindowInsets.Type.navigationBars() - && source.insetsRoundedCornerFrame()) { + && source.hasFlags(InsetsSource.FLAG_INSETS_ROUNDED_CORNER)) { mTempRect.inset(source.calculateVisibleInsets(mTempRect)); } } @@ -270,6 +277,12 @@ public class DividerView extends FrameLayout implements View.OnTouchListener { } @Override + public PointerIcon onResolvePointerIcon(MotionEvent event, int pointerIndex) { + return PointerIcon.getSystemIcon(getContext(), + isLandscape() ? TYPE_HORIZONTAL_DOUBLE_ARROW : TYPE_VERTICAL_DOUBLE_ARROW); + } + + @Override public boolean onTouch(View v, MotionEvent event) { if (mSplitLayout == null || !mInteractive) { return false; @@ -371,6 +384,43 @@ public class DividerView extends FrameLayout implements View.OnTouchListener { mViewHost.relayout(lp); } + @Override + public boolean onHoverEvent(MotionEvent event) { + if (!DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI, CURSOR_HOVER_STATES_ENABLED, + /* defaultValue = */ false)) { + return false; + } + + if (event.getAction() == MotionEvent.ACTION_HOVER_ENTER) { + setHovering(); + return true; + } else if (event.getAction() == MotionEvent.ACTION_HOVER_EXIT) { + releaseHovering(); + return true; + } + return false; + } + + @VisibleForTesting + void setHovering() { + mHandle.setHovering(true, true); + mHandle.animate() + .setInterpolator(Interpolators.TOUCH_RESPONSE) + .setDuration(TOUCH_ANIMATION_DURATION) + .translationZ(mTouchElevation) + .start(); + } + + @VisibleForTesting + void releaseHovering() { + mHandle.setHovering(false, true); + mHandle.animate() + .setInterpolator(Interpolators.FAST_OUT_SLOW_IN) + .setDuration(TOUCH_RELEASE_ANIMATION_DURATION) + .translationZ(0) + .start(); + } + /** * Set divider should interactive to user or not. * diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvWMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvWMShellModule.java index 12d51f54a09c..47d58afb6aba 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvWMShellModule.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvWMShellModule.java @@ -25,6 +25,7 @@ import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.DisplayImeController; import com.android.wm.shell.common.DisplayInsetsController; +import com.android.wm.shell.common.LaunchAdjacentController; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.common.SystemWindows; @@ -86,13 +87,14 @@ public class TvWMShellModule { TransactionPool transactionPool, IconProvider iconProvider, Optional<RecentTasksController> recentTasks, + LaunchAdjacentController launchAdjacentController, @ShellMainThread ShellExecutor mainExecutor, Handler mainHandler, SystemWindows systemWindows) { return new TvSplitScreenController(context, shellInit, shellCommandHandler, shellController, shellTaskOrganizer, syncQueue, rootTDAOrganizer, displayController, displayImeController, displayInsetsController, dragAndDropController, transitions, - transactionPool, iconProvider, recentTasks, mainExecutor, mainHandler, - systemWindows); + transactionPool, iconProvider, recentTasks, launchAdjacentController, mainExecutor, + mainHandler, systemWindows); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java index c491fed5d896..34a6e0ae406c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java @@ -46,6 +46,7 @@ import com.android.wm.shell.common.DisplayInsetsController; import com.android.wm.shell.common.DisplayLayout; import com.android.wm.shell.common.DockStateReader; import com.android.wm.shell.common.FloatingContentCoordinator; +import com.android.wm.shell.common.LaunchAdjacentController; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.common.SystemWindows; @@ -275,6 +276,13 @@ public abstract class WMShellBaseModule { return new WindowManagerShellWrapper(mainExecutor); } + @WMSingleton + @Provides + static LaunchAdjacentController provideLaunchAdjacentController( + SyncTransactionQueue syncQueue) { + return new LaunchAdjacentController(syncQueue); + } + // // Back animation // diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java index cff317259f1e..99959aeb0e8c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java @@ -41,12 +41,14 @@ import com.android.wm.shell.common.DisplayImeController; import com.android.wm.shell.common.DisplayInsetsController; import com.android.wm.shell.common.DisplayLayout; import com.android.wm.shell.common.FloatingContentCoordinator; +import com.android.wm.shell.common.LaunchAdjacentController; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.common.SystemWindows; import com.android.wm.shell.common.TabletopModeController; import com.android.wm.shell.common.TaskStackListenerImpl; import com.android.wm.shell.common.TransactionPool; +import com.android.wm.shell.common.annotations.ShellAnimationThread; import com.android.wm.shell.common.annotations.ShellBackgroundThread; import com.android.wm.shell.common.annotations.ShellMainThread; import com.android.wm.shell.desktopmode.DesktopModeController; @@ -55,6 +57,7 @@ import com.android.wm.shell.desktopmode.DesktopModeTaskRepository; import com.android.wm.shell.desktopmode.DesktopTasksController; import com.android.wm.shell.desktopmode.EnterDesktopTaskTransitionHandler; import com.android.wm.shell.desktopmode.ExitDesktopTaskTransitionHandler; +import com.android.wm.shell.desktopmode.ToggleResizeDesktopTaskTransitionHandler; import com.android.wm.shell.draganddrop.DragAndDropController; import com.android.wm.shell.freeform.FreeformComponents; import com.android.wm.shell.freeform.FreeformTaskListener; @@ -202,8 +205,7 @@ public abstract class WMShellModule { SyncTransactionQueue syncQueue, Transitions transitions, Optional<DesktopModeController> desktopModeController, - Optional<DesktopTasksController> desktopTasksController, - Optional<SplitScreenController> splitScreenController) { + Optional<DesktopTasksController> desktopTasksController) { if (DesktopModeStatus.isAnyEnabled()) { return new DesktopModeWindowDecorViewModel( context, @@ -214,8 +216,7 @@ public abstract class WMShellModule { syncQueue, transitions, desktopModeController, - desktopTasksController, - splitScreenController); + desktopTasksController); } return new CaptionWindowDecorViewModel( context, @@ -263,8 +264,13 @@ public abstract class WMShellModule { static FreeformTaskTransitionHandler provideFreeformTaskTransitionHandler( ShellInit shellInit, Transitions transitions, - WindowDecorViewModel windowDecorViewModel) { - return new FreeformTaskTransitionHandler(shellInit, transitions, windowDecorViewModel); + Context context, + WindowDecorViewModel windowDecorViewModel, + DisplayController displayController, + @ShellMainThread ShellExecutor mainExecutor, + @ShellAnimationThread ShellExecutor animExecutor) { + return new FreeformTaskTransitionHandler(shellInit, transitions, context, + windowDecorViewModel, displayController, mainExecutor, animExecutor); } @WMSingleton @@ -327,11 +333,14 @@ public abstract class WMShellModule { TransactionPool transactionPool, IconProvider iconProvider, Optional<RecentTasksController> recentTasks, + LaunchAdjacentController launchAdjacentController, + Optional<WindowDecorViewModel> windowDecorViewModel, @ShellMainThread ShellExecutor mainExecutor) { return new SplitScreenController(context, shellInit, shellCommandHandler, shellController, shellTaskOrganizer, syncQueue, rootTaskDisplayAreaOrganizer, displayController, displayImeController, displayInsetsController, dragAndDropController, transitions, - transactionPool, iconProvider, recentTasks, mainExecutor); + transactionPool, iconProvider, recentTasks, launchAdjacentController, + windowDecorViewModel, mainExecutor); } // @@ -535,11 +544,13 @@ public abstract class WMShellModule { Optional<PipTouchHandler> pipTouchHandlerOptional, Optional<RecentsTransitionHandler> recentsTransitionHandler, KeyguardTransitionHandler keyguardTransitionHandler, + Optional<DesktopModeController> desktopModeController, + Optional<DesktopTasksController> desktopTasksController, Optional<UnfoldTransitionHandler> unfoldHandler, Transitions transitions) { return new DefaultMixedHandler(shellInit, transitions, splitScreenOptional, pipTouchHandlerOptional, recentsTransitionHandler, keyguardTransitionHandler, - unfoldHandler); + desktopModeController, desktopTasksController, unfoldHandler); } @WMSingleton @@ -671,6 +682,7 @@ public abstract class WMShellModule { static DesktopTasksController provideDesktopTasksController( Context context, ShellInit shellInit, + ShellCommandHandler shellCommandHandler, ShellController shellController, DisplayController displayController, ShellTaskOrganizer shellTaskOrganizer, @@ -679,13 +691,16 @@ public abstract class WMShellModule { Transitions transitions, EnterDesktopTaskTransitionHandler enterDesktopTransitionHandler, ExitDesktopTaskTransitionHandler exitDesktopTransitionHandler, + ToggleResizeDesktopTaskTransitionHandler toggleResizeDesktopTaskTransitionHandler, @DynamicOverride DesktopModeTaskRepository desktopModeTaskRepository, + LaunchAdjacentController launchAdjacentController, @ShellMainThread ShellExecutor mainExecutor ) { - return new DesktopTasksController(context, shellInit, shellController, displayController, - shellTaskOrganizer, syncQueue, rootTaskDisplayAreaOrganizer, transitions, - enterDesktopTransitionHandler, exitDesktopTransitionHandler, - desktopModeTaskRepository, mainExecutor); + return new DesktopTasksController(context, shellInit, shellCommandHandler, shellController, + displayController, shellTaskOrganizer, syncQueue, rootTaskDisplayAreaOrganizer, + transitions, enterDesktopTransitionHandler, exitDesktopTransitionHandler, + toggleResizeDesktopTaskTransitionHandler, desktopModeTaskRepository, + launchAdjacentController, mainExecutor); } @WMSingleton @@ -697,6 +712,13 @@ public abstract class WMShellModule { @WMSingleton @Provides + static ToggleResizeDesktopTaskTransitionHandler provideToggleResizeDesktopTaskTransitionHandler( + Transitions transitions) { + return new ToggleResizeDesktopTaskTransitionHandler(transitions); + } + + @WMSingleton + @Provides static ExitDesktopTaskTransitionHandler provideExitDesktopTaskTransitionHandler( Transitions transitions, Context context diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java index b9d2be280efb..db6c258e84c2 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java @@ -33,6 +33,7 @@ import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_DE import android.app.ActivityManager.RunningTaskInfo; import android.app.WindowConfiguration; import android.content.Context; +import android.content.res.TypedArray; import android.database.ContentObserver; import android.graphics.Region; import android.net.Uri; @@ -414,6 +415,25 @@ public class DesktopModeController implements RemoteCallable<DesktopModeControll } /** + * Applies the proper surface states (rounded corners) to tasks when desktop mode is active. + * This is intended to be used when desktop mode is part of another animation but isn't, itself, + * animating. + */ + public void syncSurfaceState(@NonNull TransitionInfo info, + SurfaceControl.Transaction finishTransaction) { + // Add rounded corners to freeform windows + final TypedArray ta = mContext.obtainStyledAttributes( + new int[]{android.R.attr.dialogCornerRadius}); + final int cornerRadius = ta.getDimensionPixelSize(0, 0); + ta.recycle(); + for (TransitionInfo.Change change: info.getChanges()) { + if (change.getTaskInfo().getWindowingMode() == WINDOWING_MODE_FREEFORM) { + finishTransaction.setCornerRadius(change.getLeash(), cornerRadius); + } + } + } + + /** * A {@link ContentObserver} for listening to changes to {@link Settings.System#DESKTOP_MODE} */ private final class SettingsObserver extends ContentObserver { @@ -500,6 +520,11 @@ public class DesktopModeController implements RemoteCallable<DesktopModeControll } @Override + public void showDesktopApp(int taskId) throws RemoteException { + // TODO + } + + @Override public int getVisibleTaskCount(int displayId) throws RemoteException { int[] result = new int[1]; executeRemoteCallWithTaskPermission(mController, "getVisibleTaskCount", @@ -508,5 +533,20 @@ public class DesktopModeController implements RemoteCallable<DesktopModeControll ); return result[0]; } + + @Override + public void stashDesktopApps(int displayId) throws RemoteException { + // Stashing of desktop apps not needed. Apps always launch on desktop + } + + @Override + public void hideStashedDesktopApps(int displayId) throws RemoteException { + // Stashing of desktop apps not needed. Apps always launch on desktop + } + + @Override + public void setTaskListener(IDesktopTaskListener listener) throws RemoteException { + // TODO(b/261234402): move visibility from sysui state to listener + } } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt index 3ab175d3b68a..711df0d89936 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt @@ -25,6 +25,7 @@ import androidx.core.util.keyIterator import androidx.core.util.valueIterator import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE import com.android.wm.shell.util.KtProtoLog +import java.io.PrintWriter import java.util.concurrent.Executor import java.util.function.Consumer @@ -43,6 +44,7 @@ class DesktopModeTaskRepository { */ val activeTasks: ArraySet<Int> = ArraySet(), val visibleTasks: ArraySet<Int> = ArraySet(), + var stashed: Boolean = false ) // Tasks currently in freeform mode, ordered from top to bottom (top is at index 0). @@ -85,8 +87,10 @@ class DesktopModeTaskRepository { visibleTasksListeners[visibleTasksListener] = executor displayData.keyIterator().forEach { displayId -> val visibleTasks = getVisibleTaskCount(displayId) + val stashed = isStashed(displayId) executor.execute { visibleTasksListener.onVisibilityChanged(displayId, visibleTasks > 0) + visibleTasksListener.onStashedChanged(displayId, stashed) } } } @@ -312,6 +316,52 @@ class DesktopModeTaskRepository { } /** + * Update stashed status on display with id [displayId] + */ + fun setStashed(displayId: Int, stashed: Boolean) { + val data = displayData.getOrCreate(displayId) + val oldValue = data.stashed + data.stashed = stashed + if (oldValue != stashed) { + KtProtoLog.d( + WM_SHELL_DESKTOP_MODE, + "DesktopTaskRepo: mark stashed=%b displayId=%d", + stashed, + displayId + ) + visibleTasksListeners.forEach { (listener, executor) -> + executor.execute { listener.onStashedChanged(displayId, stashed) } + } + } + } + + /** + * Check if display with id [displayId] has desktop tasks stashed + */ + fun isStashed(displayId: Int): Boolean { + return displayData[displayId]?.stashed ?: false + } + + internal fun dump(pw: PrintWriter, prefix: String) { + val innerPrefix = "$prefix " + pw.println("${prefix}DesktopModeTaskRepository") + dumpDisplayData(pw, innerPrefix) + pw.println("${innerPrefix}freeformTasksInZOrder=${freeformTasksInZOrder.toDumpString()}") + pw.println("${innerPrefix}activeTasksListeners=${activeTasksListeners.size}") + pw.println("${innerPrefix}visibleTasksListeners=${visibleTasksListeners.size}") + } + + private fun dumpDisplayData(pw: PrintWriter, prefix: String) { + val innerPrefix = "$prefix " + displayData.forEach { displayId, data -> + pw.println("${prefix}Display $displayId:") + pw.println("${innerPrefix}activeTasks=${data.activeTasks.toDumpString()}") + pw.println("${innerPrefix}visibleTasks=${data.visibleTasks.toDumpString()}") + pw.println("${innerPrefix}stashed=${data.stashed}") + } + } + + /** * Defines interface for classes that can listen to changes for active tasks in desktop mode. */ interface ActiveTasksListener { @@ -331,5 +381,15 @@ class DesktopModeTaskRepository { */ @JvmDefault fun onVisibilityChanged(displayId: Int, hasVisibleFreeformTasks: Boolean) {} + + /** + * Called when the desktop stashed status changes. + */ + @JvmDefault + fun onStashedChanged(displayId: Int, stashed: Boolean) {} } } + +private fun <T> Iterable<T>.toDumpString(): String { + return joinToString(separator = ", ", prefix = "[", postfix = "]") +}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt index 91bb155d9d01..2b763ad8f14d 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt @@ -16,6 +16,7 @@ package com.android.wm.shell.desktopmode +import android.R import android.app.ActivityManager.RunningTaskInfo import android.app.WindowConfiguration.ACTIVITY_TYPE_HOME import android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD @@ -24,11 +25,13 @@ import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN import android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED import android.app.WindowConfiguration.WindowingMode import android.content.Context +import android.content.res.TypedArray import android.graphics.Point import android.graphics.Rect import android.graphics.Region import android.os.IBinder import android.os.SystemProperties +import android.util.DisplayMetrics.DENSITY_DEFAULT import android.view.SurfaceControl import android.view.WindowManager.TRANSIT_CHANGE import android.view.WindowManager.TRANSIT_NONE @@ -36,7 +39,6 @@ import android.view.WindowManager.TRANSIT_OPEN import android.view.WindowManager.TRANSIT_TO_FRONT import android.window.TransitionInfo import android.window.TransitionRequestInfo -import android.window.WindowContainerToken import android.window.WindowContainerTransaction import androidx.annotation.BinderThread import com.android.wm.shell.RootTaskDisplayAreaOrganizer @@ -44,18 +46,23 @@ import com.android.wm.shell.ShellTaskOrganizer import com.android.wm.shell.common.DisplayController import com.android.wm.shell.common.ExecutorUtils import com.android.wm.shell.common.ExternalInterfaceBinder +import com.android.wm.shell.common.LaunchAdjacentController import com.android.wm.shell.common.RemoteCallable import com.android.wm.shell.common.ShellExecutor +import com.android.wm.shell.common.SingleInstanceRemoteListener import com.android.wm.shell.common.SyncTransactionQueue import com.android.wm.shell.common.annotations.ExternalThread import com.android.wm.shell.common.annotations.ShellMainThread import com.android.wm.shell.desktopmode.DesktopModeTaskRepository.VisibleTasksListener import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE +import com.android.wm.shell.sysui.ShellCommandHandler import com.android.wm.shell.sysui.ShellController import com.android.wm.shell.sysui.ShellInit import com.android.wm.shell.sysui.ShellSharedConstants import com.android.wm.shell.transition.Transitions import com.android.wm.shell.util.KtProtoLog +import com.android.wm.shell.windowdecor.DesktopModeWindowDecoration +import java.io.PrintWriter import java.util.concurrent.Executor import java.util.function.Consumer @@ -63,6 +70,7 @@ import java.util.function.Consumer class DesktopTasksController( private val context: Context, shellInit: ShellInit, + private val shellCommandHandler: ShellCommandHandler, private val shellController: ShellController, private val displayController: DisplayController, private val shellTaskOrganizer: ShellTaskOrganizer, @@ -71,7 +79,10 @@ class DesktopTasksController( private val transitions: Transitions, private val enterDesktopTaskTransitionHandler: EnterDesktopTaskTransitionHandler, private val exitDesktopTaskTransitionHandler: ExitDesktopTaskTransitionHandler, + private val toggleResizeDesktopTaskTransitionHandler: + ToggleResizeDesktopTaskTransitionHandler, private val desktopModeTaskRepository: DesktopModeTaskRepository, + private val launchAdjacentController: LaunchAdjacentController, @ShellMainThread private val mainExecutor: ShellExecutor ) : RemoteCallable<DesktopTasksController>, Transitions.TransitionHandler { @@ -82,6 +93,11 @@ class DesktopTasksController( visualIndicator?.releaseVisualIndicator(t) visualIndicator = null } + private val taskVisibilityListener = object : VisibleTasksListener { + override fun onVisibilityChanged(displayId: Int, hasVisibleFreeformTasks: Boolean) { + launchAdjacentController.launchAdjacentEnabled = !hasVisibleFreeformTasks + } + } init { desktopMode = DesktopModeImpl() @@ -92,12 +108,14 @@ class DesktopTasksController( private fun onInit() { KtProtoLog.d(WM_SHELL_DESKTOP_MODE, "Initialize DesktopTasksController") + shellCommandHandler.addDumpCallback(this::dump, this) shellController.addExternalInterface( ShellSharedConstants.KEY_EXTRA_SHELL_DESKTOP_MODE, { createExternalInterface() }, this ) transitions.addHandler(this) + desktopModeTaskRepository.addVisibleTasksListener(taskVisibilityListener, mainExecutor) } /** Show all tasks, that are part of the desktop, on top of launcher */ @@ -118,27 +136,58 @@ class DesktopTasksController( } } + /** + * Stash desktop tasks on display with id [displayId]. + * + * When desktop tasks are stashed, launcher home screen icons are fully visible. New apps + * launched in this state will be added to the desktop. Existing desktop tasks will be brought + * back to front during the launch. + */ + fun stashDesktopApps(displayId: Int) { + KtProtoLog.v(WM_SHELL_DESKTOP_MODE, "DesktopTasksController: stashDesktopApps") + desktopModeTaskRepository.setStashed(displayId, true) + } + + /** + * Clear the stashed state for the given display + */ + fun hideStashedDesktopApps(displayId: Int) { + KtProtoLog.v( + WM_SHELL_DESKTOP_MODE, + "DesktopTasksController: hideStashedApps displayId=%d", + displayId + ) + desktopModeTaskRepository.setStashed(displayId, false) + } + /** Get number of tasks that are marked as visible */ fun getVisibleTaskCount(displayId: Int): Int { return desktopModeTaskRepository.getVisibleTaskCount(displayId) } /** Move a task with given `taskId` to desktop */ - fun moveToDesktop(taskId: Int) { - shellTaskOrganizer.getRunningTaskInfo(taskId)?.let { task -> moveToDesktop(task) } + fun moveToDesktop(taskId: Int, wct: WindowContainerTransaction = WindowContainerTransaction()) { + shellTaskOrganizer.getRunningTaskInfo(taskId)?.let { + task -> moveToDesktop(task, wct) + } } - /** Move a task to desktop */ - fun moveToDesktop(task: RunningTaskInfo) { + /** + * Move a task to desktop + */ + fun moveToDesktop( + task: RunningTaskInfo, + wct: WindowContainerTransaction = WindowContainerTransaction() + ) { KtProtoLog.v( WM_SHELL_DESKTOP_MODE, "DesktopTasksController: moveToDesktop taskId=%d", task.taskId ) - val wct = WindowContainerTransaction() // Bring other apps to front first bringDesktopAppsToFront(task.displayId, wct) - addMoveToDesktopChanges(wct, task.token) + addMoveToDesktopChanges(wct, task) + if (Transitions.ENABLE_SHELL_TRANSITIONS) { transitions.startTransition(TRANSIT_CHANGE, wct, null /* handler */) } else { @@ -158,7 +207,7 @@ class DesktopTasksController( ) val wct = WindowContainerTransaction() moveHomeTaskToFront(wct) - addMoveToDesktopChanges(wct, taskInfo.getToken()) + addMoveToDesktopChanges(wct, taskInfo) wct.setBounds(taskInfo.token, startBounds) if (Transitions.ENABLE_SHELL_TRANSITIONS) { @@ -178,7 +227,7 @@ class DesktopTasksController( ) val wct = WindowContainerTransaction() bringDesktopAppsToFront(taskInfo.displayId, wct) - addMoveToDesktopChanges(wct, taskInfo.getToken()) + addMoveToDesktopChanges(wct, taskInfo) wct.setBounds(taskInfo.token, freeformBounds) if (Transitions.ENABLE_SHELL_TRANSITIONS) { @@ -204,7 +253,7 @@ class DesktopTasksController( ) val wct = WindowContainerTransaction() - addMoveToFullscreenChanges(wct, task.token) + addMoveToFullscreenChanges(wct, task) if (Transitions.ENABLE_SHELL_TRANSITIONS) { transitions.startTransition(TRANSIT_CHANGE, wct, null /* handler */) } else { @@ -223,7 +272,7 @@ class DesktopTasksController( task.taskId ) val wct = WindowContainerTransaction() - addMoveToFullscreenChanges(wct, task.token) + addMoveToFullscreenChanges(wct, task) if (Transitions.ENABLE_SHELL_TRANSITIONS) { enterDesktopTaskTransitionHandler.startCancelMoveToDesktopMode(wct, position, mOnAnimationFinishedCallback) @@ -240,7 +289,7 @@ class DesktopTasksController( task.taskId ) val wct = WindowContainerTransaction() - addMoveToFullscreenChanges(wct, task.token) + addMoveToFullscreenChanges(wct, task) if (Transitions.ENABLE_SHELL_TRANSITIONS) { exitDesktopTaskTransitionHandler.startTransition( @@ -252,6 +301,11 @@ class DesktopTasksController( } /** Move a task to the front */ + fun moveTaskToFront(taskId: Int) { + shellTaskOrganizer.getRunningTaskInfo(taskId)?.let { task -> moveTaskToFront(task) } + } + + /** Move a task to the front */ fun moveTaskToFront(taskInfo: RunningTaskInfo) { KtProtoLog.v( WM_SHELL_DESKTOP_MODE, @@ -331,6 +385,49 @@ class DesktopTasksController( } } + /** Quick-resizes a desktop task, toggling between the stable bounds and the default bounds. */ + fun toggleDesktopTaskSize(taskInfo: RunningTaskInfo, windowDecor: DesktopModeWindowDecoration) { + val displayLayout = displayController.getDisplayLayout(taskInfo.displayId) ?: return + + val stableBounds = Rect() + displayLayout.getStableBounds(stableBounds) + val destinationBounds = Rect() + if (taskInfo.configuration.windowConfiguration.bounds == stableBounds) { + // The desktop task is currently occupying the whole stable bounds, toggle to the + // default bounds. + getDefaultDesktopTaskBounds( + density = taskInfo.configuration.densityDpi.toFloat() / DENSITY_DEFAULT, + stableBounds = stableBounds, + outBounds = destinationBounds + ) + } else { + // Toggle to the stable bounds. + destinationBounds.set(stableBounds) + } + + val wct = WindowContainerTransaction().setBounds(taskInfo.token, destinationBounds) + if (Transitions.ENABLE_SHELL_TRANSITIONS) { + toggleResizeDesktopTaskTransitionHandler.startTransition( + wct, + taskInfo.taskId, + windowDecor + ) + } else { + shellTaskOrganizer.applyTransaction(wct) + } + } + + private fun getDefaultDesktopTaskBounds(density: Float, stableBounds: Rect, outBounds: Rect) { + val width = (DESKTOP_MODE_DEFAULT_WIDTH_DP * density + 0.5f).toInt() + val height = (DESKTOP_MODE_DEFAULT_HEIGHT_DP * density + 0.5f).toInt() + outBounds.set(0, 0, width, height) + // Center the task in stable bounds + outBounds.offset( + stableBounds.centerX() - outBounds.centerX(), + stableBounds.centerY() - outBounds.centerY() + ) + } + /** * Get windowing move for a given `taskId` * @@ -397,83 +494,169 @@ class DesktopTasksController( transition: IBinder, request: TransitionRequestInfo ): WindowContainerTransaction? { + KtProtoLog.v( + WM_SHELL_DESKTOP_MODE, + "DesktopTasksController: handleRequest request=%s", + request + ) // Check if we should skip handling this transition + var reason = "" val shouldHandleRequest = when { // Only handle open or to front transitions - request.type != TRANSIT_OPEN && request.type != TRANSIT_TO_FRONT -> false + request.type != TRANSIT_OPEN && request.type != TRANSIT_TO_FRONT -> { + reason = "transition type not handled (${request.type})" + false + } // Only handle when it is a task transition - request.triggerTask == null -> false + request.triggerTask == null -> { + reason = "triggerTask is null" + false + } // Only handle standard type tasks - request.triggerTask.activityType != ACTIVITY_TYPE_STANDARD -> false + request.triggerTask.activityType != ACTIVITY_TYPE_STANDARD -> { + reason = "activityType not handled (${request.triggerTask.activityType})" + false + } // Only handle fullscreen or freeform tasks request.triggerTask.windowingMode != WINDOWING_MODE_FULLSCREEN && - request.triggerTask.windowingMode != WINDOWING_MODE_FREEFORM -> false + request.triggerTask.windowingMode != WINDOWING_MODE_FREEFORM -> { + reason = "windowingMode not handled (${request.triggerTask.windowingMode})" + false + } // Otherwise process it else -> true } if (!shouldHandleRequest) { + KtProtoLog.v( + WM_SHELL_DESKTOP_MODE, + "DesktopTasksController: skipping handleRequest reason=%s", + reason + ) return null } val task: RunningTaskInfo = request.triggerTask - val activeTasks = desktopModeTaskRepository.getActiveTasks(task.displayId) - // Check if we should switch a fullscreen task to freeform - if (task.windowingMode == WINDOWING_MODE_FULLSCREEN) { - // If there are any visible desktop tasks, switch the task to freeform - if (activeTasks.any { desktopModeTaskRepository.isVisibleTask(it) }) { - KtProtoLog.d( + val result = when { + // If display has tasks stashed, handle as stashed launch + desktopModeTaskRepository.isStashed(task.displayId) -> handleStashedTaskLaunch(task) + // Check if fullscreen task should be updated + task.windowingMode == WINDOWING_MODE_FULLSCREEN -> handleFullscreenTaskLaunch(task) + // Check if freeform task should be updated + task.windowingMode == WINDOWING_MODE_FREEFORM -> handleFreeformTaskLaunch(task) + else -> { + null + } + } + KtProtoLog.v( + WM_SHELL_DESKTOP_MODE, + "DesktopTasksController: handleRequest result=%s", + result ?: "null" + ) + return result + } + + /** + * Applies the proper surface states (rounded corners) to tasks when desktop mode is active. + * This is intended to be used when desktop mode is part of another animation but isn't, itself, + * animating. + */ + fun syncSurfaceState( + info: TransitionInfo, + finishTransaction: SurfaceControl.Transaction + ) { + // Add rounded corners to freeform windows + val ta: TypedArray = context.obtainStyledAttributes( + intArrayOf(R.attr.dialogCornerRadius)) + val cornerRadius = ta.getDimensionPixelSize(0, 0).toFloat() + ta.recycle() + info.changes + .filter { it.taskInfo?.windowingMode == WINDOWING_MODE_FREEFORM } + .forEach { finishTransaction.setCornerRadius(it.leash, cornerRadius) } + } + + private fun handleFreeformTaskLaunch(task: RunningTaskInfo): WindowContainerTransaction? { + KtProtoLog.v(WM_SHELL_DESKTOP_MODE, "DesktopTasksController: handleFreeformTaskLaunch") + val activeTasks = desktopModeTaskRepository.getActiveTasks(task.displayId) + if (activeTasks.none { desktopModeTaskRepository.isVisibleTask(it) }) { + KtProtoLog.d( WM_SHELL_DESKTOP_MODE, - "DesktopTasksController: switch fullscreen task to freeform on transition" + - " taskId=%d", + "DesktopTasksController: switch freeform task to fullscreen oon transition" + + " taskId=%d", task.taskId - ) - return WindowContainerTransaction().also { wct -> - addMoveToDesktopChanges(wct, task.token) - } + ) + return WindowContainerTransaction().also { wct -> + addMoveToFullscreenChanges(wct, task) } } + return null + } - // CHeck if we should switch a freeform task to fullscreen - if (task.windowingMode == WINDOWING_MODE_FREEFORM) { - // If no visible desktop tasks, switch this task to freeform as the transition came - // outside of this controller - if (activeTasks.none { desktopModeTaskRepository.isVisibleTask(it) }) { - KtProtoLog.d( + private fun handleFullscreenTaskLaunch(task: RunningTaskInfo): WindowContainerTransaction? { + KtProtoLog.v(WM_SHELL_DESKTOP_MODE, "DesktopTasksController: handleFullscreenTaskLaunch") + val activeTasks = desktopModeTaskRepository.getActiveTasks(task.displayId) + if (activeTasks.any { desktopModeTaskRepository.isVisibleTask(it) }) { + KtProtoLog.d( WM_SHELL_DESKTOP_MODE, - "DesktopTasksController: switch freeform task to fullscreen oon transition" + - " taskId=%d", + "DesktopTasksController: switch fullscreen task to freeform on transition" + + " taskId=%d", task.taskId - ) - return WindowContainerTransaction().also { wct -> - addMoveToFullscreenChanges(wct, task.token) - } + ) + return WindowContainerTransaction().also { wct -> + addMoveToDesktopChanges(wct, task) } } return null } + private fun handleStashedTaskLaunch(task: RunningTaskInfo): WindowContainerTransaction { + KtProtoLog.d( + WM_SHELL_DESKTOP_MODE, + "DesktopTasksController: launch apps with stashed on transition taskId=%d", + task.taskId + ) + val wct = WindowContainerTransaction() + bringDesktopAppsToFront(task.displayId, wct) + addMoveToDesktopChanges(wct, task) + desktopModeTaskRepository.setStashed(task.displayId, false) + return wct + } + private fun addMoveToDesktopChanges( wct: WindowContainerTransaction, - token: WindowContainerToken + taskInfo: RunningTaskInfo ) { - wct.setWindowingMode(token, WINDOWING_MODE_FREEFORM) - wct.reorder(token, true /* onTop */) + val displayWindowingMode = taskInfo.configuration.windowConfiguration.displayWindowingMode + val targetWindowingMode = if (displayWindowingMode == WINDOWING_MODE_FREEFORM) { + // Display windowing is freeform, set to undefined and inherit it + WINDOWING_MODE_UNDEFINED + } else { + WINDOWING_MODE_FREEFORM + } + wct.setWindowingMode(taskInfo.token, targetWindowingMode) + wct.reorder(taskInfo.token, true /* onTop */) if (isDesktopDensityOverrideSet()) { - wct.setDensityDpi(token, getDesktopDensityDpi()) + wct.setDensityDpi(taskInfo.token, getDesktopDensityDpi()) } } private fun addMoveToFullscreenChanges( wct: WindowContainerTransaction, - token: WindowContainerToken + taskInfo: RunningTaskInfo ) { - wct.setWindowingMode(token, WINDOWING_MODE_FULLSCREEN) - wct.setBounds(token, null) + val displayWindowingMode = taskInfo.configuration.windowConfiguration.displayWindowingMode + val targetWindowingMode = if (displayWindowingMode == WINDOWING_MODE_FULLSCREEN) { + // Display windowing is fullscreen, set to undefined and inherit it + WINDOWING_MODE_UNDEFINED + } else { + WINDOWING_MODE_FULLSCREEN + } + wct.setWindowingMode(taskInfo.token, targetWindowingMode) + wct.setBounds(taskInfo.token, null) if (isDesktopDensityOverrideSet()) { - wct.setDensityDpi(token, getFullscreenDensityDpi()) + wct.setDensityDpi(taskInfo.token, getFullscreenDensityDpi()) } } @@ -524,14 +707,19 @@ class DesktopTasksController( * Perform checks required on drag end. Move to fullscreen if drag ends in status bar area. * * @param taskInfo the task being dragged. - * @param position position of surface when drag ends + * @param position position of surface when drag ends. + * @param y the Y position of the motion event. + * @param windowDecor the window decoration for the task being dragged */ fun onDragPositioningEnd( taskInfo: RunningTaskInfo, - position: Point + position: Point, + y: Float, + windowDecor: DesktopModeWindowDecoration ) { val statusBarHeight = getStatusBarHeight(taskInfo) - if (position.y <= statusBarHeight && taskInfo.windowingMode == WINDOWING_MODE_FREEFORM) { + if (y <= statusBarHeight && taskInfo.windowingMode == WINDOWING_MODE_FREEFORM) { + windowDecor.incrementRelayoutBlock() moveToFullscreenWithAnimation(taskInfo, position) } } @@ -632,6 +820,12 @@ class DesktopTasksController( desktopModeTaskRepository.setTaskCornerListener(listener, callbackExecutor) } + private fun dump(pw: PrintWriter, prefix: String) { + val innerPrefix = "$prefix " + pw.println("${prefix}DesktopTasksController") + desktopModeTaskRepository.dump(pw, innerPrefix) + } + /** The interface for calls from outside the shell, within the host process. */ @ExternalThread private inner class DesktopModeImpl : DesktopMode { @@ -658,8 +852,51 @@ class DesktopTasksController( @BinderThread private class IDesktopModeImpl(private var controller: DesktopTasksController?) : IDesktopMode.Stub(), ExternalInterfaceBinder { + + private lateinit var remoteListener: + SingleInstanceRemoteListener<DesktopTasksController, IDesktopTaskListener> + + private val listener: VisibleTasksListener = object : VisibleTasksListener { + override fun onVisibilityChanged(displayId: Int, visible: Boolean) { + KtProtoLog.v( + WM_SHELL_DESKTOP_MODE, + "IDesktopModeImpl: onVisibilityChanged display=%d visible=%b", + displayId, + visible + ) + remoteListener.call { l -> l.onVisibilityChanged(displayId, visible) } + } + + override fun onStashedChanged(displayId: Int, stashed: Boolean) { + KtProtoLog.v( + WM_SHELL_DESKTOP_MODE, + "IDesktopModeImpl: onStashedChanged display=%d stashed=%b", + displayId, + stashed + ) + remoteListener.call { l -> l.onStashedChanged(displayId, stashed) } + } + } + + init { + remoteListener = + SingleInstanceRemoteListener<DesktopTasksController, IDesktopTaskListener>( + controller, + { c -> + c.desktopModeTaskRepository.addVisibleTasksListener( + listener, + c.mainExecutor + ) + }, + { c -> + c.desktopModeTaskRepository.removeVisibleTasksListener(listener) + } + ) + } + /** Invalidates this instance, preventing future calls from updating the controller. */ override fun invalidate() { + remoteListener.unregister() controller = null } @@ -670,6 +907,27 @@ class DesktopTasksController( ) { c -> c.showDesktopApps(displayId) } } + override fun stashDesktopApps(displayId: Int) { + ExecutorUtils.executeRemoteCallWithTaskPermission( + controller, + "stashDesktopApps" + ) { c -> c.stashDesktopApps(displayId) } + } + + override fun hideStashedDesktopApps(displayId: Int) { + ExecutorUtils.executeRemoteCallWithTaskPermission( + controller, + "hideStashedDesktopApps" + ) { c -> c.hideStashedDesktopApps(displayId) } + } + + override fun showDesktopApp(taskId: Int) { + ExecutorUtils.executeRemoteCallWithTaskPermission( + controller, + "showDesktopApp" + ) { c -> c.moveTaskToFront(taskId) } + } + override fun getVisibleTaskCount(displayId: Int): Int { val result = IntArray(1) ExecutorUtils.executeRemoteCallWithTaskPermission( @@ -680,13 +938,33 @@ class DesktopTasksController( ) return result[0] } + + override fun setTaskListener(listener: IDesktopTaskListener?) { + KtProtoLog.v( + WM_SHELL_DESKTOP_MODE, + "IDesktopModeImpl: set task listener=%s", + listener ?: "null" + ) + ExecutorUtils.executeRemoteCallWithTaskPermission( + controller, + "setTaskListener" + ) { _ -> listener?.let { remoteListener.register(it) } ?: remoteListener.unregister() } + } } companion object { private val DESKTOP_DENSITY_OVERRIDE = - SystemProperties.getInt("persist.wm.debug.desktop_mode_density", 0) + SystemProperties.getInt("persist.wm.debug.desktop_mode_density", 284) private val DESKTOP_DENSITY_ALLOWED_RANGE = (100..1000) + // Override default freeform task width when desktop mode is enabled. In dips. + private val DESKTOP_MODE_DEFAULT_WIDTH_DP = + SystemProperties.getInt("persist.wm.debug.desktop_mode.default_width", 840) + + // Override default freeform task height when desktop mode is enabled. In dips. + private val DESKTOP_MODE_DEFAULT_HEIGHT_DP = + SystemProperties.getInt("persist.wm.debug.desktop_mode.default_height", 630) + /** * Check if desktop density override is enabled */ diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl index 899d67267e69..ee3a080e7318 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl @@ -16,6 +16,8 @@ package com.android.wm.shell.desktopmode; +import com.android.wm.shell.desktopmode.IDesktopTaskListener; + /** * Interface that is exposed to remote callers to manipulate desktop mode features. */ @@ -24,6 +26,18 @@ interface IDesktopMode { /** Show apps on the desktop on the given display */ void showDesktopApps(int displayId); + /** Stash apps on the desktop to allow launching another app from home screen */ + void stashDesktopApps(int displayId); + + /** Hide apps that may be stashed */ + void hideStashedDesktopApps(int displayId); + + /** Bring task with the given id to front */ + oneway void showDesktopApp(int taskId); + /** Get count of visible desktop tasks on the given display */ int getVisibleTaskCount(int displayId); + + /** Set listener that will receive callbacks about updates to desktop tasks */ + oneway void setTaskListener(IDesktopTaskListener listener); }
\ No newline at end of file diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopTaskListener.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopTaskListener.aidl new file mode 100644 index 000000000000..39128a863ec9 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopTaskListener.aidl @@ -0,0 +1,30 @@ +/* + * 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.desktopmode; + +/** + * Allows external processes to register a listener in WMShell to get updates about desktop task + * state. + */ +interface IDesktopTaskListener { + + /** Desktop task visibility has change. Visible if at least 1 task is visible. */ + oneway void onVisibilityChanged(int displayId, boolean visible); + + /** Desktop task stashed status has changed. */ + oneway void onStashedChanged(int displayId, boolean stashed); +}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ToggleResizeDesktopTaskTransitionHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ToggleResizeDesktopTaskTransitionHandler.kt new file mode 100644 index 000000000000..94788e45e2b6 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ToggleResizeDesktopTaskTransitionHandler.kt @@ -0,0 +1,154 @@ +/* + * 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.desktopmode + +import android.animation.Animator +import android.animation.RectEvaluator +import android.animation.ValueAnimator +import android.graphics.Rect +import android.os.IBinder +import android.util.SparseArray +import android.view.SurfaceControl +import android.view.WindowManager.TRANSIT_CHANGE +import android.window.TransitionInfo +import android.window.TransitionRequestInfo +import android.window.WindowContainerTransaction +import androidx.core.animation.addListener +import com.android.wm.shell.transition.Transitions +import com.android.wm.shell.transition.Transitions.TRANSIT_DESKTOP_MODE_TOGGLE_RESIZE +import com.android.wm.shell.windowdecor.DesktopModeWindowDecoration +import java.util.function.Supplier + +/** Handles the animation of quick resizing of desktop tasks. */ +class ToggleResizeDesktopTaskTransitionHandler( + private val transitions: Transitions, + private val transactionSupplier: Supplier<SurfaceControl.Transaction> +) : Transitions.TransitionHandler { + + private val rectEvaluator = RectEvaluator(Rect()) + private val taskToDecorationMap = SparseArray<DesktopModeWindowDecoration>() + + private var boundsAnimator: Animator? = null + + constructor( + transitions: Transitions + ) : this(transitions, Supplier { SurfaceControl.Transaction() }) + + /** Starts a quick resize transition. */ + fun startTransition( + wct: WindowContainerTransaction, + taskId: Int, + windowDecoration: DesktopModeWindowDecoration + ) { + // Pause relayout until the transition animation finishes. + windowDecoration.incrementRelayoutBlock() + transitions.startTransition(TRANSIT_DESKTOP_MODE_TOGGLE_RESIZE, wct, this) + taskToDecorationMap.put(taskId, windowDecoration) + } + + override fun startAnimation( + transition: IBinder, + info: TransitionInfo, + startTransaction: SurfaceControl.Transaction, + finishTransaction: SurfaceControl.Transaction, + finishCallback: Transitions.TransitionFinishCallback + ): Boolean { + val change = findRelevantChange(info) + val leash = change.leash + val taskId = change.taskInfo.taskId + val startBounds = change.startAbsBounds + val endBounds = change.endAbsBounds + val windowDecor = + taskToDecorationMap.removeReturnOld(taskId) + ?: throw IllegalStateException("Window decoration not found for task $taskId") + + val tx = transactionSupplier.get() + boundsAnimator?.cancel() + boundsAnimator = + ValueAnimator.ofObject(rectEvaluator, startBounds, endBounds) + .setDuration(RESIZE_DURATION_MS) + .apply { + addListener( + onStart = { + startTransaction + .setPosition( + leash, + startBounds.left.toFloat(), + startBounds.top.toFloat() + ) + .setWindowCrop(leash, startBounds.width(), startBounds.height()) + .show(leash) + windowDecor.showResizeVeil(startTransaction, startBounds) + }, + onEnd = { + finishTransaction + .setPosition( + leash, + endBounds.left.toFloat(), + endBounds.top.toFloat() + ) + .setWindowCrop(leash, endBounds.width(), endBounds.height()) + .show(leash) + windowDecor.hideResizeVeil() + finishCallback.onTransitionFinished(null, null) + boundsAnimator = null + } + ) + addUpdateListener { anim -> + val rect = anim.animatedValue as Rect + tx.setPosition(leash, rect.left.toFloat(), rect.top.toFloat()) + .setWindowCrop(leash, rect.width(), rect.height()) + .show(leash) + windowDecor.updateResizeVeil(tx, rect) + } + start() + } + return true + } + + override fun handleRequest( + transition: IBinder, + request: TransitionRequestInfo + ): WindowContainerTransaction? { + return null + } + + private fun findRelevantChange(info: TransitionInfo): TransitionInfo.Change { + val matchingChanges = + info.changes.filter { c -> + !isWallpaper(c) && isValidTaskChange(c) && c.mode == TRANSIT_CHANGE + } + if (matchingChanges.size != 1) { + throw IllegalStateException( + "Expected 1 relevant change but found: ${matchingChanges.size}" + ) + } + return matchingChanges.first() + } + + private fun isWallpaper(change: TransitionInfo.Change): Boolean { + return (change.flags and TransitionInfo.FLAG_IS_WALLPAPER) != 0 + } + + private fun isValidTaskChange(change: TransitionInfo.Change): Boolean { + return change.taskInfo != null && change.taskInfo?.taskId != -1 + } + + companion object { + private const val RESIZE_DURATION_MS = 300L + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/docs/debugging.md b/libs/WindowManager/Shell/src/com/android/wm/shell/docs/debugging.md index 99922fbc2d95..f9ea1d4e2a07 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/docs/debugging.md +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/docs/debugging.md @@ -21,11 +21,17 @@ This code itself will not compile by itself, but the `protologtool` will preproc building to check the log state (is enabled) before printing the print format style log. **Notes** -- ProtoLogs currently only work from soong builds (ie. via make/mp). We need to reimplement the - tool for use with SysUI-studio +- ProtoLogs are only fully supported from soong builds (ie. via make/mp). In SysUI-studio it falls + back to log via Logcat - Non-text ProtoLogs are not currently supported with the Shell library (you can't view them with traces in Winscope) +### Kotlin + +Protolog tool does not yet have support for Kotlin code (see [b/168581922](https://b.corp.google.com/issues/168581922)). +For logging in Kotlin, use the [KtProtoLog](frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/util/KtProtoLog.kt) +class which has a similar API to the Java ProtoLog class. + ### Enabling ProtoLog command line logging Run these commands to enable protologs for both WM Core and WM Shell to print to logcat. ```shell diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionHandler.java index 04fc79acadbd..55e34fe3d836 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionHandler.java @@ -19,9 +19,15 @@ package com.android.wm.shell.freeform; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ValueAnimator; import android.app.ActivityManager; import android.app.WindowConfiguration; +import android.content.Context; +import android.graphics.Rect; import android.os.IBinder; +import android.util.ArrayMap; import android.view.SurfaceControl; import android.view.WindowManager; import android.window.TransitionInfo; @@ -31,6 +37,8 @@ import android.window.WindowContainerTransaction; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import com.android.wm.shell.common.DisplayController; +import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.sysui.ShellInit; import com.android.wm.shell.transition.Transitions; import com.android.wm.shell.windowdecor.WindowDecorViewModel; @@ -39,23 +47,37 @@ import java.util.ArrayList; import java.util.List; /** - * The {@link Transitions.TransitionHandler} that handles freeform task maximizing and restoring - * transitions. + * The {@link Transitions.TransitionHandler} that handles freeform task maximizing, closing, and + * restoring transitions. */ public class FreeformTaskTransitionHandler implements Transitions.TransitionHandler, FreeformTaskTransitionStarter { - + private static final int CLOSE_ANIM_DURATION = 400; + private final Context mContext; private final Transitions mTransitions; private final WindowDecorViewModel mWindowDecorViewModel; + private final DisplayController mDisplayController; + private final ShellExecutor mMainExecutor; + private final ShellExecutor mAnimExecutor; private final List<IBinder> mPendingTransitionTokens = new ArrayList<>(); + private final ArrayMap<IBinder, ArrayList<Animator>> mAnimations = new ArrayMap<>(); + public FreeformTaskTransitionHandler( ShellInit shellInit, Transitions transitions, - WindowDecorViewModel windowDecorViewModel) { + Context context, + WindowDecorViewModel windowDecorViewModel, + DisplayController displayController, + ShellExecutor mainExecutor, + ShellExecutor animExecutor) { mTransitions = transitions; + mContext = context; mWindowDecorViewModel = windowDecorViewModel; + mDisplayController = displayController; + mMainExecutor = mainExecutor; + mAnimExecutor = animExecutor; if (Transitions.ENABLE_SHELL_TRANSITIONS) { shellInit.addInitCallback(this::onInit, this); } @@ -103,6 +125,14 @@ public class FreeformTaskTransitionHandler @NonNull SurfaceControl.Transaction finishT, @NonNull Transitions.TransitionFinishCallback finishCallback) { boolean transitionHandled = false; + final ArrayList<Animator> animations = new ArrayList<>(); + final Runnable onAnimFinish = () -> { + if (!animations.isEmpty()) return; + mMainExecutor.execute(() -> { + mAnimations.remove(transition); + finishCallback.onTransitionFinished(null /* wct */, null /* wctCB */); + }); + }; for (TransitionInfo.Change change : info.getChanges()) { if ((change.getFlags() & TransitionInfo.FLAG_IS_WALLPAPER) != 0) { continue; @@ -121,21 +151,45 @@ public class FreeformTaskTransitionHandler case WindowManager.TRANSIT_TO_BACK: transitionHandled |= startMinimizeTransition(transition); break; + case WindowManager.TRANSIT_CLOSE: + if (change.getTaskInfo().getWindowingMode() == WINDOWING_MODE_FREEFORM) { + transitionHandled |= startCloseTransition(transition, change, + finishT, animations, onAnimFinish); + } + break; } } - - mPendingTransitionTokens.remove(transition); - if (!transitionHandled) { return false; } - + mAnimations.put(transition, animations); + // startT must be applied before animations start. startT.apply(); - mTransitions.getMainExecutor().execute( - () -> finishCallback.onTransitionFinished(null, null)); + mAnimExecutor.execute(() -> { + for (Animator anim : animations) { + anim.start(); + } + }); + // Run this here in case no animators are created. + onAnimFinish.run(); + mPendingTransitionTokens.remove(transition); return true; } + @Override + public void mergeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info, + @NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget, + @NonNull Transitions.TransitionFinishCallback finishCallback) { + ArrayList<Animator> animations = mAnimations.get(mergeTarget); + if (animations == null) return; + mAnimExecutor.execute(() -> { + for (Animator anim : animations) { + anim.end(); + } + }); + + } + private boolean startChangeTransition( IBinder transition, int type, @@ -165,6 +219,36 @@ public class FreeformTaskTransitionHandler return mPendingTransitionTokens.contains(transition); } + private boolean startCloseTransition(IBinder transition, TransitionInfo.Change change, + SurfaceControl.Transaction finishT, ArrayList<Animator> animations, + Runnable onAnimFinish) { + if (!mPendingTransitionTokens.contains(transition)) return false; + int screenHeight = mDisplayController + .getDisplayLayout(change.getTaskInfo().displayId).height(); + ValueAnimator animator = new ValueAnimator(); + animator.setDuration(CLOSE_ANIM_DURATION) + .setFloatValues(0f, 1f); + SurfaceControl.Transaction t = new SurfaceControl.Transaction(); + SurfaceControl sc = change.getLeash(); + finishT.hide(sc); + Rect startBounds = new Rect(change.getTaskInfo().configuration.windowConfiguration + .getBounds()); + animator.addUpdateListener(animation -> { + t.setPosition(sc, startBounds.left, + startBounds.top + (animation.getAnimatedFraction() * screenHeight)); + t.apply(); + }); + animator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + animations.remove(animator); + onAnimFinish.run(); + } + }); + animations.add(animator); + return true; + } + @Nullable @Override public WindowContainerTransaction handleRequest(@NonNull IBinder transition, diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java index 9729a4007bac..da455f85d908 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java @@ -33,9 +33,10 @@ import android.view.WindowManager; import androidx.annotation.NonNull; import com.android.wm.shell.R; -import com.android.wm.shell.bubbles.DismissView; -import com.android.wm.shell.common.DismissCircleView; +import com.android.wm.shell.bubbles.DismissViewUtils; import com.android.wm.shell.common.ShellExecutor; +import com.android.wm.shell.common.bubbles.DismissCircleView; +import com.android.wm.shell.common.bubbles.DismissView; import com.android.wm.shell.common.magnetictarget.MagnetizedObject; import com.android.wm.shell.pip.PipUiEventLogger; @@ -106,6 +107,7 @@ public class PipDismissTargetHandler implements ViewTreeObserver.OnPreDrawListen } mTargetViewContainer = new DismissView(mContext); + DismissViewUtils.setup(mTargetViewContainer); mTargetView = mTargetViewContainer.getCircle(); mTargetViewContainer.setOnApplyWindowInsetsListener((view, windowInsets) -> { if (!windowInsets.equals(mWindowInsets)) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java b/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java index 2a61445b27ba..b0fa9936d879 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java @@ -49,7 +49,7 @@ public enum ShellProtoLogGroup implements IProtoLogGroup { Consts.TAG_WM_SPLIT_SCREEN), WM_SHELL_SYSUI_EVENTS(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false, Consts.TAG_WM_SHELL), - WM_SHELL_DESKTOP_MODE(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false, + WM_SHELL_DESKTOP_MODE(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, true, Consts.TAG_WM_SHELL), WM_SHELL_FLOATING_APPS(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false, Consts.TAG_WM_SHELL), diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java index 89538cb394d4..e52235fda80f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java @@ -24,6 +24,9 @@ import android.window.WindowContainerTransaction; import com.android.launcher3.icons.IconProvider; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.SyncTransactionQueue; +import com.android.wm.shell.windowdecor.WindowDecorViewModel; + +import java.util.Optional; /** * Main stage for split-screen mode. When split-screen is active all standard activity types launch @@ -35,9 +38,10 @@ class MainStage extends StageTaskListener { MainStage(Context context, ShellTaskOrganizer taskOrganizer, int displayId, StageListenerCallbacks callbacks, SyncTransactionQueue syncQueue, - SurfaceSession surfaceSession, IconProvider iconProvider) { + SurfaceSession surfaceSession, IconProvider iconProvider, + Optional<WindowDecorViewModel> windowDecorViewModel) { super(context, taskOrganizer, displayId, callbacks, syncQueue, surfaceSession, - iconProvider); + iconProvider, windowDecorViewModel); } boolean isActive() { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java index 8639b36faf4c..9903113c5453 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java @@ -25,6 +25,9 @@ import android.window.WindowContainerTransaction; import com.android.launcher3.icons.IconProvider; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.SyncTransactionQueue; +import com.android.wm.shell.windowdecor.WindowDecorViewModel; + +import java.util.Optional; /** * Side stage for split-screen mode. Only tasks that are explicitly pinned to this stage show up @@ -37,9 +40,10 @@ class SideStage extends StageTaskListener { SideStage(Context context, ShellTaskOrganizer taskOrganizer, int displayId, StageListenerCallbacks callbacks, SyncTransactionQueue syncQueue, - SurfaceSession surfaceSession, IconProvider iconProvider) { + SurfaceSession surfaceSession, IconProvider iconProvider, + Optional<WindowDecorViewModel> windowDecorViewModel) { super(context, taskOrganizer, displayId, callbacks, syncQueue, surfaceSession, - iconProvider); + iconProvider, windowDecorViewModel); } boolean removeAllTasks(WindowContainerTransaction wct, boolean toTop) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java index e7a367f1992d..e294229038f2 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java @@ -76,6 +76,7 @@ import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.DisplayImeController; import com.android.wm.shell.common.DisplayInsetsController; import com.android.wm.shell.common.ExternalInterfaceBinder; +import com.android.wm.shell.common.LaunchAdjacentController; import com.android.wm.shell.common.RemoteCallable; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.SingleInstanceRemoteListener; @@ -94,6 +95,7 @@ import com.android.wm.shell.sysui.ShellCommandHandler; import com.android.wm.shell.sysui.ShellController; import com.android.wm.shell.sysui.ShellInit; import com.android.wm.shell.transition.Transitions; +import com.android.wm.shell.windowdecor.WindowDecorViewModel; import java.io.PrintWriter; import java.lang.annotation.Retention; @@ -171,6 +173,8 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, private final TransactionPool mTransactionPool; private final IconProvider mIconProvider; private final Optional<RecentTasksController> mRecentTasksOptional; + private final LaunchAdjacentController mLaunchAdjacentController; + private final Optional<WindowDecorViewModel> mWindowDecorViewModel; private final SplitScreenShellCommandHandler mSplitScreenShellCommandHandler; private final String[] mAppsSupportMultiInstances; @@ -197,6 +201,8 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, TransactionPool transactionPool, IconProvider iconProvider, Optional<RecentTasksController> recentTasks, + LaunchAdjacentController launchAdjacentController, + Optional<WindowDecorViewModel> windowDecorViewModel, ShellExecutor mainExecutor) { mShellCommandHandler = shellCommandHandler; mShellController = shellController; @@ -213,6 +219,8 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, mTransactionPool = transactionPool; mIconProvider = iconProvider; mRecentTasksOptional = recentTasks; + mLaunchAdjacentController = launchAdjacentController; + mWindowDecorViewModel = windowDecorViewModel; mSplitScreenShellCommandHandler = new SplitScreenShellCommandHandler(this); // TODO(b/238217847): Temporarily add this check here until we can remove the dynamic // override for this controller from the base module @@ -242,6 +250,8 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, TransactionPool transactionPool, IconProvider iconProvider, RecentTasksController recentTasks, + LaunchAdjacentController launchAdjacentController, + WindowDecorViewModel windowDecorViewModel, ShellExecutor mainExecutor, StageCoordinator stageCoordinator) { mShellCommandHandler = shellCommandHandler; @@ -259,6 +269,8 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, mTransactionPool = transactionPool; mIconProvider = iconProvider; mRecentTasksOptional = Optional.of(recentTasks); + mLaunchAdjacentController = launchAdjacentController; + mWindowDecorViewModel = Optional.of(windowDecorViewModel); mStageCoordinator = stageCoordinator; mSplitScreenShellCommandHandler = new SplitScreenShellCommandHandler(this); shellInit.addInitCallback(this::onInit, this); @@ -291,13 +303,15 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, mStageCoordinator = createStageCoordinator(); } mDragAndDropController.ifPresent(controller -> controller.setSplitScreenController(this)); + mWindowDecorViewModel.ifPresent(viewModel -> viewModel.setSplitScreenController(this)); } protected StageCoordinator createStageCoordinator() { return new StageCoordinator(mContext, DEFAULT_DISPLAY, mSyncQueue, mTaskOrganizer, mDisplayController, mDisplayImeController, - mDisplayInsetsController, mTransitions, mTransactionPool, - mIconProvider, mMainExecutor, mRecentTasksOptional); + mDisplayInsetsController, mTransitions, mTransactionPool, mIconProvider, + mMainExecutor, mRecentTasksOptional, mLaunchAdjacentController, + mWindowDecorViewModel); } @Override diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java index 964ba9f9aa7c..6e6e147a234b 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java @@ -121,6 +121,7 @@ import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.DisplayImeController; import com.android.wm.shell.common.DisplayInsetsController; +import com.android.wm.shell.common.LaunchAdjacentController; import com.android.wm.shell.common.ScreenshotUtils; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.SyncTransactionQueue; @@ -138,6 +139,7 @@ import com.android.wm.shell.transition.LegacyTransitions; import com.android.wm.shell.transition.Transitions; import com.android.wm.shell.util.SplitBounds; import com.android.wm.shell.util.TransitionUtil; +import com.android.wm.shell.windowdecor.WindowDecorViewModel; import java.io.PrintWriter; import java.util.ArrayList; @@ -193,6 +195,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, // if user is opening another task(s). private final ArrayList<Integer> mPausingTasks = new ArrayList<>(); private final Optional<RecentTasksController> mRecentTasks; + private final LaunchAdjacentController mLaunchAdjacentController; + private final Optional<WindowDecorViewModel> mWindowDecorViewModel; private final Rect mTempRect1 = new Rect(); private final Rect mTempRect2 = new Rect(); @@ -270,7 +274,9 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, DisplayInsetsController displayInsetsController, Transitions transitions, TransactionPool transactionPool, IconProvider iconProvider, ShellExecutor mainExecutor, - Optional<RecentTasksController> recentTasks) { + Optional<RecentTasksController> recentTasks, + LaunchAdjacentController launchAdjacentController, + Optional<WindowDecorViewModel> windowDecorViewModel) { mContext = context; mDisplayId = displayId; mSyncQueue = syncQueue; @@ -278,6 +284,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, mLogger = new SplitscreenEventLogger(); mMainExecutor = mainExecutor; mRecentTasks = recentTasks; + mLaunchAdjacentController = launchAdjacentController; + mWindowDecorViewModel = windowDecorViewModel; taskOrganizer.createRootTask(displayId, WINDOWING_MODE_FULLSCREEN, this /* listener */); @@ -288,7 +296,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, mMainStageListener, mSyncQueue, mSurfaceSession, - iconProvider); + iconProvider, + mWindowDecorViewModel); mSideStage = new SideStage( mContext, mTaskOrganizer, @@ -296,7 +305,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, mSideStageListener, mSyncQueue, mSurfaceSession, - iconProvider); + iconProvider, + mWindowDecorViewModel); mDisplayController = displayController; mDisplayImeController = displayImeController; mDisplayInsetsController = displayInsetsController; @@ -323,7 +333,9 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, DisplayInsetsController displayInsetsController, SplitLayout splitLayout, Transitions transitions, TransactionPool transactionPool, ShellExecutor mainExecutor, - Optional<RecentTasksController> recentTasks) { + Optional<RecentTasksController> recentTasks, + LaunchAdjacentController launchAdjacentController, + Optional<WindowDecorViewModel> windowDecorViewModel) { mContext = context; mDisplayId = displayId; mSyncQueue = syncQueue; @@ -340,6 +352,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, mLogger = new SplitscreenEventLogger(); mMainExecutor = mainExecutor; mRecentTasks = recentTasks; + mLaunchAdjacentController = launchAdjacentController; + mWindowDecorViewModel = windowDecorViewModel; mDisplayController.addDisplayWindowListener(this); transitions.addHandler(this); mSplitUnsupportedToast = Toast.makeText(mContext, @@ -1733,7 +1747,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, if (mRootTaskInfo == null || mRootTaskInfo.taskId != taskInfo.taskId) { throw new IllegalArgumentException(this + "\n Unknown task info changed: " + taskInfo); } - + mWindowDecorViewModel.ifPresent(viewModel -> viewModel.onTaskInfoChanged(taskInfo)); mRootTaskInfo = taskInfo; if (mSplitLayout != null && mSplitLayout.updateConfiguration(mRootTaskInfo.configuration) @@ -1781,7 +1795,6 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, wct.reparent(mSideStage.mRootTaskInfo.token, mRootTaskInfo.token, true); // Make the stages adjacent to each other so they occlude what's behind them. wct.setAdjacentRoots(mMainStage.mRootTaskInfo.token, mSideStage.mRootTaskInfo.token); - wct.setLaunchAdjacentFlagRoot(mSideStage.mRootTaskInfo.token); setRootForceTranslucent(true, wct); mSplitLayout.getInvisibleBounds(mTempRect1); wct.setBounds(mSideStage.mRootTaskInfo.token, mTempRect1); @@ -1789,6 +1802,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, mSyncQueue.runInSync(t -> { t.setPosition(mSideStage.mRootLeash, mTempRect1.left, mTempRect1.top); }); + mLaunchAdjacentController.setLaunchAdjacentRoot(mSideStage.mRootTaskInfo.token); } /** Callback when split roots have child task appeared under it, this is a little different from @@ -1818,9 +1832,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, private void onRootTaskVanished() { final WindowContainerTransaction wct = new WindowContainerTransaction(); - if (mRootTaskInfo != null) { - wct.clearLaunchAdjacentFlagRoot(mRootTaskInfo.token); - } + mLaunchAdjacentController.clearLaunchAdjacentRoot(); applyExitSplitScreen(null /* childrenToTop */, wct, EXIT_REASON_ROOT_TASK_VANISHED); mDisplayInsetsController.removeInsetsChangedListener(mDisplayId, mSplitLayout); } @@ -2441,6 +2453,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, mSplitLayout.setFreezeDividerWindow(false); final StageChangeRecord record = new StageChangeRecord(); + final int transitType = info.getType(); + boolean hasEnteringPip = false; for (int iC = 0; iC < info.getChanges().size(); ++iC) { final TransitionInfo.Change change = info.getChanges().get(iC); if (change.getMode() == TRANSIT_CHANGE @@ -2448,6 +2462,10 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, mSplitLayout.update(startTransaction); } + if (mMixedHandler.isEnteringPip(change, transitType)) { + hasEnteringPip = true; + } + final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo(); if (taskInfo == null) continue; if (taskInfo.token.equals(mRootTaskInfo.token)) { @@ -2496,6 +2514,13 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, } } } + + if (hasEnteringPip) { + mMixedHandler.animatePendingEnterPipFromSplit(transition, info, + startTransaction, finishTransaction, finishCallback); + return true; + } + final ArraySet<StageTaskListener> dismissStages = record.getShouldDismissedStage(); if (mMainStage.getChildCount() == 0 || mSideStage.getChildCount() == 0 || dismissStages.size() == 1) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java index a01eddbc9b9f..3ef4f024a8ea 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java @@ -51,8 +51,10 @@ import com.android.wm.shell.common.SurfaceUtils; import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.common.split.SplitDecorManager; import com.android.wm.shell.splitscreen.SplitScreen.StageType; +import com.android.wm.shell.windowdecor.WindowDecorViewModel; import java.io.PrintWriter; +import java.util.Optional; import java.util.function.Predicate; /** @@ -87,6 +89,7 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener { private final SurfaceSession mSurfaceSession; private final SyncTransactionQueue mSyncQueue; private final IconProvider mIconProvider; + private final Optional<WindowDecorViewModel> mWindowDecorViewModel; protected ActivityManager.RunningTaskInfo mRootTaskInfo; protected SurfaceControl mRootLeash; @@ -98,12 +101,14 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener { StageTaskListener(Context context, ShellTaskOrganizer taskOrganizer, int displayId, StageListenerCallbacks callbacks, SyncTransactionQueue syncQueue, - SurfaceSession surfaceSession, IconProvider iconProvider) { + SurfaceSession surfaceSession, IconProvider iconProvider, + Optional<WindowDecorViewModel> windowDecorViewModel) { mContext = context; mCallbacks = callbacks; mSyncQueue = syncQueue; mSurfaceSession = surfaceSession; mIconProvider = iconProvider; + mWindowDecorViewModel = windowDecorViewModel; taskOrganizer.createRootTask(displayId, WINDOWING_MODE_MULTI_WINDOW, this); } @@ -202,6 +207,7 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener { @Override @CallSuper public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) { + mWindowDecorViewModel.ifPresent(viewModel -> viewModel.onTaskInfoChanged(taskInfo)); if (mRootTaskInfo.taskId == taskInfo.taskId) { // Inflates split decor view only when the root task is visible. if (!ENABLE_SHELL_TRANSITIONS && mRootTaskInfo.isVisible != taskInfo.isVisible) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitScreenController.java index 27d520d81c41..a2301b133426 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitScreenController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitScreenController.java @@ -27,6 +27,7 @@ import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.DisplayImeController; import com.android.wm.shell.common.DisplayInsetsController; +import com.android.wm.shell.common.LaunchAdjacentController; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.common.SystemWindows; @@ -58,6 +59,7 @@ public class TvSplitScreenController extends SplitScreenController { private final TransactionPool mTransactionPool; private final IconProvider mIconProvider; private final Optional<RecentTasksController> mRecentTasksOptional; + private final LaunchAdjacentController mLaunchAdjacentController; private final Handler mMainHandler; private final SystemWindows mSystemWindows; @@ -77,13 +79,15 @@ public class TvSplitScreenController extends SplitScreenController { TransactionPool transactionPool, IconProvider iconProvider, Optional<RecentTasksController> recentTasks, + LaunchAdjacentController launchAdjacentController, ShellExecutor mainExecutor, Handler mainHandler, SystemWindows systemWindows) { super(context, shellInit, shellCommandHandler, shellController, shellTaskOrganizer, syncQueue, rootTDAOrganizer, displayController, displayImeController, displayInsetsController, dragAndDropController, transitions, transactionPool, - iconProvider, recentTasks, mainExecutor); + iconProvider, recentTasks, launchAdjacentController, Optional.empty(), + mainExecutor); mTaskOrganizer = shellTaskOrganizer; mSyncQueue = syncQueue; @@ -96,6 +100,7 @@ public class TvSplitScreenController extends SplitScreenController { mTransactionPool = transactionPool; mIconProvider = iconProvider; mRecentTasksOptional = recentTasks; + mLaunchAdjacentController = launchAdjacentController; mMainHandler = mainHandler; mSystemWindows = systemWindows; @@ -111,7 +116,7 @@ public class TvSplitScreenController extends SplitScreenController { mTaskOrganizer, mDisplayController, mDisplayImeController, mDisplayInsetsController, mTransitions, mTransactionPool, mIconProvider, mMainExecutor, mMainHandler, - mRecentTasksOptional, mSystemWindows); + mRecentTasksOptional, mLaunchAdjacentController, mSystemWindows); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvStageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvStageCoordinator.java index 4d563fbb7f04..79476919221e 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvStageCoordinator.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvStageCoordinator.java @@ -24,6 +24,7 @@ import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.DisplayImeController; import com.android.wm.shell.common.DisplayInsetsController; +import com.android.wm.shell.common.LaunchAdjacentController; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.common.SystemWindows; @@ -51,10 +52,11 @@ public class TvStageCoordinator extends StageCoordinator IconProvider iconProvider, ShellExecutor mainExecutor, Handler mainHandler, Optional<RecentTasksController> recentTasks, + LaunchAdjacentController launchAdjacentController, SystemWindows systemWindows) { super(context, displayId, syncQueue, taskOrganizer, displayController, displayImeController, displayInsetsController, transitions, transactionPool, iconProvider, - mainExecutor, recentTasks); + mainExecutor, recentTasks, launchAdjacentController, Optional.empty()); mTvSplitMenuController = new TvSplitMenuController(context, this, systemWindows, mainHandler); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java index e83780a6fc77..a28ce55e8c94 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java @@ -43,6 +43,9 @@ import android.window.WindowContainerTransactionCallback; import com.android.internal.protolog.common.ProtoLog; import com.android.wm.shell.common.split.SplitScreenUtils; +import com.android.wm.shell.desktopmode.DesktopModeController; +import com.android.wm.shell.desktopmode.DesktopModeStatus; +import com.android.wm.shell.desktopmode.DesktopTasksController; import com.android.wm.shell.keyguard.KeyguardTransitionHandler; import com.android.wm.shell.pip.PipTransitionController; import com.android.wm.shell.pip.phone.PipTouchHandler; @@ -69,6 +72,8 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, private RecentsTransitionHandler mRecentsHandler; private StageCoordinator mSplitHandler; private final KeyguardTransitionHandler mKeyguardHandler; + private DesktopModeController mDesktopModeController; + private DesktopTasksController mDesktopTasksController; private UnfoldTransitionHandler mUnfoldHandler; private static class MixedTransition { @@ -86,8 +91,11 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, /** Keyguard exit/occlude/unocclude transition. */ static final int TYPE_KEYGUARD = 5; + /** Recents Transition while in desktop mode. */ + static final int TYPE_RECENTS_DURING_DESKTOP = 6; + /** Fuld/Unfold transition. */ - static final int TYPE_UNFOLD = 6; + static final int TYPE_UNFOLD = 7; /** The default animation for this mixed transition. */ static final int ANIM_TYPE_DEFAULT = 0; @@ -141,6 +149,8 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, Optional<PipTouchHandler> pipTouchHandlerOptional, Optional<RecentsTransitionHandler> recentsHandlerOptional, KeyguardTransitionHandler keyguardHandler, + Optional<DesktopModeController> desktopModeControllerOptional, + Optional<DesktopTasksController> desktopTasksControllerOptional, Optional<UnfoldTransitionHandler> unfoldHandler) { mPlayer = player; mKeyguardHandler = keyguardHandler; @@ -158,6 +168,8 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, if (mRecentsHandler != null) { mRecentsHandler.addMixer(this); } + mDesktopModeController = desktopModeControllerOptional.orElse(null); + mDesktopTasksController = desktopTasksControllerOptional.orElse(null); mUnfoldHandler = unfoldHandler.orElse(null); }, this); } @@ -238,7 +250,8 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, @Override public Transitions.TransitionHandler handleRecentsRequest(WindowContainerTransaction outWCT) { - if (mRecentsHandler != null && mSplitHandler.isSplitScreenVisible()) { + if (mRecentsHandler != null && (mSplitHandler.isSplitScreenVisible() + || DesktopModeStatus.isActive(mPlayer.getContext()))) { return this; } return null; @@ -253,6 +266,13 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, MixedTransition.TYPE_RECENTS_DURING_SPLIT, transition); mixed.mLeftoversHandler = mRecentsHandler; mActiveTransitions.add(mixed); + } else if (DesktopModeStatus.isActive(mPlayer.getContext())) { + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Got a recents request while " + + "desktop mode is active, so treat it as Mixed."); + final MixedTransition mixed = new MixedTransition( + MixedTransition.TYPE_RECENTS_DURING_DESKTOP, transition); + mixed.mLeftoversHandler = mRecentsHandler; + mActiveTransitions.add(mixed); } else { throw new IllegalStateException("Accepted a recents transition but don't know how to" + " handle it"); @@ -339,6 +359,9 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, } else if (mixed.mType == MixedTransition.TYPE_KEYGUARD) { return animateKeyguard(mixed, info, startTransaction, finishTransaction, finishCallback); + } else if (mixed.mType == MixedTransition.TYPE_RECENTS_DURING_DESKTOP) { + return animateRecentsDuringDesktop(mixed, info, startTransaction, finishTransaction, + finishCallback); } else if (mixed.mType == MixedTransition.TYPE_UNFOLD) { return animateUnfold(mixed, info, startTransaction, finishTransaction, finishCallback); } else { @@ -447,7 +470,8 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, } finishCallback.onTransitionFinished(mixed.mFinishWCT, wctCB); }; - if (isGoingHome) { + if (isGoingHome || mSplitHandler.getSplitItemPosition(pipChange.getLastParent()) + != SPLIT_POSITION_UNDEFINED) { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Animation is actually mixed " + "since entering-PiP caused us to leave split and return home."); // We need to split the transition into 2 parts: the pip part (animated by pip) @@ -516,10 +540,27 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, } /** + * This is intended to be called by SplitCoordinator as a helper to mix a split handling + * transition with an entering-pip change. The use-case for this is when an auto-pip change + * gets collected into the transition which has already claimed by + * StageCoordinator.handleRequest. This happens when launching a fullscreen app while having an + * auto-pip activity in the foreground split pair. + */ + // TODO(b/287704263): Remove when split/mixed are reversed. + public boolean animatePendingEnterPipFromSplit(IBinder transition, TransitionInfo info, + SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT, + Transitions.TransitionFinishCallback finishCallback) { + final MixedTransition mixed = new MixedTransition( + MixedTransition.TYPE_ENTER_PIP_FROM_SPLIT, transition); + mActiveTransitions.add(mixed); + return animateEnterPipFromSplit(mixed, info, startT, finishT, finishCallback); + } + + /** * This is intended to be called by SplitCoordinator as a helper to mix an already-pending * split transition with a display-change. The use-case for this is when a display * change/rotation gets collected into a split-screen enter/exit transition which has already - * been claimed by StageCoordinator.handleRequest . This happens during launcher tests. + * been claimed by StageCoordinator.handleRequest. This happens during launcher tests. */ public boolean animatePendingSplitWithDisplayChange(@NonNull IBinder transition, @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction startT, @@ -621,6 +662,30 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, return true; } + private boolean animateRecentsDuringDesktop(@NonNull final MixedTransition mixed, + @NonNull TransitionInfo info, + @NonNull SurfaceControl.Transaction startTransaction, + @NonNull SurfaceControl.Transaction finishTransaction, + @NonNull Transitions.TransitionFinishCallback finishCallback) { + boolean consumed = mRecentsHandler.startAnimation( + mixed.mTransition, info, startTransaction, finishTransaction, finishCallback); + if (!consumed) { + return false; + } + //Sync desktop mode state (proto 1) + if (mDesktopModeController != null) { + mDesktopModeController.syncSurfaceState(info, finishTransaction); + return true; + } + //Sync desktop mode state (proto 2) + if (mDesktopTasksController != null) { + mDesktopTasksController.syncSurfaceState(info, finishTransaction); + return true; + } + + return false; + } + private boolean animateUnfold(@NonNull final MixedTransition mixed, @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction startTransaction, @@ -657,6 +722,13 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, return mPipHandler.requestHasPipEnter(request); } + /** Whether a particular change is a window that is entering pip. */ + // TODO(b/287704263): Remove when split/mixed are reversed. + public boolean isEnteringPip(TransitionInfo.Change change, + @WindowManager.TransitionType int transitType) { + return mPipHandler.isEnteringPip(change, transitType); + } + @Override public void mergeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget, @@ -700,6 +772,9 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, finishCallback); } else if (mixed.mType == MixedTransition.TYPE_KEYGUARD) { mKeyguardHandler.mergeAnimation(transition, info, t, mergeTarget, finishCallback); + } else if (mixed.mType == MixedTransition.TYPE_RECENTS_DURING_DESKTOP) { + mixed.mLeftoversHandler.mergeAnimation(transition, info, t, mergeTarget, + finishCallback); } else if (mixed.mType == MixedTransition.TYPE_UNFOLD) { mUnfoldHandler.mergeAnimation(transition, info, t, mergeTarget, finishCallback); } else { @@ -727,6 +802,8 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, mixed.mLeftoversHandler.onTransitionConsumed(transition, aborted, finishT); } else if (mixed.mType == MixedTransition.TYPE_KEYGUARD) { mKeyguardHandler.onTransitionConsumed(transition, aborted, finishT); + } else if (mixed.mType == MixedTransition.TYPE_RECENTS_DURING_DESKTOP) { + mixed.mLeftoversHandler.onTransitionConsumed(transition, aborted, finishT); } else if (mixed.mType == MixedTransition.TYPE_UNFOLD) { mUnfoldHandler.onTransitionConsumed(transition, aborted, finishT); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java index 2327d86ab618..d8a88770072d 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java @@ -161,6 +161,10 @@ public class Transitions implements RemoteCallable<Transitions>, public static final int TRANSIT_CANCEL_ENTERING_DESKTOP_MODE = WindowManager.TRANSIT_FIRST_CUSTOM + 13; + /** Transition type to animate the toggle resize between the max and default desktop sizes. */ + public static final int TRANSIT_DESKTOP_MODE_TOGGLE_RESIZE = + WindowManager.TRANSIT_FIRST_CUSTOM + 14; + private final WindowOrganizer mOrganizer; private final Context mContext; private final ShellExecutor mMainExecutor; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/animation/FullscreenUnfoldTaskAnimator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/animation/FullscreenUnfoldTaskAnimator.java index f81fc6fbea49..6bba0d1386fb 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/animation/FullscreenUnfoldTaskAnimator.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/animation/FullscreenUnfoldTaskAnimator.java @@ -110,7 +110,7 @@ public class FullscreenUnfoldTaskAnimator implements UnfoldTaskAnimator, for (int i = state.sourceSize() - 1; i >= 0; i--) { final InsetsSource source = state.sourceAt(i); if (source.getType() == WindowInsets.Type.navigationBars() - && source.insetsRoundedCornerFrame()) { + && source.hasFlags(InsetsSource.FLAG_INSETS_ROUNDED_CORNER)) { return source; } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/animation/SplitTaskUnfoldAnimator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/animation/SplitTaskUnfoldAnimator.java index a4cf149cc3b5..21994a997be5 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/animation/SplitTaskUnfoldAnimator.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/animation/SplitTaskUnfoldAnimator.java @@ -50,11 +50,11 @@ import com.android.wm.shell.sysui.ShellController; import com.android.wm.shell.unfold.UnfoldAnimationController; import com.android.wm.shell.unfold.UnfoldBackgroundController; +import dagger.Lazy; + import java.util.Optional; import java.util.concurrent.Executor; -import dagger.Lazy; - /** * This helper class contains logic that calculates scaling and cropping parameters * for the folding/unfolding animation. As an input it receives TaskInfo objects and @@ -149,7 +149,7 @@ public class SplitTaskUnfoldAnimator implements UnfoldTaskAnimator, for (int i = state.sourceSize() - 1; i >= 0; i--) { final InsetsSource source = state.sourceAt(i); if (source.getType() == WindowInsets.Type.navigationBars() - && source.insetsRoundedCornerFrame()) { + && source.hasFlags(InsetsSource.FLAG_INSETS_ROUNDED_CORNER)) { return source; } } 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 39fb7936747e..92c2a7c03ee4 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 @@ -33,11 +33,14 @@ import android.window.TransitionInfo; import android.window.WindowContainerToken; import android.window.WindowContainerTransaction; +import androidx.annotation.Nullable; + import com.android.wm.shell.R; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.freeform.FreeformTaskTransitionStarter; +import com.android.wm.shell.splitscreen.SplitScreenController; import com.android.wm.shell.transition.Transitions; /** @@ -89,6 +92,9 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel { } @Override + public void setSplitScreenController(SplitScreenController splitScreenController) {} + + @Override public boolean onTaskOpening( RunningTaskInfo taskInfo, SurfaceControl taskSurface, @@ -254,7 +260,7 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel { * @return {@code true} if a drag is happening; or {@code false} if it is not */ @Override - public boolean handleMotionEvent(MotionEvent e) { + public boolean handleMotionEvent(@Nullable View v, MotionEvent e) { final RunningTaskInfo taskInfo = mTaskOrganizer.getRunningTaskInfo(mTaskId); if (taskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN) { return false; @@ -268,7 +274,10 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel { return false; } case MotionEvent.ACTION_MOVE: { - int dragPointerIdx = e.findPointerIndex(mDragPointerId); + if (e.findPointerIndex(mDragPointerId) == -1) { + mDragPointerId = e.getPointerId(0); + } + final int dragPointerIdx = e.findPointerIndex(mDragPointerId); mDragPositioningCallback.onDragPositioningMove( e.getRawX(dragPointerIdx), e.getRawY(dragPointerIdx)); mIsDragging = true; @@ -276,7 +285,10 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel { } case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: { - int dragPointerIdx = e.findPointerIndex(mDragPointerId); + if (e.findPointerIndex(mDragPointerId) == -1) { + mDragPointerId = e.getPointerId(0); + } + final int dragPointerIdx = e.findPointerIndex(mDragPointerId); mDragPositioningCallback.onDragPositioningEnd( e.getRawX(dragPointerIdx), e.getRawY(dragPointerIdx)); final boolean wasDragging = mIsDragging; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java index 116af7094e13..b217bd39a446 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java @@ -113,7 +113,7 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL mRelayoutParams.reset(); mRelayoutParams.mRunningTaskInfo = taskInfo; mRelayoutParams.mLayoutResId = R.layout.caption_window_decor; - mRelayoutParams.mCaptionHeightId = R.dimen.freeform_decor_caption_height; + mRelayoutParams.mCaptionHeightId = getCaptionHeightId(); mRelayoutParams.mShadowRadiusId = shadowRadiusID; mRelayoutParams.mApplyStartTransactionOnDraw = applyStartTransactionOnDraw; @@ -143,6 +143,7 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL mHandler, mChoreographer, mDisplay.getDisplayId(), + 0 /* taskCornerRadius */, mDecorationContainerSurface, mDragPositioningCallback); } @@ -221,4 +222,9 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL closeDragResizeListener(); super.close(); } + + @Override + int getCaptionHeightId() { + return R.dimen.freeform_decor_caption_height; + } } 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 9fd57d7e1201..331835cca142 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java @@ -19,6 +19,8 @@ package com.android.wm.shell.windowdecor; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; +import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; +import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT; import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT; @@ -42,6 +44,7 @@ import android.os.IBinder; import android.os.Looper; import android.util.SparseArray; import android.view.Choreographer; +import android.view.GestureDetector; import android.view.InputChannel; import android.view.InputEvent; import android.view.InputEventReceiver; @@ -53,6 +56,7 @@ import android.view.View; import android.view.WindowManager; import android.window.TransitionInfo; import android.window.WindowContainerToken; +import android.window.WindowContainerTransaction; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -106,7 +110,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { private final Supplier<SurfaceControl.Transaction> mTransactionFactory; private final Transitions mTransitions; - private Optional<SplitScreenController> mSplitScreenController; + private SplitScreenController mSplitScreenController; private ValueAnimator mDragToDesktopValueAnimator; private final Rect mDragToDesktopAnimationStartBounds = new Rect(); @@ -121,8 +125,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { SyncTransactionQueue syncQueue, Transitions transitions, Optional<DesktopModeController> desktopModeController, - Optional<DesktopTasksController> desktopTasksController, - Optional<SplitScreenController> splitScreenController) { + Optional<DesktopTasksController> desktopTasksController) { this( context, mainHandler, @@ -133,7 +136,6 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { transitions, desktopModeController, desktopTasksController, - splitScreenController, new DesktopModeWindowDecoration.Factory(), new InputMonitorFactory(), SurfaceControl.Transaction::new); @@ -150,7 +152,6 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { Transitions transitions, Optional<DesktopModeController> desktopModeController, Optional<DesktopTasksController> desktopTasksController, - Optional<SplitScreenController> splitScreenController, DesktopModeWindowDecoration.Factory desktopModeWindowDecorFactory, InputMonitorFactory inputMonitorFactory, Supplier<SurfaceControl.Transaction> transactionFactory) { @@ -160,7 +161,6 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { mActivityTaskManager = mContext.getSystemService(ActivityTaskManager.class); mTaskOrganizer = taskOrganizer; mDisplayController = displayController; - mSplitScreenController = splitScreenController; mSyncQueue = syncQueue; mTransitions = transitions; mDesktopModeController = desktopModeController; @@ -177,6 +177,11 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { } @Override + public void setSplitScreenController(SplitScreenController splitScreenController) { + mSplitScreenController = splitScreenController; + } + + @Override public boolean onTaskOpening( ActivityManager.RunningTaskInfo taskInfo, SurfaceControl taskSurface, @@ -193,7 +198,10 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { @NonNull TransitionInfo info, @NonNull TransitionInfo.Change change) { if (change.getMode() == WindowManager.TRANSIT_CHANGE - && (info.getType() == Transitions.TRANSIT_ENTER_DESKTOP_MODE)) { + && (info.getType() == Transitions.TRANSIT_ENTER_DESKTOP_MODE + || info.getType() == Transitions.TRANSIT_CANCEL_ENTERING_DESKTOP_MODE + || info.getType() == Transitions.TRANSIT_EXIT_DESKTOP_MODE + || info.getType() == Transitions.TRANSIT_DESKTOP_MODE_TOGGLE_RESIZE)) { mWindowDecorByTaskId.get(change.getTaskInfo().taskId) .addTransitionPausingRelayout(transition); } @@ -236,7 +244,6 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT) { final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.get(taskInfo.taskId); - if (!shouldShowWindowDecor(taskInfo)) { if (decoration != null) { destroyWindowDecoration(taskInfo); @@ -275,15 +282,17 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { } } - private class DesktopModeTouchEventListener implements - View.OnClickListener, View.OnTouchListener, DragDetector.MotionEventHandler { + private class DesktopModeTouchEventListener extends GestureDetector.SimpleOnGestureListener + implements View.OnClickListener, View.OnTouchListener, DragDetector.MotionEventHandler { private final int mTaskId; private final WindowContainerToken mTaskToken; private final DragPositioningCallback mDragPositioningCallback; private final DragDetector mDragDetector; + private final GestureDetector mGestureDetector; private boolean mIsDragging; + private boolean mShouldClick; private int mDragPointerId = -1; private DesktopModeTouchEventListener( @@ -293,6 +302,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { mTaskToken = taskInfo.token; mDragPositioningCallback = dragPositioningCallback; mDragDetector = new DragDetector(this); + mGestureDetector = new GestureDetector(mContext, this); } @Override @@ -301,14 +311,14 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { final int id = v.getId(); if (id == R.id.close_window || id == R.id.close_button) { mTaskOperations.closeTask(mTaskToken); - if (mSplitScreenController.isPresent() - && mSplitScreenController.get().isSplitScreenVisible()) { - int remainingTaskPosition = mTaskId == mSplitScreenController.get() + if (mSplitScreenController != null + && mSplitScreenController.isSplitScreenVisible()) { + int remainingTaskPosition = mTaskId == mSplitScreenController .getTaskInfo(SPLIT_POSITION_TOP_OR_LEFT).taskId ? SPLIT_POSITION_BOTTOM_OR_RIGHT : SPLIT_POSITION_TOP_OR_LEFT; - ActivityManager.RunningTaskInfo remainingTask = mSplitScreenController.get() + ActivityManager.RunningTaskInfo remainingTask = mSplitScreenController .getTaskInfo(remainingTaskPosition); - mSplitScreenController.get().moveTaskToFullscreen(remainingTask.taskId); + mSplitScreenController.moveTaskToFullscreen(remainingTask.taskId); } } else if (id == R.id.back_button) { mTaskOperations.injectBackKey(); @@ -321,7 +331,13 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { } } else if (id == R.id.desktop_button) { mDesktopModeController.ifPresent(c -> c.setDesktopModeActive(true)); - mDesktopTasksController.ifPresent(c -> c.moveToDesktop(mTaskId)); + if (mDesktopTasksController.isPresent()) { + final WindowContainerTransaction wct = new WindowContainerTransaction(); + // App sometimes draws before the insets from WindowDecoration#relayout have + // been added, so they must be added here + mWindowDecorByTaskId.get(mTaskId).addCaptionInset(wct); + mDesktopTasksController.get().moveToDesktop(mTaskId, wct); + } decoration.closeHandleMenu(); } else if (id == R.id.fullscreen_button) { mDesktopModeController.ifPresent(c -> c.setDesktopModeActive(false)); @@ -347,7 +363,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { return false; } moveTaskToFront(mTaskOrganizer.getRunningTaskInfo(mTaskId)); - return mDragDetector.onMotionEvent(e); + return mDragDetector.onMotionEvent(v, e); } private void moveTaskToFront(RunningTaskInfo taskInfo) { @@ -362,7 +378,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { * @return {@code true} if the motion event is handled. */ @Override - public boolean handleMotionEvent(MotionEvent e) { + public boolean handleMotionEvent(@Nullable View v, MotionEvent e) { final RunningTaskInfo taskInfo = mTaskOrganizer.getRunningTaskInfo(mTaskId); if (DesktopModeStatus.isProto2Enabled() && taskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN) { @@ -373,6 +389,9 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { == WINDOWING_MODE_FULLSCREEN) { return false; } + if (mGestureDetector.onTouchEvent(e)) { + return true; + } switch (e.getActionMasked()) { case MotionEvent.ACTION_DOWN: { mDragPointerId = e.getPointerId(0); @@ -380,21 +399,38 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { 0 /* ctrlType */, e.getRawX(0), e.getRawY(0)); mIsDragging = false; - return false; + mShouldClick = true; + return true; } case MotionEvent.ACTION_MOVE: { final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.get(mTaskId); + if (e.findPointerIndex(mDragPointerId) == -1) { + mDragPointerId = e.getPointerId(0); + } final int dragPointerIdx = e.findPointerIndex(mDragPointerId); mDesktopTasksController.ifPresent(c -> c.onDragPositioningMove(taskInfo, decoration.mTaskSurface, e.getRawY(dragPointerIdx))); mDragPositioningCallback.onDragPositioningMove( e.getRawX(dragPointerIdx), e.getRawY(dragPointerIdx)); mIsDragging = true; + mShouldClick = false; return true; } case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: { + final boolean wasDragging = mIsDragging; + if (!wasDragging) { + if (mShouldClick && v != null) { + v.performClick(); + mShouldClick = false; + return true; + } + return false; + } + if (e.findPointerIndex(mDragPointerId) == -1) { + mDragPointerId = e.getPointerId(0); + } final int dragPointerIdx = e.findPointerIndex(mDragPointerId); // Position of the task is calculated by subtracting the raw location of the // motion event (the location of the motion relative to the display) by the @@ -405,14 +441,22 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { mDragPositioningCallback.onDragPositioningEnd( e.getRawX(dragPointerIdx), e.getRawY(dragPointerIdx)); mDesktopTasksController.ifPresent(c -> c.onDragPositioningEnd(taskInfo, - position)); - final boolean wasDragging = mIsDragging; + position, e.getRawY(), mWindowDecorByTaskId.get(mTaskId))); mIsDragging = false; - return wasDragging; + return true; } } return true; } + + @Override + public boolean onDoubleTap(@NonNull MotionEvent e) { + final RunningTaskInfo taskInfo = mTaskOrganizer.getRunningTaskInfo(mTaskId); + mDesktopTasksController.ifPresent(c -> { + c.toggleDesktopTaskSize(taskInfo, mWindowDecorByTaskId.get(taskInfo.taskId)); + }); + return true; + } } // InputEventReceiver to listen for touch input outside of caption bounds @@ -540,9 +584,11 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { relevantDecor.mTaskInfo.configuration.windowConfiguration.getBounds()); boolean dragFromStatusBarAllowed = false; if (DesktopModeStatus.isProto2Enabled()) { - // In proto2 any full screen task can be dragged to freeform - dragFromStatusBarAllowed = relevantDecor.mTaskInfo.getWindowingMode() - == WINDOWING_MODE_FULLSCREEN; + // In proto2 any full screen or multi-window task can be dragged to + // freeform. + final int windowingMode = relevantDecor.mTaskInfo.getWindowingMode(); + dragFromStatusBarAllowed = windowingMode == WINDOWING_MODE_FULLSCREEN + || windowingMode == WINDOWING_MODE_MULTI_WINDOW; } if (dragFromStatusBarAllowed && relevantDecor.checkTouchEventInHandle(ev)) { @@ -571,9 +617,9 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { return; } else if (mDragToDesktopAnimationStarted) { Point position = new Point((int) ev.getX(), (int) ev.getY()); + relevantDecor.incrementRelayoutBlock(); mDesktopTasksController.ifPresent( - c -> c.cancelMoveToFreeform(relevantDecor.mTaskInfo, - position)); + c -> c.cancelMoveToFreeform(relevantDecor.mTaskInfo, position)); mDragToDesktopAnimationStarted = false; return; } @@ -699,8 +745,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { @Nullable private DesktopModeWindowDecoration getRelevantWindowDecor(MotionEvent ev) { - if (mSplitScreenController.isPresent() - && mSplitScreenController.get().isSplitScreenVisible()) { + if (mSplitScreenController != null && mSplitScreenController.isSplitScreenVisible()) { // We can't look at focused task here as only one task will have focus. return getSplitScreenDecor(ev); } else { @@ -711,9 +756,9 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { @Nullable private DesktopModeWindowDecoration getSplitScreenDecor(MotionEvent ev) { ActivityManager.RunningTaskInfo topOrLeftTask = - mSplitScreenController.get().getTaskInfo(SPLIT_POSITION_TOP_OR_LEFT); + mSplitScreenController.getTaskInfo(SPLIT_POSITION_TOP_OR_LEFT); ActivityManager.RunningTaskInfo bottomOrRightTask = - mSplitScreenController.get().getTaskInfo(SPLIT_POSITION_BOTTOM_OR_RIGHT); + mSplitScreenController.getTaskInfo(SPLIT_POSITION_BOTTOM_OR_RIGHT); if (topOrLeftTask != null && topOrLeftTask.getConfiguration() .windowConfiguration.getBounds().contains((int) ev.getX(), (int) ev.getY())) { return mWindowDecorByTaskId.get(topOrLeftTask.taskId); @@ -765,11 +810,12 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { private boolean shouldShowWindowDecor(RunningTaskInfo taskInfo) { if (taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) return true; - if (mSplitScreenController.isPresent() - && mSplitScreenController.get().isTaskRootOrStageRoot(taskInfo.taskId)) { + if (mSplitScreenController != null + && mSplitScreenController.isTaskRootOrStageRoot(taskInfo.taskId)) { return false; } return DesktopModeStatus.isProto2Enabled() + && taskInfo.getWindowingMode() != WINDOWING_MODE_PINNED && taskInfo.getActivityType() == ACTIVITY_TYPE_STANDARD && mDisplayController.getDisplayContext(taskInfo.displayId) .getResources().getConfiguration().smallestScreenWidthDp >= 600; @@ -796,6 +842,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { mMainChoreographer, mSyncQueue); mWindowDecorByTaskId.put(taskInfo.taskId, windowDecoration); + windowDecoration.createResizeVeil(); final DragPositioningCallback dragPositioningCallback = createDragPositioningCallback( windowDecoration, taskInfo); @@ -814,14 +861,18 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { @NonNull DesktopModeWindowDecoration windowDecoration, @NonNull RunningTaskInfo taskInfo) { final int screenWidth = mDisplayController.getDisplayLayout(taskInfo.displayId).width(); - final Rect disallowedAreaForEndBounds = new Rect(0, 0, screenWidth, - getStatusBarHeight(taskInfo.displayId)); + final Rect disallowedAreaForEndBounds; + if (DesktopModeStatus.isProto2Enabled()) { + disallowedAreaForEndBounds = new Rect(0, 0, screenWidth, + getStatusBarHeight(taskInfo.displayId)); + } else { + disallowedAreaForEndBounds = null; + } if (!DesktopModeStatus.isVeiledResizeEnabled()) { return new FluidResizeTaskPositioner(mTaskOrganizer, windowDecoration, mDisplayController, disallowedAreaForEndBounds, mDragStartListener, mTransactionFactory); } else { - windowDecoration.createResizeVeil(); return new VeiledResizeTaskPositioner(mTaskOrganizer, windowDecoration, mDisplayController, disallowedAreaForEndBounds, mDragStartListener, mTransitions); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java index ce11b2604559..bc89385a0d13 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java @@ -23,7 +23,6 @@ import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.res.Configuration; -import android.content.res.TypedArray; import android.graphics.Point; import android.graphics.PointF; import android.graphics.Rect; @@ -39,6 +38,7 @@ import android.view.View; import android.view.ViewConfiguration; import android.window.WindowContainerTransaction; +import com.android.internal.policy.ScreenDecorationsUtils; import com.android.launcher3.icons.IconProvider; import com.android.wm.shell.R; import com.android.wm.shell.ShellTaskOrganizer; @@ -178,15 +178,12 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin mRelayoutParams.reset(); mRelayoutParams.mRunningTaskInfo = taskInfo; mRelayoutParams.mLayoutResId = windowDecorLayoutId; - mRelayoutParams.mCaptionHeightId = R.dimen.freeform_decor_caption_height; + mRelayoutParams.mCaptionHeightId = getCaptionHeightId(); mRelayoutParams.mShadowRadiusId = shadowRadiusID; mRelayoutParams.mApplyStartTransactionOnDraw = applyStartTransactionOnDraw; - final TypedArray ta = mContext.obtainStyledAttributes( - new int[]{android.R.attr.dialogCornerRadius}); - mRelayoutParams.mCornerRadius = ta.getDimensionPixelSize(0, 0); - ta.recycle(); - + mRelayoutParams.mCornerRadius = + (int) ScreenDecorationsUtils.getWindowCornerRadius(mContext); relayout(mRelayoutParams, startT, finishT, wct, oldRootView, mResult); // After this line, mTaskInfo is up-to-date and should be used instead of taskInfo @@ -235,6 +232,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin mHandler, mChoreographer, mDisplay.getDisplayId(), + mRelayoutParams.mCornerRadius, mDecorationContainerSurface, mDragPositioningCallback); } @@ -294,23 +292,37 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin } /** - * Fade in the resize veil + * Show the resize veil. */ - void showResizeVeil(Rect taskBounds) { + public void showResizeVeil(Rect taskBounds) { mResizeVeil.showVeil(mTaskSurface, taskBounds); } /** + * Show the resize veil. + */ + public void showResizeVeil(SurfaceControl.Transaction tx, Rect taskBounds) { + mResizeVeil.showVeil(tx, mTaskSurface, taskBounds, false /* fadeIn */); + } + + /** * Set new bounds for the resize veil */ - void updateResizeVeil(Rect newBounds) { + public void updateResizeVeil(Rect newBounds) { mResizeVeil.updateResizeVeil(newBounds); } /** + * Set new bounds for the resize veil + */ + public void updateResizeVeil(SurfaceControl.Transaction tx, Rect newBounds) { + mResizeVeil.updateResizeVeil(tx, newBounds); + } + + /** * Fade the resize veil out. */ - void hideResizeVeil() { + public void hideResizeVeil() { mResizeVeil.hideVeil(); } @@ -479,6 +491,11 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin } } + @Override + int getCaptionHeightId() { + return R.dimen.freeform_decor_caption_height; + } + /** * Add transition to mTransitionsPausingRelayout */ diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragDetector.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragDetector.java index 65b5a7a17afe..da268988bac7 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragDetector.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragDetector.java @@ -24,6 +24,9 @@ import static android.view.MotionEvent.ACTION_UP; import android.graphics.PointF; import android.view.MotionEvent; +import android.view.View; + +import androidx.annotation.Nullable; /** * A detector for touch inputs that differentiates between drag and click inputs. It receives a flow @@ -54,14 +57,24 @@ class DragDetector { * * @return the result returned by {@link #mEventHandler}, or the result when * {@link #mEventHandler} handles the previous down event if the event shouldn't be passed - */ + */ boolean onMotionEvent(MotionEvent ev) { + return onMotionEvent(null /* view */, ev); + } + + /** + * The receiver of the {@link MotionEvent} flow. + * + * @return the result returned by {@link #mEventHandler}, or the result when + * {@link #mEventHandler} handles the previous down event if the event shouldn't be passed + */ + boolean onMotionEvent(View v, MotionEvent ev) { final boolean isTouchScreen = (ev.getSource() & SOURCE_TOUCHSCREEN) == SOURCE_TOUCHSCREEN; if (!isTouchScreen) { // Only touches generate noisy moves, so mouse/trackpad events don't need to filtered // to take the slop threshold into consideration. - return mEventHandler.handleMotionEvent(ev); + return mEventHandler.handleMotionEvent(v, ev); } switch (ev.getActionMasked()) { case ACTION_DOWN: { @@ -69,12 +82,15 @@ class DragDetector { float rawX = ev.getRawX(0); float rawY = ev.getRawY(0); mInputDownPoint.set(rawX, rawY); - mResultOfDownAction = mEventHandler.handleMotionEvent(ev); + mResultOfDownAction = mEventHandler.handleMotionEvent(v, ev); return mResultOfDownAction; } case ACTION_MOVE: { + if (ev.findPointerIndex(mDragPointerId) == -1) { + mDragPointerId = ev.getPointerId(0); + } + final int dragPointerIndex = ev.findPointerIndex(mDragPointerId); if (!mIsDragEvent) { - int dragPointerIndex = ev.findPointerIndex(mDragPointerId); float dx = ev.getRawX(dragPointerIndex) - mInputDownPoint.x; float dy = ev.getRawY(dragPointerIndex) - mInputDownPoint.y; // Touches generate noisy moves, so only once the move is past the touch @@ -84,7 +100,7 @@ class DragDetector { // The event handler should only be notified about 'move' events if a drag has been // detected. if (mIsDragEvent) { - return mEventHandler.handleMotionEvent(ev); + return mEventHandler.handleMotionEvent(v, ev); } else { return mResultOfDownAction; } @@ -92,10 +108,10 @@ class DragDetector { case ACTION_UP: case ACTION_CANCEL: { resetState(); - return mEventHandler.handleMotionEvent(ev); + return mEventHandler.handleMotionEvent(v, ev); } default: - return mEventHandler.handleMotionEvent(ev); + return mEventHandler.handleMotionEvent(v, ev); } } @@ -111,6 +127,6 @@ class DragDetector { } interface MotionEventHandler { - boolean handleMotionEvent(MotionEvent ev); + boolean handleMotionEvent(@Nullable View v, MotionEvent ev); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java index 287d86187288..e5fc66afbdfc 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java @@ -42,6 +42,7 @@ import android.view.InputEventReceiver; import android.view.MotionEvent; import android.view.PointerIcon; import android.view.SurfaceControl; +import android.view.View; import android.view.ViewConfiguration; import android.view.WindowManagerGlobal; @@ -55,7 +56,6 @@ import com.android.internal.view.BaseIWindow; */ class DragResizeInputListener implements AutoCloseable { private static final String TAG = "DragResizeInputListener"; - private final IWindowSession mWindowSession = WindowManagerGlobal.getWindowSession(); private final Handler mHandler; private final Choreographer mChoreographer; @@ -73,6 +73,7 @@ class DragResizeInputListener implements AutoCloseable { private int mTaskHeight; private int mResizeHandleThickness; private int mCornerSize; + private int mTaskCornerRadius; private Rect mLeftTopCornerBounds; private Rect mRightTopCornerBounds; @@ -87,12 +88,14 @@ class DragResizeInputListener implements AutoCloseable { Handler handler, Choreographer choreographer, int displayId, + int taskCornerRadius, SurfaceControl decorationSurface, DragPositioningCallback callback) { mInputManager = context.getSystemService(InputManager.class); mHandler = handler; mChoreographer = choreographer; mDisplayId = displayId; + mTaskCornerRadius = taskCornerRadius; mDecorationSurface = decorationSurface; // Use a fake window as the backing surface is a container layer and we don't want to create // a buffer layer for it so we can't use ViewRootImpl. @@ -126,12 +129,7 @@ class DragResizeInputListener implements AutoCloseable { } /** - * Updates geometry of this drag resize handler. Needs to be called every time there is a size - * change to notify the input event receiver it's ready to take the next input event. Otherwise - * it'll keep batching move events and the drag resize process is stalled. - * - * This is also used to update the touch regions of this handler every event dispatched here is - * a potential resize request. + * Updates the geometry (the touch region) of this drag resize handler. * * @param taskWidth The width of the task. * @param taskHeight The height of the task. @@ -303,7 +301,7 @@ class DragResizeInputListener implements AutoCloseable { } @Override - public boolean handleMotionEvent(MotionEvent e) { + public boolean handleMotionEvent(View v, MotionEvent e) { boolean result = false; // Check if this is a touch event vs mouse event. // Touch events are tracked in four corners. Other events are tracked in resize edges. @@ -383,19 +381,64 @@ class DragResizeInputListener implements AutoCloseable { @DragPositioningCallback.CtrlType private int calculateResizeHandlesCtrlType(float x, float y) { int ctrlType = 0; - if (x < 0) { + // mTaskCornerRadius is only used in comparing with corner regions. Comparisons with + // sides will use the bounds specified in setGeometry and not go into task bounds. + if (x < mTaskCornerRadius) { ctrlType |= CTRL_TYPE_LEFT; } - if (x > mTaskWidth) { + if (x > mTaskWidth - mTaskCornerRadius) { ctrlType |= CTRL_TYPE_RIGHT; } - if (y < 0) { + if (y < mTaskCornerRadius) { ctrlType |= CTRL_TYPE_TOP; } - if (y > mTaskHeight) { + if (y > mTaskHeight - mTaskCornerRadius) { ctrlType |= CTRL_TYPE_BOTTOM; } - return ctrlType; + return checkDistanceFromCenter(ctrlType, x, y); + } + + // If corner input is not within appropriate distance of corner radius, do not use it. + // If input is not on a corner or is within valid distance, return ctrlType. + @DragPositioningCallback.CtrlType + private int checkDistanceFromCenter(@DragPositioningCallback.CtrlType int ctrlType, + float x, float y) { + int centerX; + int centerY; + + // Determine center of rounded corner circle; this is simply the corner if radius is 0. + switch (ctrlType) { + case CTRL_TYPE_LEFT | CTRL_TYPE_TOP: { + centerX = mTaskCornerRadius; + centerY = mTaskCornerRadius; + break; + } + case CTRL_TYPE_LEFT | CTRL_TYPE_BOTTOM: { + centerX = mTaskCornerRadius; + centerY = mTaskHeight - mTaskCornerRadius; + break; + } + case CTRL_TYPE_RIGHT | CTRL_TYPE_TOP: { + centerX = mTaskWidth - mTaskCornerRadius; + centerY = mTaskCornerRadius; + break; + } + case CTRL_TYPE_RIGHT | CTRL_TYPE_BOTTOM: { + centerX = mTaskWidth - mTaskCornerRadius; + centerY = mTaskHeight - mTaskCornerRadius; + break; + } + default: { + return ctrlType; + } + } + double distanceFromCenter = Math.hypot(x - centerX, y - centerY); + + if (distanceFromCenter < mTaskCornerRadius + mResizeHandleThickness + && distanceFromCenter >= mTaskCornerRadius) { + return ctrlType; + } + return 0; } @DragPositioningCallback.CtrlType 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 index 82771095cd82..bfce72bcadf0 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/ResizeVeil.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/ResizeVeil.java @@ -102,11 +102,17 @@ public class ResizeVeil { } /** - * Animate veil's alpha to 1, fading it in. + * 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 parentSurface, Rect taskBounds) { + public void showVeil(SurfaceControl.Transaction t, SurfaceControl parentSurface, + Rect taskBounds, boolean fadeIn) { // Parent surface can change, ensure it is up to date. - SurfaceControl.Transaction t = mSurfaceControlTransactionSupplier.get(); if (!parentSurface.equals(mParentSurface)) { t.reparent(mVeilSurface, parentSurface); mParentSurface = parentSurface; @@ -115,22 +121,36 @@ public class ResizeVeil { int backgroundColorId = getBackgroundColorId(); mViewHost.getView().setBackgroundColor(mContext.getColor(backgroundColorId)); - final ValueAnimator animator = new ValueAnimator(); - animator.setFloatValues(0f, 1f); - animator.setDuration(RESIZE_ALPHA_DURATION); - animator.addUpdateListener(animation -> { - t.setAlpha(mVeilSurface, animator.getAnimatedFraction()); - t.apply(); - }); - relayout(taskBounds, t); - t.show(mVeilSurface) - .addTransactionCommittedListener(mContext.getMainExecutor(), () -> animator.start()) - .setAlpha(mVeilSurface, 0); + if (fadeIn) { + final ValueAnimator animator = new ValueAnimator(); + animator.setFloatValues(0f, 1f); + animator.setDuration(RESIZE_ALPHA_DURATION); + animator.addUpdateListener(animation -> { + t.setAlpha(mVeilSurface, animator.getAnimatedFraction()); + t.apply(); + }); + + t.show(mVeilSurface) + .addTransactionCommittedListener( + mContext.getMainExecutor(), () -> animator.start()) + .setAlpha(mVeilSurface, 0); + } else { + // Show the veil immediately at full opacity. + t.show(mVeilSurface).setAlpha(mVeilSurface, 1); + } mViewHost.getView().getViewRootImpl().applyTransactionOnDraw(t); } /** + * Animate veil's alpha to 1, fading it in. + */ + public void showVeil(SurfaceControl parentSurface, Rect taskBounds) { + 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. */ @@ -147,6 +167,16 @@ public class ResizeVeil { */ public void updateResizeVeil(Rect newBounds) { SurfaceControl.Transaction t = mSurfaceControlTransactionSupplier.get(); + updateResizeVeil(t, newBounds); + } + + /** + * Calls relayout to update task and veil bounds. + * + * @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) { relayout(newBounds, t); mViewHost.getView().getViewRootImpl().applyTransactionOnDraw(t); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java index 58c78e6a5b9f..39b90218dce1 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java @@ -55,7 +55,7 @@ public class VeiledResizeTaskPositioner implements DragPositioningCallback, private final Rect mRepositionTaskBounds = new Rect(); // If a task move (not resize) finishes in this region, the positioner will not attempt to // finalize the bounds there using WCT#setBounds - private final Rect mDisallowedAreaForEndBounds = new Rect(); + private final Rect mDisallowedAreaForEndBounds; private final Supplier<SurfaceControl.Transaction> mTransactionSupplier; private int mCtrlType; @@ -77,7 +77,7 @@ public class VeiledResizeTaskPositioner implements DragPositioningCallback, mDesktopWindowDecoration = windowDecoration; mDisplayController = displayController; mDragStartListener = dragStartListener; - mDisallowedAreaForEndBounds.set(disallowedAreaForEndBounds); + mDisallowedAreaForEndBounds = new Rect(disallowedAreaForEndBounds); mTransactionSupplier = supplier; mTransitions = transitions; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecorViewModel.java index 9f03d9aec166..ae1a3d914be3 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecorViewModel.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecorViewModel.java @@ -22,6 +22,7 @@ import android.view.SurfaceControl; import android.window.TransitionInfo; import com.android.wm.shell.freeform.FreeformTaskTransitionStarter; +import com.android.wm.shell.splitscreen.SplitScreenController; /** * The interface used by some {@link com.android.wm.shell.ShellTaskOrganizer.TaskListener} to help @@ -39,6 +40,11 @@ public interface WindowDecorViewModel { void setFreeformTaskTransitionStarter(FreeformTaskTransitionStarter transitionStarter); /** + * Sets the {@link SplitScreenController} if available. + */ + void setSplitScreenController(SplitScreenController splitScreenController); + + /** * Creates a window decoration for the given task. Can be {@code null} for Fullscreen tasks but * not Freeform ones. * 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 ac5ff2075901..ddc7fef0599f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java @@ -237,7 +237,12 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> final int captionHeight = loadDimensionPixelSize(resources, params.mCaptionHeightId); final int captionWidth = taskBounds.width(); + + // We use mDecorationContainerSurface to define input window for task resizing; by layering + // it in front of mCaptionContainerSurface, we can allow it to handle input prior to + // caption view itself, treating corner inputs as resize events rather than repositioning. startT.setWindowCrop(mCaptionContainerSurface, captionWidth, captionHeight) + .setLayer(mCaptionContainerSurface, -1) .show(mCaptionContainerSurface); if (ViewRootImpl.CAPTION_ON_SHELL) { @@ -248,6 +253,9 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> mCaptionInsetsRect.bottom = mCaptionInsetsRect.top + captionHeight + params.mCaptionY; wct.addInsetsSource(mTaskInfo.token, mOwner, 0 /* index */, WindowInsets.Type.captionBar(), mCaptionInsetsRect); + wct.addInsetsSource(mTaskInfo.token, + mOwner, 0 /* index */, WindowInsets.Type.mandatorySystemGestures(), + mCaptionInsetsRect); } else { startT.hide(mCaptionContainerSurface); } @@ -264,6 +272,7 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> .setColor(mTaskSurface, mTmpColor) .show(mTaskSurface); finishT.setPosition(mTaskSurface, taskPosition.x, taskPosition.y) + .setShadowRadius(mTaskSurface, shadowRadius) .setWindowCrop(mTaskSurface, outResult.mWidth, outResult.mHeight); if (mTaskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) { startT.setCornerRadius(mTaskSurface, params.mCornerRadius); @@ -301,6 +310,10 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> } } + int getCaptionHeightId() { + return Resources.ID_NULL; + } + /** * Obtains the {@link Display} instance for the display ID in {@link #mTaskInfo} if it exists or * registers {@link #mOnDisplaysChangedListener} if it doesn't. @@ -345,6 +358,8 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> final WindowContainerTransaction wct = mWindowContainerTransactionSupplier.get(); wct.removeInsetsSource(mTaskInfo.token, mOwner, 0 /* index */, WindowInsets.Type.captionBar()); + wct.removeInsetsSource(mTaskInfo.token, + mOwner, 0 /* index */, WindowInsets.Type.mandatorySystemGestures()); mTaskOrganizer.applyTransaction(wct); } @@ -413,6 +428,21 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> mSurfaceControlTransactionSupplier); } + /** + * Adds caption inset source to a WCT + */ + public void addCaptionInset(WindowContainerTransaction wct) { + final int captionHeightId = getCaptionHeightId(); + if (!ViewRootImpl.CAPTION_ON_SHELL || captionHeightId == Resources.ID_NULL) { + return; + } + + final int captionHeight = loadDimensionPixelSize(mContext.getResources(), captionHeightId); + final Rect captionInsets = new Rect(0, 0, 0, captionHeight); + wct.addInsetsSource(mTaskInfo.token, mOwner, 0 /* index */, WindowInsets.Type.captionBar(), + captionInsets); + } + static class RelayoutParams { RunningTaskInfo mRunningTaskInfo; int mLayoutResId; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeWindowDecorationViewHolder.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeWindowDecorationViewHolder.kt index 514ea52cb8ae..d293cf73a0f7 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeWindowDecorationViewHolder.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeWindowDecorationViewHolder.kt @@ -1,28 +1,35 @@ package com.android.wm.shell.windowdecor.viewholder import android.app.ActivityManager.RunningTaskInfo +import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM import android.content.Context import android.graphics.Color import android.view.View +import android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS /** * Encapsulates the root [View] of a window decoration and its children to facilitate looking up * children (via findViewById) and updating to the latest data from [RunningTaskInfo]. */ internal abstract class DesktopModeWindowDecorationViewHolder(rootView: View) { - val context: Context = rootView.context + val context: Context = rootView.context - /** - * A signal to the view holder that new data is available and that the views should be updated - * to reflect it. - */ - abstract fun bindData(taskInfo: RunningTaskInfo) + /** + * A signal to the view holder that new data is available and that the views should be updated to + * reflect it. + */ + abstract fun bindData(taskInfo: RunningTaskInfo) - /** - * Whether the caption items should use the 'light' color variant so that there's good contrast - * with the caption background color. - */ - protected fun shouldUseLightCaptionColors(taskInfo: RunningTaskInfo): Boolean { - return Color.valueOf(taskInfo.taskDescription.statusBarColor).luminance() < 0.5 + /** + * Whether the caption items should use the 'light' color variant so that there's good contrast + * with the caption background color. + */ + protected fun shouldUseLightCaptionColors(taskInfo: RunningTaskInfo): Boolean { + return if (Color.alpha(taskInfo.taskDescription.statusBarColor) != 0 && + taskInfo.windowingMode == WINDOWING_MODE_FREEFORM) { + Color.valueOf(taskInfo.taskDescription.statusBarColor).luminance() < 0.5 + } else { + taskInfo.taskDescription.statusBarAppearance and APPEARANCE_LIGHT_STATUS_BARS == 0 } + } } diff --git a/libs/WindowManager/Shell/tests/flicker/Android.bp b/libs/WindowManager/Shell/tests/flicker/Android.bp index b6696c70dbb1..e382a0f0aeda 100644 --- a/libs/WindowManager/Shell/tests/flicker/Android.bp +++ b/libs/WindowManager/Shell/tests/flicker/Android.bp @@ -23,14 +23,33 @@ package { default_applicable_licenses: ["frameworks_base_license"], } -android_test { - name: "WMShellFlickerTests", +filegroup { + name: "WMShellFlickerTestsBase-src", + srcs: ["src/com/android/wm/shell/flicker/*.kt"], +} + +filegroup { + name: "WMShellFlickerTestsBubbles-src", + srcs: ["src/**/bubble/*.kt"], +} + +filegroup { + name: "WMShellFlickerTestsPip-src", + srcs: ["src/**/pip/*.kt"], +} + +filegroup { + name: "WMShellFlickerTestsSplitScreen-src", srcs: [ - "src/**/*.java", - "src/**/*.kt", + "src/**/splitscreen/*.kt", + "src/**/splitscreen/benchmark/*.kt", ], - manifest: "AndroidManifest.xml", - test_config: "AndroidTest.xml", +} + +java_defaults { + name: "WMShellFlickerTestsDefault", + manifest: "manifests/AndroidManifest.xml", + test_config_template: "AndroidTestTemplate.xml", platform_apis: true, certificate: "platform", optimize: { @@ -40,16 +59,70 @@ android_test { libs: ["android.test.runner"], static_libs: [ "androidx.test.ext.junit", + "flickertestapplib", "flickerlib", - "flickerlib-apphelpers", "flickerlib-helpers", - "truth-prebuilt", - "app-helpers-core", + "platform-test-annotations", + "wm-flicker-common-app-helpers", + "wm-flicker-common-assertions", "launcher-helper-lib", "launcher-aosp-tapl", - "wm-flicker-common-assertions", - "wm-flicker-common-app-helpers", - "platform-test-annotations", - "flickertestapplib", + ], + data: [ + ":FlickerTestApp", + "trace_config/*", + ], +} + +android_test { + name: "WMShellFlickerTestsOther", + defaults: ["WMShellFlickerTestsDefault"], + additional_manifests: ["manifests/AndroidManifestOther.xml"], + package_name: "com.android.wm.shell.flicker", + instrumentation_target_package: "com.android.wm.shell.flicker", + srcs: [ + "src/**/*.java", + "src/**/*.kt", + ], + exclude_srcs: [ + ":WMShellFlickerTestsBubbles-src", + ":WMShellFlickerTestsPip-src", + ":WMShellFlickerTestsSplitScreen-src", + ], +} + +android_test { + name: "WMShellFlickerTestsBubbles", + defaults: ["WMShellFlickerTestsDefault"], + additional_manifests: ["manifests/AndroidManifestBubbles.xml"], + package_name: "com.android.wm.shell.flicker.bubbles", + instrumentation_target_package: "com.android.wm.shell.flicker.bubbles", + srcs: [ + ":WMShellFlickerTestsBase-src", + ":WMShellFlickerTestsBubbles-src", + ], +} + +android_test { + name: "WMShellFlickerTestsPip", + defaults: ["WMShellFlickerTestsDefault"], + additional_manifests: ["manifests/AndroidManifestPip.xml"], + package_name: "com.android.wm.shell.flicker.pip", + instrumentation_target_package: "com.android.wm.shell.flicker.pip", + srcs: [ + ":WMShellFlickerTestsBase-src", + ":WMShellFlickerTestsPip-src", + ], +} + +android_test { + name: "WMShellFlickerTestsSplitScreen", + defaults: ["WMShellFlickerTestsDefault"], + additional_manifests: ["manifests/AndroidManifestSplitScreen.xml"], + package_name: "com.android.wm.shell.flicker.splitscreen", + instrumentation_target_package: "com.android.wm.shell.flicker.splitscreen", + srcs: [ + ":WMShellFlickerTestsBase-src", + ":WMShellFlickerTestsSplitScreen-src", ], } diff --git a/libs/WindowManager/Shell/tests/flicker/AndroidTest.xml b/libs/WindowManager/Shell/tests/flicker/AndroidTest.xml deleted file mode 100644 index b5937ae80f0a..000000000000 --- a/libs/WindowManager/Shell/tests/flicker/AndroidTest.xml +++ /dev/null @@ -1,53 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - * Copyright 2020 Google Inc. All Rights Reserved. - --> -<configuration description="Runs WindowManager Shell Flicker Tests"> - <option name="test-tag" value="FlickerTests" /> - <target_preparer class="com.android.tradefed.targetprep.DeviceSetup"> - <!-- keeps the screen on during tests --> - <option name="screen-always-on" value="on" /> - <!-- prevents the phone from restarting --> - <option name="force-skip-system-props" value="true" /> - <!-- set WM tracing verbose level to all --> - <option name="run-command" value="cmd window tracing level all" /> - <!-- set WM tracing to frame (avoid incomplete states) --> - <option name="run-command" value="cmd window tracing frame" /> - <!-- disable betterbug as it's log collection dialogues cause flakes in e2e tests --> - <option name="run-command" value="pm disable com.google.android.internal.betterbug" /> - <!-- ensure lock screen mode is swipe --> - <option name="run-command" value="locksettings set-disabled false" /> - <!-- restart launcher to activate TAPL --> - <option name="run-command" value="setprop ro.test_harness 1 ; am force-stop com.google.android.apps.nexuslauncher" /> - <!-- Ensure output directory is empty at the start --> - <option name="run-command" value="rm -rf /sdcard/flicker" /> - <!-- Increase trace size: 20mb for WM and 80mb for SF --> - <option name="run-command" value="cmd window tracing size 20480" /> - <option name="run-command" value="su root service call SurfaceFlinger 1029 i32 81920" /> - </target_preparer> - <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer"> - <option name="run-command" value="settings put secure show_ime_with_hard_keyboard 1" /> - <option name="run-command" value="settings put system show_touches 1" /> - <option name="run-command" value="settings put system pointer_location 1" /> - <option name="teardown-command" value="settings delete secure show_ime_with_hard_keyboard" /> - <option name="teardown-command" value="settings delete system show_touches" /> - <option name="teardown-command" value="settings delete system pointer_location" /> - <option name="teardown-command" value="cmd overlay enable com.android.internal.systemui.navbar.gestural" /> - </target_preparer> - <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> - <option name="cleanup-apks" value="true"/> - <option name="test-file-name" value="WMShellFlickerTests.apk"/> - <option name="test-file-name" value="FlickerTestApp.apk" /> - </target_preparer> - <test class="com.android.tradefed.testtype.AndroidJUnitTest"> - <option name="package" value="com.android.wm.shell.flicker"/> - <option name="shell-timeout" value="6600s" /> - <option name="test-timeout" value="6000s" /> - <option name="hidden-api-checks" value="false" /> - </test> - <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector"> - <option name="directory-keys" value="/sdcard/flicker" /> - <option name="collect-on-run-ended-only" value="true" /> - <option name="clean-up" value="true" /> - </metrics_collector> -</configuration> diff --git a/libs/WindowManager/Shell/tests/flicker/AndroidTestTemplate.xml b/libs/WindowManager/Shell/tests/flicker/AndroidTestTemplate.xml new file mode 100644 index 000000000000..991d7b5480c4 --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/AndroidTestTemplate.xml @@ -0,0 +1,99 @@ +<?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. + --> +<configuration description="Runs WindowManager Shell Flicker Tests {MODULE}"> + <option name="test-tag" value="FlickerTests"/> + <!-- Needed for storing the perfetto trace files in the sdcard/test_results--> + <option name="isolated-storage" value="false"/> + + <target_preparer class="com.android.tradefed.targetprep.DeviceSetup"> + <!-- keeps the screen on during tests --> + <option name="screen-always-on" value="on"/> + <!-- prevents the phone from restarting --> + <option name="force-skip-system-props" value="true"/> + <!-- set WM tracing verbose level to all --> + <option name="run-command" value="cmd window tracing level all"/> + <!-- set WM tracing to frame (avoid incomplete states) --> + <option name="run-command" value="cmd window tracing frame"/> + <!-- disable betterbug as it's log collection dialogues cause flakes in e2e tests --> + <option name="run-command" value="pm disable com.google.android.internal.betterbug"/> + <!-- ensure lock screen mode is swipe --> + <option name="run-command" value="locksettings set-disabled false"/> + <!-- restart launcher to activate TAPL --> + <option name="run-command" + value="setprop ro.test_harness 1 ; am force-stop com.google.android.apps.nexuslauncher"/> + <!-- Increase trace size: 20mb for WM and 80mb for SF --> + <option name="run-command" value="cmd window tracing size 20480"/> + <option name="run-command" value="su root service call SurfaceFlinger 1029 i32 81920"/> + </target_preparer> + <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer"> + <option name="test-user-token" value="%TEST_USER%"/> + <option name="run-command" value="rm -rf /data/user/%TEST_USER%/files/*"/> + <option name="run-command" value="settings put secure show_ime_with_hard_keyboard 1"/> + <option name="run-command" value="settings put system show_touches 1"/> + <option name="run-command" value="settings put system pointer_location 1"/> + <option name="teardown-command" + value="settings delete secure show_ime_with_hard_keyboard"/> + <option name="teardown-command" value="settings delete system show_touches"/> + <option name="teardown-command" value="settings delete system pointer_location"/> + <option name="teardown-command" + value="cmd overlay enable com.android.internal.systemui.navbar.gestural"/> + </target_preparer> + <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> + <option name="cleanup-apks" value="true"/> + <option name="test-file-name" value="{MODULE}.apk"/> + <option name="test-file-name" value="FlickerTestApp.apk"/> + </target_preparer> + <!-- Needed for pushing the trace config file --> + <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/> + <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer"> + <option name="push-file" + key="trace_config.textproto" + value="/data/misc/perfetto-traces/trace_config.textproto" + /> + <!--Install the content provider automatically when we push some file in sdcard folder.--> + <!--Needed to avoid the installation during the test suite.--> + <option name="push-file" key="trace_config.textproto" value="/sdcard/sample.textproto"/> + </target_preparer> + <test class="com.android.tradefed.testtype.AndroidJUnitTest"> + <option name="package" value="{PACKAGE}"/> + <option name="shell-timeout" value="6600s"/> + <option name="test-timeout" value="6000s"/> + <option name="hidden-api-checks" value="false"/> + <option name="device-listeners" value="android.device.collectors.PerfettoListener"/> + <!-- PerfettoListener related arguments --> + <option name="instrumentation-arg" key="perfetto_config_text_proto" value="true"/> + <option name="instrumentation-arg" + key="perfetto_config_file" + value="trace_config.textproto" + /> + <option name="instrumentation-arg" key="per_run" value="true"/> + </test> + <!-- Needed for pulling the collected trace config on to the host --> + <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector"> + <option name="pull-pattern-keys" value="perfetto_file_path"/> + <option name="directory-keys" + value="/data/user/0/com.android.wm.shell.flicker/files"/> + <option name="directory-keys" + value="/data/user/0/com.android.wm.shell.flicker.bubbles/files"/> + <option name="directory-keys" + value="/data/user/0/com.android.server.wm.flicker.pip/files"/> + <option name="directory-keys" + value="/data/user/0/com.android.server.wm.flicker.splitscreen/files"/> + <option name="collect-on-run-ended-only" value="true"/> + <option name="clean-up" value="true"/> + </metrics_collector> +</configuration> diff --git a/libs/WindowManager/Shell/tests/flicker/AndroidManifest.xml b/libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifest.xml index 4721741611cf..4721741611cf 100644 --- a/libs/WindowManager/Shell/tests/flicker/AndroidManifest.xml +++ b/libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifest.xml diff --git a/libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifestBubbles.xml b/libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifestBubbles.xml new file mode 100644 index 000000000000..437871f1bb8f --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifestBubbles.xml @@ -0,0 +1,24 @@ +<!-- + ~ Copyright (C) 2023 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.wm.shell.flicker.bubble"> + + <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" + android:targetPackage="com.android.wm.shell.flicker.bubble" + android:label="WindowManager Flicker Tests"> + </instrumentation> +</manifest> diff --git a/libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifestOther.xml b/libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifestOther.xml new file mode 100644 index 000000000000..cf642f63a41d --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifestOther.xml @@ -0,0 +1,24 @@ +<!-- + ~ Copyright (C) 2023 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.wm.shell.flicker"> + + <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" + android:targetPackage="com.android.wm.shell.flicker" + android:label="WindowManager Flicker Tests"> + </instrumentation> +</manifest> diff --git a/libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifestPip.xml b/libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifestPip.xml new file mode 100644 index 000000000000..5a8155a66d30 --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifestPip.xml @@ -0,0 +1,24 @@ +<!-- + ~ Copyright (C) 2023 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.wm.shell.flicker.pip"> + + <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" + android:targetPackage="com.android.wm.shell.flicker.pip" + android:label="WindowManager Flicker Tests"> + </instrumentation> +</manifest> diff --git a/libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifestSplitScreen.xml b/libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifestSplitScreen.xml new file mode 100644 index 000000000000..887d8db3042f --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifestSplitScreen.xml @@ -0,0 +1,24 @@ +<!-- + ~ Copyright (C) 2023 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.wm.shell.flicker.splitscreen"> + + <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" + android:targetPackage="com.android.wm.shell.flicker.splitscreen" + android:label="WindowManager Flicker Tests"> + </instrumentation> +</manifest> diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/BaseAppCompat.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/BaseAppCompat.kt index 61781565270b..3000008628fe 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/BaseAppCompat.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/BaseAppCompat.kt @@ -23,16 +23,15 @@ import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.FlickerTest import com.android.server.wm.flicker.helpers.setRotation import com.android.server.wm.flicker.helpers.LetterboxAppHelper -import android.tools.device.flicker.legacy.FlickerTestFactory import android.tools.device.flicker.legacy.IFlickerTestData import com.android.wm.shell.flicker.BaseTest import com.android.wm.shell.flicker.appWindowIsVisibleAtStart import com.android.wm.shell.flicker.appWindowIsVisibleAtEnd +import com.android.wm.shell.flicker.appWindowKeepVisible import com.android.wm.shell.flicker.layerKeepVisible import org.junit.After import org.junit.Assume import org.junit.Before -import org.junit.runners.Parameterized abstract class BaseAppCompat(flicker: FlickerTest) : BaseTest(flicker) { protected val context: Context = instrumentation.context @@ -58,12 +57,13 @@ abstract class BaseAppCompat(flicker: FlickerTest) : BaseTest(flicker) { cmdHelper = CommandsHelper.getInstance(instrumentation) Assume.assumeTrue(tapl.isTablet && isIgnoreOrientationRequest()) letterboxStyle = mapLetterboxStyle() + resetLetterboxStyle() setLetterboxEducationEnabled(false) } @After fun after() { - resetLetterboxEducationEnabled() + resetLetterboxStyle() } private fun mapLetterboxStyle(): HashMap<String, String> { @@ -87,9 +87,8 @@ abstract class BaseAppCompat(flicker: FlickerTest) : BaseTest(flicker) { return letterboxStyle } - private fun resetLetterboxEducationEnabled() { - val enabled = getLetterboxStyle().getValue("Is education enabled") - cmdHelper.executeShellCommand("wm set-letterbox-style --isEducationEnabled $enabled") + private fun resetLetterboxStyle() { + cmdHelper.executeShellCommand("wm reset-letterbox-style") } private fun setLetterboxEducationEnabled(enabled: Boolean) { @@ -126,25 +125,21 @@ abstract class BaseAppCompat(flicker: FlickerTest) : BaseTest(flicker) { flicker.appWindowIsVisibleAtEnd(letterboxApp) } + fun assertLetterboxAppKeepVisible() { + assertLetterboxAppWindowKeepVisible() + assertLetterboxAppLayerKeepVisible() + } + fun assertAppLetterboxedAtEnd() = flicker.assertLayersEnd { isVisible(ComponentNameMatcher.LETTERBOX) } fun assertAppLetterboxedAtStart() = flicker.assertLayersStart { isVisible(ComponentNameMatcher.LETTERBOX) } + fun assertAppStaysLetterboxed() = + flicker.assertLayers { isVisible(ComponentNameMatcher.LETTERBOX) } + fun assertLetterboxAppLayerKeepVisible() = flicker.layerKeepVisible(letterboxApp) - companion object { - /** - * Creates the test configurations. - * - * See [FlickerTestFactory.rotationTests] for configuring screen orientation and - * navigation modes. - */ - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun getParams(): Collection<FlickerTest> { - return FlickerTestFactory.rotationTests() - } - } + fun assertLetterboxAppWindowKeepVisible() = flicker.appWindowKeepVisible(letterboxApp) } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/OpenAppInSizeCompatModeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/OpenAppInSizeCompatModeTest.kt index c2141a370f10..3d83455932f5 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/OpenAppInSizeCompatModeTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/OpenAppInSizeCompatModeTest.kt @@ -21,6 +21,7 @@ import android.tools.common.traces.component.ComponentNameMatcher import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.FlickerTestFactory import androidx.test.filters.RequiresDevice import org.junit.Test import org.junit.runner.RunWith @@ -90,4 +91,18 @@ class OpenAppInSizeCompatModeTest(flicker: FlickerTest) : BaseAppCompat(flicker) .isInvisible(ComponentNameMatcher.ROTATION) } } + + companion object { + /** + * Creates the test configurations. + * + * See [FlickerTestFactory.rotationTests] for configuring screen orientation and + * navigation modes. + */ + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams(): Collection<FlickerTest> { + return FlickerTestFactory.rotationTests() + } + } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/RepositionFixedPortraitAppTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/RepositionFixedPortraitAppTest.kt new file mode 100644 index 000000000000..c3355ede525e --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/RepositionFixedPortraitAppTest.kt @@ -0,0 +1,103 @@ +/* + * 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.flicker.appcompat + +import android.platform.test.annotations.Postsubmit +import android.tools.common.Rotation +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerBuilder +import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.helpers.WindowUtils + +import androidx.test.filters.RequiresDevice +import org.junit.Test + +import org.junit.runner.RunWith +import org.junit.runners.Parameterized + +/** + * Test launching a fixed portrait letterboxed app in landscape and repositioning to the right. + * + * To run this test: `atest WMShellFlickerTests:RepositionFixedPortraitAppTest` + * Actions: + * ``` + * Launch a fixed portrait app in landscape to letterbox app + * Double tap to the right to reposition app and wait for app to move + * ``` + * + * Notes: + * ``` + * Some default assertions (e.g., nav bar, status bar and screen covered) + * are inherited [BaseTest] + * ``` + */ +@RequiresDevice +@RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) +class RepositionFixedPortraitAppTest(flicker: FlickerTest) : BaseAppCompat(flicker) { + + val displayBounds = WindowUtils.getDisplayBounds(flicker.scenario.startRotation).bounds + /** {@inheritDoc} */ + override val transition: FlickerBuilder.() -> Unit + get() = { + setup { + setStartRotation() + letterboxApp.launchViaIntent(wmHelper) + } + transitions { + letterboxApp.repositionHorizontally(displayBounds, true) + letterboxApp.waitForAppToMoveHorizontallyTo(wmHelper, displayBounds, true) + } + teardown { + letterboxApp.repositionHorizontally(displayBounds, false) + letterboxApp.exit(wmHelper) + } + } + + @Postsubmit + @Test + fun letterboxedAppHasRoundedCorners() = assertLetterboxAppAtEndHasRoundedCorners() + + @Postsubmit + @Test + fun letterboxAppLayerKeepVisible() = assertLetterboxAppLayerKeepVisible() + + @Postsubmit + @Test + fun appStaysLetterboxed() = assertAppStaysLetterboxed() + + @Postsubmit + @Test + fun appKeepVisible() = assertLetterboxAppKeepVisible() + + companion object { + /** + * Creates the test configurations. + * + * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and + * navigation modes. + */ + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams(): Collection<FlickerTest> { + return FlickerTestFactory.nonRotationTests( + supportedRotations = listOf(Rotation.ROTATION_90) + ) + } + } +}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/RestartAppInSizeCompatModeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/RestartAppInSizeCompatModeTest.kt index b0e1a42306df..c2057d23a2c4 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/RestartAppInSizeCompatModeTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/RestartAppInSizeCompatModeTest.kt @@ -20,6 +20,7 @@ import android.platform.test.annotations.Postsubmit import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.FlickerTestFactory import android.tools.device.helpers.WindowUtils import androidx.test.filters.RequiresDevice import org.junit.Test @@ -88,4 +89,18 @@ class RestartAppInSizeCompatModeTest(flicker: FlickerTest) : BaseAppCompat(flick val displayBounds = WindowUtils.getDisplayBounds(flicker.scenario.endRotation) flicker.assertLayersEnd { visibleRegion(letterboxApp).coversAtMost(displayBounds) } } + + companion object { + /** + * Creates the test configurations. + * + * See [FlickerTestFactory.rotationTests] for configuring screen orientation and + * navigation modes. + */ + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams(): Collection<FlickerTest> { + return FlickerTestFactory.rotationTests() + } + } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt index d1f0980786bf..77e980532281 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt @@ -45,7 +45,7 @@ import org.junit.runners.Parameterized /** * Test copy content from the left to the right side of the split-screen. * - * To run this test: `atest WMShellFlickerTests:CopyContentInSplit` + * To run this test: `atest WMShellFlickerTestsSplitScreen:CopyContentInSplit` */ @RequiresDevice @RunWith(Parameterized::class) diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByDivider.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByDivider.kt index 4505b9978b76..dc79e76c7972 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByDivider.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByDivider.kt @@ -41,7 +41,7 @@ import org.junit.runners.Parameterized /** * Test dismiss split screen by dragging the divider bar. * - * To run this test: `atest WMShellFlickerTests:DismissSplitScreenByDivider` + * To run this test: `atest WMShellFlickerTestsSplitScreen:DismissSplitScreenByDivider` */ @RequiresDevice @RunWith(Parameterized::class) diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt index e05b22141e4d..f712fc7cf00c 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt @@ -38,7 +38,7 @@ import org.junit.runners.Parameterized /** * Test dismiss split screen by go home. * - * To run this test: `atest WMShellFlickerTests:DismissSplitScreenByGoHome` + * To run this test: `atest WMShellFlickerTestsSplitScreen:DismissSplitScreenByGoHome` */ @RequiresDevice @RunWith(Parameterized::class) diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt index 63c5d14d85e1..4a615c110819 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt @@ -43,7 +43,7 @@ import org.junit.runners.Parameterized /** * Test resize split by dragging the divider bar. * - * To run this test: `atest WMShellFlickerTests:DragDividerToResize` + * To run this test: `atest WMShellFlickerTestsSplitScreen:DragDividerToResize` */ @RequiresDevice @RunWith(Parameterized::class) diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt index e55868675da7..5c82f4bd0c7c 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt @@ -45,7 +45,7 @@ import org.junit.runners.Parameterized * Test enter split screen by dragging app icon from all apps. This test is only for large screen * devices. * - * To run this test: `atest WMShellFlickerTests:EnterSplitScreenByDragFromAllApps` + * To run this test: `atest WMShellFlickerTestsSplitScreen:EnterSplitScreenByDragFromAllApps` */ @RequiresDevice @RunWith(Parameterized::class) diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromNotification.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromNotification.kt index ab8ecc54e71c..c2e6f87903fa 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromNotification.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromNotification.kt @@ -44,7 +44,7 @@ import org.junit.runners.Parameterized * Test enter split screen by dragging app icon from notification. This test is only for large * screen devices. * - * To run this test: `atest WMShellFlickerTests:EnterSplitScreenByDragFromNotification` + * To run this test: `atest WMShellFlickerTestsSplitScreen:EnterSplitScreenByDragFromNotification` */ @RequiresDevice @RunWith(Parameterized::class) diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromShortcut.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromShortcut.kt index 516ca97bc531..3247737d32fb 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromShortcut.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromShortcut.kt @@ -41,7 +41,7 @@ import org.junit.runners.Parameterized /** * Test enter split screen by dragging a shortcut. This test is only for large screen devices. * - * To run this test: `atest WMShellFlickerTests:EnterSplitScreenByDragFromShortcut` + * To run this test: `atest WMShellFlickerTestsSplitScreen:EnterSplitScreenByDragFromShortcut` */ @RequiresDevice @RunWith(Parameterized::class) diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromTaskbar.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromTaskbar.kt index 4af7e248b660..7786c2748f73 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromTaskbar.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromTaskbar.kt @@ -45,7 +45,7 @@ import org.junit.runners.Parameterized * Test enter split screen by dragging app icon from taskbar. This test is only for large screen * devices. * - * To run this test: `atest WMShellFlickerTests:EnterSplitScreenByDragFromTaskbar` + * To run this test: `atest WMShellFlickerTestsSplitScreen:EnterSplitScreenByDragFromTaskbar` */ @RequiresDevice @RunWith(Parameterized::class) diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenFromOverview.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenFromOverview.kt index faad9e82ffef..5a9340826cf0 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenFromOverview.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenFromOverview.kt @@ -40,7 +40,7 @@ import org.junit.runners.Parameterized /** * Test enter split screen from Overview. * - * To run this test: `atest WMShellFlickerTests:EnterSplitScreenFromOverview` + * To run this test: `atest WMShellFlickerTestsSplitScreen:EnterSplitScreenFromOverview` */ @RequiresDevice @RunWith(Parameterized::class) diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt index 03b8a75a1f32..c6fb44880aa0 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt @@ -44,7 +44,7 @@ import org.junit.runners.Parameterized /** * Test double tap the divider bar to switch the two apps. * - * To run this test: `atest WMShellFlickerTests:SwitchAppByDoubleTapDivider` + * To run this test: `atest WMShellFlickerTestsSplitScreen:SwitchAppByDoubleTapDivider` */ @RequiresDevice @RunWith(Parameterized::class) diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromAnotherApp.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromAnotherApp.kt index 078d95de1dd0..90621dd3988d 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromAnotherApp.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromAnotherApp.kt @@ -39,7 +39,7 @@ import org.junit.runners.Parameterized /** * Test quick switch to split pair from another app. * - * To run this test: `atest WMShellFlickerTests:SwitchBackToSplitFromAnotherApp` + * To run this test: `atest WMShellFlickerTestsSplitScreen:SwitchBackToSplitFromAnotherApp` */ @RequiresDevice @RunWith(Parameterized::class) diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt index 7c84243e00d7..56224820fa97 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt @@ -39,7 +39,7 @@ import org.junit.runners.Parameterized /** * Test quick switch to split pair from home. * - * To run this test: `atest WMShellFlickerTests:SwitchBackToSplitFromHome` + * To run this test: `atest WMShellFlickerTestsSplitScreen:SwitchBackToSplitFromHome` */ @RequiresDevice @RunWith(Parameterized::class) diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt index 7c46d3e099a2..517f8825132c 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt @@ -39,7 +39,7 @@ import org.junit.runners.Parameterized /** * Test switch back to split pair from recent. * - * To run this test: `atest WMShellFlickerTests:SwitchBackToSplitFromRecent` + * To run this test: `atest WMShellFlickerTestsSplitScreen:SwitchBackToSplitFromRecent` */ @RequiresDevice @RunWith(Parameterized::class) diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairs.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairs.kt index 674ba40f6a1f..1e37fa53c75a 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairs.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairs.kt @@ -47,7 +47,7 @@ import org.junit.runners.Parameterized /** * Test quick switch between two split pairs. * - * To run this test: `atest WMShellFlickerTests:SwitchBetweenSplitPairs` + * To run this test: `atest WMShellFlickerTestsSplitScreen:SwitchBetweenSplitPairs` */ @RequiresDevice @RunWith(Parameterized::class) diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/UnlockKeyguardToSplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/UnlockKeyguardToSplitScreen.kt index 676c150815ad..411af21e91d0 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/UnlockKeyguardToSplitScreen.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/UnlockKeyguardToSplitScreen.kt @@ -39,7 +39,7 @@ import org.junit.runners.Parameterized /** * Test unlocking insecure keyguard to back to split screen tasks and verify the transition behavior. * - * To run this test: `atest WMShellFlickerTests:UnlockKeyguardToSplitScreen` + * To run this test: `atest WMShellFlickerTestsSplitScreen:UnlockKeyguardToSplitScreen` */ @RequiresDevice @Postsubmit diff --git a/libs/WindowManager/Shell/tests/flicker/trace_config/trace_config.textproto b/libs/WindowManager/Shell/tests/flicker/trace_config/trace_config.textproto new file mode 100644 index 000000000000..406ada97a07d --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/trace_config/trace_config.textproto @@ -0,0 +1,75 @@ +# 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. + +# proto-message: TraceConfig + +# Enable periodic flushing of the trace buffer into the output file. +write_into_file: true + +# Writes the userspace buffer into the file every 1s. +file_write_period_ms: 2500 + +# See b/126487238 - we need to guarantee ordering of events. +flush_period_ms: 30000 + +# The trace buffers needs to be big enough to hold |file_write_period_ms| of +# trace data. The trace buffer sizing depends on the number of trace categories +# enabled and the device activity. + +# RSS events +buffers: { + size_kb: 63488 + fill_policy: RING_BUFFER +} + +data_sources { + config { + name: "linux.process_stats" + target_buffer: 0 + # polled per-process memory counters and process/thread names. + # If you don't want the polled counters, remove the "process_stats_config" + # section, but keep the data source itself as it still provides on-demand + # thread/process naming for ftrace data below. + process_stats_config { + scan_all_processes_on_start: true + } + } +} + +data_sources: { + config { + name: "linux.ftrace" + ftrace_config { + ftrace_events: "ftrace/print" + ftrace_events: "task/task_newtask" + ftrace_events: "task/task_rename" + atrace_categories: "ss" + atrace_categories: "wm" + atrace_categories: "am" + atrace_categories: "aidl" + atrace_categories: "input" + atrace_categories: "binder_driver" + atrace_categories: "sched_process_exit" + atrace_apps: "com.android.server.wm.flicker.testapp" + atrace_apps: "com.android.systemui" + atrace_apps: "com.android.wm.shell.flicker" + atrace_apps: "com.android.wm.shell.flicker.other" + atrace_apps: "com.android.wm.shell.flicker.bubbles" + atrace_apps: "com.android.wm.shell.flicker.pip" + atrace_apps: "com.android.wm.shell.flicker.splitscreen" + atrace_apps: "com.google.android.apps.nexuslauncher" + } + } +} + diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/MockToken.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/MockToken.java index 09d474d1f97c..a97c19f17412 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/MockToken.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/MockToken.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.wm.shell.desktopmode; +package com.android.wm.shell; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -25,16 +25,16 @@ import android.window.WindowContainerToken; /** * {@link WindowContainerToken} wrapper that supports a mock binder */ -class MockToken { +public class MockToken { private final WindowContainerToken mToken; - MockToken() { + public MockToken() { mToken = mock(WindowContainerToken.class); IBinder binder = mock(IBinder.class); when(mToken.asBinder()).thenReturn(binder); } - WindowContainerToken token() { + public WindowContainerToken token() { return mToken; } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/bar/BubbleBarHandleViewTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/bar/BubbleBarHandleViewTest.java new file mode 100644 index 000000000000..d38b848fbb4d --- /dev/null +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/bar/BubbleBarHandleViewTest.java @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.wm.shell.bubbles.bar; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import android.graphics.drawable.ColorDrawable; +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; + +import androidx.core.content.ContextCompat; +import androidx.test.filters.SmallTest; + +import com.android.wm.shell.R; +import com.android.wm.shell.ShellTestCase; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +@SmallTest +@RunWith(AndroidTestingRunner.class) +@TestableLooper.RunWithLooper +public class BubbleBarHandleViewTest extends ShellTestCase { + private BubbleBarHandleView mHandleView; + + @Before + public void setup() { + mHandleView = new BubbleBarHandleView(mContext); + } + + @Test + public void testUpdateHandleColor_lightBg() { + mHandleView.updateHandleColor(false /* isRegionDark */, false /* animated */); + + assertTrue(mHandleView.getClipToOutline()); + assertTrue(mHandleView.getBackground() instanceof ColorDrawable); + ColorDrawable bgDrawable = (ColorDrawable) mHandleView.getBackground(); + assertEquals(bgDrawable.getColor(), + ContextCompat.getColor(mContext, R.color.bubble_bar_expanded_view_handle_dark)); + } + + @Test + public void testUpdateHandleColor_darkBg() { + mHandleView.updateHandleColor(true /* isRegionDark */, false /* animated */); + + assertTrue(mHandleView.getClipToOutline()); + assertTrue(mHandleView.getBackground() instanceof ColorDrawable); + ColorDrawable bgDrawable = (ColorDrawable) mHandleView.getBackground(); + assertEquals(bgDrawable.getColor(), + ContextCompat.getColor(mContext, R.color.bubble_bar_expanded_view_handle_light)); + } +} diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/LaunchAdjacentControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/LaunchAdjacentControllerTest.kt new file mode 100644 index 000000000000..9dc816b65d2e --- /dev/null +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/LaunchAdjacentControllerTest.kt @@ -0,0 +1,172 @@ +/* + * 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.common + +import android.os.IBinder +import android.testing.AndroidTestingRunner +import android.window.WindowContainerTransaction +import android.window.WindowContainerTransaction.HierarchyOp +import androidx.test.filters.SmallTest +import com.android.wm.shell.MockToken +import com.android.wm.shell.ShellTestCase +import com.google.common.truth.Truth.assertThat +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.ArgumentCaptor +import org.mockito.Mock +import org.mockito.Mockito.any +import org.mockito.Mockito.atLeastOnce +import org.mockito.Mockito.clearInvocations +import org.mockito.Mockito.never +import org.mockito.Mockito.verify + +@SmallTest +@RunWith(AndroidTestingRunner::class) +class LaunchAdjacentControllerTest : ShellTestCase() { + + private lateinit var controller: LaunchAdjacentController + + @Mock private lateinit var syncQueue: SyncTransactionQueue + + @Before + fun setUp() { + controller = LaunchAdjacentController(syncQueue) + } + + @Test + fun newInstance_enabledByDefault() { + assertThat(controller.launchAdjacentEnabled).isTrue() + } + + @Test + fun setLaunchAdjacentRoot_launchAdjacentEnabled_setsFlagRoot() { + val token = MockToken().token() + controller.setLaunchAdjacentRoot(token) + val wct = getLatestTransactionOrFail() + assertThat(wct.getSetLaunchAdjacentFlagRootContainer()).isEqualTo(token.asBinder()) + } + + @Test + fun setLaunchAdjacentRoot_launchAdjacentDisabled_doesNotUpdateFlagRoot() { + val token = MockToken().token() + controller.launchAdjacentEnabled = false + controller.setLaunchAdjacentRoot(token) + verify(syncQueue, never()).queue(any()) + } + + @Test + fun clearLaunchAdjacentRoot_launchAdjacentEnabled_clearsFlagRoot() { + val token = MockToken().token() + controller.setLaunchAdjacentRoot(token) + controller.clearLaunchAdjacentRoot() + val wct = getLatestTransactionOrFail() + assertThat(wct.getClearLaunchAdjacentFlagRootContainer()).isEqualTo(token.asBinder()) + } + + @Test + fun clearLaunchAdjacentRoot_launchAdjacentDisabled_clearsFlagRoot() { + val token = MockToken().token() + controller.setLaunchAdjacentRoot(token) + controller.launchAdjacentEnabled = false + clearInvocations(syncQueue) + + controller.clearLaunchAdjacentRoot() + val wct = getLatestTransactionOrFail() + assertThat(wct.getClearLaunchAdjacentFlagRootContainer()).isEqualTo(token.asBinder()) + } + + @Test + fun setLaunchAdjacentEnabled_wasDisabledWithContainerSet_setsFlagRoot() { + val token = MockToken().token() + controller.setLaunchAdjacentRoot(token) + controller.launchAdjacentEnabled = false + clearInvocations(syncQueue) + + controller.launchAdjacentEnabled = true + val wct = getLatestTransactionOrFail() + assertThat(wct.getSetLaunchAdjacentFlagRootContainer()).isEqualTo(token.asBinder()) + } + + @Test + fun setLaunchAdjacentEnabled_containerNotSet_doesNotUpdateFlagRoot() { + controller.launchAdjacentEnabled = false + controller.launchAdjacentEnabled = true + verify(syncQueue, never()).queue(any()) + } + + @Test + fun setLaunchAdjacentEnabled_multipleTimes_setsFlagRootOnce() { + val token = MockToken().token() + controller.setLaunchAdjacentRoot(token) + controller.launchAdjacentEnabled = true + controller.launchAdjacentEnabled = true + // Only execute once + verify(syncQueue).queue(any()) + } + + @Test + fun setLaunchAdjacentDisabled_containerSet_clearsFlagRoot() { + val token = MockToken().token() + controller.setLaunchAdjacentRoot(token) + controller.launchAdjacentEnabled = false + val wct = getLatestTransactionOrFail() + assertThat(wct.getClearLaunchAdjacentFlagRootContainer()).isEqualTo(token.asBinder()) + } + + @Test + fun setLaunchAdjacentDisabled_containerNotSet_doesNotUpdateFlagRoot() { + controller.launchAdjacentEnabled = false + verify(syncQueue, never()).queue(any()) + } + + @Test + fun setLaunchAdjacentDisabled_multipleTimes_setsFlagRootOnce() { + val token = MockToken().token() + controller.setLaunchAdjacentRoot(token) + clearInvocations(syncQueue) + controller.launchAdjacentEnabled = false + controller.launchAdjacentEnabled = false + // Only execute once + verify(syncQueue).queue(any()) + } + + private fun getLatestTransactionOrFail(): WindowContainerTransaction { + val arg = ArgumentCaptor.forClass(WindowContainerTransaction::class.java) + verify(syncQueue, atLeastOnce()).queue(arg.capture()) + return arg.allValues.last().also { assertThat(it).isNotNull() } + } +} + +private fun WindowContainerTransaction.getSetLaunchAdjacentFlagRootContainer(): IBinder { + return hierarchyOps + // Find the operation with the correct type + .filter { op -> op.type == HierarchyOp.HIERARCHY_OP_TYPE_SET_LAUNCH_ADJACENT_FLAG_ROOT } + // For set flag root operation, toTop is false + .filter { op -> !op.toTop } + .map { it.container } + .first() +} + +private fun WindowContainerTransaction.getClearLaunchAdjacentFlagRootContainer(): IBinder { + return hierarchyOps + // Find the operation with the correct type + .filter { op -> op.type == HierarchyOp.HIERARCHY_OP_TYPE_SET_LAUNCH_ADJACENT_FLAG_ROOT } + // For clear flag root operation, toTop is true + .filter { op -> op.toTop } + .map { it.container } + .first() +} diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/DividerViewTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/DividerViewTest.java new file mode 100644 index 000000000000..145c8f0ab8af --- /dev/null +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/DividerViewTest.java @@ -0,0 +1,123 @@ +/* + * 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.common.split; + +import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; + +import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.CURSOR_HOVER_STATES_ENABLED; + +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import android.content.res.Configuration; +import android.graphics.Rect; +import android.os.SystemClock; +import android.provider.DeviceConfig; +import android.view.InputDevice; +import android.view.InsetsState; +import android.view.MotionEvent; + +import androidx.test.annotation.UiThreadTest; +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.filters.SmallTest; + +import com.android.wm.shell.ShellTaskOrganizer; +import com.android.wm.shell.ShellTestCase; +import com.android.wm.shell.common.DisplayController; +import com.android.wm.shell.common.DisplayImeController; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +/** Tests for {@link DividerView} */ +@SmallTest +@RunWith(AndroidJUnit4.class) +public class DividerViewTest extends ShellTestCase { + private @Mock SplitWindowManager.ParentContainerCallbacks mCallbacks; + private @Mock SplitLayout.SplitLayoutHandler mSplitLayoutHandler; + private @Mock DisplayController mDisplayController; + private @Mock DisplayImeController mDisplayImeController; + private @Mock ShellTaskOrganizer mTaskOrganizer; + private SplitLayout mSplitLayout; + private DividerView mDividerView; + + @Before + @UiThreadTest + public void setup() { + MockitoAnnotations.initMocks(this); + Configuration configuration = getConfiguration(); + mSplitLayout = new SplitLayout("TestSplitLayout", mContext, configuration, + mSplitLayoutHandler, mCallbacks, mDisplayController, mDisplayImeController, + mTaskOrganizer, SplitLayout.PARALLAX_NONE); + SplitWindowManager splitWindowManager = new SplitWindowManager("TestSplitWindowManager", + mContext, + configuration, mCallbacks); + splitWindowManager.init(mSplitLayout, new InsetsState()); + mDividerView = spy((DividerView) splitWindowManager.getDividerView()); + } + + @Test + @UiThreadTest + public void testHoverDividerView() { + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI, CURSOR_HOVER_STATES_ENABLED, + "true", false); + + Rect dividerBounds = mSplitLayout.getDividerBounds(); + int x = dividerBounds.centerX(); + int y = dividerBounds.centerY(); + long downTime = SystemClock.uptimeMillis(); + mDividerView.onHoverEvent(getMotionEvent(downTime, MotionEvent.ACTION_HOVER_ENTER, x, y)); + + verify(mDividerView, times(1)).setHovering(); + + mDividerView.onHoverEvent(getMotionEvent(downTime, MotionEvent.ACTION_HOVER_EXIT, x, y)); + + verify(mDividerView, times(1)).releaseHovering(); + + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI, CURSOR_HOVER_STATES_ENABLED, + "false", false); + } + + private static MotionEvent getMotionEvent(long eventTime, int action, float x, float y) { + MotionEvent.PointerProperties properties = new MotionEvent.PointerProperties(); + properties.id = 0; + properties.toolType = MotionEvent.TOOL_TYPE_UNKNOWN; + + MotionEvent.PointerCoords coords = new MotionEvent.PointerCoords(); + coords.pressure = 1; + coords.size = 1; + coords.x = x; + coords.y = y; + + return MotionEvent.obtain(eventTime, eventTime, action, 1, + new MotionEvent.PointerProperties[]{properties}, + new MotionEvent.PointerCoords[]{coords}, 0, 0, 1.0f, 1.0f, 0, 0, + InputDevice.SOURCE_TOUCHSCREEN, 0); + } + + private static Configuration getConfiguration() { + final Configuration configuration = new Configuration(); + configuration.unset(); + configuration.orientation = ORIENTATION_LANDSCAPE; + configuration.windowConfiguration.setRotation(0); + configuration.windowConfiguration.setBounds(new Rect(0, 0, 1080, 2160)); + return configuration; + } +} diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeControllerTest.java index d6387ee5ae13..605a762a395f 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeControllerTest.java @@ -59,6 +59,7 @@ import android.window.WindowContainerTransaction.HierarchyOp; import androidx.test.filters.SmallTest; import com.android.dx.mockito.inline.extended.StaticMockitoSession; +import com.android.wm.shell.MockToken; import com.android.wm.shell.RootTaskDisplayAreaOrganizer; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.ShellTestCase; diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepositoryTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepositoryTest.kt index 3bc2f0e8674e..3fe78efdf2b1 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepositoryTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepositoryTest.kt @@ -129,6 +129,18 @@ class DesktopModeTaskRepositoryTest : ShellTestCase() { } @Test + fun addListener_notifiesStashed() { + repo.setStashed(DEFAULT_DISPLAY, true) + val listener = TestVisibilityListener() + val executor = TestShellExecutor() + repo.addVisibleTasksListener(listener, executor) + executor.flushAll() + + assertThat(listener.stashedOnDefaultDisplay).isTrue() + assertThat(listener.stashedChangesOnDefaultDisplay).isEqualTo(1) + } + + @Test fun addListener_tasksOnDifferentDisplay_doesNotNotify() { repo.updateVisibleFreeformTasks(SECOND_DISPLAY, taskId = 1, visible = true) val listener = TestVisibilityListener() @@ -313,6 +325,65 @@ class DesktopModeTaskRepositoryTest : ShellTestCase() { assertThat(tasks.first()).isEqualTo(6) } + @Test + fun setStashed_stateIsUpdatedForTheDisplay() { + repo.setStashed(DEFAULT_DISPLAY, true) + assertThat(repo.isStashed(DEFAULT_DISPLAY)).isTrue() + assertThat(repo.isStashed(SECOND_DISPLAY)).isFalse() + + repo.setStashed(DEFAULT_DISPLAY, false) + assertThat(repo.isStashed(DEFAULT_DISPLAY)).isFalse() + } + + @Test + fun setStashed_notifyListener() { + val listener = TestVisibilityListener() + val executor = TestShellExecutor() + repo.addVisibleTasksListener(listener, executor) + repo.setStashed(DEFAULT_DISPLAY, true) + executor.flushAll() + assertThat(listener.stashedOnDefaultDisplay).isTrue() + assertThat(listener.stashedChangesOnDefaultDisplay).isEqualTo(1) + + repo.setStashed(DEFAULT_DISPLAY, false) + executor.flushAll() + assertThat(listener.stashedOnDefaultDisplay).isFalse() + assertThat(listener.stashedChangesOnDefaultDisplay).isEqualTo(2) + } + + @Test + fun setStashed_secondCallDoesNotNotify() { + val listener = TestVisibilityListener() + val executor = TestShellExecutor() + repo.addVisibleTasksListener(listener, executor) + repo.setStashed(DEFAULT_DISPLAY, true) + repo.setStashed(DEFAULT_DISPLAY, true) + executor.flushAll() + assertThat(listener.stashedChangesOnDefaultDisplay).isEqualTo(1) + } + + @Test + fun setStashed_tracksPerDisplay() { + val listener = TestVisibilityListener() + val executor = TestShellExecutor() + repo.addVisibleTasksListener(listener, executor) + + repo.setStashed(DEFAULT_DISPLAY, true) + executor.flushAll() + assertThat(listener.stashedOnDefaultDisplay).isTrue() + assertThat(listener.stashedOnSecondaryDisplay).isFalse() + + repo.setStashed(SECOND_DISPLAY, true) + executor.flushAll() + assertThat(listener.stashedOnDefaultDisplay).isTrue() + assertThat(listener.stashedOnSecondaryDisplay).isTrue() + + repo.setStashed(DEFAULT_DISPLAY, false) + executor.flushAll() + assertThat(listener.stashedOnDefaultDisplay).isFalse() + assertThat(listener.stashedOnSecondaryDisplay).isTrue() + } + class TestListener : DesktopModeTaskRepository.ActiveTasksListener { var activeChangesOnDefaultDisplay = 0 var activeChangesOnSecondaryDisplay = 0 @@ -332,6 +403,12 @@ class DesktopModeTaskRepositoryTest : ShellTestCase() { var visibleChangesOnDefaultDisplay = 0 var visibleChangesOnSecondaryDisplay = 0 + var stashedOnDefaultDisplay = false + var stashedOnSecondaryDisplay = false + + var stashedChangesOnDefaultDisplay = 0 + var stashedChangesOnSecondaryDisplay = 0 + override fun onVisibilityChanged(displayId: Int, hasVisibleFreeformTasks: Boolean) { when (displayId) { DEFAULT_DISPLAY -> { @@ -345,6 +422,20 @@ class DesktopModeTaskRepositoryTest : ShellTestCase() { else -> fail("Visible task listener received unexpected display id: $displayId") } } + + override fun onStashedChanged(displayId: Int, stashed: Boolean) { + when (displayId) { + DEFAULT_DISPLAY -> { + stashedOnDefaultDisplay = stashed + stashedChangesOnDefaultDisplay++ + } + SECOND_DISPLAY -> { + stashedOnSecondaryDisplay = stashed + stashedChangesOnDefaultDisplay++ + } + else -> fail("Visible task listener received unexpected display id: $displayId") + } + } } companion object { diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt index 1335ebf105a6..1477cf7415cf 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt @@ -39,17 +39,20 @@ import androidx.test.filters.SmallTest import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession import com.android.dx.mockito.inline.extended.ExtendedMockito.never import com.android.dx.mockito.inline.extended.StaticMockitoSession +import com.android.wm.shell.MockToken import com.android.wm.shell.RootTaskDisplayAreaOrganizer import com.android.wm.shell.ShellTaskOrganizer import com.android.wm.shell.ShellTestCase import com.android.wm.shell.TestRunningTaskInfoBuilder import com.android.wm.shell.TestShellExecutor import com.android.wm.shell.common.DisplayController +import com.android.wm.shell.common.LaunchAdjacentController import com.android.wm.shell.common.ShellExecutor import com.android.wm.shell.common.SyncTransactionQueue import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createFreeformTask import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createFullscreenTask import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createHomeTask +import com.android.wm.shell.sysui.ShellCommandHandler import com.android.wm.shell.sysui.ShellController import com.android.wm.shell.sysui.ShellInit import com.android.wm.shell.transition.Transitions @@ -77,6 +80,7 @@ import org.mockito.Mockito.`when` as whenever class DesktopTasksControllerTest : ShellTestCase() { @Mock lateinit var testExecutor: ShellExecutor + @Mock lateinit var shellCommandHandler: ShellCommandHandler @Mock lateinit var shellController: ShellController @Mock lateinit var displayController: DisplayController @Mock lateinit var shellTaskOrganizer: ShellTaskOrganizer @@ -85,12 +89,16 @@ class DesktopTasksControllerTest : ShellTestCase() { @Mock lateinit var transitions: Transitions @Mock lateinit var exitDesktopTransitionHandler: ExitDesktopTaskTransitionHandler @Mock lateinit var enterDesktopTransitionHandler: EnterDesktopTaskTransitionHandler + @Mock lateinit var mToggleResizeDesktopTaskTransitionHandler: + ToggleResizeDesktopTaskTransitionHandler + @Mock lateinit var launchAdjacentController: LaunchAdjacentController private lateinit var mockitoSession: StaticMockitoSession private lateinit var controller: DesktopTasksController private lateinit var shellInit: ShellInit private lateinit var desktopModeTaskRepository: DesktopModeTaskRepository + private val shellExecutor = TestShellExecutor() // Mock running tasks are registered here so we can get the list from mock shell task organizer private val runningTasks = mutableListOf<RunningTaskInfo>() @@ -114,6 +122,7 @@ class DesktopTasksControllerTest : ShellTestCase() { return DesktopTasksController( context, shellInit, + shellCommandHandler, shellController, displayController, shellTaskOrganizer, @@ -122,8 +131,10 @@ class DesktopTasksControllerTest : ShellTestCase() { transitions, enterDesktopTransitionHandler, exitDesktopTransitionHandler, + mToggleResizeDesktopTaskTransitionHandler, desktopModeTaskRepository, - TestShellExecutor() + launchAdjacentController, + shellExecutor ) } @@ -262,8 +273,9 @@ class DesktopTasksControllerTest : ShellTestCase() { } @Test - fun moveToDesktop() { + fun moveToDesktop_displayFullscreen_windowingModeSetToFreeform() { val task = setUpFullscreenTask() + task.configuration.windowConfiguration.displayWindowingMode = WINDOWING_MODE_FULLSCREEN controller.moveToDesktop(task) val wct = getLatestWct(expectTransition = TRANSIT_CHANGE) assertThat(wct.changes[task.token.asBinder()]?.windowingMode) @@ -271,6 +283,16 @@ class DesktopTasksControllerTest : ShellTestCase() { } @Test + fun moveToDesktop_displayFreeform_windowingModeSetToUndefined() { + val task = setUpFullscreenTask() + task.configuration.windowConfiguration.displayWindowingMode = WINDOWING_MODE_FREEFORM + controller.moveToDesktop(task) + val wct = getLatestWct(expectTransition = TRANSIT_CHANGE) + assertThat(wct.changes[task.token.asBinder()]?.windowingMode) + .isEqualTo(WINDOWING_MODE_UNDEFINED) + } + + @Test fun moveToDesktop_nonExistentTask_doesNothing() { controller.moveToDesktop(999) verifyWCTNotExecuted() @@ -317,12 +339,23 @@ class DesktopTasksControllerTest : ShellTestCase() { } @Test - fun moveToFullscreen() { + fun moveToFullscreen_displayFullscreen_windowingModeSetToUndefined() { val task = setUpFreeformTask() + task.configuration.windowConfiguration.displayWindowingMode = WINDOWING_MODE_FULLSCREEN controller.moveToFullscreen(task) val wct = getLatestWct(expectTransition = TRANSIT_CHANGE) assertThat(wct.changes[task.token.asBinder()]?.windowingMode) - .isEqualTo(WINDOWING_MODE_FULLSCREEN) + .isEqualTo(WINDOWING_MODE_UNDEFINED) + } + + @Test + fun moveToFullscreen_displayFreeform_windowingModeSetToFullscreen() { + val task = setUpFreeformTask() + task.configuration.windowConfiguration.displayWindowingMode = WINDOWING_MODE_FREEFORM + controller.moveToFullscreen(task) + val wct = getLatestWct(expectTransition = TRANSIT_CHANGE) + assertThat(wct.changes[task.token.asBinder()]?.windowingMode) + .isEqualTo(WINDOWING_MODE_FULLSCREEN) } @Test @@ -345,6 +378,18 @@ class DesktopTasksControllerTest : ShellTestCase() { } @Test + fun moveTaskToFront_postsWctWithReorderOp() { + val task1 = setUpFreeformTask() + setUpFreeformTask() + + controller.moveTaskToFront(task1) + + val wct = getLatestWct(expectTransition = TRANSIT_TO_FRONT) + assertThat(wct.hierarchyOps).hasSize(1) + wct.assertReorderAt(index = 0, task1) + } + + @Test fun moveToNextDisplay_noOtherDisplays() { whenever(rootTaskDisplayAreaOrganizer.displayIds).thenReturn(intArrayOf(DEFAULT_DISPLAY)) val task = setUpFreeformTask(displayId = DEFAULT_DISPLAY) @@ -451,6 +496,27 @@ class DesktopTasksControllerTest : ShellTestCase() { } @Test + fun handleRequest_fullscreenTask_desktopStashed_returnWCTWithAllAppsBroughtToFront() { + assumeTrue(ENABLE_SHELL_TRANSITIONS) + + val stashedFreeformTask = setUpFreeformTask(DEFAULT_DISPLAY) + markTaskHidden(stashedFreeformTask) + + val fullscreenTask = createFullscreenTask(DEFAULT_DISPLAY) + + controller.stashDesktopApps(DEFAULT_DISPLAY) + + val result = controller.handleRequest(Binder(), createTransition(fullscreenTask)) + assertThat(result).isNotNull() + result!!.assertReorderSequence(stashedFreeformTask, fullscreenTask) + assertThat(result.changes[fullscreenTask.token.asBinder()]?.windowingMode) + .isEqualTo(WINDOWING_MODE_FREEFORM) + + // Stashed state should be cleared + assertThat(desktopModeTaskRepository.isStashed(DEFAULT_DISPLAY)).isFalse() + } + + @Test fun handleRequest_freeformTask_freeformVisible_returnNull() { assumeTrue(ENABLE_SHELL_TRANSITIONS) @@ -501,6 +567,25 @@ class DesktopTasksControllerTest : ShellTestCase() { } @Test + fun handleRequest_freeformTask_desktopStashed_returnWCTWithAllAppsBroughtToFront() { + assumeTrue(ENABLE_SHELL_TRANSITIONS) + + val stashedFreeformTask = setUpFreeformTask(DEFAULT_DISPLAY) + markTaskHidden(stashedFreeformTask) + + val freeformTask = createFreeformTask(DEFAULT_DISPLAY) + + controller.stashDesktopApps(DEFAULT_DISPLAY) + + val result = controller.handleRequest(Binder(), createTransition(freeformTask)) + assertThat(result).isNotNull() + result?.assertReorderSequence(stashedFreeformTask, freeformTask) + + // Stashed state should be cleared + assertThat(desktopModeTaskRepository.isStashed(DEFAULT_DISPLAY)).isFalse() + } + + @Test fun handleRequest_notOpenOrToFrontTransition_returnNull() { assumeTrue(ENABLE_SHELL_TRANSITIONS) @@ -539,6 +624,46 @@ class DesktopTasksControllerTest : ShellTestCase() { assertThat(controller.handleRequest(Binder(), createTransition(task))).isNull() } + @Test + fun stashDesktopApps_stateUpdates() { + controller.stashDesktopApps(DEFAULT_DISPLAY) + + assertThat(desktopModeTaskRepository.isStashed(DEFAULT_DISPLAY)).isTrue() + assertThat(desktopModeTaskRepository.isStashed(SECOND_DISPLAY)).isFalse() + } + + @Test + fun hideStashedDesktopApps_stateUpdates() { + desktopModeTaskRepository.setStashed(DEFAULT_DISPLAY, true) + desktopModeTaskRepository.setStashed(SECOND_DISPLAY, true) + controller.hideStashedDesktopApps(DEFAULT_DISPLAY) + + assertThat(desktopModeTaskRepository.isStashed(DEFAULT_DISPLAY)).isFalse() + // Check that second display is not affected + assertThat(desktopModeTaskRepository.isStashed(SECOND_DISPLAY)).isTrue() + } + + @Test + fun desktopTasksVisibilityChange_visible_setLaunchAdjacentDisabled() { + val task = setUpFreeformTask() + clearInvocations(launchAdjacentController) + + markTaskVisible(task) + shellExecutor.flushAll() + verify(launchAdjacentController).launchAdjacentEnabled = false + } + + @Test + fun desktopTasksVisibilityChange_invisible_setLaunchAdjacentEnabled() { + val task = setUpFreeformTask() + markTaskVisible(task) + clearInvocations(launchAdjacentController) + + markTaskHidden(task) + shellExecutor.flushAll() + verify(launchAdjacentController).launchAdjacentEnabled = true + } + private fun setUpFreeformTask(displayId: Int = DEFAULT_DISPLAY): RunningTaskInfo { val task = createFreeformTask(displayId) whenever(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task) diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTestHelpers.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTestHelpers.kt index cf1ff3214d87..29a757c19d98 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTestHelpers.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTestHelpers.kt @@ -22,6 +22,7 @@ import android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN import android.view.Display.DEFAULT_DISPLAY +import com.android.wm.shell.MockToken import com.android.wm.shell.TestRunningTaskInfoBuilder class DesktopTestHelpers { diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/MainStageTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/MainStageTests.java index 68cb57c14d8c..b1befc46f383 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/MainStageTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/MainStageTests.java @@ -41,6 +41,8 @@ import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.util.Optional; + /** Tests for {@link MainStage} */ @SmallTest @RunWith(AndroidJUnit4.class) @@ -61,7 +63,7 @@ public class MainStageTests extends ShellTestCase { MockitoAnnotations.initMocks(this); mRootTaskInfo = new TestRunningTaskInfoBuilder().build(); mMainStage = new MainStage(mContext, mTaskOrganizer, DEFAULT_DISPLAY, mCallbacks, - mSyncQueue, mSurfaceSession, mIconProvider); + mSyncQueue, mSurfaceSession, mIconProvider, Optional.empty()); mMainStage.onTaskAppeared(mRootTaskInfo, mRootLeash); } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SideStageTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SideStageTests.java index 3b42a48b5a40..549bd3fcabfb 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SideStageTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SideStageTests.java @@ -46,6 +46,8 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.mockito.Spy; +import java.util.Optional; + /** Tests for {@link SideStage} */ @SmallTest @RunWith(AndroidJUnit4.class) @@ -66,7 +68,7 @@ public class SideStageTests extends ShellTestCase { MockitoAnnotations.initMocks(this); mRootTask = new TestRunningTaskInfoBuilder().build(); mSideStage = new SideStage(mContext, mTaskOrganizer, DEFAULT_DISPLAY, mCallbacks, - mSyncQueue, mSurfaceSession, mIconProvider); + mSyncQueue, mSurfaceSession, mIconProvider, Optional.empty()); mSideStage.onTaskAppeared(mRootTask, mRootLeash); } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java index fb17d8799bda..e8a1e91acd4d 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java @@ -61,6 +61,7 @@ import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.DisplayImeController; import com.android.wm.shell.common.DisplayInsetsController; import com.android.wm.shell.common.DisplayLayout; +import com.android.wm.shell.common.LaunchAdjacentController; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.common.TransactionPool; @@ -71,6 +72,7 @@ import com.android.wm.shell.sysui.ShellController; import com.android.wm.shell.sysui.ShellInit; import com.android.wm.shell.sysui.ShellSharedConstants; import com.android.wm.shell.transition.Transitions; +import com.android.wm.shell.windowdecor.WindowDecorViewModel; import org.junit.Before; import org.junit.Test; @@ -102,6 +104,8 @@ public class SplitScreenControllerTests extends ShellTestCase { @Mock IconProvider mIconProvider; @Mock StageCoordinator mStageCoordinator; @Mock RecentTasksController mRecentTasks; + @Mock LaunchAdjacentController mLaunchAdjacentController; + @Mock WindowDecorViewModel mWindowDecorViewModel; @Captor ArgumentCaptor<Intent> mIntentCaptor; private ShellController mShellController; @@ -117,7 +121,8 @@ public class SplitScreenControllerTests extends ShellTestCase { mShellCommandHandler, mShellController, mTaskOrganizer, mSyncQueue, mRootTDAOrganizer, mDisplayController, mDisplayImeController, mDisplayInsetsController, mDragAndDropController, mTransitions, mTransactionPool, - mIconProvider, mRecentTasks, mMainExecutor, mStageCoordinator)); + mIconProvider, mRecentTasks, mLaunchAdjacentController, mWindowDecorViewModel, + mMainExecutor, mStageCoordinator)); } @Test diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java index 4e446c684d86..a3009a55198f 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java @@ -31,12 +31,14 @@ import com.android.wm.shell.TestRunningTaskInfoBuilder; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.DisplayImeController; import com.android.wm.shell.common.DisplayInsetsController; +import com.android.wm.shell.common.LaunchAdjacentController; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.common.TransactionPool; import com.android.wm.shell.common.split.SplitLayout; import com.android.wm.shell.recents.RecentTasksController; import com.android.wm.shell.transition.Transitions; +import com.android.wm.shell.windowdecor.WindowDecorViewModel; import java.util.Optional; @@ -76,10 +78,13 @@ public class SplitTestUtils { DisplayInsetsController insetsController, SplitLayout splitLayout, Transitions transitions, TransactionPool transactionPool, ShellExecutor mainExecutor, - Optional<RecentTasksController> recentTasks) { + Optional<RecentTasksController> recentTasks, + LaunchAdjacentController launchAdjacentController, + Optional<WindowDecorViewModel> windowDecorViewModel) { super(context, displayId, syncQueue, taskOrganizer, mainStage, sideStage, displayController, imeController, insetsController, splitLayout, - transitions, transactionPool, mainExecutor, recentTasks); + transitions, transactionPool, mainExecutor, recentTasks, + launchAdjacentController, windowDecorViewModel); // Prepare root task for testing. mRootTask = new TestRunningTaskInfoBuilder().build(); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java index 3b05651f884b..5efd9ad97a3e 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java @@ -70,12 +70,15 @@ import com.android.wm.shell.TransitionInfoBuilder; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.DisplayImeController; import com.android.wm.shell.common.DisplayInsetsController; +import com.android.wm.shell.common.LaunchAdjacentController; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.common.TransactionPool; import com.android.wm.shell.common.split.SplitDecorManager; import com.android.wm.shell.common.split.SplitLayout; +import com.android.wm.shell.transition.DefaultMixedHandler; import com.android.wm.shell.transition.Transitions; +import com.android.wm.shell.windowdecor.WindowDecorViewModel; import org.junit.Before; import org.junit.Test; @@ -100,7 +103,10 @@ public class SplitTransitionTests extends ShellTestCase { @Mock private Transitions mTransitions; @Mock private SurfaceSession mSurfaceSession; @Mock private IconProvider mIconProvider; + @Mock private WindowDecorViewModel mWindowDecorViewModel; @Mock private ShellExecutor mMainExecutor; + @Mock private LaunchAdjacentController mLaunchAdjacentController; + @Mock private DefaultMixedHandler mMixedHandler; private SplitLayout mSplitLayout; private MainStage mMainStage; private SideStage mSideStage; @@ -121,16 +127,18 @@ public class SplitTransitionTests extends ShellTestCase { mSplitLayout = SplitTestUtils.createMockSplitLayout(); mMainStage = spy(new MainStage(mContext, mTaskOrganizer, DEFAULT_DISPLAY, mock( StageTaskListener.StageListenerCallbacks.class), mSyncQueue, mSurfaceSession, - mIconProvider)); + mIconProvider, Optional.of(mWindowDecorViewModel))); mMainStage.onTaskAppeared(new TestRunningTaskInfoBuilder().build(), createMockSurface()); mSideStage = spy(new SideStage(mContext, mTaskOrganizer, DEFAULT_DISPLAY, mock( StageTaskListener.StageListenerCallbacks.class), mSyncQueue, mSurfaceSession, - mIconProvider)); + mIconProvider, Optional.of(mWindowDecorViewModel))); mSideStage.onTaskAppeared(new TestRunningTaskInfoBuilder().build(), createMockSurface()); mStageCoordinator = new SplitTestUtils.TestStageCoordinator(mContext, DEFAULT_DISPLAY, mSyncQueue, mTaskOrganizer, mMainStage, mSideStage, mDisplayController, mDisplayImeController, mDisplayInsetsController, mSplitLayout, mTransitions, - mTransactionPool, mMainExecutor, Optional.empty()); + mTransactionPool, mMainExecutor, Optional.empty(), + mLaunchAdjacentController, Optional.empty()); + mStageCoordinator.setMixedHandler(mMixedHandler); mSplitScreenTransitions = mStageCoordinator.getSplitTransitions(); doAnswer((Answer<IBinder>) invocation -> mock(IBinder.class)) .when(mTransitions).startTransition(anyInt(), any(), any()); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java index 2dcdc74e8ae7..e59d09cd1ee1 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java @@ -65,6 +65,7 @@ import com.android.wm.shell.TestShellExecutor; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.DisplayImeController; import com.android.wm.shell.common.DisplayInsetsController; +import com.android.wm.shell.common.LaunchAdjacentController; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.common.TransactionPool; @@ -107,6 +108,8 @@ public class StageCoordinatorTests extends ShellTestCase { private DisplayInsetsController mDisplayInsetsController; @Mock private TransactionPool mTransactionPool; + @Mock + private LaunchAdjacentController mLaunchAdjacentController; private final Rect mBounds1 = new Rect(10, 20, 30, 40); private final Rect mBounds2 = new Rect(5, 10, 15, 20); @@ -130,7 +133,7 @@ public class StageCoordinatorTests extends ShellTestCase { mStageCoordinator = spy(new StageCoordinator(mContext, DEFAULT_DISPLAY, mSyncQueue, mTaskOrganizer, mMainStage, mSideStage, mDisplayController, mDisplayImeController, mDisplayInsetsController, mSplitLayout, mTransitions, mTransactionPool, - mMainExecutor, Optional.empty())); + mMainExecutor, Optional.empty(), mLaunchAdjacentController, Optional.empty())); mDividerLeash = new SurfaceControl.Builder(mSurfaceSession).setName("fakeDivider").build(); when(mSplitLayout.getBounds1()).thenReturn(mBounds1); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java index 1a1bebd28aef..df1e2e16f485 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java @@ -43,6 +43,7 @@ import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.ShellTestCase; import com.android.wm.shell.TestRunningTaskInfoBuilder; import com.android.wm.shell.common.SyncTransactionQueue; +import com.android.wm.shell.windowdecor.WindowDecorViewModel; import org.junit.Before; import org.junit.Test; @@ -52,6 +53,8 @@ import org.mockito.Captor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.util.Optional; + /** * Tests for {@link StageTaskListener} * Build/Install/Run: @@ -71,6 +74,8 @@ public final class StageTaskListenerTests extends ShellTestCase { private SyncTransactionQueue mSyncQueue; @Mock private IconProvider mIconProvider; + @Mock + private WindowDecorViewModel mWindowDecorViewModel; @Captor private ArgumentCaptor<SyncTransactionQueue.TransactionRunnable> mRunnableCaptor; private SurfaceSession mSurfaceSession = new SurfaceSession(); @@ -89,7 +94,8 @@ public final class StageTaskListenerTests extends ShellTestCase { mCallbacks, mSyncQueue, mSurfaceSession, - mIconProvider); + mIconProvider, + Optional.of(mWindowDecorViewModel)); mRootTask = new TestRunningTaskInfoBuilder().build(); mRootTask.parentTaskId = INVALID_TASK_ID; mSurfaceControl = new SurfaceControl.Builder(mSurfaceSession).setName("test").build(); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.java index 41bab95b7dd4..23158eac94de 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.java @@ -53,7 +53,6 @@ import com.android.wm.shell.common.DisplayLayout; import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.desktopmode.DesktopModeController; import com.android.wm.shell.desktopmode.DesktopTasksController; -import com.android.wm.shell.splitscreen.SplitScreenController; import com.android.wm.shell.transition.Transitions; import org.junit.Before; @@ -82,7 +81,6 @@ public class DesktopModeWindowDecorViewModelTests extends ShellTestCase { @Mock private ShellTaskOrganizer mTaskOrganizer; @Mock private DisplayController mDisplayController; @Mock private DisplayLayout mDisplayLayout; - @Mock private SplitScreenController mSplitScreenController; @Mock private SyncTransactionQueue mSyncQueue; @Mock private DesktopModeController mDesktopModeController; @Mock private DesktopTasksController mDesktopTasksController; @@ -111,7 +109,6 @@ public class DesktopModeWindowDecorViewModelTests extends ShellTestCase { mTransitions, Optional.of(mDesktopModeController), Optional.of(mDesktopTasksController), - Optional.of(mSplitScreenController), mDesktopModeWindowDecorFactory, mMockInputMonitorFactory, mTransactionFactory diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragDetectorTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragDetectorTest.kt index 8f84008e8d2d..3fbab0f9e2bb 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragDetectorTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragDetectorTest.kt @@ -55,7 +55,7 @@ class DragDetectorTest { fun setUp() { MockitoAnnotations.initMocks(this) - `when`(eventHandler.handleMotionEvent(any())).thenReturn(true) + `when`(eventHandler.handleMotionEvent(any(), any())).thenReturn(true) dragDetector = DragDetector(eventHandler) dragDetector.setTouchSlop(SLOP) @@ -72,13 +72,13 @@ class DragDetectorTest { @Test fun testNoMove_passesDownAndUp() { assertTrue(dragDetector.onMotionEvent(createMotionEvent(MotionEvent.ACTION_DOWN))) - verify(eventHandler).handleMotionEvent(argThat { + verify(eventHandler).handleMotionEvent(any(), argThat { return@argThat it.action == MotionEvent.ACTION_DOWN && it.x == X && it.y == Y && it.source == InputDevice.SOURCE_TOUCHSCREEN }) assertTrue(dragDetector.onMotionEvent(createMotionEvent(MotionEvent.ACTION_UP))) - verify(eventHandler).handleMotionEvent(argThat { + verify(eventHandler).handleMotionEvent(any(), argThat { return@argThat it.action == MotionEvent.ACTION_UP && it.x == X && it.y == Y && it.source == InputDevice.SOURCE_TOUCHSCREEN }) @@ -86,12 +86,12 @@ class DragDetectorTest { @Test fun testMoveInSlop_touch_passesDownAndUp() { - `when`(eventHandler.handleMotionEvent(argThat { + `when`(eventHandler.handleMotionEvent(any(), argThat { return@argThat it.action == MotionEvent.ACTION_DOWN })).thenReturn(false) assertFalse(dragDetector.onMotionEvent(createMotionEvent(MotionEvent.ACTION_DOWN))) - verify(eventHandler).handleMotionEvent(argThat { + verify(eventHandler).handleMotionEvent(any(), argThat { return@argThat it.action == MotionEvent.ACTION_DOWN && it.x == X && it.y == Y && it.source == InputDevice.SOURCE_TOUCHSCREEN }) @@ -99,12 +99,12 @@ class DragDetectorTest { val newX = X + SLOP - 1 assertFalse( dragDetector.onMotionEvent(createMotionEvent(MotionEvent.ACTION_MOVE, newX, Y))) - verify(eventHandler, never()).handleMotionEvent(argThat { + verify(eventHandler, never()).handleMotionEvent(any(), argThat { return@argThat it.action == MotionEvent.ACTION_MOVE }) assertTrue(dragDetector.onMotionEvent(createMotionEvent(MotionEvent.ACTION_UP, newX, Y))) - verify(eventHandler).handleMotionEvent(argThat { + verify(eventHandler).handleMotionEvent(any(), argThat { return@argThat it.action == MotionEvent.ACTION_UP && it.x == newX && it.y == Y && it.source == InputDevice.SOURCE_TOUCHSCREEN }) @@ -112,13 +112,13 @@ class DragDetectorTest { @Test fun testMoveInSlop_mouse_passesDownMoveAndUp() { - `when`(eventHandler.handleMotionEvent(argThat { + `when`(eventHandler.handleMotionEvent(any(), argThat { it.action == MotionEvent.ACTION_DOWN })).thenReturn(false) assertFalse(dragDetector.onMotionEvent( createMotionEvent(MotionEvent.ACTION_DOWN, isTouch = false))) - verify(eventHandler).handleMotionEvent(argThat { + verify(eventHandler).handleMotionEvent(any(), argThat { return@argThat it.action == MotionEvent.ACTION_DOWN && it.x == X && it.y == Y && it.source == InputDevice.SOURCE_MOUSE }) @@ -126,14 +126,14 @@ class DragDetectorTest { val newX = X + SLOP - 1 assertTrue(dragDetector.onMotionEvent( createMotionEvent(MotionEvent.ACTION_MOVE, newX, Y, isTouch = false))) - verify(eventHandler).handleMotionEvent(argThat { + verify(eventHandler).handleMotionEvent(any(), argThat { return@argThat it.action == MotionEvent.ACTION_MOVE && it.x == newX && it.y == Y && it.source == InputDevice.SOURCE_MOUSE }) assertTrue(dragDetector.onMotionEvent( createMotionEvent(MotionEvent.ACTION_UP, newX, Y, isTouch = false))) - verify(eventHandler).handleMotionEvent(argThat { + verify(eventHandler).handleMotionEvent(any(), argThat { return@argThat it.action == MotionEvent.ACTION_UP && it.x == newX && it.y == Y && it.source == InputDevice.SOURCE_MOUSE }) @@ -141,25 +141,25 @@ class DragDetectorTest { @Test fun testMoveBeyondSlop_passesDownMoveAndUp() { - `when`(eventHandler.handleMotionEvent(argThat { + `when`(eventHandler.handleMotionEvent(any(), argThat { it.action == MotionEvent.ACTION_DOWN })).thenReturn(false) assertFalse(dragDetector.onMotionEvent(createMotionEvent(MotionEvent.ACTION_DOWN))) - verify(eventHandler).handleMotionEvent(argThat { + verify(eventHandler).handleMotionEvent(any(), argThat { return@argThat it.action == MotionEvent.ACTION_DOWN && it.x == X && it.y == Y && it.source == InputDevice.SOURCE_TOUCHSCREEN }) val newX = X + SLOP + 1 assertTrue(dragDetector.onMotionEvent(createMotionEvent(MotionEvent.ACTION_MOVE, newX, Y))) - verify(eventHandler).handleMotionEvent(argThat { + verify(eventHandler).handleMotionEvent(any(), argThat { return@argThat it.action == MotionEvent.ACTION_MOVE && it.x == newX && it.y == Y && it.source == InputDevice.SOURCE_TOUCHSCREEN }) assertTrue(dragDetector.onMotionEvent(createMotionEvent(MotionEvent.ACTION_UP, newX, Y))) - verify(eventHandler).handleMotionEvent(argThat { + verify(eventHandler).handleMotionEvent(any(), argThat { return@argThat it.action == MotionEvent.ACTION_UP && it.x == newX && it.y == Y && it.source == InputDevice.SOURCE_TOUCHSCREEN }) @@ -167,12 +167,12 @@ class DragDetectorTest { @Test fun testPassesHoverEnter() { - `when`(eventHandler.handleMotionEvent(argThat { + `when`(eventHandler.handleMotionEvent(any(), argThat { it.action == MotionEvent.ACTION_HOVER_ENTER })).thenReturn(false) assertFalse(dragDetector.onMotionEvent(createMotionEvent(MotionEvent.ACTION_HOVER_ENTER))) - verify(eventHandler).handleMotionEvent(argThat { + verify(eventHandler).handleMotionEvent(any(), argThat { return@argThat it.action == MotionEvent.ACTION_HOVER_ENTER && it.x == X && it.y == Y }) } @@ -180,7 +180,7 @@ class DragDetectorTest { @Test fun testPassesHoverMove() { assertTrue(dragDetector.onMotionEvent(createMotionEvent(MotionEvent.ACTION_HOVER_MOVE))) - verify(eventHandler).handleMotionEvent(argThat { + verify(eventHandler).handleMotionEvent(any(), argThat { return@argThat it.action == MotionEvent.ACTION_HOVER_MOVE && it.x == X && it.y == Y }) } @@ -188,7 +188,7 @@ class DragDetectorTest { @Test fun testPassesHoverExit() { assertTrue(dragDetector.onMotionEvent(createMotionEvent(MotionEvent.ACTION_HOVER_EXIT))) - verify(eventHandler).handleMotionEvent(argThat { + verify(eventHandler).handleMotionEvent(any(), argThat { return@argThat it.action == MotionEvent.ACTION_HOVER_EXIT && it.x == X && it.y == Y }) } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java index 5a2326b9c393..7fc1c99bb44e 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java @@ -57,7 +57,6 @@ import android.window.SurfaceSyncGroup; import android.window.WindowContainerTransaction; import androidx.test.filters.SmallTest; -import androidx.test.platform.app.InstrumentationRegistry; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.ShellTestCase; @@ -316,7 +315,8 @@ public class WindowDecorationTests extends ShellTestCase { releaseOrder.verify(t).remove(captionContainerSurface); releaseOrder.verify(t).remove(decorContainerSurface); releaseOrder.verify(t).apply(); - verify(mMockWindowContainerTransaction) + // Expect to remove two insets sources, the caption insets and the mandatory gesture insets. + verify(mMockWindowContainerTransaction, Mockito.times(2)) .removeInsetsSource(eq(taskInfo.token), any(), anyInt(), anyInt()); } @@ -410,15 +410,17 @@ public class WindowDecorationTests extends ShellTestCase { verify(additionalWindowSurfaceBuilder).build(); verify(mMockSurfaceControlAddWindowT).setPosition(additionalWindowSurface, 0, 0); final int width = WindowDecoration.loadDimensionPixelSize( - mContext.getResources(), mCaptionMenuWidthId); + windowDecor.mDecorWindowContext.getResources(), mCaptionMenuWidthId); final int height = WindowDecoration.loadDimensionPixelSize( - mContext.getResources(), mRelayoutParams.mCaptionHeightId); + windowDecor.mDecorWindowContext.getResources(), mRelayoutParams.mCaptionHeightId); verify(mMockSurfaceControlAddWindowT).setWindowCrop(additionalWindowSurface, width, height); - final int shadowRadius = WindowDecoration.loadDimensionPixelSize(mContext.getResources(), + final int shadowRadius = WindowDecoration.loadDimensionPixelSize( + windowDecor.mDecorWindowContext.getResources(), mCaptionMenuShadowRadiusId); verify(mMockSurfaceControlAddWindowT) .setShadowRadius(additionalWindowSurface, shadowRadius); - final int cornerRadius = WindowDecoration.loadDimensionPixelSize(mContext.getResources(), + final int cornerRadius = WindowDecoration.loadDimensionPixelSize( + windowDecor.mDecorWindowContext.getResources(), mCaptionMenuCornerRadiusId); verify(mMockSurfaceControlAddWindowT) .setCornerRadius(additionalWindowSurface, cornerRadius); @@ -513,8 +515,7 @@ public class WindowDecorationTests extends ShellTestCase { private TestWindowDecoration createWindowDecoration( ActivityManager.RunningTaskInfo taskInfo, SurfaceControl testSurface) { - return new TestWindowDecoration(InstrumentationRegistry.getInstrumentation().getContext(), - mMockDisplayController, mMockShellTaskOrganizer, + return new TestWindowDecoration(mContext, mMockDisplayController, mMockShellTaskOrganizer, taskInfo, testSurface, new MockObjectSupplier<>(mMockSurfaceControlBuilders, () -> createMockSurfaceControlBuilder(mock(SurfaceControl.class))), diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index 16b35ffcabac..a5518eb9f854 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -405,8 +405,17 @@ void CanvasContext::prepareTree(TreeInfo& info, int64_t* uiFrameInfo, int64_t sy // If the previous frame was dropped we don't need to hold onto it, so // just keep using the previous frame's structure instead - if (!wasSkipped(mCurrentFrameInfo)) { + if (wasSkipped(mCurrentFrameInfo)) { + // Use the oldest skipped frame in case we skip more than a single frame + if (!mSkippedFrameInfo) { + mSkippedFrameInfo.emplace(); + mSkippedFrameInfo->vsyncId = + mCurrentFrameInfo->get(FrameInfoIndex::FrameTimelineVsyncId); + mSkippedFrameInfo->startTime = mCurrentFrameInfo->get(FrameInfoIndex::FrameStartTime); + } + } else { mCurrentFrameInfo = mJankTracker.startFrame(); + mSkippedFrameInfo.reset(); } mCurrentFrameInfo->importUiThreadInfo(uiFrameInfo); @@ -602,10 +611,18 @@ void CanvasContext::draw(bool solelyTextureViewUpdates) { if (vsyncId != UiFrameInfoBuilder::INVALID_VSYNC_ID) { const auto inputEventId = static_cast<int32_t>(mCurrentFrameInfo->get(FrameInfoIndex::InputEventId)); - native_window_set_frame_timeline_info( - mNativeSurface->getNativeWindow(), frameCompleteNr, vsyncId, inputEventId, - mCurrentFrameInfo->get(FrameInfoIndex::FrameStartTime), - solelyTextureViewUpdates); + const ANativeWindowFrameTimelineInfo ftl = { + .frameNumber = frameCompleteNr, + .frameTimelineVsyncId = vsyncId, + .inputEventId = inputEventId, + .startTimeNanos = mCurrentFrameInfo->get(FrameInfoIndex::FrameStartTime), + .useForRefreshRateSelection = solelyTextureViewUpdates, + .skippedFrameVsyncId = mSkippedFrameInfo ? mSkippedFrameInfo->vsyncId + : UiFrameInfoBuilder::INVALID_VSYNC_ID, + .skippedFrameStartTimeNanos = + mSkippedFrameInfo ? mSkippedFrameInfo->startTime : 0, + }; + native_window_set_frame_timeline_info(mNativeSurface->getNativeWindow(), ftl); } } diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h index 5219b5757008..32ac5af94c14 100644 --- a/libs/hwui/renderthread/CanvasContext.h +++ b/libs/hwui/renderthread/CanvasContext.h @@ -366,6 +366,12 @@ private: ColorMode mColorMode = ColorMode::Default; float mTargetSdrHdrRatio = 1.f; + + struct SkippedFrameInfo { + int64_t vsyncId; + int64_t startTime; + }; + std::optional<SkippedFrameInfo> mSkippedFrameInfo; }; } /* namespace renderthread */ diff --git a/packages/CarrierDefaultApp/assets/slice_purchase_test.html b/packages/CarrierDefaultApp/assets/slice_purchase_test.html index 67d218413e74..917276b9ce65 100644 --- a/packages/CarrierDefaultApp/assets/slice_purchase_test.html +++ b/packages/CarrierDefaultApp/assets/slice_purchase_test.html @@ -20,7 +20,7 @@ <meta charset="UTF-8"> <meta name="description" content=" This is a HTML page that calls and verifies responses from the @JavascriptInterface functions of - SlicePurchaseWebInterface. Test slice purchase application behavior using ADB shell commands and + DataBoostWebServiceFlow. Test slice purchase application behavior using ADB shell commands and the APIs below: FROM TERMINAL: @@ -65,8 +65,8 @@ <p id="requested_capability"></p> <h2>Notify purchase successful</h2> - <button type="button" onclick="testNotifyPurchaseSuccessful(60000)"> - Notify purchase successful for 1 minute + <button type="button" onclick="testNotifyPurchaseSuccessful()"> + Notify purchase successful </button> <p id="purchase_successful"></p> @@ -75,5 +75,11 @@ Notify purchase failed </button> <p id="purchase_failed"></p> + + <h2>Dismiss flow</h2> + <button type="button" onclick="testDismissFlow()"> + Dismiss flow + </button> + <p id="dismiss_flow"></p> </body> </html> diff --git a/packages/CarrierDefaultApp/assets/slice_purchase_test.js b/packages/CarrierDefaultApp/assets/slice_purchase_test.js index 02c4feac7ee4..be397a115921 100644 --- a/packages/CarrierDefaultApp/assets/slice_purchase_test.js +++ b/packages/CarrierDefaultApp/assets/slice_purchase_test.js @@ -15,19 +15,25 @@ */ function testGetRequestedCapability() { - let capability = SlicePurchaseWebInterface.getRequestedCapability(); + let capability = DataBoostWebServiceFlow.getRequestedCapability(); document.getElementById("requested_capability").innerHTML = "Premium capability requested: " + capability; } -function testNotifyPurchaseSuccessful(duration_ms_long = 0) { - SlicePurchaseWebInterface.notifyPurchaseSuccessful(duration_ms_long); +function testNotifyPurchaseSuccessful() { + DataBoostWebServiceFlow.notifyPurchaseSuccessful(); document.getElementById("purchase_successful").innerHTML = - "Notified purchase success for duration: " + duration_ms_long; + "Notified purchase successful."; } function testNotifyPurchaseFailed(failure_code = 0, failure_reason = "unknown") { - SlicePurchaseWebInterface.notifyPurchaseFailed(failure_code, failure_reason); + DataBoostWebServiceFlow.notifyPurchaseFailed(failure_code, failure_reason); document.getElementById("purchase_failed").innerHTML = "Notified purchase failed."; } + +function testDismissFlow() { + DataBoostWebServiceFlow.dismissFlow(); + document.getElementById("dismiss_flow").innerHTML = + "Called dismiss flow."; +} diff --git a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/SlicePurchaseWebInterface.java b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/DataBoostWebServiceFlow.java index 8547898df678..4500a220523d 100644 --- a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/SlicePurchaseWebInterface.java +++ b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/DataBoostWebServiceFlow.java @@ -24,13 +24,13 @@ import android.webkit.JavascriptInterface; import com.android.phone.slice.SlicePurchaseController; /** - * Slice purchase web interface class allowing carrier websites to send responses back to the + * Data boost web service flow interface allowing carrier websites to send responses back to the * slice purchase application using JavaScript. */ -public class SlicePurchaseWebInterface { +public class DataBoostWebServiceFlow { @NonNull SlicePurchaseActivity mActivity; - public SlicePurchaseWebInterface(@NonNull SlicePurchaseActivity activity) { + public DataBoostWebServiceFlow(@NonNull SlicePurchaseActivity activity) { mActivity = activity; } @@ -41,7 +41,7 @@ public class SlicePurchaseWebInterface { * This can be called using the JavaScript below: * <script type="text/javascript"> * function getRequestedCapability(duration) { - * SlicePurchaseWebInterface.getRequestedCapability(); + * DataBoostWebServiceFlow.getRequestedCapability(); * } * </script> */ @@ -57,16 +57,14 @@ public class SlicePurchaseWebInterface { * * This can be called using the JavaScript below: * <script type="text/javascript"> - * function notifyPurchaseSuccessful(duration_ms_long = 0) { - * SlicePurchaseWebInterface.notifyPurchaseSuccessful(duration_ms_long); + * function notifyPurchaseSuccessful() { + * DataBoostWebServiceFlow.notifyPurchaseSuccessful(); * } * </script> - * - * @param duration The duration for which the premium capability is purchased in milliseconds. */ @JavascriptInterface - public void notifyPurchaseSuccessful(long duration) { - mActivity.onPurchaseSuccessful(duration); + public void notifyPurchaseSuccessful() { + mActivity.onPurchaseSuccessful(); } /** @@ -76,7 +74,7 @@ public class SlicePurchaseWebInterface { * This can be called using the JavaScript below: * <script type="text/javascript"> * function notifyPurchaseFailed(failure_code = 0, failure_reason = "unknown") { - * SlicePurchaseWebInterface.notifyPurchaseFailed(); + * DataBoostWebServiceFlow.notifyPurchaseFailed(); * } * </script> * @@ -90,4 +88,21 @@ public class SlicePurchaseWebInterface { @Nullable String failureReason) { mActivity.onPurchaseFailed(failureCode, failureReason); } + + /** + * Interface method allowing the carrier website to notify the slice purchase application that + * the service flow ended prematurely. This can be due to user action, an error in the + * web sheet logic, or an error on the network side. + * + * This can be called using the JavaScript below: + * <script type="text/javascript"> + * function dismissFlow() { + * DataBoostWebServiceFlow.dismissFlow(); + * } + * </script> + */ + @JavascriptInterface + public void dismissFlow() { + mActivity.onDismissFlow(); + } } diff --git a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/SlicePurchaseActivity.java b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/SlicePurchaseActivity.java index 946185a3c420..2530257d629a 100644 --- a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/SlicePurchaseActivity.java +++ b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/SlicePurchaseActivity.java @@ -27,26 +27,25 @@ import android.telephony.TelephonyManager; import android.text.TextUtils; import android.util.Log; import android.view.KeyEvent; +import android.webkit.CookieManager; import android.webkit.WebView; import com.android.phone.slice.SlicePurchaseController; import java.net.URL; -import java.util.concurrent.TimeUnit; /** * Activity that launches when the user clicks on the performance boost notification. * This will open a {@link WebView} for the carrier website to allow the user to complete the * premium capability purchase. * The carrier website can get the requested premium capability using the JavaScript interface - * method {@code SlicePurchaseWebInterface.getRequestedCapability()}. + * method {@code DataBoostWebServiceFlow.getRequestedCapability()}. * If the purchase is successful, the carrier website shall notify the slice purchase application * using the JavaScript interface method - * {@code SlicePurchaseWebInterface.notifyPurchaseSuccessful(duration)}, where {@code duration} is - * the optional duration of the performance boost. + * {@code DataBoostWebServiceFlow.notifyPurchaseSuccessful()}. * If the purchase was not successful, the carrier website shall notify the slice purchase * application using the JavaScript interface method - * {@code SlicePurchaseWebInterface.notifyPurchaseFailed(code, reason)}, where {@code code} is the + * {@code DataBoostWebServiceFlow.notifyPurchaseFailed(code, reason)}, where {@code code} is the * {@link SlicePurchaseController.FailureCode} indicating the reason for failure and {@code reason} * is the human-readable reason for failure if the failure code is * {@link SlicePurchaseController#FAILURE_CODE_UNKNOWN}. @@ -118,15 +117,11 @@ public class SlicePurchaseActivity extends Activity { setupWebView(); } - protected void onPurchaseSuccessful(long duration) { + protected void onPurchaseSuccessful() { logd("onPurchaseSuccessful: Carrier website indicated successfully purchased premium " - + "capability " + TelephonyManager.convertPremiumCapabilityToString(mCapability) - + (duration > 0 ? " for " + TimeUnit.MILLISECONDS.toMinutes(duration) + " minutes." - : ".")); - Intent intent = new Intent(); - intent.putExtra(SlicePurchaseController.EXTRA_PURCHASE_DURATION, duration); - SlicePurchaseBroadcastReceiver.sendSlicePurchaseAppResponseWithData(mApplicationContext, - mIntent, SlicePurchaseController.EXTRA_INTENT_SUCCESS, intent); + + "capability " + TelephonyManager.convertPremiumCapabilityToString(mCapability)); + SlicePurchaseBroadcastReceiver.sendSlicePurchaseAppResponse( + mIntent, SlicePurchaseController.EXTRA_INTENT_SUCCESS); finishAndRemoveTask(); } @@ -143,6 +138,14 @@ public class SlicePurchaseActivity extends Activity { finishAndRemoveTask(); } + protected void onDismissFlow() { + logd("onDismissFlow: Dismiss flow called while purchasing premium capability " + + TelephonyManager.convertPremiumCapabilityToString(mCapability)); + SlicePurchaseBroadcastReceiver.sendSlicePurchaseAppResponse( + mIntent, SlicePurchaseController.EXTRA_INTENT_REQUEST_FAILED); + finishAndRemoveTask(); + } + @Override public boolean onKeyDown(int keyCode, @NonNull KeyEvent event) { // Pressing back in the WebView will go to the previous page instead of closing @@ -174,11 +177,17 @@ public class SlicePurchaseActivity extends Activity { // Create WebView mWebView = new WebView(this); + // Clear any cookies and state that might be saved from previous sessions + CookieManager.getInstance().removeAllCookies(null); + CookieManager.getInstance().flush(); + mWebView.clearCache(true); + mWebView.clearHistory(); + // Enable JavaScript for the carrier purchase website to send results back to // the slice purchase application. mWebView.getSettings().setJavaScriptEnabled(true); mWebView.addJavascriptInterface( - new SlicePurchaseWebInterface(this), "SlicePurchaseWebInterface"); + new DataBoostWebServiceFlow(this), "DataBoostWebServiceFlow"); // Display WebView setContentView(mWebView); diff --git a/packages/CarrierDefaultApp/tests/unit/src/com/android/carrierdefaultapp/SlicePurchaseActivityTest.java b/packages/CarrierDefaultApp/tests/unit/src/com/android/carrierdefaultapp/SlicePurchaseActivityTest.java index daf0b42dbd7c..cc103fa98e65 100644 --- a/packages/CarrierDefaultApp/tests/unit/src/com/android/carrierdefaultapp/SlicePurchaseActivityTest.java +++ b/packages/CarrierDefaultApp/tests/unit/src/com/android/carrierdefaultapp/SlicePurchaseActivityTest.java @@ -53,7 +53,9 @@ public class SlicePurchaseActivityTest extends ActivityUnitTestCase<SlicePurchas private static final int PHONE_ID = 0; @Mock PendingIntent mPendingIntent; + @Mock PendingIntent mSuccessfulIntent; @Mock PendingIntent mCanceledIntent; + @Mock PendingIntent mRequestFailedIntent; @Mock CarrierConfigManager mCarrierConfigManager; @Mock NotificationManager mNotificationManager; @Mock PersistableBundle mPersistableBundle; @@ -107,25 +109,28 @@ public class SlicePurchaseActivityTest extends ActivityUnitTestCase<SlicePurchas doReturn(true).when(mCanceledIntent).isBroadcast(); doReturn(mCanceledIntent).when(spiedIntent).getParcelableExtra( eq(SlicePurchaseController.EXTRA_INTENT_CANCELED), eq(PendingIntent.class)); + doReturn(TelephonyManager.PHONE_PROCESS_NAME).when(mSuccessfulIntent).getCreatorPackage(); + doReturn(true).when(mSuccessfulIntent).isBroadcast(); + doReturn(mSuccessfulIntent).when(spiedIntent).getParcelableExtra( + eq(SlicePurchaseController.EXTRA_INTENT_SUCCESS), eq(PendingIntent.class)); + doReturn(TelephonyManager.PHONE_PROCESS_NAME).when(mRequestFailedIntent) + .getCreatorPackage(); + doReturn(true).when(mRequestFailedIntent).isBroadcast(); + doReturn(mRequestFailedIntent).when(spiedIntent).getParcelableExtra( + eq(SlicePurchaseController.EXTRA_INTENT_REQUEST_FAILED), eq(PendingIntent.class)); mSlicePurchaseActivity = startActivity(spiedIntent, null, null); } @Test public void testOnPurchaseSuccessful() throws Exception { - int duration = 5 * 60 * 1000; // 5 minutes - int invalidDuration = -1; - mSlicePurchaseActivity.onPurchaseSuccessful(duration); - ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class); - verify(mPendingIntent).send(eq(mContext), eq(0), intentCaptor.capture()); - Intent intent = intentCaptor.getValue(); - assertEquals(duration, intent.getLongExtra( - SlicePurchaseController.EXTRA_PURCHASE_DURATION, invalidDuration)); + mSlicePurchaseActivity.onPurchaseSuccessful(); + verify(mSuccessfulIntent).send(); } @Test public void testOnPurchaseFailed() throws Exception { - int failureCode = SlicePurchaseController.FAILURE_CODE_SERVER_UNREACHABLE; + int failureCode = SlicePurchaseController.FAILURE_CODE_CARRIER_URL_UNAVAILABLE; String failureReason = "Server unreachable"; mSlicePurchaseActivity.onPurchaseFailed(failureCode, failureReason); ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class); @@ -142,4 +147,10 @@ public class SlicePurchaseActivityTest extends ActivityUnitTestCase<SlicePurchas mSlicePurchaseActivity.onDestroy(); verify(mCanceledIntent).send(); } + + @Test + public void testOnDismissFlow() throws Exception { + mSlicePurchaseActivity.onDismissFlow(); + verify(mRequestFailedIntent).send(); + } } diff --git a/packages/CompanionDeviceManager/res/layout/activity_confirmation.xml b/packages/CompanionDeviceManager/res/layout/activity_confirmation.xml index aae30dfe6223..a0b34690696f 100644 --- a/packages/CompanionDeviceManager/res/layout/activity_confirmation.xml +++ b/packages/CompanionDeviceManager/res/layout/activity_confirmation.xml @@ -144,7 +144,7 @@ android:visibility="gone" android:duplicateParentState="true" android:clickable="false" - android:text="@string/consent_no" /> + android:text="@string/consent_cancel" /> </LinearLayout> diff --git a/packages/CompanionDeviceManager/res/values-af/strings.xml b/packages/CompanionDeviceManager/res/values-af/strings.xml index 2c0af5d3aa84..6c54f70aad19 100644 --- a/packages/CompanionDeviceManager/res/values-af/strings.xml +++ b/packages/CompanionDeviceManager/res/values-af/strings.xml @@ -17,15 +17,14 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="4470785958457506021">"Metgeseltoestel-bestuurder"</string> - <string name="confirmation_title" msgid="4593465730772390351">"Gee <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> toegang tot <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> + <string name="confirmation_title" msgid="2244241995958340998">"Gee die app <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> toegang tot <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> <string name="profile_name_watch" msgid="576290739483672360">"horlosie"</string> - <string name="chooser_title" msgid="2262294130493605839">"Kies \'n <xliff:g id="PROFILE_NAME">%1$s</xliff:g> om deur <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> bestuur te word"</string> - <string name="summary_watch" msgid="898569637110705523">"Hierdie app is nodig om jou <xliff:g id="DEVICE_NAME">%1$s</xliff:g> te bestuur. <xliff:g id="APP_NAME">%2$s</xliff:g> sal toegelaat word om inligting te sinkroniseer, soos die naam van iemand wat bel, interaksie met jou kennisgewings te hê, en sal toegang tot jou Foon-, SMS-, Kontakte-, Kalender-, Oproeprekords-, en Toestelle in die Omtrek-toestemmings hê."</string> - <string name="summary_watch_single_device" msgid="3173948915947011333">"Hierdie app sal toegelaat word om inligting te sinkroniseer, soos die naam van iemand wat bel, en sal toegang tot hierdie toestemmings op jou <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> hê"</string> + <string name="chooser_title_non_profile" msgid="6035023914517087400">"Kies ’n toestel wat <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> moet bestuur"</string> + <string name="chooser_title" msgid="2235819929238267637">"Kies ’n <xliff:g id="PROFILE_NAME">%1$s</xliff:g> om op te stel"</string> + <string name="summary_watch" msgid="7962014927042971830">"Hierdie app sal toegelaat word om inligting te sinkroniseer, soos die naam van iemand wat bel, en sal toegang tot hierdie toestemmings op jou <xliff:g id="DEVICE_NAME">%1$s</xliff:g> hê"</string> <string name="confirmation_title_glasses" msgid="8288346850537727333">"Laat <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> toe om <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> te bestuur?"</string> - <string name="profile_name_glasses" msgid="8488394059007275998">"bril"</string> - <string name="summary_glasses_multi_device" msgid="615259525961937348">"Hierdie app is nodig om <xliff:g id="DEVICE_NAME">%1$s</xliff:g> te bestuur. <xliff:g id="APP_NAME">%2$s</xliff:g> sal toegelaat word om interaksie met jou kennisgewings te hê en sal toegang tot jou Foon-, SMS-, Kontakte-, Mikrofoon-, en Toestelle in die Omtrek-toestemmings hê."</string> - <string name="summary_glasses_single_device" msgid="3000909894067413398">"Hierdie app sal toegang tot hierdie toestemmings op jou <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> hê"</string> + <string name="profile_name_glasses" msgid="3506504967216601277">"toestel"</string> + <string name="summary_glasses" msgid="2872254734959842579">"Hierdie app sal toegang tot hierdie toestemmings op jou <xliff:g id="DEVICE_NAME">%1$s</xliff:g> hê"</string> <string name="title_app_streaming" msgid="2270331024626446950">"Gee <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> toegang tot hierdie inligting op jou foon"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Oorkruistoestel-dienste"</string> <string name="helper_summary_app_streaming" msgid="2396773196949578425">"<xliff:g id="APP_NAME">%1$s</xliff:g> versoek tans namens jou <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> toestemming om apps tussen jou toestelle te stroom"</string> @@ -38,10 +37,10 @@ <string name="title_nearby_device_streaming" msgid="7269956847378799794">"Laat <strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> toe om hierdie handeling uit te voer?"</string> <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<xliff:g id="APP_NAME">%1$s</xliff:g> versoek tans namens jou <xliff:g id="DEVICE_NAME">%2$s</xliff:g> toestemming om apps en ander stelselkenmerke na toestelle in die omtrek te stroom"</string> <string name="profile_name_generic" msgid="6851028682723034988">"toestel"</string> - <string name="summary_generic_single_device" msgid="4181180669689590417">"Hierdie app sal inligting kan sinkroniseer, soos die naam van iemand wat bel, tussen jou foon en <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="summary_generic" msgid="1761976003668044801">"Hierdie app sal inligting kan sinkroniseer, soos die naam van iemand wat bel, tussen jou foon en die gekose toestel"</string> <string name="consent_yes" msgid="8344487259618762872">"Laat toe"</string> <string name="consent_no" msgid="2640796915611404382">"Moenie toelaat nie"</string> + <string name="consent_cancel" msgid="5655005528379285841">"Kanselleer"</string> <string name="consent_back" msgid="2560683030046918882">"Terug"</string> <string name="permission_expand" msgid="893185038020887411">"Vou <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g> uit"</string> <string name="permission_collapse" msgid="3320833884220844084">"Vou <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g> in"</string> diff --git a/packages/CompanionDeviceManager/res/values-am/strings.xml b/packages/CompanionDeviceManager/res/values-am/strings.xml index b8964b5202b3..d9ddf714ff0d 100644 --- a/packages/CompanionDeviceManager/res/values-am/strings.xml +++ b/packages/CompanionDeviceManager/res/values-am/strings.xml @@ -17,15 +17,14 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="4470785958457506021">"አጃቢ የመሣሪያ አስተዳዳሪ"</string> - <string name="confirmation_title" msgid="4593465730772390351">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>ን እንዲደርስ ይፈቀድለት?"</string> + <string name="confirmation_title" msgid="2244241995958340998">"መተግበሪያው <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>ን እንዲደርስ ይፈቀድለት?"</string> <string name="profile_name_watch" msgid="576290739483672360">"ሰዓት"</string> - <string name="chooser_title" msgid="2262294130493605839">"በ<strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> የሚተዳደር <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ይምረጡ"</string> - <string name="summary_watch" msgid="898569637110705523">"የእርስዎን <xliff:g id="DEVICE_NAME">%1$s</xliff:g> ለማስተዳደር ይህ መተግበሪያ ያስፈልጋል። <xliff:g id="APP_NAME">%2$s</xliff:g> እንደ የሚደውል ሰው ስም፣ ከማሳወቂያዎችዎ ጋር መስተጋብር እንዲፈጥር እና የእርስዎን ስልክ፣ ኤስኤምኤስ፣ ዕውቅያዎች፣ የቀን መቁጠሪያ፣ የጥሪ ምዝግብ ማስታወሻዎች እና በአቅራቢያ ያሉ መሣሪያዎችን መድረስ ያሉ መረጃዎችን እንዲያሰምር ይፈቀድለታል።"</string> - <string name="summary_watch_single_device" msgid="3173948915947011333">"ይህ መተግበሪያ እንደ የሚደውል ሰው ስም ያለ መረጃን እንዲያሰምር እና እነዚህን ፈቃዶች በእርስዎ <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> ላይ እንዲደርስ ይፈቀድለታል"</string> + <string name="chooser_title_non_profile" msgid="6035023914517087400">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> የሚያስተዳድረው መሣሪያ ይምረጡ"</string> + <string name="chooser_title" msgid="2235819929238267637">"የሚያዋቅሩት <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ይምረጡ"</string> + <string name="summary_watch" msgid="7962014927042971830">"ይህ መተግበሪያ እንደ የሚደውል ሰው ስም ያለ መረጃን እንዲያሰምር እና እነዚህን ፈቃዶች በእርስዎ <xliff:g id="DEVICE_NAME">%1$s</xliff:g> ላይ እንዲደርስ ይፈቀድለታል"</string> <string name="confirmation_title_glasses" msgid="8288346850537727333">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>ን እንዲያስተዳድር ይፈቅዳሉ?"</string> - <string name="profile_name_glasses" msgid="8488394059007275998">"መነጽሮች"</string> - <string name="summary_glasses_multi_device" msgid="615259525961937348">"ይህ መተግበሪያ <xliff:g id="DEVICE_NAME">%1$s</xliff:g>ን ለማስተዳደር ያስፈልጋል። <xliff:g id="APP_NAME">%2$s</xliff:g> ከማሳወቂያዎችዎ ጋር መስተጋብር እንዲፈጥር እና የእርስዎን ስልክ፣ ኤስኤምኤስ፣ ዕውቂያዎች፣ ማይክሮፎን እና በአቅራቢያ ያሉ መሣሪያዎች ፈቃዶችን እንዲደርስ ይፈቀድለታል።"</string> - <string name="summary_glasses_single_device" msgid="3000909894067413398">"ይህ መተግበሪያ በእርስዎ <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> ላይ እነዚህን ፈቃዶች እንዲደርስ ይፈቀድለታል"</string> + <string name="profile_name_glasses" msgid="3506504967216601277">"መሣሪያ"</string> + <string name="summary_glasses" msgid="2872254734959842579">"ይህ መተግበሪያ በእርስዎ <xliff:g id="DEVICE_NAME">%1$s</xliff:g> ላይ እነዚህን ፈቃዶች እንዲደርስ ይፈቀድለታል"</string> <string name="title_app_streaming" msgid="2270331024626446950">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ይህን መረጃ ከስልክዎ እንዲደርስበት ይፍቀዱለት"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"መሣሪያ ተሻጋሪ አገልግሎቶች"</string> <string name="helper_summary_app_streaming" msgid="2396773196949578425">"<xliff:g id="APP_NAME">%1$s</xliff:g> በእርስዎ መሣሪያዎች መካከል መተግበሪያዎችን በዥረት ለመልቀቅ የእርስዎን <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> ወክሎ ፈቃድ እየጠየቀ ነው"</string> @@ -38,10 +37,10 @@ <string name="title_nearby_device_streaming" msgid="7269956847378799794">"<strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> ይህን እርምጃ እንዲወስድ ፈቃድ ይሰጠው?"</string> <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<xliff:g id="APP_NAME">%1$s</xliff:g> የእርስዎን <xliff:g id="DEVICE_NAME">%2$s</xliff:g> በመወከል በአቅራቢያ ላሉ መሣሪያዎች መተግበሪያዎች እና ሌሎች የስርዓት ባህሪያትን በዥረት ለመልቀቅ ፈቃድ እየጠየቀ ነው"</string> <string name="profile_name_generic" msgid="6851028682723034988">"መሣሪያ"</string> - <string name="summary_generic_single_device" msgid="4181180669689590417">"ይህ መተግበሪያ እንደ የሚደውል ሰው ስም ያለ መረጃን በስልክዎ እና <xliff:g id="DEVICE_NAME">%1$s</xliff:g> መካከል ማስመር ይችላል"</string> <string name="summary_generic" msgid="1761976003668044801">"ይህ መተግበሪያ እንደ የሚደውል ሰው ስም ያለ መረጃን በስልክዎ እና በተመረጠው መሣሪያ መካከል ማስመር ይችላል"</string> <string name="consent_yes" msgid="8344487259618762872">"ፍቀድ"</string> <string name="consent_no" msgid="2640796915611404382">"አትፍቀድ"</string> + <string name="consent_cancel" msgid="5655005528379285841">"ይቅር"</string> <string name="consent_back" msgid="2560683030046918882">"ተመለስ"</string> <string name="permission_expand" msgid="893185038020887411">"<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>ን ዘርጋ"</string> <string name="permission_collapse" msgid="3320833884220844084">"<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>ን ሰብስብ"</string> diff --git a/packages/CompanionDeviceManager/res/values-ar/strings.xml b/packages/CompanionDeviceManager/res/values-ar/strings.xml index 3a4a211edfa7..8a6f22779591 100644 --- a/packages/CompanionDeviceManager/res/values-ar/strings.xml +++ b/packages/CompanionDeviceManager/res/values-ar/strings.xml @@ -17,15 +17,14 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="4470785958457506021">"تطبيق \"مدير الجهاز المصاحب\""</string> - <string name="confirmation_title" msgid="4593465730772390351">"هل تريد السماح لتطبيق <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> بالوصول إلى <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>؟"</string> + <string name="confirmation_title" msgid="2244241995958340998">"هل تريد السماح لـ <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> بالوصول إلى <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>؟"</string> <string name="profile_name_watch" msgid="576290739483672360">"الساعة"</string> - <string name="chooser_title" msgid="2262294130493605839">"اختَر <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ليديرها تطبيق <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> - <string name="summary_watch" msgid="898569637110705523">"هذا التطبيق مطلوب لإدارة \"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>\". سيتم السماح لتطبيق \"<xliff:g id="APP_NAME">%2$s</xliff:g>\" بمزامنة المعلومات، مثلاً اسم المتصل، والتفاعل مع الإشعارات والوصول إلى هاتفك، والرسائل القصيرة، وجهات الاتصال، والتقويم، وسجلات المكالمات وأذونات الأجهزة المجاورة."</string> - <string name="summary_watch_single_device" msgid="3173948915947011333">"سيتم السماح لهذا التطبيق بمزامنة المعلومات، مثلاً اسم المتصل، والوصول إلى الأذونات التالية على <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>."</string> + <string name="chooser_title_non_profile" msgid="6035023914517087400">"اختيار جهاز ليديره تطبيق <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>"</string> + <string name="chooser_title" msgid="2235819929238267637">"اختيار \"<xliff:g id="PROFILE_NAME">%1$s</xliff:g>\" لإعداده"</string> + <string name="summary_watch" msgid="7962014927042971830">"سيتم السماح لهذا التطبيق بمزامنة المعلومات، مثلاً اسم المتصل، والوصول إلى هذه الأذونات على \"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>\"."</string> <string name="confirmation_title_glasses" msgid="8288346850537727333">"السماح لتطبيق <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> بإدارة <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string> - <string name="profile_name_glasses" msgid="8488394059007275998">"النظارة"</string> - <string name="summary_glasses_multi_device" msgid="615259525961937348">"يجب توفّر هذا التطبيق لإدارة \"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>\". سيتم السماح لتطبيق \"<xliff:g id="APP_NAME">%2$s</xliff:g>\" بالتفاعل مع الإشعارات والوصول إلى أذونات الهاتف والرسائل القصيرة وجهات الاتصال والميكروفون والأجهزة المجاورة."</string> - <string name="summary_glasses_single_device" msgid="3000909894067413398">"سيتم السماح لهذا التطبيق بالوصول إلى الأذونات التالية على <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>."</string> + <string name="profile_name_glasses" msgid="3506504967216601277">"جهاز"</string> + <string name="summary_glasses" msgid="2872254734959842579">"سيتم السماح لهذا التطبيق بالوصول إلى هذه الأذونات على \"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>\"."</string> <string name="title_app_streaming" msgid="2270331024626446950">"السماح لتطبيق <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> بالوصول إلى هذه المعلومات من هاتفك"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"الخدمات التي تعمل بين الأجهزة"</string> <string name="helper_summary_app_streaming" msgid="2396773196949578425">"يطلب تطبيق \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" الحصول على إذن نيابةً عن \"<xliff:g id="DISPLAY_NAME">%2$s</xliff:g>\" لبثّ محتوى التطبيقات بين أجهزتك."</string> @@ -38,10 +37,10 @@ <string name="title_nearby_device_streaming" msgid="7269956847378799794">"هل تريد السماح للتطبيق <strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> باتّخاذ هذا الإجراء؟"</string> <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"يطلب \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" الحصول على إذن نيابةً عن \"<xliff:g id="DEVICE_NAME">%2$s</xliff:g>\" لبثّ التطبيقات وميزات النظام الأخرى إلى أجهزتك المجاورة."</string> <string name="profile_name_generic" msgid="6851028682723034988">"جهاز"</string> - <string name="summary_generic_single_device" msgid="4181180669689590417">"سيتمكّن هذا التطبيق من مزامنة المعلومات، مثل اسم المتصل، بين هاتفك و\"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>\"."</string> <string name="summary_generic" msgid="1761976003668044801">"سيتمكّن هذا التطبيق من مزامنة المعلومات، مثل اسم المتصل، بين هاتفك والجهاز المحدّد."</string> <string name="consent_yes" msgid="8344487259618762872">"السماح"</string> <string name="consent_no" msgid="2640796915611404382">"عدم السماح"</string> + <string name="consent_cancel" msgid="5655005528379285841">"إلغاء"</string> <string name="consent_back" msgid="2560683030046918882">"رجوع"</string> <string name="permission_expand" msgid="893185038020887411">"توسيع <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string> <string name="permission_collapse" msgid="3320833884220844084">"تصغير <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string> diff --git a/packages/CompanionDeviceManager/res/values-as/strings.xml b/packages/CompanionDeviceManager/res/values-as/strings.xml index 1ac0ed3fbc3a..28a06e66fed9 100644 --- a/packages/CompanionDeviceManager/res/values-as/strings.xml +++ b/packages/CompanionDeviceManager/res/values-as/strings.xml @@ -17,15 +17,14 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="4470785958457506021">"কম্পেনিয়ন ডিভাইচ মেনেজাৰ"</string> - <string name="confirmation_title" msgid="4593465730772390351">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>ক <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> এক্সেছ কৰিবলৈ দিবনে?"</string> + <string name="confirmation_title" msgid="2244241995958340998">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> এপ্টোক <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> এক্সেছ কৰিবলৈ দিবনে?"</string> <string name="profile_name_watch" msgid="576290739483672360">"ঘড়ী"</string> - <string name="chooser_title" msgid="2262294130493605839">"<strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>এ পৰিচালনা কৰিব লগা এটা <xliff:g id="PROFILE_NAME">%1$s</xliff:g> বাছনি কৰক"</string> - <string name="summary_watch" msgid="898569637110705523">"আপোনাৰ <xliff:g id="DEVICE_NAME">%1$s</xliff:g> পৰিচালনা কৰিবলৈ এই এপ্টোৰ আৱশ্যক। <xliff:g id="APP_NAME">%2$s</xliff:g>ক কল কৰোঁতাৰ নামৰ দৰে তথ্য ছিংক কৰিবলৈ, আপোনাৰ জাননীৰ সৈতে ভাব-বিনিময় কৰিবলৈ আৰু আপোনাৰ ফ’ন, এছএমএছ, সম্পৰ্ক, কেলেণ্ডাৰ, কল লগ আৰু নিকটৱৰ্তী ডিভাইচৰ অনুমতি এক্সেছ কৰিবলৈ দিয়া হ’ব।"</string> - <string name="summary_watch_single_device" msgid="3173948915947011333">"এই এপ্টোক ফ’ন কৰা লোকৰ নামৰ দৰে তথ্য ছিংক কৰিবলৈ আৰু আপোনাৰ <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>ত এই অনুমতিসমূহ এক্সেছ কৰিবলৈ অনুমতি দিয়া হ’ব"</string> + <string name="chooser_title_non_profile" msgid="6035023914517087400">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>এ পৰিচালনা কৰিবলগীয়া এটা ডিভাইচ বাছনি কৰক"</string> + <string name="chooser_title" msgid="2235819929238267637">"ছেট আপ কৰিবলৈ এটা <xliff:g id="PROFILE_NAME">%1$s</xliff:g> বাছনি কৰক"</string> + <string name="summary_watch" msgid="7962014927042971830">"এই এপ্টোক ফ’ন কৰা লোকৰ নামৰ দৰে তথ্য ছিংক কৰিবলৈ আৰু আপোনাৰ <xliff:g id="DEVICE_NAME">%1$s</xliff:g>ত এই অনুমতিসমূহ এক্সেছ কৰিবলৈ অনুমতি দিয়া হ’ব"</string> <string name="confirmation_title_glasses" msgid="8288346850537727333">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>ক <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> পৰিচালনা কৰিবলৈ দিবনে?"</string> - <string name="profile_name_glasses" msgid="8488394059007275998">"চছ্মা"</string> - <string name="summary_glasses_multi_device" msgid="615259525961937348">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> পৰিচালনা কৰিবলৈ এই এপ্টোৰ আৱশ্যক। <xliff:g id="APP_NAME">%2$s</xliff:g>ক আপোনাৰ অনুমতিসমূহৰ সৈতে ভাব-বিনিময় কৰিবলৈ আৰু আপোনাৰ ফ’ন, এছএমএছ, সম্পৰ্ক, মাইক্ৰ’ফ’ন আৰু নিকটৱৰ্তী ডিভাইচৰ অনুমতিসমূহ এক্সেছ কৰিবলৈ দিয়া হ’ব।"</string> - <string name="summary_glasses_single_device" msgid="3000909894067413398">"এই এপ্টোক আপোনাৰ <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>ত এই অনুমতিসমূহ এক্সেছ কৰিবলৈ অনুমতি দিয়া হ’ব"</string> + <string name="profile_name_glasses" msgid="3506504967216601277">"ডিভাইচ"</string> + <string name="summary_glasses" msgid="2872254734959842579">"এই এপ্টোক আপোনাৰ <xliff:g id="DEVICE_NAME">%1$s</xliff:g>ত এই অনুমতিসমূহ এক্সেছ কৰিবলৈ অনুমতি দিয়া হ’ব"</string> <string name="title_app_streaming" msgid="2270331024626446950">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>ক আপোনাৰ ফ’নৰ পৰা এই তথ্যখিনি এক্সেছ কৰাৰ অনুমতি দিয়ক"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"ক্ৰছ-ডিভাইচ সেৱাসমূহ"</string> <string name="helper_summary_app_streaming" msgid="2396773196949578425">"<xliff:g id="APP_NAME">%1$s</xliff:g>এ আপোনাৰ <xliff:g id="DISPLAY_NAME">%2$s</xliff:g>ৰ হৈ আপোনাৰ ডিভাইচসমূহৰ মাজত এপ্ ষ্ট্ৰীম কৰাৰ বাবে অনুৰোধ জনাইছে"</string> @@ -38,10 +37,10 @@ <string name="title_nearby_device_streaming" msgid="7269956847378799794">"<strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong>ক এই কাৰ্যটো সম্পাদন কৰিবলৈ দিবনে?"</string> <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<xliff:g id="APP_NAME">%1$s</xliff:g>এ আপোনাৰ <xliff:g id="DEVICE_NAME">%2$s</xliff:g>ৰ হৈ নিকটৱৰ্তী ডিভাইচত এপ্ আৰু ছিষ্টেমৰ অন্য সুবিধাসমূহ ষ্ট্ৰীম কৰাৰ অনুমতি দিবলৈ অনুৰোধ জনাইছে"</string> <string name="profile_name_generic" msgid="6851028682723034988">"ডিভাইচ"</string> - <string name="summary_generic_single_device" msgid="4181180669689590417">"এই এপ্টোৱে আপোনাৰ ফ’ন আৰু বাছনি কৰা <xliff:g id="DEVICE_NAME">%1$s</xliff:g>ৰ মাজত কল কৰোঁতাৰ নামৰ দৰে তথ্য ছিংক কৰিব পাৰিব"</string> <string name="summary_generic" msgid="1761976003668044801">"এই এপ্টোৱে আপোনাৰ ফ’ন আৰু বাছনি কৰা ডিভাইচটোৰ মাজত কল কৰোঁতাৰ নামৰ দৰে তথ্য ছিংক কৰিব পাৰিব"</string> <string name="consent_yes" msgid="8344487259618762872">"অনুমতি দিয়ক"</string> <string name="consent_no" msgid="2640796915611404382">"অনুমতি নিদিব"</string> + <string name="consent_cancel" msgid="5655005528379285841">"বাতিল কৰক"</string> <string name="consent_back" msgid="2560683030046918882">"উভতি যাওক"</string> <string name="permission_expand" msgid="893185038020887411">"<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g> বিস্তাৰ কৰক"</string> <string name="permission_collapse" msgid="3320833884220844084">"<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g> সংকোচন কৰক"</string> diff --git a/packages/CompanionDeviceManager/res/values-az/strings.xml b/packages/CompanionDeviceManager/res/values-az/strings.xml index 79a045da5f2c..18ac2aee9c3b 100644 --- a/packages/CompanionDeviceManager/res/values-az/strings.xml +++ b/packages/CompanionDeviceManager/res/values-az/strings.xml @@ -17,15 +17,14 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="4470785958457506021">"Kompanyon Cihaz Meneceri"</string> - <string name="confirmation_title" msgid="4593465730772390351">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> tətbiqinə <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> cihazına daxil olmaq icazəsi verilsin?"</string> + <string name="confirmation_title" msgid="2244241995958340998">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> tətbiqinə <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> cihazına daxil olmaq icazəsi verilsin?"</string> <string name="profile_name_watch" msgid="576290739483672360">"izləyin"</string> - <string name="chooser_title" msgid="2262294130493605839">"<strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> tərəfindən idarə ediləcək <xliff:g id="PROFILE_NAME">%1$s</xliff:g> seçin"</string> - <string name="summary_watch" msgid="898569637110705523">"Tətbiq <xliff:g id="DEVICE_NAME">%1$s</xliff:g> cihazını idarə etmək üçün lazımdır. <xliff:g id="APP_NAME">%2$s</xliff:g> zəng edənin adı kimi məlumatları sinxronlaşdıracaq, bildirişlərə giriş edəcək, habelə Telefon, SMS, Kontaktlar, Təqvim, Zəng qeydləri və Yaxınlıqdakı cihazlar üzrə icazələrə daxil olacaq."</string> - <string name="summary_watch_single_device" msgid="3173948915947011333">"Bu tətbiq zəng edənin adı kimi məlumatları sinxronlaşdıra, <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> bu icazələrə daxil ola biləcək"</string> + <string name="chooser_title_non_profile" msgid="6035023914517087400">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> tərəfindən idarə ediləcək cihaz seçin"</string> + <string name="chooser_title" msgid="2235819929238267637">"Ayarlamaq üçün <xliff:g id="PROFILE_NAME">%1$s</xliff:g> seçin"</string> + <string name="summary_watch" msgid="7962014927042971830">"Bu tətbiq zəng edənin adı kimi məlumatları sinxronlaşdıra, <xliff:g id="DEVICE_NAME">%1$s</xliff:g> bu icazələrə daxil ola biləcək"</string> <string name="confirmation_title_glasses" msgid="8288346850537727333">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> tətbiqinə <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> cihazını idarə etmək icazəsi verilsin?"</string> - <string name="profile_name_glasses" msgid="8488394059007275998">"eynək"</string> - <string name="summary_glasses_multi_device" msgid="615259525961937348">"Bu tətbiq <xliff:g id="DEVICE_NAME">%1$s</xliff:g> cihazını idarə etmək üçün lazımdır. <xliff:g id="APP_NAME">%2$s</xliff:g> bildirişlərə, Telefon, SMS, Kontaktlar, Mikrofon və Yaxınlıqdakı cihazlar icazələrinə giriş əldə edəcək."</string> - <string name="summary_glasses_single_device" msgid="3000909894067413398">"Bu tətbiq <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> bu icazələrə daxil ola biləcək"</string> + <string name="profile_name_glasses" msgid="3506504967216601277">"cihazda"</string> + <string name="summary_glasses" msgid="2872254734959842579">"Bu tətbiq <xliff:g id="DEVICE_NAME">%1$s</xliff:g> bu icazələrə daxil ola biləcək"</string> <string name="title_app_streaming" msgid="2270331024626446950">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> tətbiqinə telefonunuzdan bu məlumata giriş icazəsi verin"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Cihazlararası xidmətlər"</string> <string name="helper_summary_app_streaming" msgid="2396773196949578425">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> adından cihazlar arasında tətbiqləri yayımlamaq icazəsi istəyir"</string> @@ -38,10 +37,10 @@ <string name="title_nearby_device_streaming" msgid="7269956847378799794">"<strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> cihazına bu əməliyyatı yerinə yetirmək icazəsi verilsin?"</string> <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="DEVICE_NAME">%2$s</xliff:g> adından tətbiq və digər sistem funksiyalarını yaxınlıqdakı cihazlara yayımlamaq icazəsi sitəyir"</string> <string name="profile_name_generic" msgid="6851028682723034988">"cihaz"</string> - <string name="summary_generic_single_device" msgid="4181180669689590417">"Tətbiq zəng edənin adı kimi məlumatları telefon ilə <xliff:g id="DEVICE_NAME">%1$s</xliff:g> arasında sinxronlaşdıracaq"</string> <string name="summary_generic" msgid="1761976003668044801">"Tətbiq zəng edənin adı kimi məlumatları telefon ilə seçilmiş cihaz arasında sinxronlaşdıracaq"</string> <string name="consent_yes" msgid="8344487259618762872">"İcazə verin"</string> <string name="consent_no" msgid="2640796915611404382">"İcazə verməyin"</string> + <string name="consent_cancel" msgid="5655005528379285841">"Ləğv edin"</string> <string name="consent_back" msgid="2560683030046918882">"Geriyə"</string> <string name="permission_expand" msgid="893185038020887411">"Genişləndirin: <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string> <string name="permission_collapse" msgid="3320833884220844084">"Yığcamlaşdırın: <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string> diff --git a/packages/CompanionDeviceManager/res/values-b+sr+Latn/strings.xml b/packages/CompanionDeviceManager/res/values-b+sr+Latn/strings.xml index bb1629085870..b37ab4b9e547 100644 --- a/packages/CompanionDeviceManager/res/values-b+sr+Latn/strings.xml +++ b/packages/CompanionDeviceManager/res/values-b+sr+Latn/strings.xml @@ -17,15 +17,14 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="4470785958457506021">"Menadžer pridruženog uređaja"</string> - <string name="confirmation_title" msgid="4593465730772390351">"Dozvolite da <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> pristupa uređaju <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string> + <string name="confirmation_title" msgid="2244241995958340998">"Dozvolite da aplikacija <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> pristupa uređaju <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string> <string name="profile_name_watch" msgid="576290739483672360">"sat"</string> - <string name="chooser_title" msgid="2262294130493605839">"Odaberite <xliff:g id="PROFILE_NAME">%1$s</xliff:g> kojim će upravljati aplikacija <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> - <string name="summary_watch" msgid="898569637110705523">"Ova aplikacija je potrebna za upravljanje uređajem <xliff:g id="DEVICE_NAME">%1$s</xliff:g>. <xliff:g id="APP_NAME">%2$s</xliff:g> će dobiti dozvolu za sinhronizovanje informacija, poput osobe koja upućuje poziv, za interakciju sa obaveštenjima i pristup dozvolama za telefon, SMS, kontakte, kalendar, evidencije poziva i uređaje u blizini."</string> - <string name="summary_watch_single_device" msgid="3173948915947011333">"Ovoj aplikaciji će biti dozvoljeno da sinhronizuje podatke, poput imena osobe koja upućuje poziv, i pristupa tim dozvolama na vašem uređaju (<xliff:g id="DEVICE_TYPE">%1$s</xliff:g>)"</string> + <string name="chooser_title_non_profile" msgid="6035023914517087400">"Odaberite uređaj kojim će upravljati aplikacija <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>"</string> + <string name="chooser_title" msgid="2235819929238267637">"Odaberite profil <xliff:g id="PROFILE_NAME">%1$s</xliff:g> koji želite da podesite"</string> + <string name="summary_watch" msgid="7962014927042971830">"Ovoj aplikaciji će biti dozvoljeno da sinhronizuje podatke, poput imena osobe koja upućuje poziv, i pristupa tim dozvolama na vašem uređaju (<xliff:g id="DEVICE_NAME">%1$s</xliff:g>)"</string> <string name="confirmation_title_glasses" msgid="8288346850537727333">"Želite li da dozvolite da <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> upravlja uređajem <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> - <string name="profile_name_glasses" msgid="8488394059007275998">"naočare"</string> - <string name="summary_glasses_multi_device" msgid="615259525961937348">"Ova aplikacija je potrebna za upravljanje uređajem <xliff:g id="DEVICE_NAME">%1$s</xliff:g>. <xliff:g id="APP_NAME">%2$s</xliff:g> će dobiti dozvolu za interakciju sa obaveštenjima i pristup dozvolama za telefon, SMS, kontakte, mikrofon i uređaje u blizini."</string> - <string name="summary_glasses_single_device" msgid="3000909894067413398">"Ovoj aplikaciji će biti dozvoljeno da pristupa ovim dozvolama na vašem uređaju (<xliff:g id="DEVICE_TYPE">%1$s</xliff:g>)"</string> + <string name="profile_name_glasses" msgid="3506504967216601277">"uređaj"</string> + <string name="summary_glasses" msgid="2872254734959842579">"Ovoj aplikaciji će biti dozvoljeno da pristupa ovim dozvolama na vašem uređaju (<xliff:g id="DEVICE_NAME">%1$s</xliff:g>)"</string> <string name="title_app_streaming" msgid="2270331024626446950">"Dozvolite da <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> pristupa ovim informacijama sa telefona"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Usluge na više uređaja"</string> <string name="helper_summary_app_streaming" msgid="2396773196949578425">"<xliff:g id="APP_NAME">%1$s</xliff:g> zahteva dozvolu u ime uređaja <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> za strimovanje aplikacija između uređaja"</string> @@ -38,10 +37,10 @@ <string name="title_nearby_device_streaming" msgid="7269956847378799794">"Želite li da dozvolite da <strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> obavi ovu radnju?"</string> <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> zahteva dozvolu u ime uređaja <xliff:g id="DEVICE_NAME">%2$s</xliff:g> da strimuje aplikacije i druge sistemske funkcije na uređaje u blizini"</string> <string name="profile_name_generic" msgid="6851028682723034988">"uređaj"</string> - <string name="summary_generic_single_device" msgid="4181180669689590417">"Ova aplikacija će moći da sinhronizuje podatke, poput imena osobe koja upućuje poziv, između telefona i uređaja <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="summary_generic" msgid="1761976003668044801">"Ova aplikacija će moći da sinhronizuje podatke, poput imena osobe koja upućuje poziv, između telefona i odabranog uređaja"</string> <string name="consent_yes" msgid="8344487259618762872">"Dozvoli"</string> <string name="consent_no" msgid="2640796915611404382">"Ne dozvoli"</string> + <string name="consent_cancel" msgid="5655005528379285841">"Otkaži"</string> <string name="consent_back" msgid="2560683030046918882">"Nazad"</string> <string name="permission_expand" msgid="893185038020887411">"Proširi <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string> <string name="permission_collapse" msgid="3320833884220844084">"Skupi <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string> diff --git a/packages/CompanionDeviceManager/res/values-be/strings.xml b/packages/CompanionDeviceManager/res/values-be/strings.xml index 9c12f5a7d20c..41889fdf5730 100644 --- a/packages/CompanionDeviceManager/res/values-be/strings.xml +++ b/packages/CompanionDeviceManager/res/values-be/strings.xml @@ -17,15 +17,14 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="4470785958457506021">"Менеджар спадарожнай прылады"</string> - <string name="confirmation_title" msgid="4593465730772390351">"Дазволіць праграме <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> доступ да прылады <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> + <string name="confirmation_title" msgid="2244241995958340998">"Дазволіць праграме <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> доступ да прылады <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> <string name="profile_name_watch" msgid="576290739483672360">"гадзіннік"</string> - <string name="chooser_title" msgid="2262294130493605839">"Выберыце прыладу (<xliff:g id="PROFILE_NAME">%1$s</xliff:g>), якой будзе кіраваць праграма <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> - <string name="summary_watch" msgid="898569637110705523">"Гэта праграма неабходная для кіравання прыладай \"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>\". <xliff:g id="APP_NAME">%2$s</xliff:g> зможа сінхранізаваць інфармацыю (напрыклад, імя таго, хто звоніць), узаемадзейнічаць з вашымі апавяшчэннямі, а таксама атрымае доступ да тэлефона, SMS, кантактаў, календара, журналаў выклікаў і прылад паблізу."</string> - <string name="summary_watch_single_device" msgid="3173948915947011333">"Гэта праграма зможа сінхранізаваць інфармацыю (напрыклад, імя таго, хто звоніць) на вашай прыладзе \"<xliff:g id="DEVICE_TYPE">%1$s</xliff:g>\" і атрымае наступныя дазволы"</string> + <string name="chooser_title_non_profile" msgid="6035023914517087400">"Выберыце прыладу (<xliff:g id="APP_NAME">%1$s</xliff:g>), якой будзе кіраваць праграма <strong></strong>"</string> + <string name="chooser_title" msgid="2235819929238267637">"Выберыце імя <xliff:g id="PROFILE_NAME">%1$s</xliff:g> для наладжвання"</string> + <string name="summary_watch" msgid="7962014927042971830">"Гэтая праграма зможа сінхранізаваць інфармацыю (напрыклад, імя таго, хто звоніць) на вашай прыладзе \"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>\" і атрымае наступныя дазволы"</string> <string name="confirmation_title_glasses" msgid="8288346850537727333">"Дазволіць праграме <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> кіраваць прыладай <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> - <string name="profile_name_glasses" msgid="8488394059007275998">"акуляры"</string> - <string name="summary_glasses_multi_device" msgid="615259525961937348">"Гэта праграма неабходная для кіравання прыладай \"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>\". <xliff:g id="APP_NAME">%2$s</xliff:g> зможа ўзаемадзейнічаць з вашымі апавяшчэннямі і атрымае доступ да тэлефона, SMS, кантактаў, мікрафона і прылад паблізу."</string> - <string name="summary_glasses_single_device" msgid="3000909894067413398">"Гэта праграма будзе мець на вашай прыладзе \"<xliff:g id="DEVICE_TYPE">%1$s</xliff:g>\" наступныя дазволы"</string> + <string name="profile_name_glasses" msgid="3506504967216601277">"прылада"</string> + <string name="summary_glasses" msgid="2872254734959842579">"Гэтая праграма будзе мець на вашай прыладзе \"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>\" наступныя дазволы"</string> <string name="title_app_streaming" msgid="2270331024626446950">"Дазвольце праграме <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> мець доступ да гэтай інфармацыі з вашага тэлефона"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Сэрвісы для некалькіх прылад"</string> <string name="helper_summary_app_streaming" msgid="2396773196949578425">"Праграма \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" запытвае дазвол ад імя вашай прылады \"<xliff:g id="DISPLAY_NAME">%2$s</xliff:g>\" на трансляцыю праграм паміж вашымі прыладамі"</string> @@ -38,10 +37,10 @@ <string name="title_nearby_device_streaming" msgid="7269956847378799794">"Дазволіць прыладзе <strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> выканаць гэта дзеянне?"</string> <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"Праграма \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" запытвае дазвол ад імя вашай прылады \"<xliff:g id="DEVICE_NAME">%2$s</xliff:g>\" на перадачу плынню змесціва праграм і іншых функцый сістэмы на прылады паблізу"</string> <string name="profile_name_generic" msgid="6851028682723034988">"прылада"</string> - <string name="summary_generic_single_device" msgid="4181180669689590417">"Гэта праграма зможа сінхранізаваць інфармацыю (напрыклад, імя таго, хто звоніць) паміж тэлефонам і прыладай \"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>\""</string> <string name="summary_generic" msgid="1761976003668044801">"Гэта праграма зможа сінхранізаваць інфармацыю (напрыклад, імя таго, хто звоніць) паміж тэлефонам і выбранай прыладай"</string> <string name="consent_yes" msgid="8344487259618762872">"Дазволіць"</string> <string name="consent_no" msgid="2640796915611404382">"Не дазваляць"</string> + <string name="consent_cancel" msgid="5655005528379285841">"Скасаваць"</string> <string name="consent_back" msgid="2560683030046918882">"Назад"</string> <string name="permission_expand" msgid="893185038020887411">"Разгарнуць <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string> <string name="permission_collapse" msgid="3320833884220844084">"Згарнуць <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string> diff --git a/packages/CompanionDeviceManager/res/values-bg/strings.xml b/packages/CompanionDeviceManager/res/values-bg/strings.xml index 4df41776e7e4..bb53c5c44eca 100644 --- a/packages/CompanionDeviceManager/res/values-bg/strings.xml +++ b/packages/CompanionDeviceManager/res/values-bg/strings.xml @@ -17,15 +17,14 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string> - <string name="confirmation_title" msgid="4593465730772390351">"Да се разреши ли на <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> да осъществява достъп до устройството <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string> + <string name="confirmation_title" msgid="2244241995958340998">"Да се разреши ли на приложението <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> да осъществява достъп до устройството <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> <string name="profile_name_watch" msgid="576290739483672360">"часовник"</string> - <string name="chooser_title" msgid="2262294130493605839">"Изберете <xliff:g id="PROFILE_NAME">%1$s</xliff:g> за управление от <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> - <string name="summary_watch" msgid="898569637110705523">"Това приложение е необходимо за управление на <xliff:g id="DEVICE_NAME">%1$s</xliff:g>. <xliff:g id="APP_NAME">%2$s</xliff:g> ще получи право да синхронизира различна информация, като например името на обаждащия се, да взаимодейства с известията ви и достъп до разрешенията за телефона, SMS съобщенията, контактите, календара, списъците с обажданията и устройствата в близост."</string> - <string name="summary_watch_single_device" msgid="3173948915947011333">"Това приложение ще получи право да синхронизира различна информация, като например името на обаждащия се, и достъп до следните разрешения за вашия <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string> + <string name="chooser_title_non_profile" msgid="6035023914517087400">"Изберете устройство, което да се управлява от <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>"</string> + <string name="chooser_title" msgid="2235819929238267637">"Изберете <xliff:g id="PROFILE_NAME">%1$s</xliff:g>, за да го настроите"</string> + <string name="summary_watch" msgid="7962014927042971830">"Това приложение ще получи право да синхронизира различна информация, като например името на обаждащия се, и достъп до следните разрешения за вашия <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="confirmation_title_glasses" msgid="8288346850537727333">"Разрешавате ли на <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> да управлява устройството <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> - <string name="profile_name_glasses" msgid="8488394059007275998">"очилата"</string> - <string name="summary_glasses_multi_device" msgid="615259525961937348">"Това приложение е необходимо за управление на <xliff:g id="DEVICE_NAME">%1$s</xliff:g>. Приложението <xliff:g id="APP_NAME">%2$s</xliff:g> ще получи право да взаимодейства с известията ви, както и достъп до разрешенията за телефона, SMS съобщенията, контактите, микрофона и устройствата в близост."</string> - <string name="summary_glasses_single_device" msgid="3000909894067413398">"Това приложение ще има достъп до следните разрешения за вашия <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>:"</string> + <string name="profile_name_glasses" msgid="3506504967216601277">"устройство"</string> + <string name="summary_glasses" msgid="2872254734959842579">"Това приложение ще има достъп до следните разрешения за вашето <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="title_app_streaming" msgid="2270331024626446950">"Разрешете на <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> да осъществява достъп до тази информация от телефона ви"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Услуги за различни устройства"</string> <string name="helper_summary_app_streaming" msgid="2396773196949578425">"<xliff:g id="APP_NAME">%1$s</xliff:g> иска разрешение от името на <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> да предава поточно приложения между устройствата ви"</string> @@ -38,10 +37,10 @@ <string name="title_nearby_device_streaming" msgid="7269956847378799794">"Разрешавате ли на <strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> да предприема това действие?"</string> <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<xliff:g id="APP_NAME">%1$s</xliff:g> иска разрешение от името на <xliff:g id="DEVICE_NAME">%2$s</xliff:g> да предава поточно приложения и други системни функции към устройства в близост"</string> <string name="profile_name_generic" msgid="6851028682723034988">"устройство"</string> - <string name="summary_generic_single_device" msgid="4181180669689590417">"Това приложение ще може да синхронизира различна информация, като например името на обаждащия се, между телефона ви и <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="summary_generic" msgid="1761976003668044801">"Това приложение ще може да синхронизира различна информация, като например името на обаждащия се, между телефона ви и избраното устройство"</string> <string name="consent_yes" msgid="8344487259618762872">"Разрешаване"</string> <string name="consent_no" msgid="2640796915611404382">"Забраняване"</string> + <string name="consent_cancel" msgid="5655005528379285841">"Отказ"</string> <string name="consent_back" msgid="2560683030046918882">"Назад"</string> <string name="permission_expand" msgid="893185038020887411">"Разгъване на <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string> <string name="permission_collapse" msgid="3320833884220844084">"Свиване на <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string> diff --git a/packages/CompanionDeviceManager/res/values-bn/strings.xml b/packages/CompanionDeviceManager/res/values-bn/strings.xml index 735b620b7d0e..bc0af0bd1373 100644 --- a/packages/CompanionDeviceManager/res/values-bn/strings.xml +++ b/packages/CompanionDeviceManager/res/values-bn/strings.xml @@ -17,15 +17,14 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string> - <string name="confirmation_title" msgid="4593465730772390351">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> অ্যাপকে <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> অ্যাক্সেস করার অনুমতি দেবেন?"</string> + <string name="confirmation_title" msgid="2244241995958340998">"<strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> অ্যাক্সেস করার জন্য <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> অ্যাপকে কি অনুমতি দিতে চান?"</string> <string name="profile_name_watch" msgid="576290739483672360">"ঘড়ি"</string> - <string name="chooser_title" msgid="2262294130493605839">"<xliff:g id="PROFILE_NAME">%1$s</xliff:g> বেছে নিন যেটি <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> ম্যানেজ করবে"</string> - <string name="summary_watch" msgid="898569637110705523">"আপনার <xliff:g id="DEVICE_NAME">%1$s</xliff:g> ম্যানেজ করতে এই অ্যাপটি প্রয়োজন। <xliff:g id="APP_NAME">%2$s</xliff:g> অ্যাপকে কলারের নাম ও আপনার বিজ্ঞপ্তির সাথে ইন্টার্যাক্ট করা সংক্রান্ত তথ্য সিঙ্কের অনুমতি দেওয়া হবে এবং আপনার ফোন, এসএমএস, পরিচিতি, ক্যালেন্ডার, কল লগ এবং আশেপাশের ডিভাইস ব্যবহার করার অনুমতির মতো তথ্যে অ্যাক্সেস দেওয়া হবে।"</string> - <string name="summary_watch_single_device" msgid="3173948915947011333">"এই অ্যাপকে, কল করছেন এমন কোনও ব্যক্তির নামের মতো তথ্য সিঙ্ক এবং আপনার <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>-এ এইসব অনুমতি অ্যাক্সেস করতে দেওয়া হবে"</string> + <string name="chooser_title_non_profile" msgid="6035023914517087400">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ম্যানেজ করা যাবে এমন একটি ডিভাইস বেছে নিন"</string> + <string name="chooser_title" msgid="2235819929238267637">"সেট-আপ করতে কোনও <xliff:g id="PROFILE_NAME">%1$s</xliff:g> বেছে নিন"</string> + <string name="summary_watch" msgid="7962014927042971830">"এই অ্যাপকে, কল করছেন এমন কোনও ব্যক্তির নামের মতো তথ্য সিঙ্ক করতে এবং আপনার <xliff:g id="DEVICE_NAME">%1$s</xliff:g>-এ এইসব অনুমতি অ্যাক্সেস করতে দেওয়া হবে"</string> <string name="confirmation_title_glasses" msgid="8288346850537727333">"আপনি কি <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ম্যানেজ করার জন্য <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>-কে অনুমতি দেবেন?"</string> - <string name="profile_name_glasses" msgid="8488394059007275998">"চশমা"</string> - <string name="summary_glasses_multi_device" msgid="615259525961937348">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> ম্যানেজ করতে এই অ্যাপ দরকার। <xliff:g id="APP_NAME">%2$s</xliff:g>-কে আপনার বিজ্ঞপ্তির সাথে ইন্টার্যাক্ট করার এবং ফোন, এসএমএস, পরিচিতি, মাইক্রোফোন ও আশেপাশের ডিভাইসের অনুমতি অ্যাক্সেস করতে দেওয়া হবে।"</string> - <string name="summary_glasses_single_device" msgid="3000909894067413398">"এই অ্যাপ আপনার <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>-এ এইসব অনুমতি অ্যাক্সেস করতে পারবে"</string> + <string name="profile_name_glasses" msgid="3506504967216601277">"ডিভাইস"</string> + <string name="summary_glasses" msgid="2872254734959842579">"এই অ্যাপ আপনার <xliff:g id="DEVICE_NAME">%1$s</xliff:g>-এ এইসব অনুমতি অ্যাক্সেস করতে পারবে"</string> <string name="title_app_streaming" msgid="2270331024626446950">"আপনার ফোন থেকে <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> অ্যাপকে এই তথ্য অ্যাক্সেস করার অনুমতি দিন"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"ক্রস-ডিভাইস পরিষেবা"</string> <string name="helper_summary_app_streaming" msgid="2396773196949578425">"আপনার ডিভাইসগুলির মধ্যে অ্যাপ স্ট্রিম করার জন্য <xliff:g id="APP_NAME">%1$s</xliff:g>, <xliff:g id="DISPLAY_NAME">%2$s</xliff:g>-এর হয়ে অনুমতি চাইছে"</string> @@ -38,10 +37,10 @@ <string name="title_nearby_device_streaming" msgid="7269956847378799794">"<strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong>-কে এই কাজটি করতে দেবেন?"</string> <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"আশেপাশের ডিভাইসে অ্যাপ ও অন্যান্য সিস্টেম ফিচার স্ট্রিম করার জন্য আপনার <xliff:g id="DEVICE_NAME">%2$s</xliff:g>-এর হয়ে <xliff:g id="APP_NAME">%1$s</xliff:g> অনুমতি চেয়ে অনুরোধ করছে"</string> <string name="profile_name_generic" msgid="6851028682723034988">"ডিভাইস"</string> - <string name="summary_generic_single_device" msgid="4181180669689590417">"এই অ্যাপ, আপনার ফোন এবং <xliff:g id="DEVICE_NAME">%1$s</xliff:g> ডিভাইসের মধ্যে তথ্য সিঙ্ক করতে পারবে, যেমন কোনও কলারের নাম"</string> <string name="summary_generic" msgid="1761976003668044801">"এই অ্যাপ, আপনার ফোন এবং বেছে নেওয়া ডিভাইসের মধ্যে তথ্য সিঙ্ক করতে পারবে, যেমন কোনও কলারের নাম"</string> <string name="consent_yes" msgid="8344487259618762872">"অনুমতি দিন"</string> <string name="consent_no" msgid="2640796915611404382">"অনুমতি দেবেন না"</string> + <string name="consent_cancel" msgid="5655005528379285841">"বাতিল করুন"</string> <string name="consent_back" msgid="2560683030046918882">"ফিরুন"</string> <string name="permission_expand" msgid="893185038020887411">"<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g> বড় করুন"</string> <string name="permission_collapse" msgid="3320833884220844084">"<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g> আড়াল করুন"</string> diff --git a/packages/CompanionDeviceManager/res/values-bs/strings.xml b/packages/CompanionDeviceManager/res/values-bs/strings.xml index 2de08380385d..538ee3532f0e 100644 --- a/packages/CompanionDeviceManager/res/values-bs/strings.xml +++ b/packages/CompanionDeviceManager/res/values-bs/strings.xml @@ -17,15 +17,14 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="4470785958457506021">"Prateći upravitelj uređaja"</string> - <string name="confirmation_title" msgid="4593465730772390351">"Dozvoliti aplikaciji <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> da pristupa uređaju <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> + <string name="confirmation_title" msgid="2244241995958340998">"Dozvoliti aplikaciji <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> da pristupa uređaju <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> <string name="profile_name_watch" msgid="576290739483672360">"sat"</string> - <string name="chooser_title" msgid="2262294130493605839">"Odaberite profil \"<xliff:g id="PROFILE_NAME">%1$s</xliff:g>\" kojim će upravljati aplikacija <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> - <string name="summary_watch" msgid="898569637110705523">"Ova aplikacija je potrebna za upravljanje uređajem <xliff:g id="DEVICE_NAME">%1$s</xliff:g>. Aplikaciji <xliff:g id="APP_NAME">%2$s</xliff:g> će biti dozvoljeni sinhroniziranje informacija, kao što je ime osobe koja upućuje poziv, interakcija s obavještenjima i pristup odobrenjima za Telefon, SMS, Kontakte, Kalendar, Zapisnike poziva i Uređaje u blizini."</string> - <string name="summary_watch_single_device" msgid="3173948915947011333">"Aplikaciji će biti dozvoljeni sinhroniziranje informacija, kao što je ime osobe koja upućuje poziv i pristup ovim odobrenjima na uređaju <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string> + <string name="chooser_title_non_profile" msgid="6035023914517087400">"Odaberite uređaj kojim će upravljati aplikacija <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>"</string> + <string name="chooser_title" msgid="2235819929238267637">"Odaberite <xliff:g id="PROFILE_NAME">%1$s</xliff:g> da postavite"</string> + <string name="summary_watch" msgid="7962014927042971830">"Aplikaciji će biti dozvoljeni sinhroniziranje informacija, kao što je ime osobe koja upućuje poziv i pristup ovim odobrenjima na uređaju <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="confirmation_title_glasses" msgid="8288346850537727333">"Dozvoliti aplikaciji <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> da upravlja uređajem <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> - <string name="profile_name_glasses" msgid="8488394059007275998">"naočale"</string> - <string name="summary_glasses_multi_device" msgid="615259525961937348">"Ova aplikacija je potrebna za upravljanje uređajem <xliff:g id="DEVICE_NAME">%1$s</xliff:g>. Aplikaciji <xliff:g id="APP_NAME">%2$s</xliff:g> će biti dozvoljena interakcija s obavještenjima i pristup odobrenjima za Telefon, SMS, Kontakte, Mikrofon i Uređaje u blizini."</string> - <string name="summary_glasses_single_device" msgid="3000909894067413398">"Aplikaciji će biti dozvoljen pristup ovim odobrenjima na uređaju <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string> + <string name="profile_name_glasses" msgid="3506504967216601277">"uređaj"</string> + <string name="summary_glasses" msgid="2872254734959842579">"Aplikaciji će biti dozvoljen pristup ovim odobrenjima na uređaju <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="title_app_streaming" msgid="2270331024626446950">"Dozvolite da aplikacija <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> pristupa ovim informacijama s telefona"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Usluga na više uređaja"</string> <string name="helper_summary_app_streaming" msgid="2396773196949578425">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> u ime uređaja <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> zahtijeva odobrenje da prenosi aplikacije između vaših uređaja"</string> @@ -38,10 +37,10 @@ <string name="title_nearby_device_streaming" msgid="7269956847378799794">"Dozvoliti uređaju <strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> da poduzme ovu radnju?"</string> <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> u ime uređaja <xliff:g id="DEVICE_NAME">%2$s</xliff:g> traži odobrenje da prenosi aplikacije i druge funkcije sistema na uređajima u blizini"</string> <string name="profile_name_generic" msgid="6851028682723034988">"uređaj"</string> - <string name="summary_generic_single_device" msgid="4181180669689590417">"Ova aplikacija će moći sinhronizirati informacije, kao što je ime osobe koja upućuje poziv, između vašeg telefona i uređaja <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="summary_generic" msgid="1761976003668044801">"Ova aplikacija će moći sinhronizirati informacije, kao što je ime osobe koja upućuje poziv, između vašeg telefona i odabranog uređaja"</string> <string name="consent_yes" msgid="8344487259618762872">"Dozvoli"</string> <string name="consent_no" msgid="2640796915611404382">"Nemoj dozvoliti"</string> + <string name="consent_cancel" msgid="5655005528379285841">"Otkaži"</string> <string name="consent_back" msgid="2560683030046918882">"Nazad"</string> <string name="permission_expand" msgid="893185038020887411">"Proširivanje stavke <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string> <string name="permission_collapse" msgid="3320833884220844084">"Sužavanje stavke <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string> diff --git a/packages/CompanionDeviceManager/res/values-ca/strings.xml b/packages/CompanionDeviceManager/res/values-ca/strings.xml index 38a6dbc942aa..b6f182dc7096 100644 --- a/packages/CompanionDeviceManager/res/values-ca/strings.xml +++ b/packages/CompanionDeviceManager/res/values-ca/strings.xml @@ -17,15 +17,14 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="4470785958457506021">"Gestor de dispositius complementaris"</string> - <string name="confirmation_title" msgid="4593465730772390351">"Permet que <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> accedeixi a <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string> + <string name="confirmation_title" msgid="2244241995958340998">"Vols permetre que l\'aplicació <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> accedeixi a <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> <string name="profile_name_watch" msgid="576290739483672360">"rellotge"</string> - <string name="chooser_title" msgid="2262294130493605839">"Tria un <xliff:g id="PROFILE_NAME">%1$s</xliff:g> perquè el gestioni <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> - <string name="summary_watch" msgid="898569637110705523">"Aquesta aplicació es necessita per gestionar el dispositiu (<xliff:g id="DEVICE_NAME">%1$s</xliff:g>). <xliff:g id="APP_NAME">%2$s</xliff:g> tindrà permís per sincronitzar informació, com ara el nom d\'algú que truca, per interaccionar amb les teves notificacions i accedir al telèfon, als SMS, als contactes, al calendari, als registres de trucades i als dispositius propers."</string> - <string name="summary_watch_single_device" msgid="3173948915947011333">"Aquesta aplicació podrà sincronitzar informació, com ara el nom d\'algú que truca, i accedir a aquests permisos al dispositiu (<xliff:g id="DEVICE_TYPE">%1$s</xliff:g>)"</string> + <string name="chooser_title_non_profile" msgid="6035023914517087400">"Tria un dispositiu perquè el gestioni <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>"</string> + <string name="chooser_title" msgid="2235819929238267637">"Tria un <xliff:g id="PROFILE_NAME">%1$s</xliff:g> per configurar"</string> + <string name="summary_watch" msgid="7962014927042971830">"Aquesta aplicació podrà sincronitzar informació, com ara el nom d\'algú que truca, i accedir a aquests permisos al dispositiu (<xliff:g id="DEVICE_NAME">%1$s</xliff:g>)"</string> <string name="confirmation_title_glasses" msgid="8288346850537727333">"Permet que <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> gestioni <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string> - <string name="profile_name_glasses" msgid="8488394059007275998">"ulleres"</string> - <string name="summary_glasses_multi_device" msgid="615259525961937348">"Aquesta aplicació es necessita per gestionar el dispositiu (<xliff:g id="DEVICE_NAME">%1$s</xliff:g>). <xliff:g id="APP_NAME">%2$s</xliff:g> tindrà permís per interaccionar amb les teves notificacions i accedir al telèfon, als SMS, als contactes, al micròfon i als dispositius propers."</string> - <string name="summary_glasses_single_device" msgid="3000909894067413398">"Aquesta aplicació podrà accedir a aquests permisos del dispositiu (<xliff:g id="DEVICE_TYPE">%1$s</xliff:g>)"</string> + <string name="profile_name_glasses" msgid="3506504967216601277">"dispositiu"</string> + <string name="summary_glasses" msgid="2872254734959842579">"Aquesta aplicació podrà accedir a aquests permisos del dispositiu (<xliff:g id="DEVICE_NAME">%1$s</xliff:g>)"</string> <string name="title_app_streaming" msgid="2270331024626446950">"Permet que <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> accedeixi a aquesta informació del telèfon"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Serveis multidispositiu"</string> <string name="helper_summary_app_streaming" msgid="2396773196949578425">"<xliff:g id="APP_NAME">%1$s</xliff:g> demana permís en nom del teu dispositiu (<xliff:g id="DISPLAY_NAME">%2$s</xliff:g>) per reproduir en continu aplicacions entre els dispositius"</string> @@ -38,10 +37,10 @@ <string name="title_nearby_device_streaming" msgid="7269956847378799794">"Vols permetre que <strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> dugui a terme aquesta acció?"</string> <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<xliff:g id="APP_NAME">%1$s</xliff:g> sol·licita permís en nom del teu dispositiu (<xliff:g id="DEVICE_NAME">%2$s</xliff:g>) per reproduir en continu aplicacions i altres funcions del sistema en dispositius propers"</string> <string name="profile_name_generic" msgid="6851028682723034988">"dispositiu"</string> - <string name="summary_generic_single_device" msgid="4181180669689590417">"Aquesta aplicació podrà sincronitzar informació, com ara el nom d\'algú que truca, entre el teu telèfon i el dispositiu (<xliff:g id="DEVICE_NAME">%1$s</xliff:g>)"</string> <string name="summary_generic" msgid="1761976003668044801">"Aquesta aplicació podrà sincronitzar informació, com ara el nom d\'algú que truca, entre el teu telèfon i el dispositiu triat"</string> <string name="consent_yes" msgid="8344487259618762872">"Permet"</string> <string name="consent_no" msgid="2640796915611404382">"No permetis"</string> + <string name="consent_cancel" msgid="5655005528379285841">"Cancel·la"</string> <string name="consent_back" msgid="2560683030046918882">"Enrere"</string> <string name="permission_expand" msgid="893185038020887411">"Desplega <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string> <string name="permission_collapse" msgid="3320833884220844084">"Replega <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string> diff --git a/packages/CompanionDeviceManager/res/values-cs/strings.xml b/packages/CompanionDeviceManager/res/values-cs/strings.xml index 71518233e919..b62496d3e768 100644 --- a/packages/CompanionDeviceManager/res/values-cs/strings.xml +++ b/packages/CompanionDeviceManager/res/values-cs/strings.xml @@ -17,15 +17,14 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="4470785958457506021">"Správce doprovodných zařízení"</string> - <string name="confirmation_title" msgid="4593465730772390351">"Povolit aplikaci <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> přístup k <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> + <string name="confirmation_title" msgid="2244241995958340998">"Povolit aplikaci <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> přístup k <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> <string name="profile_name_watch" msgid="576290739483672360">"hodinky"</string> - <string name="chooser_title" msgid="2262294130493605839">"Vyberte <xliff:g id="PROFILE_NAME">%1$s</xliff:g>, které chcete spravovat pomocí aplikace <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> - <string name="summary_watch" msgid="898569637110705523">"Tato aplikace je nutná ke správě zařízení <xliff:g id="DEVICE_NAME">%1$s</xliff:g>. <xliff:g id="APP_NAME">%2$s</xliff:g> bude moci synchronizovat údaje, jako je jméno volajícího, interagovat s vašimi oznámeními a získat přístup k vašim oprávněním k telefonu, SMS, kontaktům, kalendáři, seznamům hovorů a zařízením v okolí."</string> - <string name="summary_watch_single_device" msgid="3173948915947011333">"Tato aplikace bude moci synchronizovat údaje, jako je jméno volajícího, a získat přístup k těmto oprávněním v <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string> + <string name="chooser_title_non_profile" msgid="6035023914517087400">"Vyberte zařízení, které chcete spravovat pomocí aplikace <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>"</string> + <string name="chooser_title" msgid="2235819929238267637">"Vyberte zařízení <xliff:g id="PROFILE_NAME">%1$s</xliff:g>, které chcete nastavit"</string> + <string name="summary_watch" msgid="7962014927042971830">"Tato aplikace bude moci synchronizovat údaje, jako je jméno volajícího, a získat na zařízení <xliff:g id="DEVICE_NAME">%1$s</xliff:g> přístup k těmto oprávněním"</string> <string name="confirmation_title_glasses" msgid="8288346850537727333">"Povolit aplikaci <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> spravovat zařízení <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> - <string name="profile_name_glasses" msgid="8488394059007275998">"brýle"</string> - <string name="summary_glasses_multi_device" msgid="615259525961937348">"Tato aplikace je nutná ke správě zařízení <xliff:g id="DEVICE_NAME">%1$s</xliff:g>. <xliff:g id="APP_NAME">%2$s</xliff:g> bude moci interagovat s vašimi oznámeními a získat přístup k vašim oprávněním k telefonu, SMS, kontaktům, mikrofonu a zařízením v okolí."</string> - <string name="summary_glasses_single_device" msgid="3000909894067413398">"Tato aplikace bude mít ve vašem <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> povolený přístup k těmto oprávněním:"</string> + <string name="profile_name_glasses" msgid="3506504967216601277">"zařízení"</string> + <string name="summary_glasses" msgid="2872254734959842579">"Tato aplikace bude mít ve vašem <xliff:g id="DEVICE_NAME">%1$s</xliff:g> povolený přístup k těmto oprávněním"</string> <string name="title_app_streaming" msgid="2270331024626446950">"Povolte aplikaci <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> přístup k těmto informacím z vašeho telefonu"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Služby pro více zařízení"</string> <string name="helper_summary_app_streaming" msgid="2396773196949578425">"Aplikace <xliff:g id="APP_NAME">%1$s</xliff:g> požaduje za vaše zařízení <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> oprávnění ke streamování aplikací mezi zařízeními"</string> @@ -38,10 +37,10 @@ <string name="title_nearby_device_streaming" msgid="7269956847378799794">"Povolit zařízení <strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> podniknout tuto akci?"</string> <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"Aplikace <xliff:g id="APP_NAME">%1$s</xliff:g> žádá jménem vašeho zařízení <xliff:g id="DEVICE_NAME">%2$s</xliff:g> o oprávnění streamovat aplikace a další systémové funkce do zařízení v okolí"</string> <string name="profile_name_generic" msgid="6851028682723034988">"zařízení"</string> - <string name="summary_generic_single_device" msgid="4181180669689590417">"Tato aplikace bude moci synchronizovat údaje, jako je jméno volajícího, mezi vaším telefonem a zařízením <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="summary_generic" msgid="1761976003668044801">"Tato aplikace bude moci synchronizovat údaje, jako je jméno volajícího, mezi vaším telefonem a vybraným zařízením"</string> <string name="consent_yes" msgid="8344487259618762872">"Povolit"</string> <string name="consent_no" msgid="2640796915611404382">"Nepovolovat"</string> + <string name="consent_cancel" msgid="5655005528379285841">"Zrušit"</string> <string name="consent_back" msgid="2560683030046918882">"Zpět"</string> <string name="permission_expand" msgid="893185038020887411">"Rozbalit sekci <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string> <string name="permission_collapse" msgid="3320833884220844084">"Sbalit sekci <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string> diff --git a/packages/CompanionDeviceManager/res/values-da/strings.xml b/packages/CompanionDeviceManager/res/values-da/strings.xml index 14f24543f1f8..df63e6c6dfe5 100644 --- a/packages/CompanionDeviceManager/res/values-da/strings.xml +++ b/packages/CompanionDeviceManager/res/values-da/strings.xml @@ -17,15 +17,14 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="4470785958457506021">"Medfølgende enhedsadministrator"</string> - <string name="confirmation_title" msgid="4593465730772390351">"Vil du give <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> adgang til <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> + <string name="confirmation_title" msgid="2244241995958340998">"Vil du give appen <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> adgang til <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> <string name="profile_name_watch" msgid="576290739483672360">"ur"</string> - <string name="chooser_title" msgid="2262294130493605839">"Vælg det <xliff:g id="PROFILE_NAME">%1$s</xliff:g>, som skal administreres af <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> - <string name="summary_watch" msgid="898569637110705523">"Du skal bruge denne app for at administrere <xliff:g id="DEVICE_NAME">%1$s</xliff:g>. <xliff:g id="APP_NAME">%2$s</xliff:g> får tilladelse til at interagere med dine notifikationer og synkronisere oplysninger som f.eks. navnet på en person, der ringer, og appen får adgang til dine tilladelser for Opkald, Sms, Kalender, Opkaldshistorik og Enheder i nærheden."</string> - <string name="summary_watch_single_device" msgid="3173948915947011333">"Denne app får tilladelse til at synkronisere oplysninger, f.eks. navne på dem, der ringer, og adgang til disse tilladelser på din <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string> + <string name="chooser_title_non_profile" msgid="6035023914517087400">"Vælg en enhed, som skal administreres af <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>"</string> + <string name="chooser_title" msgid="2235819929238267637">"Vælg en <xliff:g id="PROFILE_NAME">%1$s</xliff:g>, som du vil konfigurere"</string> + <string name="summary_watch" msgid="7962014927042971830">"Denne app får tilladelse til at synkronisere oplysninger, f.eks. navne på dem, der ringer, og adgang til disse tilladelser på din <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="confirmation_title_glasses" msgid="8288346850537727333">"Vil du tillade, at <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> administrerer <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> - <string name="profile_name_glasses" msgid="8488394059007275998">"briller"</string> - <string name="summary_glasses_multi_device" msgid="615259525961937348">"Du skal bruge denne app for at administrere <xliff:g id="DEVICE_NAME">%1$s</xliff:g>. <xliff:g id="APP_NAME">%2$s</xliff:g> får tilladelse til at interagere med dine notifikationer og tilgå tilladelserne Telefon, Sms, Kontakter, Mikrofon og Enheder i nærheden."</string> - <string name="summary_glasses_single_device" msgid="3000909894067413398">"Denne app får adgang til disse tilladelser på din <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string> + <string name="profile_name_glasses" msgid="3506504967216601277">"enhed"</string> + <string name="summary_glasses" msgid="2872254734959842579">"Denne app får adgang til disse tilladelser på din <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="title_app_streaming" msgid="2270331024626446950">"Giv <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> adgang til disse oplysninger fra din telefon"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Tjenester, som kan tilsluttes en anden enhed"</string> <string name="helper_summary_app_streaming" msgid="2396773196949578425">"<xliff:g id="APP_NAME">%1$s</xliff:g> anmoder om tilladelse på vegne af din <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> til at streame apps mellem dine enheder"</string> @@ -38,10 +37,10 @@ <string name="title_nearby_device_streaming" msgid="7269956847378799794">"Vil du tillade, at <strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> foretager denne handling?"</string> <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<xliff:g id="APP_NAME">%1$s</xliff:g> anmoder om tilladelse på vegne af din <xliff:g id="DEVICE_NAME">%2$s</xliff:g> til at streame apps og andre systemfunktioner til enheder i nærheden"</string> <string name="profile_name_generic" msgid="6851028682723034988">"enhed"</string> - <string name="summary_generic_single_device" msgid="4181180669689590417">"Denne app vil kunne synkronisere oplysninger som f.eks. navnet på en person, der ringer, mellem din telefon og <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="summary_generic" msgid="1761976003668044801">"Denne app vil kunne synkronisere oplysninger som f.eks. navnet på en person, der ringer, mellem din telefon og den valgte enhed"</string> <string name="consent_yes" msgid="8344487259618762872">"Tillad"</string> <string name="consent_no" msgid="2640796915611404382">"Tillad ikke"</string> + <string name="consent_cancel" msgid="5655005528379285841">"Annuller"</string> <string name="consent_back" msgid="2560683030046918882">"Tilbage"</string> <string name="permission_expand" msgid="893185038020887411">"Udvid <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string> <string name="permission_collapse" msgid="3320833884220844084">"Skjul <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string> diff --git a/packages/CompanionDeviceManager/res/values-de/strings.xml b/packages/CompanionDeviceManager/res/values-de/strings.xml index d8c6ac3be0b7..f487f9403e8b 100644 --- a/packages/CompanionDeviceManager/res/values-de/strings.xml +++ b/packages/CompanionDeviceManager/res/values-de/strings.xml @@ -17,15 +17,14 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="4470785958457506021">"Begleitgerät-Manager"</string> - <string name="confirmation_title" msgid="4593465730772390351">"Zulassen, dass <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> auf das Gerät <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> zugreifen darf?"</string> + <string name="confirmation_title" msgid="2244241995958340998">"Zulassen, dass <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> auf <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> zugreifen darf?"</string> <string name="profile_name_watch" msgid="576290739483672360">"Smartwatch"</string> - <string name="chooser_title" msgid="2262294130493605839">"Gerät „<xliff:g id="PROFILE_NAME">%1$s</xliff:g>“ auswählen, das von <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> verwaltet werden soll"</string> - <string name="summary_watch" msgid="898569637110705523">"Diese App wird zur Verwaltung deines Geräts (<xliff:g id="DEVICE_NAME">%1$s</xliff:g>) benötigt. <xliff:g id="APP_NAME">%2$s</xliff:g> darf dann Daten wie den Namen eines Anrufers synchronisieren, mit deinen Benachrichtigungen interagieren und auf die Berechtigungen „Telefon“, „SMS“, „Kontakte“, „Kalender“, „Anruflisten“ und „Geräte in der Nähe“ zugreifen."</string> - <string name="summary_watch_single_device" msgid="3173948915947011333">"Diese App darf dann Daten wie den Namen eines Anrufers synchronisieren und auf folgende Berechtigungen auf deinem <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> zugreifen"</string> + <string name="chooser_title_non_profile" msgid="6035023914517087400">"Gerät auswählen, das von <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> verwaltet werden soll"</string> + <string name="chooser_title" msgid="2235819929238267637">"<xliff:g id="PROFILE_NAME">%1$s</xliff:g> zum Einrichten auswählen"</string> + <string name="summary_watch" msgid="7962014927042971830">"Diese App darf dann Daten wie den Namen eines Anrufers synchronisieren und auf diese Berechtigungen auf deinem <xliff:g id="DEVICE_NAME">%1$s</xliff:g> zugreifen"</string> <string name="confirmation_title_glasses" msgid="8288346850537727333">"Zulassen, dass <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> das Gerät <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> verwalten darf"</string> - <string name="profile_name_glasses" msgid="8488394059007275998">"Glass-Geräte"</string> - <string name="summary_glasses_multi_device" msgid="615259525961937348">"Diese App wird zur Verwaltung deines Geräts (<xliff:g id="DEVICE_NAME">%1$s</xliff:g>) benötigt. <xliff:g id="APP_NAME">%2$s</xliff:g> darf mit deinen Benachrichtigungen interagieren und auf die Berechtigungen „Telefon“, „SMS“, „Kontakte“, „Mikrofon“ und „Geräte in der Nähe“ zugreifen."</string> - <string name="summary_glasses_single_device" msgid="3000909894067413398">"Diese App darf dann auf die folgenden Berechtigungen auf deinem <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> zugreifen:"</string> + <string name="profile_name_glasses" msgid="3506504967216601277">"Gerät"</string> + <string name="summary_glasses" msgid="2872254734959842579">"Diese App darf dann auf diese Berechtigungen auf deinem <xliff:g id="DEVICE_NAME">%1$s</xliff:g> zugreifen:"</string> <string name="title_app_streaming" msgid="2270331024626446950">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> Zugriff auf diese Informationen von deinem Smartphone gewähren"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Geräteübergreifende Dienste"</string> <string name="helper_summary_app_streaming" msgid="2396773196949578425">"<xliff:g id="APP_NAME">%1$s</xliff:g> bittet für dein <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> um die Berechtigung zum Streamen von Apps zwischen deinen Geräten"</string> @@ -38,10 +37,10 @@ <string name="title_nearby_device_streaming" msgid="7269956847378799794">"Darf das Gerät <strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> diese Aktion ausführen?"</string> <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<xliff:g id="APP_NAME">%1$s</xliff:g> bittet für dein Gerät (<xliff:g id="DEVICE_NAME">%2$s</xliff:g>) um die Berechtigung, Apps und andere Systemfunktionen auf Geräte in der Nähe zu streamen"</string> <string name="profile_name_generic" msgid="6851028682723034988">"Gerät"</string> - <string name="summary_generic_single_device" msgid="4181180669689590417">"Diese App kann dann Daten wie den Namen eines Anrufers zwischen deinem Smartphone und deinem Gerät (<xliff:g id="DEVICE_NAME">%1$s</xliff:g>) synchronisieren"</string> <string name="summary_generic" msgid="1761976003668044801">"Diese App kann dann Daten wie den Namen eines Anrufers zwischen deinem Smartphone und dem ausgewählten Gerät synchronisieren"</string> <string name="consent_yes" msgid="8344487259618762872">"Zulassen"</string> <string name="consent_no" msgid="2640796915611404382">"Nicht zulassen"</string> + <string name="consent_cancel" msgid="5655005528379285841">"Abbrechen"</string> <string name="consent_back" msgid="2560683030046918882">"Zurück"</string> <string name="permission_expand" msgid="893185038020887411">"<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g> maximieren"</string> <string name="permission_collapse" msgid="3320833884220844084">"<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g> minimieren"</string> diff --git a/packages/CompanionDeviceManager/res/values-el/strings.xml b/packages/CompanionDeviceManager/res/values-el/strings.xml index 37fc39f19f0e..1c8ecb710a26 100644 --- a/packages/CompanionDeviceManager/res/values-el/strings.xml +++ b/packages/CompanionDeviceManager/res/values-el/strings.xml @@ -17,15 +17,14 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="4470785958457506021">"Διαχείριση συνοδευτικής εφαρμογής"</string> - <string name="confirmation_title" msgid="4593465730772390351">"Να επιτρέπεται στην εφαρμογή <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> να έχει πρόσβαση στη συσκευή <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> ;"</string> + <string name="confirmation_title" msgid="2244241995958340998">"Να επιτρέπεται στην εφαρμογή <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> να έχει πρόσβαση στη συσκευή <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>;"</string> <string name="profile_name_watch" msgid="576290739483672360">"ρολόι"</string> - <string name="chooser_title" msgid="2262294130493605839">"Επιλέξτε ένα προφίλ <xliff:g id="PROFILE_NAME">%1$s</xliff:g> για διαχείριση από την εφαρμογή <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> - <string name="summary_watch" msgid="898569637110705523">"Αυτή η εφαρμογή είναι απαραίτητη για τη διαχείριση της συσκευής <xliff:g id="DEVICE_NAME">%1$s</xliff:g>. Η εφαρμογή <xliff:g id="APP_NAME">%2$s</xliff:g> θα μπορεί να συγχρονίζει πληροφορίες, όπως το όνομα ενός ατόμου που σας καλεί, να αλληλεπιδρά με τις ειδοποιήσεις σας και να αποκτά πρόσβαση στις άδειες Τηλέφωνο, SMS, Επαφές, Ημερολόγιο, Αρχεία καταγρ. κλήσ. και Συσκευές σε κοντινή απόσταση."</string> - <string name="summary_watch_single_device" msgid="3173948915947011333">"Αυτή η εφαρμογή θα μπορεί να συγχρονίζει πληροφορίες, όπως το όνομα ενός ατόμου που σας καλεί, και να αποκτά πρόσβαση σε αυτές τις άδειες στη συσκευή <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string> + <string name="chooser_title_non_profile" msgid="6035023914517087400">"Επιλέξτε μια συσκευή για διαχείριση μέσω της εφαρμογής <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>"</string> + <string name="chooser_title" msgid="2235819929238267637">"Επιλέξτε ένα προφίλ <xliff:g id="PROFILE_NAME">%1$s</xliff:g> για ρύθμιση"</string> + <string name="summary_watch" msgid="7962014927042971830">"Αυτή η εφαρμογή θα μπορεί να συγχρονίζει πληροφορίες, όπως το όνομα ενός ατόμου που σας καλεί, και να αποκτά πρόσβαση σε αυτές τις άδειες στη συσκευή <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="confirmation_title_glasses" msgid="8288346850537727333">"Να επιτρέπεται στην εφαρμογή <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> να διαχειρίζεται τη συσκευή <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> ;"</string> - <string name="profile_name_glasses" msgid="8488394059007275998">"γυαλιά"</string> - <string name="summary_glasses_multi_device" msgid="615259525961937348">"Αυτή η εφαρμογή είναι απαραίτητη για τη διαχείριση της συσκευής <xliff:g id="DEVICE_NAME">%1$s</xliff:g>. Θα επιτρέπεται στην εφαρμογή <xliff:g id="APP_NAME">%2$s</xliff:g> να αλληλεπιδρά με τις ειδοποιήσεις σας και να αποκτά πρόσβαση στις άδειες για το Τηλέφωνο, τα SMS, τις Επαφές, το Μικρόφωνο και τις Συσκευές σε κοντινή απόσταση."</string> - <string name="summary_glasses_single_device" msgid="3000909894067413398">"Αυτή η εφαρμογή θα μπορεί να έχει πρόσβαση σε αυτές τις άδειες στη συσκευή <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string> + <string name="profile_name_glasses" msgid="3506504967216601277">"συσκευή"</string> + <string name="summary_glasses" msgid="2872254734959842579">"Αυτή η εφαρμογή θα μπορεί να έχει πρόσβαση σε αυτές τις άδειες στη συσκευή <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="title_app_streaming" msgid="2270331024626446950">"Να επιτρέπεται στο <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> η πρόσβαση σε αυτές τις πληροφορίες από το τηλέφωνό σας."</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Υπηρεσίες πολλών συσκευών"</string> <string name="helper_summary_app_streaming" msgid="2396773196949578425">"Η εφαρμογή <xliff:g id="APP_NAME">%1$s</xliff:g> ζητά εκ μέρους της συσκευής σας <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> άδεια για ροή εφαρμογών μεταξύ των συσκευών σας"</string> @@ -38,10 +37,10 @@ <string name="title_nearby_device_streaming" msgid="7269956847378799794">"Να επιτρέπεται στη συσκευή <strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> να εκτελεί αυτήν την ενέργεια;"</string> <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"Η εφαρμογή <xliff:g id="APP_NAME">%1$s</xliff:g> ζητά άδεια εκ μέρους της συσκευής σας <xliff:g id="DEVICE_NAME">%2$s</xliff:g> για ροή εφαρμογών και άλλων λειτουργιών του συστήματος σε συσκευές σε κοντινή απόσταση"</string> <string name="profile_name_generic" msgid="6851028682723034988">"συσκευή"</string> - <string name="summary_generic_single_device" msgid="4181180669689590417">"Αυτή η εφαρμογή θα μπορεί να συγχρονίζει πληροφορίες μεταξύ του τηλεφώνου και της συσκευής <xliff:g id="DEVICE_NAME">%1$s</xliff:g>, όπως το όνομα ενός ατόμου που σας καλεί."</string> <string name="summary_generic" msgid="1761976003668044801">"Αυτή η εφαρμογή θα μπορεί να συγχρονίζει πληροφορίες μεταξύ του τηλεφώνου και της επιλεγμένης συσκευής σας, όπως το όνομα ενός ατόμου που σας καλεί."</string> <string name="consent_yes" msgid="8344487259618762872">"Να επιτρέπεται"</string> <string name="consent_no" msgid="2640796915611404382">"Να μην επιτρέπεται"</string> + <string name="consent_cancel" msgid="5655005528379285841">"Ακύρωση"</string> <string name="consent_back" msgid="2560683030046918882">"Πίσω"</string> <string name="permission_expand" msgid="893185038020887411">"Ανάπτυξη <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string> <string name="permission_collapse" msgid="3320833884220844084">"Σύμπτυξη <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string> diff --git a/packages/CompanionDeviceManager/res/values-en-rAU/strings.xml b/packages/CompanionDeviceManager/res/values-en-rAU/strings.xml index 2727d86b0b44..ca12145e2d4c 100644 --- a/packages/CompanionDeviceManager/res/values-en-rAU/strings.xml +++ b/packages/CompanionDeviceManager/res/values-en-rAU/strings.xml @@ -17,15 +17,14 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string> - <string name="confirmation_title" msgid="4593465730772390351">"Allow <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> to access <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> + <string name="confirmation_title" msgid="2244241995958340998">"Allow the app <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> to access <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> <string name="profile_name_watch" msgid="576290739483672360">"watch"</string> - <string name="chooser_title" msgid="2262294130493605839">"Choose a <xliff:g id="PROFILE_NAME">%1$s</xliff:g> to be managed by <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> - <string name="summary_watch" msgid="898569637110705523">"This app is needed to manage your <xliff:g id="DEVICE_NAME">%1$s</xliff:g>. <xliff:g id="APP_NAME">%2$s</xliff:g> will be allowed to sync info, like the name of someone calling, interact with your notifications and access your Phone, SMS, Contacts, Calendar, Call logs and Nearby devices permissions."</string> - <string name="summary_watch_single_device" msgid="3173948915947011333">"This app will be allowed to sync info, like the name of someone calling, and access these permissions on your <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string> + <string name="chooser_title_non_profile" msgid="6035023914517087400">"Choose a device to be managed by <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>"</string> + <string name="chooser_title" msgid="2235819929238267637">"Choose a <xliff:g id="PROFILE_NAME">%1$s</xliff:g> to set up"</string> + <string name="summary_watch" msgid="7962014927042971830">"This app will be allowed to sync info, like the name of someone calling, and access these permissions on your <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="confirmation_title_glasses" msgid="8288346850537727333">"Allow <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> to manage <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> - <string name="profile_name_glasses" msgid="8488394059007275998">"glasses"</string> - <string name="summary_glasses_multi_device" msgid="615259525961937348">"This app is needed to manage <xliff:g id="DEVICE_NAME">%1$s</xliff:g>. <xliff:g id="APP_NAME">%2$s</xliff:g> will be allowed to interact with your notifications and access your phone, SMS, contacts, microphone and Nearby devices permissions."</string> - <string name="summary_glasses_single_device" msgid="3000909894067413398">"This app will be allowed to access these permissions on your <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string> + <string name="profile_name_glasses" msgid="3506504967216601277">"device"</string> + <string name="summary_glasses" msgid="2872254734959842579">"This app will be allowed to access these permissions on your <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="title_app_streaming" msgid="2270331024626446950">"Allow <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> to access this information from your phone"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Cross-device services"</string> <string name="helper_summary_app_streaming" msgid="2396773196949578425">"<xliff:g id="APP_NAME">%1$s</xliff:g> is requesting permission on behalf of your <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> to stream apps between your devices"</string> @@ -38,10 +37,10 @@ <string name="title_nearby_device_streaming" msgid="7269956847378799794">"Allow <strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> to take this action?"</string> <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<xliff:g id="APP_NAME">%1$s</xliff:g> is requesting permission on behalf of your <xliff:g id="DEVICE_NAME">%2$s</xliff:g> to stream apps and other system features to nearby devices"</string> <string name="profile_name_generic" msgid="6851028682723034988">"device"</string> - <string name="summary_generic_single_device" msgid="4181180669689590417">"This app will be able to sync info, like the name of someone calling, between your phone and <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="summary_generic" msgid="1761976003668044801">"This app will be able to sync info, like the name of someone calling, between your phone and the chosen device"</string> <string name="consent_yes" msgid="8344487259618762872">"Allow"</string> <string name="consent_no" msgid="2640796915611404382">"Don\'t allow"</string> + <string name="consent_cancel" msgid="5655005528379285841">"Cancel"</string> <string name="consent_back" msgid="2560683030046918882">"Back"</string> <string name="permission_expand" msgid="893185038020887411">"Expand <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string> <string name="permission_collapse" msgid="3320833884220844084">"Collapse <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string> diff --git a/packages/CompanionDeviceManager/res/values-en-rCA/strings.xml b/packages/CompanionDeviceManager/res/values-en-rCA/strings.xml index cc221b4e1a8b..bd0342d0df65 100644 --- a/packages/CompanionDeviceManager/res/values-en-rCA/strings.xml +++ b/packages/CompanionDeviceManager/res/values-en-rCA/strings.xml @@ -17,15 +17,14 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string> - <string name="confirmation_title" msgid="4593465730772390351">"Allow <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> to access <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> + <string name="confirmation_title" msgid="2244241995958340998">"Allow the app <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> to access <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> <string name="profile_name_watch" msgid="576290739483672360">"watch"</string> - <string name="chooser_title" msgid="2262294130493605839">"Choose a <xliff:g id="PROFILE_NAME">%1$s</xliff:g> to be managed by <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> - <string name="summary_watch" msgid="898569637110705523">"This app is needed to manage your <xliff:g id="DEVICE_NAME">%1$s</xliff:g>. <xliff:g id="APP_NAME">%2$s</xliff:g> will be allowed to sync info, like the name of someone calling, interact with your notifications and access your Phone, SMS, Contacts, Calendar, Call logs and Nearby devices permissions."</string> - <string name="summary_watch_single_device" msgid="3173948915947011333">"This app will be allowed to sync info, like the name of someone calling, and access these permissions on your <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string> + <string name="chooser_title_non_profile" msgid="6035023914517087400">"Choose a device to be managed by <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>"</string> + <string name="chooser_title" msgid="2235819929238267637">"Choose a <xliff:g id="PROFILE_NAME">%1$s</xliff:g> to set up"</string> + <string name="summary_watch" msgid="7962014927042971830">"This app will be allowed to sync info, like the name of someone calling, and access these permissions on your <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="confirmation_title_glasses" msgid="8288346850537727333">"Allow <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> to manage <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> - <string name="profile_name_glasses" msgid="8488394059007275998">"glasses"</string> - <string name="summary_glasses_multi_device" msgid="615259525961937348">"This app is needed to manage <xliff:g id="DEVICE_NAME">%1$s</xliff:g>. <xliff:g id="APP_NAME">%2$s</xliff:g> will be allowed to interact with your notifications and access your Phone, SMS, Contacts, Microphone and Nearby devices permissions."</string> - <string name="summary_glasses_single_device" msgid="3000909894067413398">"This app will be allowed to access these permissions on your <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string> + <string name="profile_name_glasses" msgid="3506504967216601277">"device"</string> + <string name="summary_glasses" msgid="2872254734959842579">"This app will be allowed to access these permissions on your <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="title_app_streaming" msgid="2270331024626446950">"Allow <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> to access this information from your phone"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Cross-device services"</string> <string name="helper_summary_app_streaming" msgid="2396773196949578425">"<xliff:g id="APP_NAME">%1$s</xliff:g> is requesting permission on behalf of your <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> to stream apps between your devices"</string> @@ -38,10 +37,10 @@ <string name="title_nearby_device_streaming" msgid="7269956847378799794">"Allow <strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> to take this action?"</string> <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<xliff:g id="APP_NAME">%1$s</xliff:g> is requesting permission on behalf of your <xliff:g id="DEVICE_NAME">%2$s</xliff:g> to stream apps and other system features to nearby devices"</string> <string name="profile_name_generic" msgid="6851028682723034988">"device"</string> - <string name="summary_generic_single_device" msgid="4181180669689590417">"This app will be able to sync info, like the name of someone calling, between your phone and <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="summary_generic" msgid="1761976003668044801">"This app will be able to sync info, like the name of someone calling, between your phone and the chosen device"</string> <string name="consent_yes" msgid="8344487259618762872">"Allow"</string> <string name="consent_no" msgid="2640796915611404382">"Don\'t allow"</string> + <string name="consent_cancel" msgid="5655005528379285841">"Cancel"</string> <string name="consent_back" msgid="2560683030046918882">"Back"</string> <string name="permission_expand" msgid="893185038020887411">"Expand <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string> <string name="permission_collapse" msgid="3320833884220844084">"Collapse <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string> diff --git a/packages/CompanionDeviceManager/res/values-en-rGB/strings.xml b/packages/CompanionDeviceManager/res/values-en-rGB/strings.xml index 2727d86b0b44..ca12145e2d4c 100644 --- a/packages/CompanionDeviceManager/res/values-en-rGB/strings.xml +++ b/packages/CompanionDeviceManager/res/values-en-rGB/strings.xml @@ -17,15 +17,14 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string> - <string name="confirmation_title" msgid="4593465730772390351">"Allow <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> to access <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> + <string name="confirmation_title" msgid="2244241995958340998">"Allow the app <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> to access <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> <string name="profile_name_watch" msgid="576290739483672360">"watch"</string> - <string name="chooser_title" msgid="2262294130493605839">"Choose a <xliff:g id="PROFILE_NAME">%1$s</xliff:g> to be managed by <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> - <string name="summary_watch" msgid="898569637110705523">"This app is needed to manage your <xliff:g id="DEVICE_NAME">%1$s</xliff:g>. <xliff:g id="APP_NAME">%2$s</xliff:g> will be allowed to sync info, like the name of someone calling, interact with your notifications and access your Phone, SMS, Contacts, Calendar, Call logs and Nearby devices permissions."</string> - <string name="summary_watch_single_device" msgid="3173948915947011333">"This app will be allowed to sync info, like the name of someone calling, and access these permissions on your <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string> + <string name="chooser_title_non_profile" msgid="6035023914517087400">"Choose a device to be managed by <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>"</string> + <string name="chooser_title" msgid="2235819929238267637">"Choose a <xliff:g id="PROFILE_NAME">%1$s</xliff:g> to set up"</string> + <string name="summary_watch" msgid="7962014927042971830">"This app will be allowed to sync info, like the name of someone calling, and access these permissions on your <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="confirmation_title_glasses" msgid="8288346850537727333">"Allow <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> to manage <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> - <string name="profile_name_glasses" msgid="8488394059007275998">"glasses"</string> - <string name="summary_glasses_multi_device" msgid="615259525961937348">"This app is needed to manage <xliff:g id="DEVICE_NAME">%1$s</xliff:g>. <xliff:g id="APP_NAME">%2$s</xliff:g> will be allowed to interact with your notifications and access your phone, SMS, contacts, microphone and Nearby devices permissions."</string> - <string name="summary_glasses_single_device" msgid="3000909894067413398">"This app will be allowed to access these permissions on your <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string> + <string name="profile_name_glasses" msgid="3506504967216601277">"device"</string> + <string name="summary_glasses" msgid="2872254734959842579">"This app will be allowed to access these permissions on your <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="title_app_streaming" msgid="2270331024626446950">"Allow <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> to access this information from your phone"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Cross-device services"</string> <string name="helper_summary_app_streaming" msgid="2396773196949578425">"<xliff:g id="APP_NAME">%1$s</xliff:g> is requesting permission on behalf of your <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> to stream apps between your devices"</string> @@ -38,10 +37,10 @@ <string name="title_nearby_device_streaming" msgid="7269956847378799794">"Allow <strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> to take this action?"</string> <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<xliff:g id="APP_NAME">%1$s</xliff:g> is requesting permission on behalf of your <xliff:g id="DEVICE_NAME">%2$s</xliff:g> to stream apps and other system features to nearby devices"</string> <string name="profile_name_generic" msgid="6851028682723034988">"device"</string> - <string name="summary_generic_single_device" msgid="4181180669689590417">"This app will be able to sync info, like the name of someone calling, between your phone and <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="summary_generic" msgid="1761976003668044801">"This app will be able to sync info, like the name of someone calling, between your phone and the chosen device"</string> <string name="consent_yes" msgid="8344487259618762872">"Allow"</string> <string name="consent_no" msgid="2640796915611404382">"Don\'t allow"</string> + <string name="consent_cancel" msgid="5655005528379285841">"Cancel"</string> <string name="consent_back" msgid="2560683030046918882">"Back"</string> <string name="permission_expand" msgid="893185038020887411">"Expand <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string> <string name="permission_collapse" msgid="3320833884220844084">"Collapse <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string> diff --git a/packages/CompanionDeviceManager/res/values-en-rIN/strings.xml b/packages/CompanionDeviceManager/res/values-en-rIN/strings.xml index 2727d86b0b44..ca12145e2d4c 100644 --- a/packages/CompanionDeviceManager/res/values-en-rIN/strings.xml +++ b/packages/CompanionDeviceManager/res/values-en-rIN/strings.xml @@ -17,15 +17,14 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string> - <string name="confirmation_title" msgid="4593465730772390351">"Allow <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> to access <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> + <string name="confirmation_title" msgid="2244241995958340998">"Allow the app <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> to access <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> <string name="profile_name_watch" msgid="576290739483672360">"watch"</string> - <string name="chooser_title" msgid="2262294130493605839">"Choose a <xliff:g id="PROFILE_NAME">%1$s</xliff:g> to be managed by <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> - <string name="summary_watch" msgid="898569637110705523">"This app is needed to manage your <xliff:g id="DEVICE_NAME">%1$s</xliff:g>. <xliff:g id="APP_NAME">%2$s</xliff:g> will be allowed to sync info, like the name of someone calling, interact with your notifications and access your Phone, SMS, Contacts, Calendar, Call logs and Nearby devices permissions."</string> - <string name="summary_watch_single_device" msgid="3173948915947011333">"This app will be allowed to sync info, like the name of someone calling, and access these permissions on your <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string> + <string name="chooser_title_non_profile" msgid="6035023914517087400">"Choose a device to be managed by <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>"</string> + <string name="chooser_title" msgid="2235819929238267637">"Choose a <xliff:g id="PROFILE_NAME">%1$s</xliff:g> to set up"</string> + <string name="summary_watch" msgid="7962014927042971830">"This app will be allowed to sync info, like the name of someone calling, and access these permissions on your <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="confirmation_title_glasses" msgid="8288346850537727333">"Allow <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> to manage <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> - <string name="profile_name_glasses" msgid="8488394059007275998">"glasses"</string> - <string name="summary_glasses_multi_device" msgid="615259525961937348">"This app is needed to manage <xliff:g id="DEVICE_NAME">%1$s</xliff:g>. <xliff:g id="APP_NAME">%2$s</xliff:g> will be allowed to interact with your notifications and access your phone, SMS, contacts, microphone and Nearby devices permissions."</string> - <string name="summary_glasses_single_device" msgid="3000909894067413398">"This app will be allowed to access these permissions on your <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string> + <string name="profile_name_glasses" msgid="3506504967216601277">"device"</string> + <string name="summary_glasses" msgid="2872254734959842579">"This app will be allowed to access these permissions on your <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="title_app_streaming" msgid="2270331024626446950">"Allow <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> to access this information from your phone"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Cross-device services"</string> <string name="helper_summary_app_streaming" msgid="2396773196949578425">"<xliff:g id="APP_NAME">%1$s</xliff:g> is requesting permission on behalf of your <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> to stream apps between your devices"</string> @@ -38,10 +37,10 @@ <string name="title_nearby_device_streaming" msgid="7269956847378799794">"Allow <strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> to take this action?"</string> <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<xliff:g id="APP_NAME">%1$s</xliff:g> is requesting permission on behalf of your <xliff:g id="DEVICE_NAME">%2$s</xliff:g> to stream apps and other system features to nearby devices"</string> <string name="profile_name_generic" msgid="6851028682723034988">"device"</string> - <string name="summary_generic_single_device" msgid="4181180669689590417">"This app will be able to sync info, like the name of someone calling, between your phone and <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="summary_generic" msgid="1761976003668044801">"This app will be able to sync info, like the name of someone calling, between your phone and the chosen device"</string> <string name="consent_yes" msgid="8344487259618762872">"Allow"</string> <string name="consent_no" msgid="2640796915611404382">"Don\'t allow"</string> + <string name="consent_cancel" msgid="5655005528379285841">"Cancel"</string> <string name="consent_back" msgid="2560683030046918882">"Back"</string> <string name="permission_expand" msgid="893185038020887411">"Expand <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string> <string name="permission_collapse" msgid="3320833884220844084">"Collapse <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string> diff --git a/packages/CompanionDeviceManager/res/values-en-rXC/strings.xml b/packages/CompanionDeviceManager/res/values-en-rXC/strings.xml index 431a69cb64ec..70af915ea6e5 100644 --- a/packages/CompanionDeviceManager/res/values-en-rXC/strings.xml +++ b/packages/CompanionDeviceManager/res/values-en-rXC/strings.xml @@ -17,15 +17,14 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string> - <string name="confirmation_title" msgid="4593465730772390351">"Allow <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> to access <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> + <string name="confirmation_title" msgid="2244241995958340998">"Allow the app <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> to access <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> <string name="profile_name_watch" msgid="576290739483672360">"watch"</string> - <string name="chooser_title" msgid="2262294130493605839">"Choose a <xliff:g id="PROFILE_NAME">%1$s</xliff:g> to be managed by <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> - <string name="summary_watch" msgid="898569637110705523">"This app is needed to manage your <xliff:g id="DEVICE_NAME">%1$s</xliff:g>. <xliff:g id="APP_NAME">%2$s</xliff:g> will be allowed to sync info, like the name of someone calling, interact with your notifications and access your Phone, SMS, Contacts, Calendar, Call logs and Nearby devices permissions."</string> - <string name="summary_watch_single_device" msgid="3173948915947011333">"This app will be allowed to sync info, like the name of someone calling, and access these permissions on your <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string> + <string name="chooser_title_non_profile" msgid="6035023914517087400">"Choose a device to be managed by <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>"</string> + <string name="chooser_title" msgid="2235819929238267637">"Choose a <xliff:g id="PROFILE_NAME">%1$s</xliff:g> to set up"</string> + <string name="summary_watch" msgid="7962014927042971830">"This app will be allowed to sync info, like the name of someone calling, and access these permissions on your <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="confirmation_title_glasses" msgid="8288346850537727333">"Allow <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> to manage <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> - <string name="profile_name_glasses" msgid="8488394059007275998">"glasses"</string> - <string name="summary_glasses_multi_device" msgid="615259525961937348">"This app is needed to manage <xliff:g id="DEVICE_NAME">%1$s</xliff:g>. <xliff:g id="APP_NAME">%2$s</xliff:g> will be allowed to interact with your notifications and access your Phone, SMS, Contacts, Microphone and Nearby devices permissions."</string> - <string name="summary_glasses_single_device" msgid="3000909894067413398">"This app will be allowed to access these permissions on your <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string> + <string name="profile_name_glasses" msgid="3506504967216601277">"device"</string> + <string name="summary_glasses" msgid="2872254734959842579">"This app will be allowed to access these permissions on your <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="title_app_streaming" msgid="2270331024626446950">"Allow <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> to access this information from your phone"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Cross-device services"</string> <string name="helper_summary_app_streaming" msgid="2396773196949578425">"<xliff:g id="APP_NAME">%1$s</xliff:g> is requesting permission on behalf of your <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> to stream apps between your devices"</string> @@ -38,10 +37,10 @@ <string name="title_nearby_device_streaming" msgid="7269956847378799794">"Allow <strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> to take this action?"</string> <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<xliff:g id="APP_NAME">%1$s</xliff:g> is requesting permission on behalf of your <xliff:g id="DEVICE_NAME">%2$s</xliff:g> to stream apps and other system features to nearby devices"</string> <string name="profile_name_generic" msgid="6851028682723034988">"device"</string> - <string name="summary_generic_single_device" msgid="4181180669689590417">"This app will be able to sync info, like the name of someone calling, between your phone and <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="summary_generic" msgid="1761976003668044801">"This app will be able to sync info, like the name of someone calling, between your phone and the chosen device"</string> <string name="consent_yes" msgid="8344487259618762872">"Allow"</string> <string name="consent_no" msgid="2640796915611404382">"Don’t allow"</string> + <string name="consent_cancel" msgid="5655005528379285841">"Cancel"</string> <string name="consent_back" msgid="2560683030046918882">"Back"</string> <string name="permission_expand" msgid="893185038020887411">"Expand <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string> <string name="permission_collapse" msgid="3320833884220844084">"Collapse <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string> diff --git a/packages/CompanionDeviceManager/res/values-es-rUS/strings.xml b/packages/CompanionDeviceManager/res/values-es-rUS/strings.xml index 26868f6f1610..cba0b46fa501 100644 --- a/packages/CompanionDeviceManager/res/values-es-rUS/strings.xml +++ b/packages/CompanionDeviceManager/res/values-es-rUS/strings.xml @@ -17,15 +17,14 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="4470785958457506021">"Administrador de dispositivo complementario"</string> - <string name="confirmation_title" msgid="4593465730772390351">"¿Quieres permitir que <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> acceda a <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> + <string name="confirmation_title" msgid="2244241995958340998">"¿Quieres permitir que la app de <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> acceda a <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> <string name="profile_name_watch" msgid="576290739483672360">"reloj"</string> - <string name="chooser_title" msgid="2262294130493605839">"Elige un <xliff:g id="PROFILE_NAME">%1$s</xliff:g> para que la app <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> lo administre"</string> - <string name="summary_watch" msgid="898569637110705523">"Esta app es necesaria para administrar tu <xliff:g id="DEVICE_NAME">%1$s</xliff:g>. <xliff:g id="APP_NAME">%2$s</xliff:g> podrá sincronizar información, como el nombre de la persona que llama, interactuar con tus notificaciones y acceder a los permisos de Teléfono, SMS, Contactos, Calendario, Llamadas y Dispositivos cercanos."</string> - <string name="summary_watch_single_device" msgid="3173948915947011333">"Esta app podrá sincronizar información, como el nombre de alguien cuando te llame, y acceder a los siguientes permisos en tu <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string> + <string name="chooser_title_non_profile" msgid="6035023914517087400">"Elige un dispositivo para que <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> lo administre"</string> + <string name="chooser_title" msgid="2235819929238267637">"Elige un <xliff:g id="PROFILE_NAME">%1$s</xliff:g> para configurar"</string> + <string name="summary_watch" msgid="7962014927042971830">"Esta app podrá sincronizar información, como el nombre de alguien cuando te llame, y acceder a los siguientes permisos en tu <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="confirmation_title_glasses" msgid="8288346850537727333">"Permite que <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> administre <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> - <string name="profile_name_glasses" msgid="8488394059007275998">"Gafas"</string> - <string name="summary_glasses_multi_device" msgid="615259525961937348">"Esta app es necesaria para administrar <xliff:g id="DEVICE_NAME">%1$s</xliff:g>. <xliff:g id="APP_NAME">%2$s</xliff:g> podrá interactuar con tus notificaciones y acceder a los permisos de Teléfono, SMS, Contactos, Micrófono y Dispositivos cercanos."</string> - <string name="summary_glasses_single_device" msgid="3000909894067413398">"Esta app podrá acceder a los siguientes permisos en tu <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string> + <string name="profile_name_glasses" msgid="3506504967216601277">"dispositivo"</string> + <string name="summary_glasses" msgid="2872254734959842579">"Esta app podrá acceder a los siguientes permisos en tu <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="title_app_streaming" msgid="2270331024626446950">"Permite que <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> acceda a esta información de tu teléfono"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Servicios multidispositivo"</string> <string name="helper_summary_app_streaming" msgid="2396773196949578425">"<xliff:g id="APP_NAME">%1$s</xliff:g> solicita tu permiso en nombre de <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> para transmitir apps entre dispositivos"</string> @@ -38,10 +37,10 @@ <string name="title_nearby_device_streaming" msgid="7269956847378799794">"¿Permites que <strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> realice esta acción?"</string> <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<xliff:g id="APP_NAME">%1$s</xliff:g> está solicitando permiso en nombre de tu <xliff:g id="DEVICE_NAME">%2$s</xliff:g> para transmitir apps y otras funciones del sistema a dispositivos cercanos"</string> <string name="profile_name_generic" msgid="6851028682723034988">"dispositivo"</string> - <string name="summary_generic_single_device" msgid="4181180669689590417">"Esta app podrá sincronizar información, como el nombre de la persona que llama, entre el teléfono y <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="summary_generic" msgid="1761976003668044801">"Esta app podrá sincronizar información, como el nombre de la persona que llama, entre el teléfono y el dispositivo elegido"</string> <string name="consent_yes" msgid="8344487259618762872">"Permitir"</string> <string name="consent_no" msgid="2640796915611404382">"No permitir"</string> + <string name="consent_cancel" msgid="5655005528379285841">"Cancelar"</string> <string name="consent_back" msgid="2560683030046918882">"Atrás"</string> <string name="permission_expand" msgid="893185038020887411">"Expandir <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string> <string name="permission_collapse" msgid="3320833884220844084">"Contraer <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string> diff --git a/packages/CompanionDeviceManager/res/values-es/strings.xml b/packages/CompanionDeviceManager/res/values-es/strings.xml index fedb8ae63d74..50d88061e3c9 100644 --- a/packages/CompanionDeviceManager/res/values-es/strings.xml +++ b/packages/CompanionDeviceManager/res/values-es/strings.xml @@ -17,15 +17,14 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="4470785958457506021">"Gestor de dispositivos complementario"</string> - <string name="confirmation_title" msgid="4593465730772390351">"¿Permitir que <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> acceda a tu <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> + <string name="confirmation_title" msgid="2244241995958340998">"¿Permitir que la aplicación <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> acceda a <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> <string name="profile_name_watch" msgid="576290739483672360">"reloj"</string> - <string name="chooser_title" msgid="2262294130493605839">"Elige un <xliff:g id="PROFILE_NAME">%1$s</xliff:g> para gestionarlo con <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> - <string name="summary_watch" msgid="898569637110705523">"Se necesita esta aplicación para gestionar tu <xliff:g id="DEVICE_NAME">%1$s</xliff:g>. <xliff:g id="APP_NAME">%2$s</xliff:g> podrá sincronizar información (por ejemplo, el nombre de la persona que te llama), interactuar con tus notificaciones y acceder a tus permisos de teléfono, SMS, contactos, calendario, registros de llamadas y dispositivos cercanos."</string> - <string name="summary_watch_single_device" msgid="3173948915947011333">"Esta aplicación podrá sincronizar información, como el nombre de la persona que llama, y acceder a estos permisos de tu <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string> + <string name="chooser_title_non_profile" msgid="6035023914517087400">"Elige un dispositivo para que lo gestione <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>"</string> + <string name="chooser_title" msgid="2235819929238267637">"Elige el <xliff:g id="PROFILE_NAME">%1$s</xliff:g> que quieras configurar"</string> + <string name="summary_watch" msgid="7962014927042971830">"Esta aplicación podrá sincronizar información, como el nombre de la persona que llama, y acceder a estos permisos de tu <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="confirmation_title_glasses" msgid="8288346850537727333">"¿Permitir que <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> gestione <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> - <string name="profile_name_glasses" msgid="8488394059007275998">"gafas"</string> - <string name="summary_glasses_multi_device" msgid="615259525961937348">"Se necesita esta aplicación para gestionar <xliff:g id="DEVICE_NAME">%1$s</xliff:g>. <xliff:g id="APP_NAME">%2$s</xliff:g> podrá interactuar con tus notificaciones y acceder a tus permisos de teléfono, SMS, contactos, micrófono y dispositivos cercanos."</string> - <string name="summary_glasses_single_device" msgid="3000909894067413398">"Esta aplicación podrá acceder a estos permisos de tu <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string> + <string name="profile_name_glasses" msgid="3506504967216601277">"dispositivo"</string> + <string name="summary_glasses" msgid="2872254734959842579">"Esta aplicación podrá acceder a estos permisos de tu <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="title_app_streaming" msgid="2270331024626446950">"Permitir que <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> acceda a esta información de tu teléfono"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Servicios multidispositivo"</string> <string name="helper_summary_app_streaming" msgid="2396773196949578425">"<xliff:g id="APP_NAME">%1$s</xliff:g> está pidiendo permiso en nombre de tu <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> para emitir aplicaciones en otros dispositivos tuyos"</string> @@ -38,10 +37,10 @@ <string name="title_nearby_device_streaming" msgid="7269956847378799794">"¿Permitir que <strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> realice esta acción?"</string> <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<xliff:g id="APP_NAME">%1$s</xliff:g> está pidiendo permiso en nombre de tu <xliff:g id="DEVICE_NAME">%2$s</xliff:g> para emitir aplicaciones y otras funciones del sistema en dispositivos cercanos"</string> <string name="profile_name_generic" msgid="6851028682723034988">"dispositivo"</string> - <string name="summary_generic_single_device" msgid="4181180669689590417">"Esta aplicación podrá sincronizar información (por ejemplo, el nombre de la persona que te llama) entre tu teléfono y <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="summary_generic" msgid="1761976003668044801">"Esta aplicación podrá sincronizar información (por ejemplo, el nombre de la persona que te llama) entre tu teléfono y el dispositivo que elijas"</string> <string name="consent_yes" msgid="8344487259618762872">"Permitir"</string> <string name="consent_no" msgid="2640796915611404382">"No permitir"</string> + <string name="consent_cancel" msgid="5655005528379285841">"Cancelar"</string> <string name="consent_back" msgid="2560683030046918882">"Atrás"</string> <string name="permission_expand" msgid="893185038020887411">"Desplegar <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string> <string name="permission_collapse" msgid="3320833884220844084">"Contraer <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string> diff --git a/packages/CompanionDeviceManager/res/values-et/strings.xml b/packages/CompanionDeviceManager/res/values-et/strings.xml index 29a04bce69a5..2eff7ebad4f0 100644 --- a/packages/CompanionDeviceManager/res/values-et/strings.xml +++ b/packages/CompanionDeviceManager/res/values-et/strings.xml @@ -17,15 +17,14 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="4470785958457506021">"Kaasseadme haldur"</string> - <string name="confirmation_title" msgid="4593465730772390351">"Andke rakendusele <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> juurdepääs seadmele <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> + <string name="confirmation_title" msgid="2244241995958340998">"Kas anda rakendusele <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> juurdepääs seadmele <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> <string name="profile_name_watch" msgid="576290739483672360">"käekell"</string> - <string name="chooser_title" msgid="2262294130493605839">"Valige <xliff:g id="PROFILE_NAME">%1$s</xliff:g>, mida haldab rakendus <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> - <string name="summary_watch" msgid="898569637110705523">"Seda rakendust on vaja teie seadme <xliff:g id="DEVICE_NAME">%1$s</xliff:g> haldamiseks. Rakendusel <xliff:g id="APP_NAME">%2$s</xliff:g> lubatakse sünkroonida teavet, näiteks helistaja nime, kasutada teie märguandeid ning pääseda juurde teie telefoni, SMS-ide, kontaktide, kalendri, kõnelogide ja läheduses olevate seadmete lubadele."</string> - <string name="summary_watch_single_device" msgid="3173948915947011333">"Sellel rakendusel lubatakse sünkroonida teavet (nt helistaja nime) ja antakse need load teie seadmes <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string> + <string name="chooser_title_non_profile" msgid="6035023914517087400">"Valige seade, mida haldab rakendus <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>"</string> + <string name="chooser_title" msgid="2235819929238267637">"Valige <xliff:g id="PROFILE_NAME">%1$s</xliff:g>, mis seadistada"</string> + <string name="summary_watch" msgid="7962014927042971830">"Sellel rakendusel lubatakse sünkroonida teavet (nt helistaja nime) ja antakse need load teie seadmes <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="confirmation_title_glasses" msgid="8288346850537727333">"Lubage rakendusel <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> hallata seadet <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> - <string name="profile_name_glasses" msgid="8488394059007275998">"prillid"</string> - <string name="summary_glasses_multi_device" msgid="615259525961937348">"Seda rakendust on vaja seadme <xliff:g id="DEVICE_NAME">%1$s</xliff:g> haldamiseks. Rakendusel <xliff:g id="APP_NAME">%2$s</xliff:g> lubatakse kasutada teie märguandeid ning pääseda juurde teie telefoni, SMS-ide, kontaktide, mikrofoni ja läheduses olevate seadmete lubadele."</string> - <string name="summary_glasses_single_device" msgid="3000909894067413398">"Sellele rakendusele antakse need load teie seadmes <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string> + <string name="profile_name_glasses" msgid="3506504967216601277">"seade"</string> + <string name="summary_glasses" msgid="2872254734959842579">"Sellele rakendusele antakse need load teie seadmes <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="title_app_streaming" msgid="2270331024626446950">"Lubage rakendusel <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> pääseda teie telefonis juurde sellele teabele"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Seadmeülesed teenused"</string> <string name="helper_summary_app_streaming" msgid="2396773196949578425">"<xliff:g id="APP_NAME">%1$s</xliff:g> taotleb teie seadme <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> nimel luba teie seadmete vahel rakendusi voogesitada"</string> @@ -38,10 +37,10 @@ <string name="title_nearby_device_streaming" msgid="7269956847378799794">"Kas lubada seadmel <strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> teha seda toimingut?"</string> <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<xliff:g id="APP_NAME">%1$s</xliff:g> taotleb teie seadme <xliff:g id="DEVICE_NAME">%2$s</xliff:g> nimel luba voogesitada rakendusi ja muid süsteemi funktsioone läheduses olevatesse seadmetesse"</string> <string name="profile_name_generic" msgid="6851028682723034988">"seade"</string> - <string name="summary_generic_single_device" msgid="4181180669689590417">"See rakendus saab sünkroonida teavet, näiteks helistaja nime, teie telefoni ja seadme <xliff:g id="DEVICE_NAME">%1$s</xliff:g> vahel"</string> <string name="summary_generic" msgid="1761976003668044801">"See rakendus saab sünkroonida teavet, näiteks helistaja nime, teie telefoni ja valitud seadme vahel"</string> <string name="consent_yes" msgid="8344487259618762872">"Luba"</string> <string name="consent_no" msgid="2640796915611404382">"Ära luba"</string> + <string name="consent_cancel" msgid="5655005528379285841">"Tühista"</string> <string name="consent_back" msgid="2560683030046918882">"Tagasi"</string> <string name="permission_expand" msgid="893185038020887411">"Laienda: <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string> <string name="permission_collapse" msgid="3320833884220844084">"Ahenda: <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string> diff --git a/packages/CompanionDeviceManager/res/values-eu/strings.xml b/packages/CompanionDeviceManager/res/values-eu/strings.xml index a4f29e320ad5..e130074843ff 100644 --- a/packages/CompanionDeviceManager/res/values-eu/strings.xml +++ b/packages/CompanionDeviceManager/res/values-eu/strings.xml @@ -17,15 +17,14 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="4470785958457506021">"Gailu osagarriaren kudeatzailea"</string> - <string name="confirmation_title" msgid="4593465730772390351">"<strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> erabiltzeko baimena eman nahi diozu <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> aplikazioari?"</string> + <string name="confirmation_title" msgid="2244241995958340998">"<strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> gailua erabiltzeko baimena eman nahi diozu <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> aplikazioari?"</string> <string name="profile_name_watch" msgid="576290739483672360">"erlojua"</string> - <string name="chooser_title" msgid="2262294130493605839">"Aukeratu <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> aplikazioak kudeatu beharreko <xliff:g id="PROFILE_NAME">%1$s</xliff:g>"</string> - <string name="summary_watch" msgid="898569637110705523">"Aplikazioa <xliff:g id="DEVICE_NAME">%1$s</xliff:g> kudeatzeko behar da. Informazioa sinkronizatzeko (esate baterako, deitzaileen izenak), jakinarazpenekin interakzioan aritzeko, eta telefonoa, SMSak, kontaktuak, egutegia, deien erregistroak eta inguruko gailuak erabiltzeko baimena izango du <xliff:g id="APP_NAME">%2$s</xliff:g> aplikazioak."</string> - <string name="summary_watch_single_device" msgid="3173948915947011333">"<xliff:g id="DEVICE_TYPE">%1$s</xliff:g>n informazioa sinkronizatu (esate baterako, deitzaileen izenak) eta baimen hauek erabili ahalko ditu aplikazioak"</string> + <string name="chooser_title_non_profile" msgid="6035023914517087400">"Aukeratu <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> aplikazioak kudeatu behar duen gailua"</string> + <string name="chooser_title" msgid="2235819929238267637">"Aukeratu konfiguratu nahi duzun <xliff:g id="PROFILE_NAME">%1$s</xliff:g>"</string> + <string name="summary_watch" msgid="7962014927042971830">"Informazioa sinkronizatu (esate baterako, deitzaileen izenak) eta baimen hauek erabili ahalko ditu <xliff:g id="DEVICE_NAME">%1$s</xliff:g>n aplikazioak"</string> <string name="confirmation_title_glasses" msgid="8288346850537727333">"<strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> kudeatzeko baimena eman nahi diozu <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> aplikazioari?"</string> - <string name="profile_name_glasses" msgid="8488394059007275998">"betaurrekoak"</string> - <string name="summary_glasses_multi_device" msgid="615259525961937348">"Aplikazioa \"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>\" izeneko gailua kudeatzeko behar da. Jakinarazpenekin interakzioan aritzeko, eta telefonoa, SMSak, kontaktuak, mikrofonoa eta inguruko gailuak erabiltzeko baimena izango du <xliff:g id="APP_NAME">%2$s</xliff:g> aplikazioak."</string> - <string name="summary_glasses_single_device" msgid="3000909894067413398">"<xliff:g id="DEVICE_TYPE">%1$s</xliff:g>n baimen hauek erabili ahalko ditu aplikazioak:"</string> + <string name="profile_name_glasses" msgid="3506504967216601277">"gailua"</string> + <string name="summary_glasses" msgid="2872254734959842579">"Baimen hauek erabili ahalko ditu <xliff:g id="DEVICE_NAME">%1$s</xliff:g>n aplikazioak:"</string> <string name="title_app_streaming" msgid="2270331024626446950">"Eman informazioa telefonotik hartzeko baimena <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> aplikazioari"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Gailu baterako baino gehiagotarako zerbitzuak"</string> <string name="helper_summary_app_streaming" msgid="2396773196949578425">"Gailu batetik bestera aplikazioak igortzeko baimena eskatzen ari da <xliff:g id="APP_NAME">%1$s</xliff:g>, <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> gailuaren izenean"</string> @@ -38,10 +37,10 @@ <string name="title_nearby_device_streaming" msgid="7269956847378799794">"Ekintza hau gauzatzeko baimena eman nahi diozu <strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> aplikazioari?"</string> <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"Aplikazioak eta sistemaren beste eginbide batzuk inguruko gailuetara igortzeko baimena eskatzen ari da <xliff:g id="APP_NAME">%1$s</xliff:g>, <xliff:g id="DEVICE_NAME">%2$s</xliff:g> gailuaren izenean"</string> <string name="profile_name_generic" msgid="6851028682723034988">"gailua"</string> - <string name="summary_generic_single_device" msgid="4181180669689590417">"Telefonoaren eta <xliff:g id="DEVICE_NAME">%1$s</xliff:g> gailuaren artean informazioa sinkronizatzeko gai izango da aplikazioa (esate baterako, deitzaileen izenak)"</string> <string name="summary_generic" msgid="1761976003668044801">"Telefonoaren eta hautatutako gailuaren artean informazioa sinkronizatzeko gai izango da aplikazioa (esate baterako, deitzaileen izenak)"</string> <string name="consent_yes" msgid="8344487259618762872">"Eman baimena"</string> <string name="consent_no" msgid="2640796915611404382">"Ez eman baimenik"</string> + <string name="consent_cancel" msgid="5655005528379285841">"Utzi"</string> <string name="consent_back" msgid="2560683030046918882">"Atzera"</string> <string name="permission_expand" msgid="893185038020887411">"Zabaldu <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string> <string name="permission_collapse" msgid="3320833884220844084">"Tolestu <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string> diff --git a/packages/CompanionDeviceManager/res/values-fa/strings.xml b/packages/CompanionDeviceManager/res/values-fa/strings.xml index d6c1a13fc6f3..5e78aa55fdbb 100644 --- a/packages/CompanionDeviceManager/res/values-fa/strings.xml +++ b/packages/CompanionDeviceManager/res/values-fa/strings.xml @@ -17,15 +17,14 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="4470785958457506021">"مدیر دستگاه مرتبط"</string> - <string name="confirmation_title" msgid="4593465730772390351">"به <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> اجازه داده شود به <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> دسترسی پیدا کند؟"</string> + <string name="confirmation_title" msgid="2244241995958340998">"به برنامه <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> اجازه داده شود به <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> دسترسی پیدا کند؟"</string> <string name="profile_name_watch" msgid="576290739483672360">"ساعت"</string> - <string name="chooser_title" msgid="2262294130493605839">"انتخاب <xliff:g id="PROFILE_NAME">%1$s</xliff:g> برای مدیریت کردن با <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> - <string name="summary_watch" msgid="898569637110705523">"این برنامه برای مدیریت <xliff:g id="DEVICE_NAME">%1$s</xliff:g> شما لازم است. به <xliff:g id="APP_NAME">%2$s</xliff:g> اجازه داده میشود اطلاعاتی مثل نام شخصی را که تماس میگیرد همگامسازی کند، با اعلانهای شما تعامل داشته باشد، و به اجازههای «تلفن»، «پیامک»، «مخاطبین»، «تقویم»، «گزارشهای تماس»، و «دستگاههای اطراف» دسترسی داشته باشد."</string> - <string name="summary_watch_single_device" msgid="3173948915947011333">"به این برنامه اجازه داده میشود اطلاعاتی مثل نام تماسگیرنده را همگامسازی کند و به این اجازهها در <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> شما دسترسی داشته باشد"</string> + <string name="chooser_title_non_profile" msgid="6035023914517087400">"انتخاب دستگاه برای مدیریت کردن با <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>"</string> + <string name="chooser_title" msgid="2235819929238267637">"انتخاب <xliff:g id="PROFILE_NAME">%1$s</xliff:g> برای راهاندازی"</string> + <string name="summary_watch" msgid="7962014927042971830">"به این برنامه اجازه داده میشود اطلاعاتی مثل نام تماسگیرنده را همگامسازی کند و به این اجازهها در <xliff:g id="DEVICE_NAME">%1$s</xliff:g> شما دسترسی داشته باشد"</string> <string name="confirmation_title_glasses" msgid="8288346850537727333">"به <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> اجازه داده شود <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> را مدیریت کند؟"</string> - <string name="profile_name_glasses" msgid="8488394059007275998">"عینک"</string> - <string name="summary_glasses_multi_device" msgid="615259525961937348">"این برنامه برای مدیریت <xliff:g id="DEVICE_NAME">%1$s</xliff:g> لازم است. به <xliff:g id="APP_NAME">%2$s</xliff:g> اجازه داده میشود با اعلانهای شما تعامل داشته باشد و به اجازههای تلفنتان، پیامک، «مخاطبین»، «میکروفون»، و «دستگاههای اطراف» دسترسی داشته باشد."</string> - <string name="summary_glasses_single_device" msgid="3000909894067413398">"این برنامه مجاز میشود به این اجازهها در <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> شما دسترسی پیدا کند"</string> + <string name="profile_name_glasses" msgid="3506504967216601277">"دستگاه"</string> + <string name="summary_glasses" msgid="2872254734959842579">"این برنامه مجاز میشود به این اجازهها در <xliff:g id="DEVICE_NAME">%1$s</xliff:g> شما دسترسی پیدا کند"</string> <string name="title_app_streaming" msgid="2270331024626446950">"اجازه دادن به <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> برای دسترسی به اطلاعات تلفن"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"سرویسهای بیندستگاهی"</string> <string name="helper_summary_app_streaming" msgid="2396773196949578425">"<xliff:g id="APP_NAME">%1$s</xliff:g> ازطرف <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> اجازه میخواهد برنامهها را بین دستگاههای شما جاریسازی کند"</string> @@ -38,10 +37,10 @@ <string name="title_nearby_device_streaming" msgid="7269956847378799794">"به <strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> اجازه داده شود این اقدام را انجام دهد؟"</string> <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<xliff:g id="APP_NAME">%1$s</xliff:g> ازطرف <xliff:g id="DEVICE_NAME">%2$s</xliff:g> اجازه میخواهد تا برنامهها و دیگر ویژگیهای سیستم را در دستگاههای اطراف جاریسازی کند."</string> <string name="profile_name_generic" msgid="6851028682723034988">"دستگاه"</string> - <string name="summary_generic_single_device" msgid="4181180669689590417">"این برنامه مجاز میشود اطلاعتی مثل نام شخصی را که تماس میگیرد بین تلفن شما و <xliff:g id="DEVICE_NAME">%1$s</xliff:g> همگامسازی کند"</string> <string name="summary_generic" msgid="1761976003668044801">"این برنامه مجاز میشود اطلاعتی مثل نام شخصی را که تماس میگیرد بین تلفن شما و دستگاه انتخابشده همگامسازی کند"</string> <string name="consent_yes" msgid="8344487259618762872">"اجازه دادن"</string> <string name="consent_no" msgid="2640796915611404382">"اجازه ندادن"</string> + <string name="consent_cancel" msgid="5655005528379285841">"لغو"</string> <string name="consent_back" msgid="2560683030046918882">"برگشتن"</string> <string name="permission_expand" msgid="893185038020887411">"ازهم بازکردن <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string> <string name="permission_collapse" msgid="3320833884220844084">"جمع کردن <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string> diff --git a/packages/CompanionDeviceManager/res/values-fi/strings.xml b/packages/CompanionDeviceManager/res/values-fi/strings.xml index 72122cb7a9df..552c47351820 100644 --- a/packages/CompanionDeviceManager/res/values-fi/strings.xml +++ b/packages/CompanionDeviceManager/res/values-fi/strings.xml @@ -17,15 +17,14 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string> - <string name="confirmation_title" msgid="4593465730772390351">"Sallitaanko, että <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> saa pääsyn laitteeseen: <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> + <string name="confirmation_title" msgid="2244241995958340998">"Sallitaanko, että soellus (<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>) saa pääsyn laitteeseen: <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> <string name="profile_name_watch" msgid="576290739483672360">"kello"</string> - <string name="chooser_title" msgid="2262294130493605839">"Valitse <xliff:g id="PROFILE_NAME">%1$s</xliff:g>, jota <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> hallinnoi"</string> - <string name="summary_watch" msgid="898569637110705523">"Ylläpitoon (<xliff:g id="DEVICE_NAME">%1$s</xliff:g>) tarvitaan tätä sovellusta. <xliff:g id="APP_NAME">%2$s</xliff:g> saa luvan synkronoida tietoja (esimerkiksi soittajan nimen), hallinnoida ilmoituksiasi sekä pääsyn puhelimeen, tekstiviesteihin, yhteystietoihin, kalenteriin, puhelulokeihin ja lähellä olevat laitteet ‑lupiin."</string> - <string name="summary_watch_single_device" msgid="3173948915947011333">"Sovellus saa luvan synkronoida tietoja (esimerkiksi soittajan nimen) ja pääsyn näihin lupiin laitteella (<xliff:g id="DEVICE_TYPE">%1$s</xliff:g>)"</string> + <string name="chooser_title_non_profile" msgid="6035023914517087400">"Valitse laite, jota <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> hallinnoi"</string> + <string name="chooser_title" msgid="2235819929238267637">"Valitse <xliff:g id="PROFILE_NAME">%1$s</xliff:g>, niin voit suorittaa käyttöönoton"</string> + <string name="summary_watch" msgid="7962014927042971830">"Sovellus saa luvan synkronoida tietoja (esimerkiksi soittajan nimen) ja pääsyn näihin lupiin laitteella (<xliff:g id="DEVICE_NAME">%1$s</xliff:g>)"</string> <string name="confirmation_title_glasses" msgid="8288346850537727333">"Salli, että <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> saa ylläpitää laitetta: <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string> - <string name="profile_name_glasses" msgid="8488394059007275998">"lasit"</string> - <string name="summary_glasses_multi_device" msgid="615259525961937348">"Tätä sovellusta tarvitaan kohteen <xliff:g id="DEVICE_NAME">%1$s</xliff:g> ylläpitoon. <xliff:g id="APP_NAME">%2$s</xliff:g> saa luvan hallinnoida ilmoituksiasi sekä pääsyn puhelimeen, tekstiviesteihin, yhteystietoihin, mikrofoniin ja lähellä olevat laitteet ‑lupiin."</string> - <string name="summary_glasses_single_device" msgid="3000909894067413398">"Tämä sovellus saa käyttää näitä lupia laitteella (<xliff:g id="DEVICE_TYPE">%1$s</xliff:g>)"</string> + <string name="profile_name_glasses" msgid="3506504967216601277">"laite"</string> + <string name="summary_glasses" msgid="2872254734959842579">"Tämä sovellus saa käyttää näitä lupia laitteella (<xliff:g id="DEVICE_NAME">%1$s</xliff:g>)"</string> <string name="title_app_streaming" msgid="2270331024626446950">"Salli, että <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> saa pääsyn näihin puhelimesi tietoihin"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Laitteidenväliset palvelut"</string> <string name="helper_summary_app_streaming" msgid="2396773196949578425">"<xliff:g id="APP_NAME">%1$s</xliff:g> pyytää laitteesi (<xliff:g id="DISPLAY_NAME">%2$s</xliff:g>) puolesta lupaa striimata sovelluksia laitteidesi välillä"</string> @@ -38,10 +37,10 @@ <string name="title_nearby_device_streaming" msgid="7269956847378799794">"Sallitko, että <strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> voi suorittaa tämän toiminnon?"</string> <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<xliff:g id="APP_NAME">%1$s</xliff:g> pyytää laitteesi (<xliff:g id="DEVICE_NAME">%2$s</xliff:g>) puolesta lupaa striimata sovelluksia ja muita järjestelmän ominaisuuksia lähellä oleviin laitteisiin."</string> <string name="profile_name_generic" msgid="6851028682723034988">"laite"</string> - <string name="summary_generic_single_device" msgid="4181180669689590417">"Sovellus voi synkronoida tietoja (esimerkiksi soittajan nimen) puhelimesi ja laitteen (<xliff:g id="DEVICE_NAME">%1$s</xliff:g>) välillä"</string> <string name="summary_generic" msgid="1761976003668044801">"Sovellus voi synkronoida tietoja (esimerkiksi soittajan nimen) puhelimesi ja valitun laitteen välillä"</string> <string name="consent_yes" msgid="8344487259618762872">"Salli"</string> <string name="consent_no" msgid="2640796915611404382">"Älä salli"</string> + <string name="consent_cancel" msgid="5655005528379285841">"Peruuta"</string> <string name="consent_back" msgid="2560683030046918882">"Takaisin"</string> <string name="permission_expand" msgid="893185038020887411">"Laajenna <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string> <string name="permission_collapse" msgid="3320833884220844084">"Tiivistä <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string> diff --git a/packages/CompanionDeviceManager/res/values-fr-rCA/strings.xml b/packages/CompanionDeviceManager/res/values-fr-rCA/strings.xml index 6b3ed28811c3..753875b89566 100644 --- a/packages/CompanionDeviceManager/res/values-fr-rCA/strings.xml +++ b/packages/CompanionDeviceManager/res/values-fr-rCA/strings.xml @@ -17,15 +17,14 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="4470785958457506021">"Gestionnaire d\'appareil compagnon"</string> - <string name="confirmation_title" msgid="4593465730772390351">"Autoriser <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> à accéder à <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> + <string name="confirmation_title" msgid="2244241995958340998">"Autoriser l\'application <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> à accéder à <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> <string name="profile_name_watch" msgid="576290739483672360">"montre"</string> - <string name="chooser_title" msgid="2262294130493605839">"Choisissez un(e) <xliff:g id="PROFILE_NAME">%1$s</xliff:g> qui sera géré(e) par <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> - <string name="summary_watch" msgid="898569637110705523">"Cette application est nécessaire pour gérer votre <xliff:g id="DEVICE_NAME">%1$s</xliff:g>. <xliff:g id="APP_NAME">%2$s</xliff:g> aura l\'autorisation de synchroniser des informations, comme le nom de l\'appelant, d\'interagir avec vos notifications et d\'accéder à vos autorisations pour le téléphone, les messages texte, les contacts, l\'agenda, les journaux d\'appels et les appareils à proximité."</string> - <string name="summary_watch_single_device" msgid="3173948915947011333">"Cette application sera autorisée à synchroniser des informations, comme le nom de l\'appelant, et à accéder à ces autorisations sur votre <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string> + <string name="chooser_title_non_profile" msgid="6035023914517087400">"Choisir un appareil qui sera géré par <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>"</string> + <string name="chooser_title" msgid="2235819929238267637">"Choisir un appareil (<xliff:g id="PROFILE_NAME">%1$s</xliff:g>) pour le configurer"</string> + <string name="summary_watch" msgid="7962014927042971830">"Cette application sera autorisée à synchroniser des informations, comme le nom de l\'appelant, et à accéder à ces autorisations sur votre <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="confirmation_title_glasses" msgid="8288346850537727333">"Autoriser <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> à gérer <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> - <string name="profile_name_glasses" msgid="8488394059007275998">"lunettes"</string> - <string name="summary_glasses_multi_device" msgid="615259525961937348">"Cette application est nécessaire pour gérer vos <xliff:g id="DEVICE_NAME">%1$s</xliff:g>. L\'application <xliff:g id="APP_NAME">%2$s</xliff:g> sera autorisée à interagir avec vos notifications et à accéder à vos autorisations pour le téléphone, les messages texte, les contacts, le microphone et les appareils à proximité."</string> - <string name="summary_glasses_single_device" msgid="3000909894067413398">"Cette application pourra accéder à ces autorisations sur votre <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string> + <string name="profile_name_glasses" msgid="3506504967216601277">"appareil"</string> + <string name="summary_glasses" msgid="2872254734959842579">"Cette application pourra accéder à ces autorisations sur votre <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="title_app_streaming" msgid="2270331024626446950">"Autorisez <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> à accéder à ces informations à partir de votre téléphone"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Services multiappareils"</string> <string name="helper_summary_app_streaming" msgid="2396773196949578425">"<xliff:g id="APP_NAME">%1$s</xliff:g> demande l\'autorisation au nom de votre <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> pour diffuser des applications entre vos appareils"</string> @@ -38,10 +37,10 @@ <string name="title_nearby_device_streaming" msgid="7269956847378799794">"Autoriser <strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> à effectuer cette action?"</string> <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<xliff:g id="APP_NAME">%1$s</xliff:g> demande l\'autorisation, au nom de votre <xliff:g id="DEVICE_NAME">%2$s</xliff:g>, de diffuser des applications et d\'autres fonctionnalités du système sur des appareils à proximité"</string> <string name="profile_name_generic" msgid="6851028682723034988">"appareil"</string> - <string name="summary_generic_single_device" msgid="4181180669689590417">"Cette application pourra synchroniser des informations, comme le nom de l\'appelant, entre votre téléphone et <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="summary_generic" msgid="1761976003668044801">"Cette application pourra synchroniser des informations, comme le nom de l\'appelant, entre votre téléphone et l\'appareil sélectionné"</string> <string name="consent_yes" msgid="8344487259618762872">"Autoriser"</string> <string name="consent_no" msgid="2640796915611404382">"Ne pas autoriser"</string> + <string name="consent_cancel" msgid="5655005528379285841">"Annuler"</string> <string name="consent_back" msgid="2560683030046918882">"Retour"</string> <string name="permission_expand" msgid="893185038020887411">"Développer <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string> <string name="permission_collapse" msgid="3320833884220844084">"Réduire <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string> diff --git a/packages/CompanionDeviceManager/res/values-fr/strings.xml b/packages/CompanionDeviceManager/res/values-fr/strings.xml index b649f2760e51..d3704a0736ec 100644 --- a/packages/CompanionDeviceManager/res/values-fr/strings.xml +++ b/packages/CompanionDeviceManager/res/values-fr/strings.xml @@ -17,15 +17,14 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="4470785958457506021">"Gestionnaire d\'appareils associés"</string> - <string name="confirmation_title" msgid="4593465730772390351">"Autoriser <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> à accéder à <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> ?"</string> + <string name="confirmation_title" msgid="2244241995958340998">"Autoriser l\'appli <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> à accéder à <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> ?"</string> <string name="profile_name_watch" msgid="576290739483672360">"montre"</string> - <string name="chooser_title" msgid="2262294130493605839">"Sélectionnez le/la <xliff:g id="PROFILE_NAME">%1$s</xliff:g> qui sera géré(e) par <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> - <string name="summary_watch" msgid="898569637110705523">"Cette appli est nécessaire pour gérer <xliff:g id="DEVICE_NAME">%1$s</xliff:g>. <xliff:g id="APP_NAME">%2$s</xliff:g> aura l\'autorisation de synchroniser des infos (comme le nom de l\'appelant), d\'interagir avec vos notifications et d\'accéder à votre téléphone, à votre agenda, ainsi qu\'à vos SMS, contacts, journaux d\'appels et appareils à proximité."</string> - <string name="summary_watch_single_device" msgid="3173948915947011333">"Cette appli sera autorisée à synchroniser des infos (comme le nom de l\'appelant) et disposera de ces autorisations sur votre <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string> + <string name="chooser_title_non_profile" msgid="6035023914517087400">"Sélectionner l\'appareil qui sera géré par <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>"</string> + <string name="chooser_title" msgid="2235819929238267637">"Sélectionner votre <xliff:g id="PROFILE_NAME">%1$s</xliff:g> à configurer"</string> + <string name="summary_watch" msgid="7962014927042971830">"Cette appli sera autorisée à synchroniser des infos (comme le nom de l\'appelant) et disposera de ces autorisations sur votre <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="confirmation_title_glasses" msgid="8288346850537727333">"Autoriser <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> à gérer <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> ?"</string> - <string name="profile_name_glasses" msgid="8488394059007275998">"lunettes"</string> - <string name="summary_glasses_multi_device" msgid="615259525961937348">"Cette appli est nécessaire pour gérer <xliff:g id="DEVICE_NAME">%1$s</xliff:g>. <xliff:g id="APP_NAME">%2$s</xliff:g> aura l\'autorisation d\'interagir avec vos notifications et d\'accéder aux autorisations du téléphone, des SMS, des contacts, du micro et des appareils à proximité."</string> - <string name="summary_glasses_single_device" msgid="3000909894067413398">"Cette appli sera autorisée à accéder à ces autorisations sur votre <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string> + <string name="profile_name_glasses" msgid="3506504967216601277">"appareil"</string> + <string name="summary_glasses" msgid="2872254734959842579">"Cette appli sera autorisée à accéder à ces autorisations sur votre <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="title_app_streaming" msgid="2270331024626446950">"Autoriser <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> à accéder à ces informations depuis votre téléphone"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Services inter-appareils"</string> <string name="helper_summary_app_streaming" msgid="2396773196949578425">"<xliff:g id="APP_NAME">%1$s</xliff:g> demande l\'autorisation au nom de votre <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> pour caster des applis d\'un appareil à l\'autre"</string> @@ -38,10 +37,10 @@ <string name="title_nearby_device_streaming" msgid="7269956847378799794">"Autoriser <strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> à effectuer cette action ?"</string> <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<xliff:g id="APP_NAME">%1$s</xliff:g> demande l\'autorisation au nom de votre <xliff:g id="DEVICE_NAME">%2$s</xliff:g> de diffuser des applis et d\'autres fonctionnalités système en streaming sur des appareils à proximité"</string> <string name="profile_name_generic" msgid="6851028682723034988">"appareil"</string> - <string name="summary_generic_single_device" msgid="4181180669689590417">"Cette appli pourra synchroniser des infos, comme le nom de l\'appelant, entre votre téléphone et <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="summary_generic" msgid="1761976003668044801">"Cette appli pourra synchroniser des infos, comme le nom de l\'appelant, entre votre téléphone et l\'appareil choisi"</string> <string name="consent_yes" msgid="8344487259618762872">"Autoriser"</string> <string name="consent_no" msgid="2640796915611404382">"Ne pas autoriser"</string> + <string name="consent_cancel" msgid="5655005528379285841">"Annuler"</string> <string name="consent_back" msgid="2560683030046918882">"Retour"</string> <string name="permission_expand" msgid="893185038020887411">"Développer <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string> <string name="permission_collapse" msgid="3320833884220844084">"Réduire <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string> diff --git a/packages/CompanionDeviceManager/res/values-gl/strings.xml b/packages/CompanionDeviceManager/res/values-gl/strings.xml index e7ff1e3864df..a7aa72db917d 100644 --- a/packages/CompanionDeviceManager/res/values-gl/strings.xml +++ b/packages/CompanionDeviceManager/res/values-gl/strings.xml @@ -17,15 +17,14 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="4470785958457506021">"Xestor de dispositivos complementarios"</string> - <string name="confirmation_title" msgid="4593465730772390351">"Queres permitir que <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> acceda ao dispositivo (<strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>)?"</string> + <string name="confirmation_title" msgid="2244241995958340998">"Queres permitir que a aplicación <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> acceda ao dispositivo <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> <string name="profile_name_watch" msgid="576290739483672360">"reloxo"</string> - <string name="chooser_title" msgid="2262294130493605839">"Escolle un dispositivo (<xliff:g id="PROFILE_NAME">%1$s</xliff:g>) para que o xestione a aplicación <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> - <string name="summary_watch" msgid="898569637110705523">"Esta aplicación é necesaria para xestionar o teu dispositivo (<xliff:g id="DEVICE_NAME">%1$s</xliff:g>). <xliff:g id="APP_NAME">%2$s</xliff:g> poderá sincronizar información (por exemplo, o nome de quen chama), interactuar coas túas notificacións e acceder aos permisos do teu teléfono, das SMS, dos contactos, do calendario, dos rexistros de chamadas e dos dispositivos próximos."</string> - <string name="summary_watch_single_device" msgid="3173948915947011333">"Esta aplicación poderá sincronizar información (por exemplo, o nome de quen chama) e acceder a estes permisos do dispositivo (<xliff:g id="DEVICE_TYPE">%1$s</xliff:g>)"</string> + <string name="chooser_title_non_profile" msgid="6035023914517087400">"Escolle un dispositivo para que o xestione a aplicación <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>"</string> + <string name="chooser_title" msgid="2235819929238267637">"Escolle o perfil (<xliff:g id="PROFILE_NAME">%1$s</xliff:g>) que queiras configurar"</string> + <string name="summary_watch" msgid="7962014927042971830">"Esta aplicación poderá sincronizar información (por exemplo, o nome de quen chama) e acceder a estes permisos do dispositivo (<xliff:g id="DEVICE_NAME">%1$s</xliff:g>)"</string> <string name="confirmation_title_glasses" msgid="8288346850537727333">"Queres permitir que <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> xestione o dispositivo (<strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>)?"</string> - <string name="profile_name_glasses" msgid="8488394059007275998">"lentes"</string> - <string name="summary_glasses_multi_device" msgid="615259525961937348">"Esta aplicación é necesaria para xestionar o dispositivo (<xliff:g id="DEVICE_NAME">%1$s</xliff:g>). <xliff:g id="APP_NAME">%2$s</xliff:g> poderá interactuar coas túas notificacións e acceder aos permisos do teu teléfono, das SMS, dos contactos, do micrófono e dos dispositivos próximos."</string> - <string name="summary_glasses_single_device" msgid="3000909894067413398">"Esta aplicación poderá acceder a estes permisos do dispositivo (<xliff:g id="DEVICE_TYPE">%1$s</xliff:g>)"</string> + <string name="profile_name_glasses" msgid="3506504967216601277">"dispositivo"</string> + <string name="summary_glasses" msgid="2872254734959842579">"Esta aplicación poderá acceder a estes permisos do dispositivo (<xliff:g id="DEVICE_NAME">%1$s</xliff:g>)"</string> <string name="title_app_streaming" msgid="2270331024626446950">"Permitir que a aplicación <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> acceda a esta información desde o teu teléfono"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Servizos multidispositivo"</string> <string name="helper_summary_app_streaming" msgid="2396773196949578425">"<xliff:g id="APP_NAME">%1$s</xliff:g> está solicitando permiso en nome do teu dispositivo (<xliff:g id="DISPLAY_NAME">%2$s</xliff:g>) para emitir contido de aplicacións entre os teus aparellos"</string> @@ -38,10 +37,10 @@ <string name="title_nearby_device_streaming" msgid="7269956847378799794">"Queres permitir que <strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> leve a cabo esta acción?"</string> <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<xliff:g id="APP_NAME">%1$s</xliff:g> está solicitando permiso en nome do teu dispositivo (<xliff:g id="DEVICE_NAME">%2$s</xliff:g>) para emitir o contido das aplicacións e doutras funcións do sistema en dispositivos próximos"</string> <string name="profile_name_generic" msgid="6851028682723034988">"dispositivo"</string> - <string name="summary_generic_single_device" msgid="4181180669689590417">"Esta aplicación poderá sincronizar información (por exemplo, o nome de quen chama) entre o teléfono e o dispositivo (<xliff:g id="DEVICE_NAME">%1$s</xliff:g>)"</string> <string name="summary_generic" msgid="1761976003668044801">"Esta aplicación poderá sincronizar información (por exemplo, o nome de quen chama) entre o teléfono e o dispositivo escollido"</string> <string name="consent_yes" msgid="8344487259618762872">"Permitir"</string> <string name="consent_no" msgid="2640796915611404382">"Non permitir"</string> + <string name="consent_cancel" msgid="5655005528379285841">"Cancelar"</string> <string name="consent_back" msgid="2560683030046918882">"Atrás"</string> <string name="permission_expand" msgid="893185038020887411">"Despregar <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string> <string name="permission_collapse" msgid="3320833884220844084">"Contraer <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string> diff --git a/packages/CompanionDeviceManager/res/values-gu/strings.xml b/packages/CompanionDeviceManager/res/values-gu/strings.xml index 436d6cb1a462..443fb4ba824f 100644 --- a/packages/CompanionDeviceManager/res/values-gu/strings.xml +++ b/packages/CompanionDeviceManager/res/values-gu/strings.xml @@ -17,15 +17,14 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="4470785958457506021">"કમ્પેનિયન ડિવાઇસ મેનેજર"</string> - <string name="confirmation_title" msgid="4593465730772390351">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>ને <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> ઍક્સેસ કરવાની મંજૂરી આપીએ?"</string> + <string name="confirmation_title" msgid="2244241995958340998">"શું <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>ને ઍક્સેસ કરવા માટે, <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ઍપને મંજૂરી આપીએ?"</string> <string name="profile_name_watch" msgid="576290739483672360">"સ્માર્ટવૉચ"</string> - <string name="chooser_title" msgid="2262294130493605839">"<strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> દ્વારા મેનેજ કરવા માટે કોઈ <xliff:g id="PROFILE_NAME">%1$s</xliff:g> પસંદ કરો"</string> - <string name="summary_watch" msgid="898569637110705523">"તમારા <xliff:g id="DEVICE_NAME">%1$s</xliff:g>ને મેનેજ કરવા માટે આ ઍપ જરૂરી છે. <xliff:g id="APP_NAME">%2$s</xliff:g>ને કૉલ કરનાર વ્યક્તિનું નામ જેવી માહિતી સિંક કરવાની, તમારા નોટિફિકેશન સાથે ક્રિયાપ્રતિક્રિયા કરવાની અને તમારો ફોન, SMS, સંપર્કો, Calendar, કૉલ લૉગ તથા નજીકના ડિવાઇસની પરવાનગીઓ ઍક્સેસ કરવાની મંજૂરી આપવામાં આવશે."</string> - <string name="summary_watch_single_device" msgid="3173948915947011333">"આ ઍપને, કૉલ કરનાર વ્યક્તિનું નામ જેવી માહિતી સિંક કરવાની અને તમારા <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> પર આ પરવાનગીઓ ઍક્સેસ કરવાની મંજૂરી આપવામાં આવશે"</string> + <string name="chooser_title_non_profile" msgid="6035023914517087400">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> દ્વારા મેનેજ કરવા માટે કોઈ ડિવાઇસ પસંદ કરો"</string> + <string name="chooser_title" msgid="2235819929238267637">"સેટઅપ કરવા માટે કોઈ <xliff:g id="PROFILE_NAME">%1$s</xliff:g> પસંદ કરો"</string> + <string name="summary_watch" msgid="7962014927042971830">"આ ઍપને, કૉલ કરનાર વ્યક્તિનું નામ જેવી માહિતી સિંક કરવાની અને તમારા <xliff:g id="DEVICE_NAME">%1$s</xliff:g> પર આ પરવાનગીઓ ઍક્સેસ કરવાની મંજૂરી આપવામાં આવશે"</string> <string name="confirmation_title_glasses" msgid="8288346850537727333">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>ને <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> મેનેજ કરવા માટે મંજૂરી આપીએ?"</string> - <string name="profile_name_glasses" msgid="8488394059007275998">"ચશ્માં"</string> - <string name="summary_glasses_multi_device" msgid="615259525961937348">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>ને મેનેજ કરવા માટે આ ઍપ જરૂરી છે. <xliff:g id="APP_NAME">%2$s</xliff:g>ને તમારા નોટિફિકેશન સાથે ક્રિયાપ્રતિક્રિયા કરવાની અને તમારો ફોન, SMS, સંપર્કો, માઇક્રોફોન તથા નજીકના ડિવાઇસની પરવાનગીઓ ઍક્સેસ કરવાની મંજૂરી આપવામાં આવશે."</string> - <string name="summary_glasses_single_device" msgid="3000909894067413398">"આ ઍપને તમારા <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> પર આ પરવાનગીઓ ઍક્સેસ કરવાની મંજૂરી મળશે"</string> + <string name="profile_name_glasses" msgid="3506504967216601277">"ડિવાઇસ"</string> + <string name="summary_glasses" msgid="2872254734959842579">"આ ઍપને તમારા <xliff:g id="DEVICE_NAME">%1$s</xliff:g> પર આ પરવાનગીઓ ઍક્સેસ કરવાની મંજૂરી મળશે"</string> <string name="title_app_streaming" msgid="2270331024626446950">"તમારા ફોનમાંથી આ માહિતી ઍક્સેસ કરવા માટે, <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>ને મંજૂરી આપો"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"ક્રોસ-ડિવાઇસ સેવાઓ"</string> <string name="helper_summary_app_streaming" msgid="2396773196949578425">"<xliff:g id="APP_NAME">%1$s</xliff:g> તમારા <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> વતી તમારા ડિવાઇસ વચ્ચે ઍપ સ્ટ્રીમ કરવાની પરવાનગીની વિનંતી કરી રહી છે"</string> @@ -38,10 +37,10 @@ <string name="title_nearby_device_streaming" msgid="7269956847378799794">"<strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong>ને આ પગલું ભરવાની મંજૂરી આપીએ?"</string> <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<xliff:g id="APP_NAME">%1$s</xliff:g> નજીકના ડિવાઇસ પર ઍપ અને સિસ્ટમની અન્ય સુવિધાઓ સ્ટ્રીમ કરવા તમારા <xliff:g id="DEVICE_NAME">%2$s</xliff:g> વતી પરવાનગીની વિનંતી કરી રહી છે"</string> <string name="profile_name_generic" msgid="6851028682723034988">"ડિવાઇસ"</string> - <string name="summary_generic_single_device" msgid="4181180669689590417">"આ ઍપ તમારા ફોન અને <xliff:g id="DEVICE_NAME">%1$s</xliff:g> વચ્ચે, કૉલ કરનાર કોઈ વ્યક્તિનું નામ જેવી માહિતી સિંક કરી શકશે"</string> <string name="summary_generic" msgid="1761976003668044801">"આ ઍપ તમારા ફોન અને પસંદ કરેલા ડિવાઇસ વચ્ચે, કૉલ કરનાર કોઈ વ્યક્તિનું નામ જેવી માહિતી સિંક કરી શકશે"</string> <string name="consent_yes" msgid="8344487259618762872">"મંજૂરી આપો"</string> <string name="consent_no" msgid="2640796915611404382">"મંજૂરી આપશો નહીં"</string> + <string name="consent_cancel" msgid="5655005528379285841">"રદ કરો"</string> <string name="consent_back" msgid="2560683030046918882">"પાછળ"</string> <string name="permission_expand" msgid="893185038020887411">"<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>ને મોટું કરો"</string> <string name="permission_collapse" msgid="3320833884220844084">"<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>ને નાનું કરો"</string> diff --git a/packages/CompanionDeviceManager/res/values-hi/strings.xml b/packages/CompanionDeviceManager/res/values-hi/strings.xml index dd8e831cff4a..494af37ce7d5 100644 --- a/packages/CompanionDeviceManager/res/values-hi/strings.xml +++ b/packages/CompanionDeviceManager/res/values-hi/strings.xml @@ -17,15 +17,14 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="4470785958457506021">"सहयोगी डिवाइस मैनेजर"</string> - <string name="confirmation_title" msgid="4593465730772390351">"क्या <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> को ऐक्सेस करने के लिए <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> को अनुमति देनी है?"</string> + <string name="confirmation_title" msgid="2244241995958340998">"क्या <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> को ऐक्सेस करने के लिए <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ऐप्लिकेशन को अनुमति देनी है?"</string> <string name="profile_name_watch" msgid="576290739483672360">"स्मार्टवॉच"</string> - <string name="chooser_title" msgid="2262294130493605839">"कोई <xliff:g id="PROFILE_NAME">%1$s</xliff:g> चुनें, ताकि उसे <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> की मदद से मैनेज किया जा सके"</string> - <string name="summary_watch" msgid="898569637110705523">"यह ऐप्लिकेशन, <xliff:g id="DEVICE_NAME">%1$s</xliff:g> को मैनेज करने के लिए ज़रूरी है. <xliff:g id="APP_NAME">%2$s</xliff:g> को डिवाइस की जानकारी सिंक करने की अनुमति होगी. जैसे, कॉल करने वाले व्यक्ति का नाम. इसे आपकी सूचनाओं पर कार्रवाई करने के साथ-साथ आपके फ़ोन, एसएमएस, संपर्कों, कैलेंडर, कॉल लॉग, और आस-पास मौजूद डिवाइसों को ऐक्सेस करने की अनुमति भी होगी."</string> - <string name="summary_watch_single_device" msgid="3173948915947011333">"यह ऐप्लिकेशन, आपके <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> पर इन अनुमतियों को ऐक्सेस करने के साथ-साथ कॉल करने वाले व्यक्ति के नाम जैसी जानकारी सिंक कर पाएगा"</string> + <string name="chooser_title_non_profile" msgid="6035023914517087400">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> से मैनेज किया जाने वाला डिवाइस चुनें"</string> + <string name="chooser_title" msgid="2235819929238267637">"सेट अप करने के लिए कोई <xliff:g id="PROFILE_NAME">%1$s</xliff:g> चुनें"</string> + <string name="summary_watch" msgid="7962014927042971830">"यह ऐप्लिकेशन, आपके <xliff:g id="DEVICE_NAME">%1$s</xliff:g> पर इन अनुमतियों को ऐक्सेस करने के साथ-साथ कॉल करने वाले व्यक्ति के नाम जैसी जानकारी सिंक कर पाएगा"</string> <string name="confirmation_title_glasses" msgid="8288346850537727333">"क्या <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> को <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> मैनेज करने की अनुमति देनी है?"</string> - <string name="profile_name_glasses" msgid="8488394059007275998">"चश्मा"</string> - <string name="summary_glasses_multi_device" msgid="615259525961937348">"यह ऐप्लिकेशन, <xliff:g id="DEVICE_NAME">%1$s</xliff:g> मैनेज करने के लिए ज़रूरी है. <xliff:g id="APP_NAME">%2$s</xliff:g> को डिवाइस की सूचनाओं पर कार्रवाई करने की अनुमति होगी. इसे आपके फ़ोन, मैसेज, संपर्कों, माइक्रोफ़ोन, और आस-पास मौजूद डिवाइसों को ऐक्सेस करने की अनुमति भी होगी."</string> - <string name="summary_glasses_single_device" msgid="3000909894067413398">"यह ऐप्लिकेशन, आपके <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> पर इन अनुमतियों को ऐक्सेस कर पाएगा"</string> + <string name="profile_name_glasses" msgid="3506504967216601277">"डिवाइस"</string> + <string name="summary_glasses" msgid="2872254734959842579">"यह ऐप्लिकेशन, आपके <xliff:g id="DEVICE_NAME">%1$s</xliff:g> पर इन अनुमतियों को ऐक्सेस कर पाएगा"</string> <string name="title_app_streaming" msgid="2270331024626446950">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> को अपने फ़ोन से यह जानकारी ऐक्सेस करने की अनुमति दें"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"क्रॉस-डिवाइस से जुड़ी सेवाएं"</string> <string name="helper_summary_app_streaming" msgid="2396773196949578425">"<xliff:g id="APP_NAME">%1$s</xliff:g> आपके <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> की ओर से, आपके डिवाइसों के बीच ऐप्लिकेशन स्ट्रीम करने की अनुमति मांग रहा है"</string> @@ -38,10 +37,10 @@ <string name="title_nearby_device_streaming" msgid="7269956847378799794">"क्या <strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> को यह कार्रवाई करने की अनुमति देनी है?"</string> <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<xliff:g id="APP_NAME">%1$s</xliff:g> आपके <xliff:g id="DEVICE_NAME">%2$s</xliff:g> की ओर से, ऐप्लिकेशन और दूसरे सिस्टम की सुविधाओं को आस-पास मौजूद डिवाइसों पर स्ट्रीम करने की अनुमति मांग रहा है"</string> <string name="profile_name_generic" msgid="6851028682723034988">"डिवाइस"</string> - <string name="summary_generic_single_device" msgid="4181180669689590417">"यह ऐप्लिकेशन, आपके फ़ोन और <xliff:g id="DEVICE_NAME">%1$s</xliff:g> के बीच जानकारी सिंक करेगा. जैसे, कॉल करने वाले व्यक्ति का नाम"</string> <string name="summary_generic" msgid="1761976003668044801">"यह ऐप्लिकेशन, आपके फ़ोन और चुने हुए डिवाइस के बीच जानकारी सिंक करेगा. जैसे, कॉल करने वाले व्यक्ति का नाम"</string> <string name="consent_yes" msgid="8344487259618762872">"अनुमति दें"</string> <string name="consent_no" msgid="2640796915611404382">"अनुमति न दें"</string> + <string name="consent_cancel" msgid="5655005528379285841">"रद्द करें"</string> <string name="consent_back" msgid="2560683030046918882">"वापस जाएं"</string> <string name="permission_expand" msgid="893185038020887411">"<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g> को बड़ा करें"</string> <string name="permission_collapse" msgid="3320833884220844084">"<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g> को छोटा करें"</string> diff --git a/packages/CompanionDeviceManager/res/values-hr/strings.xml b/packages/CompanionDeviceManager/res/values-hr/strings.xml index d32a319727ed..5e175bc6e2e8 100644 --- a/packages/CompanionDeviceManager/res/values-hr/strings.xml +++ b/packages/CompanionDeviceManager/res/values-hr/strings.xml @@ -17,15 +17,14 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string> - <string name="confirmation_title" msgid="4593465730772390351">"Želite li dopustiti aplikaciji <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> da pristupa <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> + <string name="confirmation_title" msgid="2244241995958340998">"Želite li dopustiti aplikaciji <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> da pristupa <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> <string name="profile_name_watch" msgid="576290739483672360">"satom"</string> - <string name="chooser_title" msgid="2262294130493605839">"Odaberite <xliff:g id="PROFILE_NAME">%1$s</xliff:g> kojim će upravljati aplikacija <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> - <string name="summary_watch" msgid="898569637110705523">"Ta je aplikacija potrebna za upravljanje vašim uređajem <xliff:g id="DEVICE_NAME">%1$s</xliff:g>. Aplikacija <xliff:g id="APP_NAME">%2$s</xliff:g> moći će sinkronizirati podatke, primjerice ime pozivatelja, stupati u interakciju s vašim obavijestima i pristupati vašim dopuštenjima za telefon, SMS-ove, kontakte, kalendar, zapisnike poziva i uređaje u blizini."</string> - <string name="summary_watch_single_device" msgid="3173948915947011333">"Aplikacija će moći sinkronizirati podatke kao što je ime pozivatelja i pristupiti tim dopuštenjima na vašem <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string> + <string name="chooser_title_non_profile" msgid="6035023914517087400">"Odaberite uređaj kojim će upravljati aplikacija <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>"</string> + <string name="chooser_title" msgid="2235819929238267637">"Odaberite profil <xliff:g id="PROFILE_NAME">%1$s</xliff:g> koji želite postaviti"</string> + <string name="summary_watch" msgid="7962014927042971830">"Aplikacija će moći sinkronizirati podatke kao što je ime pozivatelja i pristupiti tim dopuštenjima na vašem uređaju <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="confirmation_title_glasses" msgid="8288346850537727333">"Dopustiti aplikaciji <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> da upravlja uređajem <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> - <string name="profile_name_glasses" msgid="8488394059007275998">"naočale"</string> - <string name="summary_glasses_multi_device" msgid="615259525961937348">"Ta je aplikacija potrebna za upravljanje uređajem <xliff:g id="DEVICE_NAME">%1$s</xliff:g>. Aplikacija <xliff:g id="APP_NAME">%2$s</xliff:g> moći će stupati u interakciju s vašim obavijestima i pristupati vašim dopuštenjima za telefon, SMS-ove, kontakte, mikrofon i uređaje u blizini."</string> - <string name="summary_glasses_single_device" msgid="3000909894067413398">"Aplikacija će moći pristupati ovim dopuštenjima na vašem <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string> + <string name="profile_name_glasses" msgid="3506504967216601277">"uređaj"</string> + <string name="summary_glasses" msgid="2872254734959842579">"Aplikacija će moći pristupati ovim dopuštenjima na vašem uređaju <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="title_app_streaming" msgid="2270331024626446950">"Omogućite aplikaciji <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> da pristupa informacijama s vašeg telefona"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Usluge na različitim uređajima"</string> <string name="helper_summary_app_streaming" msgid="2396773196949578425">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> zahtijeva dopuštenje u ime vašeg uređaja <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> za stream aplikacija s jednog uređaja na drugi"</string> @@ -38,10 +37,10 @@ <string name="title_nearby_device_streaming" msgid="7269956847378799794">"Želite li uređaju <strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> dopustiti da izvrši tu radnju?"</string> <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> zahtijeva dopuštenje u ime vašeg uređaja <xliff:g id="DEVICE_NAME">%2$s</xliff:g> za emitiranje aplikacija i drugih značajki sustava na uređajima u blizini"</string> <string name="profile_name_generic" msgid="6851028682723034988">"uređaj"</string> - <string name="summary_generic_single_device" msgid="4181180669689590417">"Ta će aplikacija moći sinkronizirati podatke između vašeg telefona i uređaja <xliff:g id="DEVICE_NAME">%1$s</xliff:g>, primjerice ime pozivatelja"</string> <string name="summary_generic" msgid="1761976003668044801">"Ta će aplikacija moći sinkronizirati podatke između vašeg telefona i odabranog uređaja, primjerice ime pozivatelja"</string> <string name="consent_yes" msgid="8344487259618762872">"Dopusti"</string> <string name="consent_no" msgid="2640796915611404382">"Nemoj dopustiti"</string> + <string name="consent_cancel" msgid="5655005528379285841">"Odustani"</string> <string name="consent_back" msgid="2560683030046918882">"Natrag"</string> <string name="permission_expand" msgid="893185038020887411">"Proširi <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string> <string name="permission_collapse" msgid="3320833884220844084">"Sažmi <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string> diff --git a/packages/CompanionDeviceManager/res/values-hu/strings.xml b/packages/CompanionDeviceManager/res/values-hu/strings.xml index 594317861122..ba480f61b840 100644 --- a/packages/CompanionDeviceManager/res/values-hu/strings.xml +++ b/packages/CompanionDeviceManager/res/values-hu/strings.xml @@ -17,15 +17,14 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="4470785958457506021">"Társeszközök kezelője"</string> - <string name="confirmation_title" msgid="4593465730772390351">"Engedélyezi a(z) <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> hozzáférését a következőhöz: <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> + <string name="confirmation_title" msgid="2244241995958340998">"Engedélyezi a(z) <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> alkalmazás számára, hogy hozzáférjen a következőhöz: <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> <string name="profile_name_watch" msgid="576290739483672360">"óra"</string> - <string name="chooser_title" msgid="2262294130493605839">"A(z) <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> alkalmazással kezelni kívánt <xliff:g id="PROFILE_NAME">%1$s</xliff:g> kiválasztása"</string> - <string name="summary_watch" msgid="898569637110705523">"Szükség van erre az alkalmazásra a következő kezeléséhez: <xliff:g id="DEVICE_NAME">%1$s</xliff:g>. A(z) <xliff:g id="APP_NAME">%2$s</xliff:g> képes lesz szinkronizálni információkat (például a hívó fél nevét), műveleteket végezhet majd az értesítésekkel, és hozzáférhet majd a Telefon, az SMS, a Névjegyek, a Naptár, a Hívásnaplók és a Közeli eszközök engedélyekhez."</string> - <string name="summary_watch_single_device" msgid="3173948915947011333">"Ez az alkalmazás képes lesz szinkronizálni információkat (például a hívó fél nevét), és hozzáférhet majd ezekhez az engedélyekhez az Ön <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> eszközén"</string> + <string name="chooser_title_non_profile" msgid="6035023914517087400">"A(z) <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> alkalmazással kezelni kívánt eszköz kiválasztása"</string> + <string name="chooser_title" msgid="2235819929238267637">"Válassza ki a beállítani kívánt <xliff:g id="PROFILE_NAME">%1$s</xliff:g> nevet."</string> + <string name="summary_watch" msgid="7962014927042971830">"Ez az alkalmazás képes lesz szinkronizálni információkat (például a hívó fél nevét), és hozzáférhet majd ezekhez az engedélyekhez az Ön <xliff:g id="DEVICE_NAME">%1$s</xliff:g> eszközén"</string> <string name="confirmation_title_glasses" msgid="8288346850537727333">"Engedélyezi, hogy a(z) <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> kezelje a következő eszközt: <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> - <string name="profile_name_glasses" msgid="8488394059007275998">"szemüveg"</string> - <string name="summary_glasses_multi_device" msgid="615259525961937348">"Erre az alkalmazásra szükség van a következő eszköz kezeléséhez: <xliff:g id="DEVICE_NAME">%1$s</xliff:g>. A(z) <xliff:g id="APP_NAME">%2$s</xliff:g> műveleteket végezhet majd az értesítésekkel, és hozzáférhet majd a Telefon, az SMS, a Névjegyek, a Mikrofon és a Közeli eszközök engedélyekhez."</string> - <string name="summary_glasses_single_device" msgid="3000909894067413398">"Az alkalmazás hozzáférhet majd ezekhez az engedélyekhez az Ön <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> eszközén"</string> + <string name="profile_name_glasses" msgid="3506504967216601277">"eszköz"</string> + <string name="summary_glasses" msgid="2872254734959842579">"Az alkalmazás hozzáférhet majd ezekhez az engedélyekhez az Ön <xliff:g id="DEVICE_NAME">%1$s</xliff:g> eszközén"</string> <string name="title_app_streaming" msgid="2270331024626446950">"Engedélyezi a(z) „<xliff:g id="APP_NAME">%1$s</xliff:g>” alkalmazás számára az információhoz való hozzáférést a telefonról"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Többeszközös szolgáltatások"</string> <string name="helper_summary_app_streaming" msgid="2396773196949578425">"A(z) <xliff:g id="APP_NAME">%1$s</xliff:g> engedélyt kér a(z) <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> nevében az alkalmazások eszközök közötti streameléséhez"</string> @@ -38,10 +37,10 @@ <string name="title_nearby_device_streaming" msgid="7269956847378799794">"Engedélyezi a(z) <strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> számára ennek a műveletnek a végrehajtását?"</string> <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"A(z) <xliff:g id="APP_NAME">%1$s</xliff:g> engedélyt kér a(z) <xliff:g id="DEVICE_NAME">%2$s</xliff:g> nevében az alkalmazások és más rendszerfunkciók közeli eszközökre történő streamelésére"</string> <string name="profile_name_generic" msgid="6851028682723034988">"eszköz"</string> - <string name="summary_generic_single_device" msgid="4181180669689590417">"Ez az alkalmazás képes lesz szinkronizálni az olyan információkat a telefon és a(z) <xliff:g id="DEVICE_NAME">%1$s</xliff:g> eszköz között, mint például a hívó fél neve."</string> <string name="summary_generic" msgid="1761976003668044801">"Ez az alkalmazás képes lesz szinkronizálni az olyan információkat a telefon és a kiválasztott eszköz között, mint például a hívó fél neve."</string> <string name="consent_yes" msgid="8344487259618762872">"Engedélyezés"</string> <string name="consent_no" msgid="2640796915611404382">"Tiltás"</string> + <string name="consent_cancel" msgid="5655005528379285841">"Mégse"</string> <string name="consent_back" msgid="2560683030046918882">"Vissza"</string> <string name="permission_expand" msgid="893185038020887411">"<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g> kibontása"</string> <string name="permission_collapse" msgid="3320833884220844084">"<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g> összecsukása"</string> diff --git a/packages/CompanionDeviceManager/res/values-hy/strings.xml b/packages/CompanionDeviceManager/res/values-hy/strings.xml index 17f46ed67e38..9385fd16a66d 100644 --- a/packages/CompanionDeviceManager/res/values-hy/strings.xml +++ b/packages/CompanionDeviceManager/res/values-hy/strings.xml @@ -17,15 +17,14 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string> - <string name="confirmation_title" msgid="4593465730772390351">"Թույլատրե՞լ <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> հավելվածին կառավարել <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> սարքը"</string> + <string name="confirmation_title" msgid="2244241995958340998">"Թույլատրե՞լ <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> հավելվածին կառավարել <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> սարքը"</string> <string name="profile_name_watch" msgid="576290739483672360">"ժամացույց"</string> - <string name="chooser_title" msgid="2262294130493605839">"Ընտրեք <xliff:g id="PROFILE_NAME">%1$s</xliff:g>ը, որը պետք է կառավարվի <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> հավելվածի կողմից"</string> - <string name="summary_watch" msgid="898569637110705523">"Այս հավելվածն անհրաժեշտ է ձեր <xliff:g id="DEVICE_NAME">%1$s</xliff:g> պրոֆիլը կառավարելու համար։ <xliff:g id="APP_NAME">%2$s</xliff:g> հավելվածը կկարողանա համաժամացնել տվյալները, օր․՝ զանգողի անունը, փոխազդել ձեր ծանուցումների հետ և կստանա «Հեռախոս», «SMS», «Կոնտակտներ», «Օրացույց», «Կանչերի ցուցակ» և «Մոտակա սարքեր» թույլտվությունները։"</string> - <string name="summary_watch_single_device" msgid="3173948915947011333">"Այս հավելվածը կկարողանա համաժամացնել տվյալները, օր․՝ զանգողի անունը, և կստանա հետևյալ թույլտվությունները ձեր <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>ում"</string> + <string name="chooser_title_non_profile" msgid="6035023914517087400">"Ընտրեք սարքը, որը պետք է կառավարվի <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> հավելվածի միջոցով"</string> + <string name="chooser_title" msgid="2235819929238267637">"Կարգավորելու համար ընտրեք <xliff:g id="PROFILE_NAME">%1$s</xliff:g>ը"</string> + <string name="summary_watch" msgid="7962014927042971830">"Այս հավելվածը կկարողանա համաժամացնել տվյալները, օր․՝ զանգողի անունը, և կստանա հետևյալ թույլտվությունները ձեր <xliff:g id="DEVICE_NAME">%1$s</xliff:g>ում"</string> <string name="confirmation_title_glasses" msgid="8288346850537727333">"Թույլատրե՞լ <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> հավելվածին կառավարել <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> սարքը"</string> - <string name="profile_name_glasses" msgid="8488394059007275998">"ակնոց"</string> - <string name="summary_glasses_multi_device" msgid="615259525961937348">"Այս հավելվածն անհրաժեշտ է <xliff:g id="DEVICE_NAME">%1$s</xliff:g> սարքը կառավարելու համար։ <xliff:g id="APP_NAME">%2$s</xliff:g> հավելվածը կկարողանա փոխազդել ձեր ծանուցումների հետ և կստանա «Հեռախոս», «SMS», «Կոնտակտներ», «Խոսափող» և «Մոտակա սարքեր» թույլտվությունները։"</string> - <string name="summary_glasses_single_device" msgid="3000909894067413398">"Այս հավելվածը կստանա հետևյալ թույլտվությունները ձեր <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>ում"</string> + <string name="profile_name_glasses" msgid="3506504967216601277">"սարք"</string> + <string name="summary_glasses" msgid="2872254734959842579">"Այս հավելվածը կստանա հետևյալ թույլտվությունները ձեր <xliff:g id="DEVICE_NAME">%1$s</xliff:g>ում"</string> <string name="title_app_streaming" msgid="2270331024626446950">"Թույլատրեք <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> հավելվածին օգտագործել այս տեղեկությունները ձեր հեռախոսից"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Միջսարքային ծառայություններ"</string> <string name="helper_summary_app_streaming" msgid="2396773196949578425">"<xliff:g id="APP_NAME">%1$s</xliff:g> հավելվածը ձեր <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> սարքի անունից թույլտվություն է խնդրում՝ ձեր սարքերի միջև հավելվածներ հեռարձակելու համար"</string> @@ -38,10 +37,10 @@ <string name="title_nearby_device_streaming" msgid="7269956847378799794">"Թույլատրե՞լ <strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> հավելվածին կատարել այս գործողությունը"</string> <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<xliff:g id="APP_NAME">%1$s</xliff:g> հավելվածը ձեր <xliff:g id="DEVICE_NAME">%2$s</xliff:g> սարքի անունից թույլտվություն է խնդրում՝ մոտակա սարքերին հավելվածներ և համակարգի այլ գործառույթներ հեռարձակելու համար"</string> <string name="profile_name_generic" msgid="6851028682723034988">"սարք"</string> - <string name="summary_generic_single_device" msgid="4181180669689590417">"Այս հավելվածը կկարողանա համաժամացնել ձեր հեռախոսի և <xliff:g id="DEVICE_NAME">%1$s</xliff:g> սարքի տվյալները, օր․՝ զանգողի անունը"</string> <string name="summary_generic" msgid="1761976003668044801">"Այս հավելվածը կկարողանա համաժամացնել ձեր հեռախոսի և ընտրված սարքի տվյալները, օր․՝ զանգողի անունը"</string> <string name="consent_yes" msgid="8344487259618762872">"Թույլատրել"</string> <string name="consent_no" msgid="2640796915611404382">"Չթույլատրել"</string> + <string name="consent_cancel" msgid="5655005528379285841">"Չեղարկել"</string> <string name="consent_back" msgid="2560683030046918882">"Հետ"</string> <string name="permission_expand" msgid="893185038020887411">"Ծավալել «<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>» բաժինը"</string> <string name="permission_collapse" msgid="3320833884220844084">"Ծալել «<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>» բաժինը"</string> diff --git a/packages/CompanionDeviceManager/res/values-in/strings.xml b/packages/CompanionDeviceManager/res/values-in/strings.xml index 39ffcd7f975d..c03a5ab619c7 100644 --- a/packages/CompanionDeviceManager/res/values-in/strings.xml +++ b/packages/CompanionDeviceManager/res/values-in/strings.xml @@ -17,15 +17,14 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="4470785958457506021">"Pengelola Perangkat Pendamping"</string> - <string name="confirmation_title" msgid="4593465730772390351">"Izinkan <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> mengakses <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> + <string name="confirmation_title" msgid="2244241995958340998">"Izinkan aplikasi <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> mengakses <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> <string name="profile_name_watch" msgid="576290739483672360">"smartwatch"</string> - <string name="chooser_title" msgid="2262294130493605839">"Pilih <xliff:g id="PROFILE_NAME">%1$s</xliff:g> untuk dikelola oleh <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> - <string name="summary_watch" msgid="898569637110705523">"Aplikasi ini diperlukan untuk mengelola <xliff:g id="DEVICE_NAME">%1$s</xliff:g>. <xliff:g id="APP_NAME">%2$s</xliff:g> akan diizinkan menyinkronkan info, seperti nama penelepon, berinteraksi dengan notifikasi, dan mengakses izin Telepon, SMS, Kontak, Kalender, Log panggilan, dan Perangkat di sekitar."</string> - <string name="summary_watch_single_device" msgid="3173948915947011333">"Aplikasi ini akan diizinkan menyinkronkan info, seperti nama penelepon, dan mengakses izin ini di <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> Anda"</string> + <string name="chooser_title_non_profile" msgid="6035023914517087400">"Pilih perangkat untuk dikelola oleh <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>"</string> + <string name="chooser_title" msgid="2235819929238267637">"Pilih <xliff:g id="PROFILE_NAME">%1$s</xliff:g> untuk disiapkan"</string> + <string name="summary_watch" msgid="7962014927042971830">"Aplikasi ini akan diizinkan menyinkronkan info, seperti nama penelepon, dan mengakses izin ini di <xliff:g id="DEVICE_NAME">%1$s</xliff:g> Anda"</string> <string name="confirmation_title_glasses" msgid="8288346850537727333">"Izinkan <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> mengelola <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> - <string name="profile_name_glasses" msgid="8488394059007275998">"kacamata pintar"</string> - <string name="summary_glasses_multi_device" msgid="615259525961937348">"Aplikasi ini diperlukan untuk mengelola <xliff:g id="DEVICE_NAME">%1$s</xliff:g>. <xliff:g id="APP_NAME">%2$s</xliff:g> akan diizinkan berinteraksi dengan notifikasi dan mengakses izin Ponsel, SMS, Kontak, Mikrofon, dan Perangkat di sekitar."</string> - <string name="summary_glasses_single_device" msgid="3000909894067413398">"Aplikasi ini akan diizinkan mengakses izin ini di <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> Anda"</string> + <string name="profile_name_glasses" msgid="3506504967216601277">"perangkat"</string> + <string name="summary_glasses" msgid="2872254734959842579">"Aplikasi ini akan diizinkan mengakses izin ini di <xliff:g id="DEVICE_NAME">%1$s</xliff:g> Anda"</string> <string name="title_app_streaming" msgid="2270331024626446950">"Izinkan <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> untuk mengakses informasi ini dari ponsel Anda"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Layanan lintas perangkat"</string> <string name="helper_summary_app_streaming" msgid="2396773196949578425">"<xliff:g id="APP_NAME">%1$s</xliff:g> meminta izin atas nama <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> untuk menstreaming aplikasi di antara perangkat Anda"</string> @@ -38,10 +37,10 @@ <string name="title_nearby_device_streaming" msgid="7269956847378799794">"Izinkan <strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> melakukan tindakan ini?"</string> <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<xliff:g id="APP_NAME">%1$s</xliff:g> meminta izin atas nama <xliff:g id="DEVICE_NAME">%2$s</xliff:g> untuk menstreaming aplikasi dan fitur sistem lainnya ke perangkat di sekitar"</string> <string name="profile_name_generic" msgid="6851028682723034988">"perangkat"</string> - <string name="summary_generic_single_device" msgid="4181180669689590417">"Aplikasi ini akan dapat menyinkronkan info, seperti nama penelepon, antara ponsel dan <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="summary_generic" msgid="1761976003668044801">"Aplikasi ini akan dapat menyinkronkan info, seperti nama penelepon, antara ponsel dan perangkat yang dipilih"</string> <string name="consent_yes" msgid="8344487259618762872">"Izinkan"</string> <string name="consent_no" msgid="2640796915611404382">"Jangan izinkan"</string> + <string name="consent_cancel" msgid="5655005528379285841">"Batalkan"</string> <string name="consent_back" msgid="2560683030046918882">"Kembali"</string> <string name="permission_expand" msgid="893185038020887411">"Luaskan <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string> <string name="permission_collapse" msgid="3320833884220844084">"Ciutkan <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string> diff --git a/packages/CompanionDeviceManager/res/values-is/strings.xml b/packages/CompanionDeviceManager/res/values-is/strings.xml index 3f55d5db22e1..fc9f5a17e1c6 100644 --- a/packages/CompanionDeviceManager/res/values-is/strings.xml +++ b/packages/CompanionDeviceManager/res/values-is/strings.xml @@ -17,15 +17,14 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="4470785958457506021">"Stjórnun fylgdartækja"</string> - <string name="confirmation_title" msgid="4593465730772390351">"Veita <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> aðgang að <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> + <string name="confirmation_title" msgid="2244241995958340998">"Veita forritinu <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> aðgang að <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> <string name="profile_name_watch" msgid="576290739483672360">"úr"</string> - <string name="chooser_title" msgid="2262294130493605839">"Velja <xliff:g id="PROFILE_NAME">%1$s</xliff:g> sem <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> á að stjórna"</string> - <string name="summary_watch" msgid="898569637110705523">"Þetta forrit er nauðsynlegt til að stjórna <xliff:g id="DEVICE_NAME">%1$s</xliff:g>. <xliff:g id="APP_NAME">%2$s</xliff:g> fær heimild til að samstilla upplýsingar, t.d. nafn þess sem hringir, og bregðast við tilkynningum og fær aðgang að heimildum fyrir síma, SMS, tengiliði, dagatal, símtalaskrár og nálæg tæki."</string> - <string name="summary_watch_single_device" msgid="3173948915947011333">"Þetta forrit fær heimild til að samstilla upplýsingar, t.d. nafn þess sem hringir, og fær aðgang að eftirfarandi heimildum í <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string> + <string name="chooser_title_non_profile" msgid="6035023914517087400">"Veldu tæki sem <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> á að stjórna"</string> + <string name="chooser_title" msgid="2235819929238267637">"Veldu <xliff:g id="PROFILE_NAME">%1$s</xliff:g> til að setja upp"</string> + <string name="summary_watch" msgid="7962014927042971830">"Þetta forrit fær heimild til að samstilla upplýsingar, t.d. nafn þess sem hringir, og fær aðgang að eftirfarandi heimildum í <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="confirmation_title_glasses" msgid="8288346850537727333">"Leyfa <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> að stjórna <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> - <string name="profile_name_glasses" msgid="8488394059007275998">"gleraugu"</string> - <string name="summary_glasses_multi_device" msgid="615259525961937348">"Þetta forrit er nauðsynlegt til að stjórna <xliff:g id="DEVICE_NAME">%1$s</xliff:g>. <xliff:g id="APP_NAME">%2$s</xliff:g> fær heimild til að bregðast við tilkynningum og fær aðgang að heimildum fyrir síma, SMS, tengiliði, hljóðnema og nálæg tæki."</string> - <string name="summary_glasses_single_device" msgid="3000909894067413398">"Þetta forrit fær aðgang að eftirfarandi heimildum í <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string> + <string name="profile_name_glasses" msgid="3506504967216601277">"tæki"</string> + <string name="summary_glasses" msgid="2872254734959842579">"Þetta forrit fær aðgang að eftirfarandi heimildum í <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="title_app_streaming" msgid="2270331024626446950">"Veita <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> aðgang að þessum upplýsingum úr símanum þínum"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Þjónustur á milli tækja"</string> <string name="helper_summary_app_streaming" msgid="2396773196949578425">"<xliff:g id="APP_NAME">%1$s</xliff:g> biður um heimild fyrir <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> til að streyma forritum á milli tækjanna þinna"</string> @@ -38,10 +37,10 @@ <string name="title_nearby_device_streaming" msgid="7269956847378799794">"Leyfa <strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> að framkvæma þessa aðgerð?"</string> <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<xliff:g id="APP_NAME">%1$s</xliff:g> biður um heimild fyrir <xliff:g id="DEVICE_NAME">%2$s</xliff:g> til að streyma forritum og öðrum kerfiseiginleikum í nálægum tækjum"</string> <string name="profile_name_generic" msgid="6851028682723034988">"tæki"</string> - <string name="summary_generic_single_device" msgid="4181180669689590417">"Þetta forrit mun geta samstillt upplýsingar, t.d. nafn þess sem hringir, á milli símans og <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="summary_generic" msgid="1761976003668044801">"Þetta forrit mun geta samstillt upplýsingar, t.d. nafn þess sem hringir, á milli símans og valins tækis"</string> <string name="consent_yes" msgid="8344487259618762872">"Leyfa"</string> <string name="consent_no" msgid="2640796915611404382">"Ekki leyfa"</string> + <string name="consent_cancel" msgid="5655005528379285841">"Hætta við"</string> <string name="consent_back" msgid="2560683030046918882">"Til baka"</string> <string name="permission_expand" msgid="893185038020887411">"Stækka <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string> <string name="permission_collapse" msgid="3320833884220844084">"Minnka <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string> diff --git a/packages/CompanionDeviceManager/res/values-it/strings.xml b/packages/CompanionDeviceManager/res/values-it/strings.xml index 117d2aa6e6f4..be431b693431 100644 --- a/packages/CompanionDeviceManager/res/values-it/strings.xml +++ b/packages/CompanionDeviceManager/res/values-it/strings.xml @@ -17,15 +17,14 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="4470785958457506021">"Gestione dispositivi associati"</string> - <string name="confirmation_title" msgid="4593465730772390351">"Vuoi consentire all\'app <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> di accedere a <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> + <string name="confirmation_title" msgid="2244241995958340998">"Vuoi consentire all\'app <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> di accedere a <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> <string name="profile_name_watch" msgid="576290739483672360">"orologio"</string> - <string name="chooser_title" msgid="2262294130493605839">"Scegli un <xliff:g id="PROFILE_NAME">%1$s</xliff:g> da gestire con <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> - <string name="summary_watch" msgid="898569637110705523">"Questa app è necessaria per gestire <xliff:g id="DEVICE_NAME">%1$s</xliff:g>. <xliff:g id="APP_NAME">%2$s</xliff:g> potrà sincronizzare informazioni, ad esempio il nome di un chiamante, interagire con le tue notifiche e accedere alle autorizzazioni Telefono, SMS, Contatti, Calendario, Registri chiamate e Dispositivi nelle vicinanze."</string> - <string name="summary_watch_single_device" msgid="3173948915947011333">"Questa app potrà sincronizzare informazioni, ad esempio il nome di un chiamante, e accedere alle seguenti autorizzazioni su <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>:"</string> + <string name="chooser_title_non_profile" msgid="6035023914517087400">"Scegli un dispositivo che sia gestito da <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>"</string> + <string name="chooser_title" msgid="2235819929238267637">"Scegli un <xliff:g id="PROFILE_NAME">%1$s</xliff:g> da configurare"</string> + <string name="summary_watch" msgid="7962014927042971830">"Questa app potrà sincronizzare informazioni, ad esempio il nome di un chiamante, e accedere alle seguenti autorizzazioni su <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="confirmation_title_glasses" msgid="8288346850537727333">"Vuoi consentire all\'app <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> di gestire <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> - <string name="profile_name_glasses" msgid="8488394059007275998">"occhiali"</string> - <string name="summary_glasses_multi_device" msgid="615259525961937348">"Questa app è necessaria per gestire <xliff:g id="DEVICE_NAME">%1$s</xliff:g>. <xliff:g id="APP_NAME">%2$s</xliff:g> potrà interagire con le tue notifiche e accedere alle autorizzazioni Telefono, SMS, Contatti, Microfono e Dispositivi nelle vicinanze."</string> - <string name="summary_glasses_single_device" msgid="3000909894067413398">"Questa app potrà accedere alle seguenti autorizzazioni su <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>:"</string> + <string name="profile_name_glasses" msgid="3506504967216601277">"dispositivo"</string> + <string name="summary_glasses" msgid="2872254734959842579">"Questa app potrà accedere alle seguenti autorizzazioni su <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="title_app_streaming" msgid="2270331024626446950">"Consenti a <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> di accedere a queste informazioni dal tuo telefono"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Servizi cross-device"</string> <string name="helper_summary_app_streaming" msgid="2396773196949578425">"<xliff:g id="APP_NAME">%1$s</xliff:g> richiede per conto del tuo <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> l\'autorizzazione a trasmettere app in streaming tra i dispositivi"</string> @@ -38,10 +37,10 @@ <string name="title_nearby_device_streaming" msgid="7269956847378799794">"Vuoi consentire a <strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> di compiere questa azione?"</string> <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<xliff:g id="APP_NAME">%1$s</xliff:g> richiede per conto di <xliff:g id="DEVICE_NAME">%2$s</xliff:g> l\'autorizzazione a trasmettere in streaming app e altre funzionalità di sistema ai dispositivi nelle vicinanze"</string> <string name="profile_name_generic" msgid="6851028682723034988">"dispositivo"</string> - <string name="summary_generic_single_device" msgid="4181180669689590417">"Questa app potrà sincronizzare informazioni, ad esempio il nome di un chiamante, tra il telefono e <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="summary_generic" msgid="1761976003668044801">"Questa app potrà sincronizzare informazioni, ad esempio il nome di un chiamante, tra il telefono e il dispositivo scelto"</string> <string name="consent_yes" msgid="8344487259618762872">"Consenti"</string> <string name="consent_no" msgid="2640796915611404382">"Non consentire"</string> + <string name="consent_cancel" msgid="5655005528379285841">"Annulla"</string> <string name="consent_back" msgid="2560683030046918882">"Indietro"</string> <string name="permission_expand" msgid="893185038020887411">"Espandi <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string> <string name="permission_collapse" msgid="3320833884220844084">"Comprimi <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string> diff --git a/packages/CompanionDeviceManager/res/values-iw/strings.xml b/packages/CompanionDeviceManager/res/values-iw/strings.xml index 573b8a742c5c..89826606a07a 100644 --- a/packages/CompanionDeviceManager/res/values-iw/strings.xml +++ b/packages/CompanionDeviceManager/res/values-iw/strings.xml @@ -17,15 +17,14 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="4470785958457506021">"ניהול מכשיר מותאם"</string> - <string name="confirmation_title" msgid="4593465730772390351">"לאשר לאפליקציה <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong&g; לגשת אל <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> + <string name="confirmation_title" msgid="2244241995958340998">"לאפשר לאפליקציה <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> לגשת אל <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> <string name="profile_name_watch" msgid="576290739483672360">"שעון"</string> - <string name="chooser_title" msgid="2262294130493605839">"בחירת <xliff:g id="PROFILE_NAME">%1$s</xliff:g> לניהול באמצעות <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> - <string name="summary_watch" msgid="898569637110705523">"האפליקציה הזו נחוצה כדי לנהל את <xliff:g id="DEVICE_NAME">%1$s</xliff:g>. האפליקציה <xliff:g id="APP_NAME">%2$s</xliff:g> תוכל לסנכרן מידע, כמו השם של מישהו שמתקשר, לבצע פעולות בהתראות ולקבל הרשאות גישה לטלפון, ל-SMS, לאנשי הקשר, ליומן, ליומני השיחות ולמכשירים בקרבת מקום."</string> - <string name="summary_watch_single_device" msgid="3173948915947011333">"האפליקציה הזו תוכל לסנכרן מידע, כמו השם של מישהו שמתקשר, ולגשת להרשאות האלה ב<xliff:g id="DEVICE_TYPE">%1$s</xliff:g> שלך"</string> + <string name="chooser_title_non_profile" msgid="6035023914517087400">"בחירה של מכשיר לניהול באמצעות <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>"</string> + <string name="chooser_title" msgid="2235819929238267637">"בחירת <xliff:g id="PROFILE_NAME">%1$s</xliff:g> להגדרה"</string> + <string name="summary_watch" msgid="7962014927042971830">"האפליקציה הזו תוכל לסנכרן מידע, כמו השם של מישהו שמתקשר, ולגשת להרשאות האלה ב<xliff:g id="DEVICE_NAME">%1$s</xliff:g> שלך"</string> <string name="confirmation_title_glasses" msgid="8288346850537727333">"מתן הרשאה לאפליקציה <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong&g; לנהל את <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string> - <string name="profile_name_glasses" msgid="8488394059007275998">"משקפיים"</string> - <string name="summary_glasses_multi_device" msgid="615259525961937348">"האפליקציה הזו נחוצה כדי לנהל את \'<xliff:g id="DEVICE_NAME">%1$s</xliff:g>\'. האפליקציה <xliff:g id="APP_NAME">%2$s</xliff:g> תוכל לבצע פעולות בהתראות ותקבל הרשאות גישה לטלפון, ל-SMS, לאנשי הקשר, למיקרופון ולמכשירים בקרבת מקום."</string> - <string name="summary_glasses_single_device" msgid="3000909894067413398">"האפליקציה הזו תוכל לגשת להרשאות האלה ב<xliff:g id="DEVICE_TYPE">%1$s</xliff:g> שלך"</string> + <string name="profile_name_glasses" msgid="3506504967216601277">"מכשיר"</string> + <string name="summary_glasses" msgid="2872254734959842579">"האפליקציה הזו תוכל לגשת להרשאות האלה ב<xliff:g id="DEVICE_NAME">%1$s</xliff:g> שלך"</string> <string name="title_app_streaming" msgid="2270331024626446950">"מתן אישור לאפליקציה <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> לגשת למידע הזה מהטלפון שלך"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"שירותים למספר מכשירים"</string> <string name="helper_summary_app_streaming" msgid="2396773196949578425">"האפליקציה <xliff:g id="APP_NAME">%1$s</xliff:g> מבקשת הרשאה עבור המכשיר <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> כדי לשדר אפליקציות בין המכשירים שלך"</string> @@ -38,10 +37,10 @@ <string name="title_nearby_device_streaming" msgid="7269956847378799794">"לתת הרשאה למכשיר <strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> לבצע את הפעולה הזו?"</string> <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"האפליקציה <xliff:g id="APP_NAME">%1$s</xliff:g> מבקשת הרשאה עבור <xliff:g id="DEVICE_NAME">%2$s</xliff:g> כדי להעביר אפליקציות ותכונות מערכת אחרות בסטרימינג למכשירים בקרבת מקום"</string> <string name="profile_name_generic" msgid="6851028682723034988">"מכשיר"</string> - <string name="summary_generic_single_device" msgid="4181180669689590417">"האפליקציה הזו תוכל לסנכרן מידע, כמו השם של מישהו שמתקשר, מהטלפון שלך למכשיר <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="summary_generic" msgid="1761976003668044801">"האפליקציה הזו תוכל לסנכרן מידע, כמו השם של מישהו שמתקשר, מהטלפון שלך למכשיר שבחרת"</string> <string name="consent_yes" msgid="8344487259618762872">"יש אישור"</string> <string name="consent_no" msgid="2640796915611404382">"אין אישור"</string> + <string name="consent_cancel" msgid="5655005528379285841">"ביטול"</string> <string name="consent_back" msgid="2560683030046918882">"חזרה"</string> <string name="permission_expand" msgid="893185038020887411">"הרחבה של <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string> <string name="permission_collapse" msgid="3320833884220844084">"כיווץ של <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string> diff --git a/packages/CompanionDeviceManager/res/values-ja/strings.xml b/packages/CompanionDeviceManager/res/values-ja/strings.xml index 0439d5f185a8..186944adf7fb 100644 --- a/packages/CompanionDeviceManager/res/values-ja/strings.xml +++ b/packages/CompanionDeviceManager/res/values-ja/strings.xml @@ -17,15 +17,14 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="4470785958457506021">"コンパニオン デバイス マネージャー"</string> - <string name="confirmation_title" msgid="4593465730772390351">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> に <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> へのアクセスを許可しますか?"</string> + <string name="confirmation_title" msgid="2244241995958340998">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> アプリに <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> へのアクセスを許可しますか?"</string> <string name="profile_name_watch" msgid="576290739483672360">"ウォッチ"</string> - <string name="chooser_title" msgid="2262294130493605839">"<strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> の管理対象となる<xliff:g id="PROFILE_NAME">%1$s</xliff:g>の選択"</string> - <string name="summary_watch" msgid="898569637110705523">"このアプリは<xliff:g id="DEVICE_NAME">%1$s</xliff:g>の管理に必要です。<xliff:g id="APP_NAME">%2$s</xliff:g> は通話相手の名前などの情報を同期したり、デバイスの通知を使用したり、電話、SMS、連絡先、カレンダー、通話履歴、付近のデバイスの権限にアクセスしたりできるようになります。"</string> - <string name="summary_watch_single_device" msgid="3173948915947011333">"このアプリは、通話相手の名前などの情報を同期したり、<xliff:g id="DEVICE_TYPE">%1$s</xliff:g>の以下の権限にアクセスしたりできるようになります"</string> + <string name="chooser_title_non_profile" msgid="6035023914517087400">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> の管理対象となるデバイスの選択"</string> + <string name="chooser_title" msgid="2235819929238267637">"設定する<xliff:g id="PROFILE_NAME">%1$s</xliff:g>の選択"</string> + <string name="summary_watch" msgid="7962014927042971830">"このアプリは、通話相手の名前などの情報を同期したり、<xliff:g id="DEVICE_NAME">%1$s</xliff:g>の以下の権限にアクセスしたりできるようになります"</string> <string name="confirmation_title_glasses" msgid="8288346850537727333">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> に <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> の管理を許可しますか?"</string> - <string name="profile_name_glasses" msgid="8488394059007275998">"眼鏡"</string> - <string name="summary_glasses_multi_device" msgid="615259525961937348">"このアプリは <xliff:g id="DEVICE_NAME">%1$s</xliff:g> の管理に必要です。<xliff:g id="APP_NAME">%2$s</xliff:g> はデバイスの通知を使用したり、電話、SMS、連絡先、マイク、付近のデバイスの権限にアクセスしたりできるようになります。"</string> - <string name="summary_glasses_single_device" msgid="3000909894067413398">"このアプリは、<xliff:g id="DEVICE_TYPE">%1$s</xliff:g>の以下の権限にアクセスできるようになります"</string> + <string name="profile_name_glasses" msgid="3506504967216601277">"デバイス"</string> + <string name="summary_glasses" msgid="2872254734959842579">"このアプリは、<xliff:g id="DEVICE_NAME">%1$s</xliff:g>の以下の権限にアクセスできるようになります"</string> <string name="title_app_streaming" msgid="2270331024626446950">"スマートフォンのこの情報へのアクセスを <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> に許可"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"クロスデバイス サービス"</string> <string name="helper_summary_app_streaming" msgid="2396773196949578425">"<xliff:g id="APP_NAME">%1$s</xliff:g> が <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> に代わってデバイス間でアプリをストリーミングする権限をリクエストしています"</string> @@ -38,10 +37,10 @@ <string name="title_nearby_device_streaming" msgid="7269956847378799794">"<strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> にこの操作の実行を許可しますか?"</string> <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<xliff:g id="APP_NAME">%1$s</xliff:g> が <xliff:g id="DEVICE_NAME">%2$s</xliff:g> に代わって、アプリやその他のシステム機能を付近のデバイスにストリーミングする権限をリクエストしています"</string> <string name="profile_name_generic" msgid="6851028682723034988">"デバイス"</string> - <string name="summary_generic_single_device" msgid="4181180669689590417">"このアプリは、あなたのスマートフォンと <xliff:g id="DEVICE_NAME">%1$s</xliff:g> との間で、通話相手の名前などの情報を同期できるようになります"</string> <string name="summary_generic" msgid="1761976003668044801">"このアプリは、あなたのスマートフォンと選択したデバイスとの間で、通話相手の名前などの情報を同期できるようになります"</string> <string name="consent_yes" msgid="8344487259618762872">"許可"</string> <string name="consent_no" msgid="2640796915611404382">"許可しない"</string> + <string name="consent_cancel" msgid="5655005528379285841">"キャンセル"</string> <string name="consent_back" msgid="2560683030046918882">"戻る"</string> <string name="permission_expand" msgid="893185038020887411">"<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>を開く"</string> <string name="permission_collapse" msgid="3320833884220844084">"<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>を閉じる"</string> diff --git a/packages/CompanionDeviceManager/res/values-ka/strings.xml b/packages/CompanionDeviceManager/res/values-ka/strings.xml index e62afad7fb0a..a7c6042ba4a8 100644 --- a/packages/CompanionDeviceManager/res/values-ka/strings.xml +++ b/packages/CompanionDeviceManager/res/values-ka/strings.xml @@ -17,15 +17,14 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="4470785958457506021">"კომპანიონი მოწყობილობების მენეჯერი"</string> - <string name="confirmation_title" msgid="4593465730772390351">"მიანიჭებთ <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> აპს <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> მოწყობილობაზე წვდომას?"</string> + <string name="confirmation_title" msgid="2244241995958340998">"ნებას რთავთ <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> აპს, წვდომა ჰქონდეს <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> მოწყობილობაზე?"</string> <string name="profile_name_watch" msgid="576290739483672360">"საათი"</string> - <string name="chooser_title" msgid="2262294130493605839">"აირჩიეთ <xliff:g id="PROFILE_NAME">%1$s</xliff:g>, რომელიც უნდა მართოს <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>-მა"</string> - <string name="summary_watch" msgid="898569637110705523">"ეს აპი საჭიროა თქვენი <xliff:g id="DEVICE_NAME">%1$s</xliff:g>-ს სამართავად. <xliff:g id="APP_NAME">%2$s</xliff:g>-ს ექნება ისეთი ინფორმაციის სინქრონიზაციის უფლება, როგორიც იმ ადამიანის სახელია, რომელიც გირეკავთ; ასევე, თქვენს შეტყობინებებთან ინტერაქციისა და თქვენს ტელეფონზე, SMS-ებზე, კონტაქტებზე, კალენდარზე, ზარების ჟურნალებსა და ახლომახლო მოწყობილობების ნებართვებზე წვდომის უფლება."</string> - <string name="summary_watch_single_device" msgid="3173948915947011333">"ეს აპი შეძლებს ინფორმაციის სინქრონიზებას (მაგალითად, იმ ადამიანის სახელი, რომელიც გირეკავთ) და ამ წვდომებზე უფლების მოპოვებას თქვენს <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>-ში"</string> + <string name="chooser_title_non_profile" msgid="6035023914517087400">"აირჩიეთ მოწყობილობა, რომელიც უნდა მართოს <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> აპმა"</string> + <string name="chooser_title" msgid="2235819929238267637">"აირჩიეთ <xliff:g id="PROFILE_NAME">%1$s</xliff:g> დასაყენებლად"</string> + <string name="summary_watch" msgid="7962014927042971830">"ეს აპი შეძლებს, დაასინქრონოს ინფორმაცია, მაგალითად, შემომავალი ზარის ავტორის სახელი და წვდომა იქონიოს ამ ნებართვებზე თქვენს <xliff:g id="DEVICE_NAME">%1$s</xliff:g>-ში"</string> <string name="confirmation_title_glasses" msgid="8288346850537727333">"ნება დართეთ <strong><xliff:g id="APP_NAME">%1$s</xliff:g>-ს</strong> მართოს <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> - <string name="profile_name_glasses" msgid="8488394059007275998">"სათვალე"</string> - <string name="summary_glasses_multi_device" msgid="615259525961937348">"ეს აპი საჭიროა თქვენი <xliff:g id="DEVICE_NAME">%1$s</xliff:g>-ის სამართავად. <xliff:g id="APP_NAME">%2$s</xliff:g> შეძლებს თქვენს შეტყობინებებთან ინტერაქციას და თქვენს ტელეფონზე, SMS-ებზე, კონტაქტებზე, მიკროფონსა და ახლომახლო მოწყობილობების ნებართვებზე წვდომას."</string> - <string name="summary_glasses_single_device" msgid="3000909894067413398">"ეს აპი შეძლებს ამ ნებართვებზე წვდომას თქვენს <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>-ში"</string> + <string name="profile_name_glasses" msgid="3506504967216601277">"მოწყობილობა"</string> + <string name="summary_glasses" msgid="2872254734959842579">"ეს აპი შეძლებს ამ ნებართვებზე წვდომას თქვენს <xliff:g id="DEVICE_NAME">%1$s</xliff:g>-ში"</string> <string name="title_app_streaming" msgid="2270331024626446950">"ნება დართეთ, რომ <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> აპს ჰქონდეს ამ ინფორმაციაზე წვდომა თქვენი ტელეფონიდან"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"მოწყობილობათშორისი სერვისები"</string> <string name="helper_summary_app_streaming" msgid="2396773196949578425">"<xliff:g id="APP_NAME">%1$s</xliff:g> ითხოვს უფლებას თქვენი <xliff:g id="DISPLAY_NAME">%2$s</xliff:g>-ის სახელით, რომ მოწყობილობებს შორის სტრიმინგი შეძლოს"</string> @@ -38,10 +37,10 @@ <string name="title_nearby_device_streaming" msgid="7269956847378799794">"გსურთ ნება მისცეთ <strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g>-ს</strong> ამ მოქმედების შესასრულებლად?"</string> <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<xliff:g id="APP_NAME">%1$s</xliff:g> ითხოვს თქვენი <xliff:g id="DEVICE_NAME">%2$s</xliff:g>-ის სახელით აპების და სისტემის სხვა ფუნქციების ახლომახლო მოწყობილობებზე სტრიმინგის ნებართვას"</string> <string name="profile_name_generic" msgid="6851028682723034988">"მოწყობილობა"</string> - <string name="summary_generic_single_device" msgid="4181180669689590417">"ეს აპი შეძლებს ინფორმაციის სინქრონიზებას თქვენს ტელეფონსა და თქვენ მიერ არჩეულ <xliff:g id="DEVICE_NAME">%1$s</xliff:g>-ს შორის, მაგალითად, იმ ადამიანის სახელის, რომელიც გირეკავთ"</string> <string name="summary_generic" msgid="1761976003668044801">"ეს აპი შეძლებს ინფორმაციის სინქრონიზებას თქვენს ტელეფონსა და თქვენ მიერ არჩეულ მოწყობილობას შორის, მაგალითად, იმ ადამიანის სახელის, რომელიც გირეკავთ"</string> <string name="consent_yes" msgid="8344487259618762872">"დაშვება"</string> <string name="consent_no" msgid="2640796915611404382">"არ დაიშვას"</string> + <string name="consent_cancel" msgid="5655005528379285841">"გაუქმება"</string> <string name="consent_back" msgid="2560683030046918882">"უკან"</string> <string name="permission_expand" msgid="893185038020887411">"<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>-ის გაფართოება"</string> <string name="permission_collapse" msgid="3320833884220844084">"<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>-ის ჩაკეცვა"</string> diff --git a/packages/CompanionDeviceManager/res/values-kk/strings.xml b/packages/CompanionDeviceManager/res/values-kk/strings.xml index 92951cb7fccc..5c6d24a0fc40 100644 --- a/packages/CompanionDeviceManager/res/values-kk/strings.xml +++ b/packages/CompanionDeviceManager/res/values-kk/strings.xml @@ -17,15 +17,14 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string> - <string name="confirmation_title" msgid="4593465730772390351">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> қолданбасына <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> құрылғысын пайдалануға рұқсат беру керек пе?"</string> + <string name="confirmation_title" msgid="2244241995958340998">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> қолданбасына <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>? құрылғысына кіруге рұқсат беріңіз"</string> <string name="profile_name_watch" msgid="576290739483672360">"сағат"</string> - <string name="chooser_title" msgid="2262294130493605839">"<strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> арқылы басқарылатын <xliff:g id="PROFILE_NAME">%1$s</xliff:g> құрылғысын таңдаңыз"</string> - <string name="summary_watch" msgid="898569637110705523">"Бұл қолданба <xliff:g id="DEVICE_NAME">%1$s</xliff:g> құрылғысын басқару үшін қажет. <xliff:g id="APP_NAME">%2$s</xliff:g> қолданбасы қоңырау шалушының аты сияқты деректі синхрондау, хабарландыруларды оқу және телефон, SMS, контактілер, күнтізбе, қоңырау журналдары мен маңайдағы құрылғылар рұқсаттарын пайдалана алады."</string> - <string name="summary_watch_single_device" msgid="3173948915947011333">"Бұл қолданба қоңырау шалушының аты сияқты деректі синхрондай алады және <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> құрылғысындағы мына рұқсаттарды пайдалана алады."</string> + <string name="chooser_title_non_profile" msgid="6035023914517087400">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> арқылы басқарылатын құрылғыны таңдаңыз"</string> + <string name="chooser_title" msgid="2235819929238267637">"Реттеу үшін <xliff:g id="PROFILE_NAME">%1$s</xliff:g> таңдаңыз"</string> + <string name="summary_watch" msgid="7962014927042971830">"Бұл қолданба қоңырау шалушының аты сияқты деректі синхрондай алады және <xliff:g id="DEVICE_NAME">%1$s</xliff:g> құрылғысындағы мына рұқсаттарды пайдалана алады."</string> <string name="confirmation_title_glasses" msgid="8288346850537727333">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> қолданбасына <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> құрылғысын басқаруға рұқсат беру керек пе?"</string> - <string name="profile_name_glasses" msgid="8488394059007275998">"көзілдірік"</string> - <string name="summary_glasses_multi_device" msgid="615259525961937348">"Бұл қолданба <xliff:g id="DEVICE_NAME">%1$s</xliff:g> құрылғысын басқару үшін қажет. <xliff:g id="APP_NAME">%2$s</xliff:g> қолданбасына хабарландыруларды оқуға, телефонды, хабарларды, контактілерді, микрофон мен маңайдағы құрылғыларды пайдалануға рұқсат беріледі."</string> - <string name="summary_glasses_single_device" msgid="3000909894067413398">"Бұл қолданба <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> құрылғысында осы рұқсаттарды пайдалана алады."</string> + <string name="profile_name_glasses" msgid="3506504967216601277">"құрылғы"</string> + <string name="summary_glasses" msgid="2872254734959842579">"Бұл қолданба <xliff:g id="DEVICE_NAME">%1$s</xliff:g> құрылғысында осы рұқсаттарды пайдалана алады."</string> <string name="title_app_streaming" msgid="2270331024626446950">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> қолданбасына телефоныңыздағы осы ақпаратты пайдалануға рұқсат беріңіз."</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Аралық құрылғы қызметтері"</string> <string name="helper_summary_app_streaming" msgid="2396773196949578425">"<xliff:g id="APP_NAME">%1$s</xliff:g> қолданбасы <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> атынан құрылғылар арасында қолданбалар трансляциялау үшін рұқсат сұрайды."</string> @@ -38,10 +37,10 @@ <string name="title_nearby_device_streaming" msgid="7269956847378799794">"<strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> құрылғысына бұл әрекетті орындауға рұқсат беру керек пе?"</string> <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<xliff:g id="APP_NAME">%1$s</xliff:g> қолданбасы <xliff:g id="DEVICE_NAME">%2$s</xliff:g> атынан қолданбалар мен басқа да жүйе функцияларын маңайдағы құрылғыларға трансляциялау рұқсатын сұрап тұр."</string> <string name="profile_name_generic" msgid="6851028682723034988">"құрылғы"</string> - <string name="summary_generic_single_device" msgid="4181180669689590417">"Бұл қолданба телефон мен <xliff:g id="DEVICE_NAME">%1$s</xliff:g> құрылғысы арасында деректі (мысалы, қоңырау шалушының атын) синхрондай алады."</string> <string name="summary_generic" msgid="1761976003668044801">"Бұл қолданба телефон мен таңдалған құрылғы арасында деректі (мысалы, қоңырау шалушының атын) синхрондай алады."</string> <string name="consent_yes" msgid="8344487259618762872">"Рұқсат беру"</string> <string name="consent_no" msgid="2640796915611404382">"Рұқсат бермеу"</string> + <string name="consent_cancel" msgid="5655005528379285841">"Бас тарту"</string> <string name="consent_back" msgid="2560683030046918882">"Артқа"</string> <string name="permission_expand" msgid="893185038020887411">"\"<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>\" панелін жаю"</string> <string name="permission_collapse" msgid="3320833884220844084">"\"<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>\" панелін жию"</string> diff --git a/packages/CompanionDeviceManager/res/values-km/strings.xml b/packages/CompanionDeviceManager/res/values-km/strings.xml index 18bd88c31500..c734cb6295fb 100644 --- a/packages/CompanionDeviceManager/res/values-km/strings.xml +++ b/packages/CompanionDeviceManager/res/values-km/strings.xml @@ -17,15 +17,14 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="4470785958457506021">"កម្មវិធីគ្រប់គ្រងឧបករណ៍ដៃគូ"</string> - <string name="confirmation_title" msgid="4593465730772390351">"អនុញ្ញាតឱ្យ <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ចូលប្រើ <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> ឬ?"</string> + <string name="confirmation_title" msgid="2244241995958340998">"អនុញ្ញាតឱ្យកម្មវិធី <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ចូលប្រើ <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> ឬ?"</string> <string name="profile_name_watch" msgid="576290739483672360">"នាឡិកា"</string> - <string name="chooser_title" msgid="2262294130493605839">"ជ្រើសរើស <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ដើម្បីឱ្យស្ថិតក្រោមការគ្រប់គ្រងរបស់ <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> - <string name="summary_watch" msgid="898569637110705523">"ត្រូវការកម្មវិធីនេះ ដើម្បីគ្រប់គ្រង <xliff:g id="DEVICE_NAME">%1$s</xliff:g> របស់អ្នក។ <xliff:g id="APP_NAME">%2$s</xliff:g> នឹងត្រូវបានអនុញ្ញាតឱ្យធ្វើសមកាលកម្មព័ត៌មាន ដូចជាឈ្មោះមនុស្សដែលហៅទូរសព្ទជាដើម ធ្វើអន្តរកម្មជាមួយការជូនដំណឹងរបស់អ្នក និងចូលប្រើការអនុញ្ញាតទូរសព្ទ, SMS, ទំនាក់ទំនង, ប្រតិទិន, កំណត់ហេតុហៅទូរសព្ទ និងឧបករណ៍នៅជិតរបស់អ្នក។"</string> - <string name="summary_watch_single_device" msgid="3173948915947011333">"កម្មវិធីនេះនឹងត្រូវបានអនុញ្ញាតឱ្យធ្វើសមកាលកម្មព័ត៌មាន ដូចជាឈ្មោះមនុស្សដែលហៅទូរសព្ទជាដើម និងចូលប្រើការអនុញ្ញាតទាំងនេះនៅលើ <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> របស់អ្នក"</string> + <string name="chooser_title_non_profile" msgid="6035023914517087400">"ជ្រើសរើសឧបករណ៍ ដើម្បីដាក់ក្រោមការគ្រប់គ្រងរបស់ <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>"</string> + <string name="chooser_title" msgid="2235819929238267637">"ជ្រើសរើស <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ដើម្បីរៀបចំ"</string> + <string name="summary_watch" msgid="7962014927042971830">"កម្មវិធីនេះនឹងត្រូវបានអនុញ្ញាតឱ្យធ្វើសមកាលកម្មព័ត៌មាន ដូចជាឈ្មោះមនុស្សដែលហៅទូរសព្ទជាដើម និងចូលប្រើប្រាស់ការអនុញ្ញាតទាំងនេះនៅលើ <xliff:g id="DEVICE_NAME">%1$s</xliff:g> របស់អ្នក"</string> <string name="confirmation_title_glasses" msgid="8288346850537727333">"អនុញ្ញាតឱ្យ <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> គ្រប់គ្រង <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> ឬ?"</string> - <string name="profile_name_glasses" msgid="8488394059007275998">"វ៉ែនតា"</string> - <string name="summary_glasses_multi_device" msgid="615259525961937348">"ត្រូវការកម្មវិធីនេះ ដើម្បីគ្រប់គ្រង <xliff:g id="DEVICE_NAME">%1$s</xliff:g>។ <xliff:g id="APP_NAME">%2$s</xliff:g> នឹងត្រូវបានអនុញ្ញាតឱ្យធ្វើអន្តរកម្មជាមួយការជូនដំណឹងរបស់អ្នក និងចូលប្រើការអនុញ្ញាតរបស់ទូរសព្ទ, SMS, ទំនាក់ទំនង, មីក្រូហ្វូន និងឧបករណ៍នៅជិតរបស់អ្នក។"</string> - <string name="summary_glasses_single_device" msgid="3000909894067413398">"កម្មវិធីនេះនឹងត្រូវបានអនុញ្ញាតឱ្យចូលប្រើការអនុញ្ញាតទាំងនេះនៅលើ <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> របស់អ្នក"</string> + <string name="profile_name_glasses" msgid="3506504967216601277">"ឧបករណ៍"</string> + <string name="summary_glasses" msgid="2872254734959842579">"កម្មវិធីនេះនឹងត្រូវបានអនុញ្ញាតឱ្យចូលប្រើប្រាស់ការអនុញ្ញាតទាំងនេះនៅលើ <xliff:g id="DEVICE_NAME">%1$s</xliff:g> របស់អ្នក"</string> <string name="title_app_streaming" msgid="2270331024626446950">"អនុញ្ញាតឱ្យ <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ចូលប្រើព័ត៌មាននេះពីទូរសព្ទរបស់អ្នក"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"សេវាកម្មឆ្លងកាត់ឧបករណ៍"</string> <string name="helper_summary_app_streaming" msgid="2396773196949578425">"<xliff:g id="APP_NAME">%1$s</xliff:g> កំពុងស្នើសុំការអនុញ្ញាតជំនួសឱ្យ <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> របស់អ្នក ដើម្បីបញ្ចាំងកម្មវិធីរវាងឧបករណ៍របស់អ្នក"</string> @@ -38,10 +37,10 @@ <string name="title_nearby_device_streaming" msgid="7269956847378799794">"អនុញ្ញាតឱ្យ <strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> ធ្វើសកម្មភាពនេះឬ?"</string> <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<xliff:g id="APP_NAME">%1$s</xliff:g> កំពុងស្នើសុំការអនុញ្ញាតជំនួសឱ្យ <xliff:g id="DEVICE_NAME">%2$s</xliff:g> របស់អ្នក ដើម្បីចាក់ផ្សាយកម្មវិធី និងមុខងារប្រព័ន្ធផ្សេងទៀតទៅកាន់ឧបករណ៍នៅជិត"</string> <string name="profile_name_generic" msgid="6851028682723034988">"ឧបករណ៍"</string> - <string name="summary_generic_single_device" msgid="4181180669689590417">"កម្មវិធីនេះនឹងអាចធ្វើសមកាលកម្មព័ត៌មាន ដូចជាឈ្មោះមនុស្សដែលហៅទូរសព្ទជាដើម រវាង <xliff:g id="DEVICE_NAME">%1$s</xliff:g> និងទូរសព្ទរបស់អ្នក"</string> <string name="summary_generic" msgid="1761976003668044801">"កម្មវិធីនេះនឹងអាចធ្វើសមកាលកម្មព័ត៌មាន ដូចជាឈ្មោះមនុស្សដែលហៅទូរសព្ទជាដើម រវាងឧបករណ៍ដែលបានជ្រើសរើស និងទូរសព្ទរបស់អ្នក"</string> <string name="consent_yes" msgid="8344487259618762872">"អនុញ្ញាត"</string> <string name="consent_no" msgid="2640796915611404382">"មិនអនុញ្ញាត"</string> + <string name="consent_cancel" msgid="5655005528379285841">"បោះបង់"</string> <string name="consent_back" msgid="2560683030046918882">"ថយក្រោយ"</string> <string name="permission_expand" msgid="893185038020887411">"ពង្រីក <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string> <string name="permission_collapse" msgid="3320833884220844084">"បង្រួម <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string> diff --git a/packages/CompanionDeviceManager/res/values-kn/strings.xml b/packages/CompanionDeviceManager/res/values-kn/strings.xml index 1080b6367196..5f04cd3d28bf 100644 --- a/packages/CompanionDeviceManager/res/values-kn/strings.xml +++ b/packages/CompanionDeviceManager/res/values-kn/strings.xml @@ -17,15 +17,14 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="4470785958457506021">"ಕಂಪ್ಯಾನಿಯನ್ ಸಾಧನ ನಿರ್ವಾಹಕರು"</string> - <string name="confirmation_title" msgid="4593465730772390351">"<strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> ಅನ್ನು ಪ್ರವೇಶಿಸಲು <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ಗೆ ಅನುಮತಿಸಬೇಕೇ?"</string> + <string name="confirmation_title" msgid="2244241995958340998">"<strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> ಅನ್ನು ಆ್ಯಕ್ಸೆಸ್ ಮಾಡಲು <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ಆ್ಯಪ್ಗೆ ಅನುಮತಿಸಬೇಕೆ?"</string> <string name="profile_name_watch" msgid="576290739483672360">"ವೀಕ್ಷಿಸಿ"</string> - <string name="chooser_title" msgid="2262294130493605839">"<strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> ಮೂಲಕ ನಿರ್ವಹಿಸಬೇಕಾದ <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ಅನ್ನು ಆಯ್ಕೆಮಾಡಿ"</string> - <string name="summary_watch" msgid="898569637110705523">"ನಿಮ್ಮ <xliff:g id="DEVICE_NAME">%1$s</xliff:g> ಅನ್ನು ನಿರ್ವಹಿಸಲು ಈ ಆ್ಯಪ್ನ ಅಗತ್ಯವಿದೆ. ಕರೆ ಮಾಡುವವರ ಹೆಸರು, ನಿಮ್ಮ ಅಧಿಸೂಚನೆಗಳೊಂದಿಗೆ ಸಂವಹನ ನಡೆಸಲು ಮತ್ತು ಫೋನ್, SMS, ಸಂಪರ್ಕಗಳು, ಕ್ಯಾಲೆಂಡರ್, ಕರೆಯ ಲಾಗ್ಗಳು ಮತ್ತು ಸಮೀಪದಲ್ಲಿರುವ ಸಾಧನಗಳ ದೃಢೀಕರಣಗಳಂತಹ ಮಾಹಿತಿಯನ್ನು ಸಿಂಕ್ ಮಾಡಲು <xliff:g id="APP_NAME">%2$s</xliff:g> ಗೆ ಸಾಧ್ಯವಾಗುತ್ತದೆ."</string> - <string name="summary_watch_single_device" msgid="3173948915947011333">"ಕರೆ ಮಾಡುವವರ ಹೆಸರಿನಂತಹ ಮಾಹಿತಿಯನ್ನು ಸಿಂಕ್ ಮಾಡಲು ಮತ್ತು ಈ ಅನುಮತಿಗಳನ್ನು ನಿಮ್ಮ <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> ನಲ್ಲಿ ಆ್ಯಕ್ಸೆಸ್ ಮಾಡಲು ಈ ಆ್ಯಪ್ಗೆ ಅನುಮತಿಸಲಾಗುತ್ತದೆ"</string> + <string name="chooser_title_non_profile" msgid="6035023914517087400">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ಮೂಲಕ ನಿರ್ವಹಿಸಬೇಕಾದ ಸಾಧನವನ್ನು ಆಯ್ಕೆಮಾಡಿ"</string> + <string name="chooser_title" msgid="2235819929238267637">"ಸೆಟಪ್ ಮಾಡಲು <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ಅನ್ನು ಆರಿಸಿ"</string> + <string name="summary_watch" msgid="7962014927042971830">"ಕರೆ ಮಾಡುವವರ ಹೆಸರಿನಂತಹ ಮಾಹಿತಿಯನ್ನು ಸಿಂಕ್ ಮಾಡಲು ಮತ್ತು ಈ ಅನುಮತಿಗಳನ್ನು ನಿಮ್ಮ <xliff:g id="DEVICE_NAME">%1$s</xliff:g> ನಲ್ಲಿ ಆ್ಯಕ್ಸೆಸ್ ಮಾಡಲು ಈ ಆ್ಯಪ್ಗೆ ಅನುಮತಿಸಲಾಗುತ್ತದೆ"</string> <string name="confirmation_title_glasses" msgid="8288346850537727333">"<strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>? ನಿರ್ವಹಿಸಲು <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ಗೆ ಅನುಮತಿಸಬೇಕೇ?"</string> - <string name="profile_name_glasses" msgid="8488394059007275998">"ಗ್ಲಾಸ್ಗಳು"</string> - <string name="summary_glasses_multi_device" msgid="615259525961937348">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> ಅನ್ನು ನಿರ್ವಹಿಸಲು ಈ ಆ್ಯಪ್ನ ಅಗತ್ಯವಿದೆ. <xliff:g id="APP_NAME">%2$s</xliff:g> ನಿಮ್ಮ ಅಧಿಸೂಚನೆಗಳೊಂದಿಗೆ ಸಂವಹನ ನಡೆಸಲು ಮತ್ತು ನಿಮ್ಮ ಫೋನ್, SMS, ಸಂಪರ್ಕಗಳು, ಮೈಕ್ರೊಫೋನ್ ಮತ್ತು ಸಮೀಪದಲ್ಲಿರುವ ಸಾಧನಗಳ ಅನುಮತಿಗಳನ್ನು ಆ್ಯಕ್ಸೆಸ್ ಮಾಡಲು ಅನುಮತಿಸಲಾಗುತ್ತದೆ."</string> - <string name="summary_glasses_single_device" msgid="3000909894067413398">"ನಿಮ್ಮ <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> ನಲ್ಲಿ ಈ ಅನುಮತಿಗಳನ್ನು ಆ್ಯಕ್ಸೆಸ್ ಮಾಡಲು ಈ ಆ್ಯಪ್ಗೆ ಅನುಮತಿಸಲಾಗುತ್ತದೆ"</string> + <string name="profile_name_glasses" msgid="3506504967216601277">"ಸಾಧನ"</string> + <string name="summary_glasses" msgid="2872254734959842579">"ನಿಮ್ಮ <xliff:g id="DEVICE_NAME">%1$s</xliff:g> ನಲ್ಲಿ ಈ ಅನುಮತಿಗಳನ್ನು ಆ್ಯಕ್ಸೆಸ್ ಮಾಡಲು ಈ ಆ್ಯಪ್ಗೆ ಅನುಮತಿಸಲಾಗುತ್ತದೆ"</string> <string name="title_app_streaming" msgid="2270331024626446950">"ನಿಮ್ಮ ಫೋನ್ ಮೂಲಕ ಈ ಮಾಹಿತಿಯನ್ನು ಆ್ಯಕ್ಸೆಸ್ ಮಾಡಲು <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ಗೆ ಅನುಮತಿಸಿ"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"ಕ್ರಾಸ್-ಡಿವೈಸ್ ಸೇವೆಗಳು"</string> <string name="helper_summary_app_streaming" msgid="2396773196949578425">"ನಿಮ್ಮ ಸಾಧನಗಳ ನಡುವೆ ಆ್ಯಪ್ಗಳನ್ನು ಸ್ಟ್ರೀಮ್ ಮಾಡಲು ನಿಮ್ಮ <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> ನ ಪರವಾಗಿ <xliff:g id="APP_NAME">%1$s</xliff:g> ಅನುಮತಿಯನ್ನು ವಿನಂತಿಸಿಕೊಳ್ಳುತ್ತಿದೆ"</string> @@ -38,10 +37,10 @@ <string name="title_nearby_device_streaming" msgid="7269956847378799794">"ಈ ಆ್ಯಕ್ಷನ್ ಅನ್ನು ತೆಗೆದುಕೊಳ್ಳಲು <strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> ಅನುಮತಿಸಬೇಕೇ?"</string> <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"ಸಮೀಪದಲ್ಲಿರುವ ಸಾಧನಗಳಿಗೆ ಆ್ಯಪ್ಗಳು ಮತ್ತು ಇತರ ಸಿಸ್ಟಂ ಫೀಚರ್ಗಳನ್ನು ಸ್ಟ್ರೀಮ್ ಮಾಡಲು ನಿಮ್ಮ <xliff:g id="DEVICE_NAME">%2$s</xliff:g> ರ ಪರವಾಗಿ <xliff:g id="APP_NAME">%1$s</xliff:g> ಅನುಮತಿಯನ್ನು ವಿನಂತಿಸುತ್ತಿದೆ"</string> <string name="profile_name_generic" msgid="6851028682723034988">"ಸಾಧನ"</string> - <string name="summary_generic_single_device" msgid="4181180669689590417">"ಮೊಬೈಲ್ ಫೋನ್ ಮತ್ತು <xliff:g id="DEVICE_NAME">%1$s</xliff:g> ಸಾಧನದ ನಡುವೆ, ಕರೆ ಮಾಡುವವರ ಹೆಸರಿನಂತಹ ಮಾಹಿತಿಯನ್ನು ಸಿಂಕ್ ಮಾಡಲು ಈ ಆ್ಯಪ್ಗೆ ಸಾಧ್ಯವಾಗುತ್ತದೆ"</string> <string name="summary_generic" msgid="1761976003668044801">"ಮೊಬೈಲ್ ಫೋನ್ ಮತ್ತು ಆಯ್ಕೆಮಾಡಿದ ಸಾಧನದ ನಡುವೆ, ಕರೆ ಮಾಡುವವರ ಹೆಸರಿನಂತಹ ಮಾಹಿತಿಯನ್ನು ಸಿಂಕ್ ಮಾಡಲು ಈ ಆ್ಯಪ್ಗೆ ಸಾಧ್ಯವಾಗುತ್ತದೆ"</string> <string name="consent_yes" msgid="8344487259618762872">"ಅನುಮತಿಸಿ"</string> <string name="consent_no" msgid="2640796915611404382">"ಅನುಮತಿಸಬೇಡಿ"</string> + <string name="consent_cancel" msgid="5655005528379285841">"ರದ್ದುಮಾಡಿ"</string> <string name="consent_back" msgid="2560683030046918882">"ಹಿಂದೆ"</string> <string name="permission_expand" msgid="893185038020887411">"<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g> ಅನ್ನು ವಿಸ್ತರಿಸಿ"</string> <string name="permission_collapse" msgid="3320833884220844084">"<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g> ಅನ್ನು ಕುಗ್ಗಿಸಿ"</string> diff --git a/packages/CompanionDeviceManager/res/values-ko/strings.xml b/packages/CompanionDeviceManager/res/values-ko/strings.xml index b131a2fa53af..e4c56b279c79 100644 --- a/packages/CompanionDeviceManager/res/values-ko/strings.xml +++ b/packages/CompanionDeviceManager/res/values-ko/strings.xml @@ -17,15 +17,14 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="4470785958457506021">"부속 기기 관리자"</string> - <string name="confirmation_title" msgid="4593465730772390351">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>에서 <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>에 액세스하도록 허용하시겠습니까?"</string> + <string name="confirmation_title" msgid="2244241995958340998">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>앱에서 <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>에 액세스하도록 허용하시겠습니까?"</string> <string name="profile_name_watch" msgid="576290739483672360">"시계"</string> - <string name="chooser_title" msgid="2262294130493605839">"<strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>에서 관리할 <xliff:g id="PROFILE_NAME">%1$s</xliff:g>를 선택"</string> - <string name="summary_watch" msgid="898569637110705523">"이 앱은 <xliff:g id="DEVICE_NAME">%1$s</xliff:g> 기기를 관리하는 데 필요합니다. <xliff:g id="APP_NAME">%2$s</xliff:g>에서 정보(예: 발신자 이름)를 동기화하고, 알림과 상호작용하고, 전화, SMS, 연락처, 캘린더, 통화 기록 및 근처 기기에 액세스할 수 있게 됩니다."</string> - <string name="summary_watch_single_device" msgid="3173948915947011333">"이 앱이 정보(예: 발신자 이름)를 동기화하고 <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>에서 이러한 권한에 액세스할 수 있게 됩니다."</string> + <string name="chooser_title_non_profile" msgid="6035023914517087400">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>에서 관리할 기기 선택"</string> + <string name="chooser_title" msgid="2235819929238267637">"설정할 <xliff:g id="PROFILE_NAME">%1$s</xliff:g> 선택"</string> + <string name="summary_watch" msgid="7962014927042971830">"이 앱이 정보(예: 발신자 이름)를 동기화하고 <xliff:g id="DEVICE_NAME">%1$s</xliff:g>에서 이러한 권한에 액세스할 수 있게 됩니다."</string> <string name="confirmation_title_glasses" msgid="8288346850537727333">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>에서 <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>? 기기를 관리하도록 허용"</string> - <string name="profile_name_glasses" msgid="8488394059007275998">"안경"</string> - <string name="summary_glasses_multi_device" msgid="615259525961937348">"이 앱은 <xliff:g id="DEVICE_NAME">%1$s</xliff:g> 기기를 관리하는 데 필요합니다. <xliff:g id="APP_NAME">%2$s</xliff:g>에서 알림과 상호작용하고 내 전화, SMS, 연락처, 마이크, 근처 기기에 대한 권한을 갖게 됩니다."</string> - <string name="summary_glasses_single_device" msgid="3000909894067413398">"앱이 <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>에서 이러한 권한에 액세스할 수 있게 됩니다."</string> + <string name="profile_name_glasses" msgid="3506504967216601277">"기기"</string> + <string name="summary_glasses" msgid="2872254734959842579">"앱이 <xliff:g id="DEVICE_NAME">%1$s</xliff:g>에서 이러한 권한에 액세스할 수 있게 됩니다."</string> <string name="title_app_streaming" msgid="2270331024626446950">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>이 휴대전화의 이 정보에 액세스하도록 허용합니다."</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"교차 기기 서비스"</string> <string name="helper_summary_app_streaming" msgid="2396773196949578425">"<xliff:g id="APP_NAME">%1$s</xliff:g>에서 <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> 대신 기기 간에 앱을 스트리밍할 수 있는 권한을 요청하고 있습니다."</string> @@ -38,10 +37,10 @@ <string name="title_nearby_device_streaming" msgid="7269956847378799794">"<strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> 기기가 이 작업을 수행하도록 허용하시겠습니까?"</string> <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<xliff:g id="APP_NAME">%1$s</xliff:g>에서 <xliff:g id="DEVICE_NAME">%2$s</xliff:g> 대신 근처 기기로 앱 및 기타 시스템 기능을 스트리밍할 권한을 요청하고 있습니다."</string> <string name="profile_name_generic" msgid="6851028682723034988">"기기"</string> - <string name="summary_generic_single_device" msgid="4181180669689590417">"이 앱에서 휴대전화와 <xliff:g id="DEVICE_NAME">%1$s</xliff:g> 간에 정보(예: 발신자 이름)를 동기화할 수 있게 됩니다."</string> <string name="summary_generic" msgid="1761976003668044801">"이 앱에서 휴대전화와 선택한 기기 간에 정보(예: 발신자 이름)를 동기화할 수 있게 됩니다."</string> <string name="consent_yes" msgid="8344487259618762872">"허용"</string> <string name="consent_no" msgid="2640796915611404382">"허용 안함"</string> + <string name="consent_cancel" msgid="5655005528379285841">"취소"</string> <string name="consent_back" msgid="2560683030046918882">"뒤로"</string> <string name="permission_expand" msgid="893185038020887411">"<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g> 펼치기"</string> <string name="permission_collapse" msgid="3320833884220844084">"<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g> 접기"</string> diff --git a/packages/CompanionDeviceManager/res/values-ky/strings.xml b/packages/CompanionDeviceManager/res/values-ky/strings.xml index 568a17394eb9..d33ccedd7fef 100644 --- a/packages/CompanionDeviceManager/res/values-ky/strings.xml +++ b/packages/CompanionDeviceManager/res/values-ky/strings.xml @@ -17,15 +17,14 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string> - <string name="confirmation_title" msgid="4593465730772390351">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> колдонмосуна <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> түзмөгүнө кирүүгө уруксат бересизби?"</string> + <string name="confirmation_title" msgid="2244241995958340998">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> колдонмосуна <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> түзмөгүнө кирүүгө уруксат бересизби?"</string> <string name="profile_name_watch" msgid="576290739483672360">"саат"</string> - <string name="chooser_title" msgid="2262294130493605839">"<xliff:g id="PROFILE_NAME">%1$s</xliff:g> <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> тарабынан башкарылсын"</string> - <string name="summary_watch" msgid="898569637110705523">"Бул колдонмо <xliff:g id="DEVICE_NAME">%1$s</xliff:g> түзмөгүңүздү башкаруу үчүн керек. <xliff:g id="APP_NAME">%2$s</xliff:g> маалыматты шайкештирип, мисалы, билдирмелериңизди көрүп, телефонуңуз, SMS билдирүүлөр, байланыштар, жылнаама, чалуулар тизмеси жана жакын жердеги түзмөктөргө болгон уруксаттарды пайдалана алат."</string> - <string name="summary_watch_single_device" msgid="3173948915947011333">"Бул колдонмого маалыматты, мисалы, чалып жаткан адамдын аты-жөнүн шайкештирүүгө жана <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> түзмөгүңүздө төмөнкүлөрдү аткарууга уруксат берилет"</string> + <string name="chooser_title_non_profile" msgid="6035023914517087400">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> аркылуу башкарыла турган түзмөктү тандаңыз"</string> + <string name="chooser_title" msgid="2235819929238267637">"Тууралоо үчүн <xliff:g id="PROFILE_NAME">%1$s</xliff:g> тандаңыз"</string> + <string name="summary_watch" msgid="7962014927042971830">"Бул колдонмого маалыматты, мисалы, чалып жаткан адамдын аты-жөнүн шайкештирүүгө жана <xliff:g id="DEVICE_NAME">%1$s</xliff:g> түзмөгүңүздө төмөнкүлөрдү аткарууга уруксат берилет"</string> <string name="confirmation_title_glasses" msgid="8288346850537727333">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> колдонмосуна <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> түзмөгүн тескөөгө уруксат бересизби?"</string> - <string name="profile_name_glasses" msgid="8488394059007275998">"көз айнектер"</string> - <string name="summary_glasses_multi_device" msgid="615259525961937348">"Бул колдонмо <xliff:g id="DEVICE_NAME">%1$s</xliff:g> түзмөгүн башкаруу үчүн керек. <xliff:g id="APP_NAME">%2$s</xliff:g> билдирмелериңизди көрүп, телефонуңуз, SMS билдирүүлөр, Байланыштар, Микрофон жана Жакын жердеги түзмөктөргө болгон уруксаттарды пайдалана алат."</string> - <string name="summary_glasses_single_device" msgid="3000909894067413398">"Бул колдонмого <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> түзмөгүңүздө төмөнкүлөрдү аткарууга уруксат берилет"</string> + <string name="profile_name_glasses" msgid="3506504967216601277">"түзмөк"</string> + <string name="summary_glasses" msgid="2872254734959842579">"Бул колдонмого <xliff:g id="DEVICE_NAME">%1$s</xliff:g> түзмөгүңүздө төмөнкүлөрдү аткарууга уруксат берилет"</string> <string name="title_app_streaming" msgid="2270331024626446950">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> колдонмосуна телефонуңуздагы ушул маалыматты көрүүгө уруксат бериңиз"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Түзмөктөр аралык кызматтар"</string> <string name="helper_summary_app_streaming" msgid="2396773196949578425">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> түзмөгүңүздүн атынан түзмөктөрүңүздүн ортосунда колдонмолорду алып ойнотууга уруксат сурап жатат"</string> @@ -38,10 +37,10 @@ <string name="title_nearby_device_streaming" msgid="7269956847378799794">"<strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> түзмөгүнө бул аракетти аткарууга уруксат бересизби?"</string> <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="DEVICE_NAME">%2$s</xliff:g> түзмөгүңүздүн атынан жакын жердеги түзмөктөрдө колдонмолорду жана системанын башка функцияларын алып ойнотууга уруксат сурап жатат"</string> <string name="profile_name_generic" msgid="6851028682723034988">"түзмөк"</string> - <string name="summary_generic_single_device" msgid="4181180669689590417">"Бул колдонмо маалыматты шайкештире алат, мисалы, чалып жаткан кишинин атын телефон жана <xliff:g id="DEVICE_NAME">%1$s</xliff:g> түзмөгү менен шайкештирет"</string> <string name="summary_generic" msgid="1761976003668044801">"Бул колдонмо маалыматты шайкештире алат, мисалы, чалып жаткан кишинин атын телефон жана тандалган түзмөк менен шайкештирет"</string> <string name="consent_yes" msgid="8344487259618762872">"Ооба"</string> <string name="consent_no" msgid="2640796915611404382">"Уруксат берилбесин"</string> + <string name="consent_cancel" msgid="5655005528379285841">"Жокко чыгаруу"</string> <string name="consent_back" msgid="2560683030046918882">"Артка"</string> <string name="permission_expand" msgid="893185038020887411">"<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g> жайып көрсөтүү"</string> <string name="permission_collapse" msgid="3320833884220844084">"<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g> жыйыштыруу"</string> diff --git a/packages/CompanionDeviceManager/res/values-lo/strings.xml b/packages/CompanionDeviceManager/res/values-lo/strings.xml index 08ea7a9b82cc..0072e4b0709b 100644 --- a/packages/CompanionDeviceManager/res/values-lo/strings.xml +++ b/packages/CompanionDeviceManager/res/values-lo/strings.xml @@ -17,15 +17,14 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="4470785958457506021">"ຕົວຈັດການອຸປະກອນປະກອບ"</string> - <string name="confirmation_title" msgid="4593465730772390351">"ອະນຸຍາດ <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ໃຫ້ເຂົ້າເຖິງ <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> ບໍ?"</string> + <string name="confirmation_title" msgid="2244241995958340998">"ອະນຸຍາດໃຫ້ແອັບ <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ເຂົ້າເຖິງ <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> ບໍ?"</string> <string name="profile_name_watch" msgid="576290739483672360">"ໂມງ"</string> - <string name="chooser_title" msgid="2262294130493605839">"ເລືອກ <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ເພື່ອໃຫ້ຖືກຈັດການໂດຍ <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> - <string name="summary_watch" msgid="898569637110705523">"ຕ້ອງໃຊ້ແອັບນີ້ເພື່ອຈັດການ <xliff:g id="DEVICE_NAME">%1$s</xliff:g> ຂອງທ່ານ. <xliff:g id="APP_NAME">%2$s</xliff:g> ຈະໄດ້ຮັບອະນຸຍາດໃຫ້ຊິ້ງຂໍ້ມູນ ເຊັ່ນ: ຊື່ຂອງຄົນທີ່ໂທເຂົ້າ, ການໂຕ້ຕອບກັບການແຈ້ງເຕືອນຂອງທ່ານ ແລະ ສິດເຂົ້າເຖິງໂທລະສັບ, SMS, ລາຍຊື່ຜູ້ຕິດຕໍ່, ປະຕິທິນ, ບັນທຶກການໂທ ແລະ ອຸປະກອນທີ່ຢູ່ໃກ້ຄຽງຂອງທ່ານ."</string> - <string name="summary_watch_single_device" msgid="3173948915947011333">"ແອັບນີ້ຈະໄດ້ຮັບອະນຸຍາດໃຫ້ຊິ້ງຂໍ້ມູນ, ເຊັ່ນ: ຊື່ຂອງຄົນທີ່ໂທເຂົ້າ ແລະ ສິດເຂົ້າເຖິງການອະນຸຍາດເຫຼົ່ານີ້ຢູ່ <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> ຂອງທ່ານ"</string> + <string name="chooser_title_non_profile" msgid="6035023914517087400">"ເລືອກອຸປະກອນທີ່ຈະໃຫ້ມີການຈັດການໂດຍ <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>"</string> + <string name="chooser_title" msgid="2235819929238267637">"ເລືອກ <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ທີ່ຈະຕັ້ງຄ່າ"</string> + <string name="summary_watch" msgid="7962014927042971830">"ແອັບນີ້ຈະໄດ້ຮັບອະນຸຍາດໃຫ້ຊິ້ງຂໍ້ມູນ, ເຊັ່ນ: ຊື່ຂອງຄົນທີ່ໂທເຂົ້າ ແລະ ສິດເຂົ້າເຖິງການອະນຸຍາດເຫຼົ່ານີ້ຢູ່ <xliff:g id="DEVICE_NAME">%1$s</xliff:g> ຂອງທ່ານ"</string> <string name="confirmation_title_glasses" msgid="8288346850537727333">"ອະນຸຍາດ <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ຈັດການ <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> ບໍ?"</string> - <string name="profile_name_glasses" msgid="8488394059007275998">"ແວ່ນຕາ"</string> - <string name="summary_glasses_multi_device" msgid="615259525961937348">"ຕ້ອງໃຊ້ແອັບນີ້ເພື່ອຈັດການ <xliff:g id="DEVICE_NAME">%1$s</xliff:g>. <xliff:g id="APP_NAME">%2$s</xliff:g> ຈະໄດ້ຮັບອະນຸຍາດໃຫ້ໂຕ້ຕອບກັບການແຈ້ງເຕືອນຂອງທ່ານ ແລະ ການອະນຸຍາດສິດເຂົ້າເຖິງໂທລະສັບ, SMS, ລາຍຊື່ຜູ້ຕິດຕໍ່, ໄມໂຄຣໂຟນ ແລະ ອຸປະກອນທີ່ຢູ່ໃກ້ຄຽງຂອງທ່ານ."</string> - <string name="summary_glasses_single_device" msgid="3000909894067413398">"ແອັບນີ້ຈະໄດ້ຮັບສິດເຂົ້າເຖິງການອະນຸຍາດເຫຼົ່ານີ້ຢູ່ <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> ຂອງທ່ານ"</string> + <string name="profile_name_glasses" msgid="3506504967216601277">"ອຸປະກອນ"</string> + <string name="summary_glasses" msgid="2872254734959842579">"ແອັບນີ້ຈະໄດ້ຮັບສິດເຂົ້າເຖິງການອະນຸຍາດເຫຼົ່ານີ້ຢູ່ <xliff:g id="DEVICE_NAME">%1$s</xliff:g> ຂອງທ່ານ"</string> <string name="title_app_streaming" msgid="2270331024626446950">"ອະນຸຍາດ <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ໃຫ້ເຂົ້າເຖິງຂໍ້ມູນນີ້ຈາກໂທລະສັບຂອງທ່ານໄດ້"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"ບໍລິການຂ້າມອຸປະກອນ"</string> <string name="helper_summary_app_streaming" msgid="2396773196949578425">"<xliff:g id="APP_NAME">%1$s</xliff:g> ກຳລັງຮ້ອງຂໍການອະນຸຍາດໃນນາມຂອງ <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> ເພື່ອສະຕຣີມແອັບລະຫວ່າງອຸປະກອນຕ່າງໆຂອງທ່ານ"</string> @@ -38,10 +37,10 @@ <string name="title_nearby_device_streaming" msgid="7269956847378799794">"ອະນຸຍາດ <strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> ເພື່ອດຳເນີນຄຳສັ່ງນີ້ບໍ?"</string> <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<xliff:g id="APP_NAME">%1$s</xliff:g> ກໍາລັງຮ້ອງຂໍການອະນຸຍາດໃນນາມ <xliff:g id="DEVICE_NAME">%2$s</xliff:g> ຂອງທ່ານເພື່ອສະຕຣີມແອັບ ແລະ ຄຸນສົມບັດລະບົບອື່ນໆໄປຫາອຸປະກອນທີ່ຢູ່ໃກ້ຄຽງ"</string> <string name="profile_name_generic" msgid="6851028682723034988">"ອຸປະກອນ"</string> - <string name="summary_generic_single_device" msgid="4181180669689590417">"ແອັບນີ້ຈະສາມາດຊິ້ງຂໍ້ມູນ ເຊັ່ນ: ຊື່ຂອງຄົນທີ່ໂທເຂົ້າ, ລະຫວ່າງໂທລະສັບຂອງທ່ານ ແລະ <xliff:g id="DEVICE_NAME">%1$s</xliff:g> ໄດ້"</string> <string name="summary_generic" msgid="1761976003668044801">"ແອັບນີ້ຈະສາມາດຊິ້ງຂໍ້ມູນ ເຊັ່ນ: ຊື່ຂອງຄົນທີ່ໂທເຂົ້າ, ລະຫວ່າງໂທລະສັບຂອງທ່ານ ແລະ ອຸປະກອນທີ່ເລືອກໄວ້ໄດ້"</string> <string name="consent_yes" msgid="8344487259618762872">"ອະນຸຍາດ"</string> <string name="consent_no" msgid="2640796915611404382">"ບໍ່ອະນຸຍາດ"</string> + <string name="consent_cancel" msgid="5655005528379285841">"ຍົກເລີກ"</string> <string name="consent_back" msgid="2560683030046918882">"ກັບຄືນ"</string> <string name="permission_expand" msgid="893185038020887411">"ຂະຫຍາຍ <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string> <string name="permission_collapse" msgid="3320833884220844084">"ຫຍໍ້ <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g> ລົງ"</string> diff --git a/packages/CompanionDeviceManager/res/values-lt/strings.xml b/packages/CompanionDeviceManager/res/values-lt/strings.xml index 0a800eeb5151..65a266df6b68 100644 --- a/packages/CompanionDeviceManager/res/values-lt/strings.xml +++ b/packages/CompanionDeviceManager/res/values-lt/strings.xml @@ -17,15 +17,14 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string> - <string name="confirmation_title" msgid="4593465730772390351">"Leisti <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> pasiekti <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> + <string name="confirmation_title" msgid="2244241995958340998">"Leisti programai <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> pasiekti <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> <string name="profile_name_watch" msgid="576290739483672360">"laikrodį"</string> - <string name="chooser_title" msgid="2262294130493605839">"Jūsų <xliff:g id="PROFILE_NAME">%1$s</xliff:g>, kurį valdys <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> (pasirinkite)"</string> - <string name="summary_watch" msgid="898569637110705523">"Ši programa reikalinga norint tvarkyti jūsų įrenginį „<xliff:g id="DEVICE_NAME">%1$s</xliff:g>“. Programai „<xliff:g id="APP_NAME">%2$s</xliff:g>“ bus leidžiama sinchronizuoti tam tikrą informaciją, pvz., skambinančio asmens vardą, sąveikauti su jūsų pranešimais ir pasiekti jūsų leidimus „Telefonas“, „SMS“, „Kontaktai“, „Kalendorius“, „Skambučių žurnalai“ ir „Įrenginiai netoliese."</string> - <string name="summary_watch_single_device" msgid="3173948915947011333">"Šiai programai bus leidžiama sinchronizuoti tam tikrą informaciją, pvz., skambinančio asmens vardą, ir pasiekti toliau nurodytus leidimus jūsų <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string> + <string name="chooser_title_non_profile" msgid="6035023914517087400">"Įrenginio, kuris bus valdomas naudojant programą <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>, pasirinkimas"</string> + <string name="chooser_title" msgid="2235819929238267637">"Norimo nustatyti <xliff:g id="PROFILE_NAME">%1$s</xliff:g> pasirinkimas"</string> + <string name="summary_watch" msgid="7962014927042971830">"Šiai programai bus leidžiama sinchronizuoti tam tikrą informaciją, pvz., skambinančio asmens vardą, ir pasiekti toliau nurodytus <xliff:g id="DEVICE_NAME">%1$s</xliff:g> leidimus"</string> <string name="confirmation_title_glasses" msgid="8288346850537727333">"Leisti <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> valdyti <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> - <string name="profile_name_glasses" msgid="8488394059007275998">"akiniai"</string> - <string name="summary_glasses_multi_device" msgid="615259525961937348">"Ši programa reikalinga norint tvarkyti įrenginį „<xliff:g id="DEVICE_NAME">%1$s</xliff:g>“. Programai „<xliff:g id="APP_NAME">%2$s</xliff:g>“ bus leidžiama sąveikauti su jūsų pranešimais ir pasiekti jūsų leidimus „Telefonas“, „SMS“, „Kontaktai“, „Mikrofonas“ ir „Įrenginiai netoliese“."</string> - <string name="summary_glasses_single_device" msgid="3000909894067413398">"Šiai programai bus leidžiama pasiekti toliau nurodytus leidimus jūsų <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>."</string> + <string name="profile_name_glasses" msgid="3506504967216601277">"įrenginio"</string> + <string name="summary_glasses" msgid="2872254734959842579">"Šiai programai bus leidžiama pasiekti toliau nurodytus <xliff:g id="DEVICE_NAME">%1$s</xliff:g> leidimus."</string> <string name="title_app_streaming" msgid="2270331024626446950">"Leisti <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> pasiekti šią informaciją iš jūsų telefono"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Pasl. keliuose įrenginiuose"</string> <string name="helper_summary_app_streaming" msgid="2396773196949578425">"Programa „<xliff:g id="APP_NAME">%1$s</xliff:g>“ prašo leidimo jūsų „<xliff:g id="DISPLAY_NAME">%2$s</xliff:g>“ vardu, kad galėtų srautu perduoti programas iš vieno įrenginio į kitą"</string> @@ -38,10 +37,10 @@ <string name="title_nearby_device_streaming" msgid="7269956847378799794">"Leisti <strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> atlikti šį veiksmą?"</string> <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"„<xliff:g id="APP_NAME">%1$s</xliff:g>“ prašo leidimo jūsų „<xliff:g id="DEVICE_NAME">%2$s</xliff:g>“ vardu, kad galėtų srautu perduoti programas ir kitas sistemos funkcijas įrenginiams netoliese"</string> <string name="profile_name_generic" msgid="6851028682723034988">"įrenginys"</string> - <string name="summary_generic_single_device" msgid="4181180669689590417">"Ši programa galės sinchronizuoti tam tikrą informaciją, pvz., skambinančio asmens vardą, su jūsų telefonu ir įrenginiu „<xliff:g id="DEVICE_NAME">%1$s</xliff:g>“"</string> <string name="summary_generic" msgid="1761976003668044801">"Ši programa galės sinchronizuoti tam tikrą informaciją, pvz., skambinančio asmens vardą, su jūsų telefonu ir pasirinktu įrenginiu"</string> <string name="consent_yes" msgid="8344487259618762872">"Leisti"</string> <string name="consent_no" msgid="2640796915611404382">"Neleisti"</string> + <string name="consent_cancel" msgid="5655005528379285841">"Atšaukti"</string> <string name="consent_back" msgid="2560683030046918882">"Atgal"</string> <string name="permission_expand" msgid="893185038020887411">"Išskleisti „<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>“"</string> <string name="permission_collapse" msgid="3320833884220844084">"Sutraukti „<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>“"</string> diff --git a/packages/CompanionDeviceManager/res/values-lv/strings.xml b/packages/CompanionDeviceManager/res/values-lv/strings.xml index 43dedbd93578..1d29edc91555 100644 --- a/packages/CompanionDeviceManager/res/values-lv/strings.xml +++ b/packages/CompanionDeviceManager/res/values-lv/strings.xml @@ -17,15 +17,14 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="4470785958457506021">"Palīgierīču pārzinis"</string> - <string name="confirmation_title" msgid="4593465730772390351">"Vai atļaut lietotnei <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> piekļūt ierīcei <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> + <string name="confirmation_title" msgid="2244241995958340998">"Vai atļaut lietotnei <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> piekļūt lietotnei <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> <string name="profile_name_watch" msgid="576290739483672360">"pulkstenis"</string> - <string name="chooser_title" msgid="2262294130493605839">"Profila (<xliff:g id="PROFILE_NAME">%1$s</xliff:g>) izvēle, ko pārvaldīt lietotnē <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> - <string name="summary_watch" msgid="898569637110705523">"Šī lietotne ir nepieciešama jūsu ierīces (<xliff:g id="DEVICE_NAME">%1$s</xliff:g>) pārvaldībai. <xliff:g id="APP_NAME">%2$s</xliff:g> drīkstēs sinhronizēt informāciju (piemēram, zvanītāja vārdu), mijiedarboties ar jūsu paziņojumiem un piekļūt atļaujām Tālrunis, Īsziņas, Kontaktpersonas, Kalendārs, Zvanu žurnāli un Tuvumā esošas ierīces."</string> - <string name="summary_watch_single_device" msgid="3173948915947011333">"Šī lietotne drīkstēs sinhronizēt informāciju, piemēram, zvanītāja vārdu, un piekļūt norādītajām atļaujām jūsu <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>."</string> + <string name="chooser_title_non_profile" msgid="6035023914517087400">"Izvēlieties ierīci, ko pārvaldīt lietotnē <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>"</string> + <string name="chooser_title" msgid="2235819929238267637">"Jāizvēlas <xliff:g id="PROFILE_NAME">%1$s</xliff:g> iestatīšanai"</string> + <string name="summary_watch" msgid="7962014927042971830">"Šī lietotne drīkstēs sinhronizēt informāciju, piemēram, zvanītāja vārdu, un piekļūt norādītajām atļaujām jūsu ierīcē (<xliff:g id="DEVICE_NAME">%1$s</xliff:g>)."</string> <string name="confirmation_title_glasses" msgid="8288346850537727333">"Vai atļaut lietotnei <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> piekļūt ierīcei <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> - <string name="profile_name_glasses" msgid="8488394059007275998">"brilles"</string> - <string name="summary_glasses_multi_device" msgid="615259525961937348">"Šī lietotne ir nepieciešama šādas ierīces pārvaldībai: <xliff:g id="DEVICE_NAME">%1$s</xliff:g>. <xliff:g id="APP_NAME">%2$s</xliff:g> drīkstēs mijiedarboties ar jūsu paziņojumiem un piekļūt atļaujām Tālrunis, Īsziņas, Kontaktpersonas, Mikrofons un Tuvumā esošas ierīces."</string> - <string name="summary_glasses_single_device" msgid="3000909894067413398">"Šai lietotnei tiks sniegta piekļuve norādītajām atļaujām jūsu <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>."</string> + <string name="profile_name_glasses" msgid="3506504967216601277">"ierīce"</string> + <string name="summary_glasses" msgid="2872254734959842579">"Šai lietotnei tiks sniegta piekļuve norādītajām atļaujām jūsu ierīcē (<xliff:g id="DEVICE_NAME">%1$s</xliff:g>)."</string> <string name="title_app_streaming" msgid="2270331024626446950">"Atļaut lietotnei <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> piekļūt šai informācijai no jūsu tālruņa"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Vairāku ierīču pakalpojumi"</string> <string name="helper_summary_app_streaming" msgid="2396773196949578425">"Lietotne <xliff:g id="APP_NAME">%1$s</xliff:g> pieprasa atļauju straumēt lietotnes starp jūsu ierīcēm šīs ierīces vārdā: <xliff:g id="DISPLAY_NAME">%2$s</xliff:g>."</string> @@ -38,10 +37,10 @@ <string name="title_nearby_device_streaming" msgid="7269956847378799794">"Vai atļaut ierīcei <strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> veikt šo darbību?"</string> <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<xliff:g id="APP_NAME">%1$s</xliff:g> pieprasa atļauju tuvumā esošās ierīcēs straumēt lietotnes un citas sistēmas funkcijas šīs ierīces vārdā: <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"ierīce"</string> - <string name="summary_generic_single_device" msgid="4181180669689590417">"Šī lietotne varēs sinhronizēt informāciju (piemēram, zvanītāja vārdu) starp jūsu tālruni un šo ierīci: <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="summary_generic" msgid="1761976003668044801">"Šī lietotne varēs sinhronizēt informāciju (piemēram, zvanītāja vārdu) starp jūsu tālruni un izvēlēto ierīci"</string> <string name="consent_yes" msgid="8344487259618762872">"Atļaut"</string> <string name="consent_no" msgid="2640796915611404382">"Neatļaut"</string> + <string name="consent_cancel" msgid="5655005528379285841">"Atcelt"</string> <string name="consent_back" msgid="2560683030046918882">"Atpakaļ"</string> <string name="permission_expand" msgid="893185038020887411">"Izvērst: <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string> <string name="permission_collapse" msgid="3320833884220844084">"Sakļaut: <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string> diff --git a/packages/CompanionDeviceManager/res/values-mk/strings.xml b/packages/CompanionDeviceManager/res/values-mk/strings.xml index 7d053a7bd583..e72e9604ddd4 100644 --- a/packages/CompanionDeviceManager/res/values-mk/strings.xml +++ b/packages/CompanionDeviceManager/res/values-mk/strings.xml @@ -17,15 +17,14 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string> - <string name="confirmation_title" msgid="4593465730772390351">"Ќе дозволите <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> да пристапува до <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> + <string name="confirmation_title" msgid="2244241995958340998">"Дозволувате апликацијата <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> да пристапува до <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> <string name="profile_name_watch" msgid="576290739483672360">"часовник"</string> - <string name="chooser_title" msgid="2262294130493605839">"Изберете <xliff:g id="PROFILE_NAME">%1$s</xliff:g> со којшто ќе управува <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> - <string name="summary_watch" msgid="898569637110705523">"Апликацијава е потребна за управување со вашиот <xliff:g id="DEVICE_NAME">%1$s</xliff:g>. <xliff:g id="APP_NAME">%2$s</xliff:g> ќе може да ги синхронизира податоците како што се имињата на јавувачите, да остварува интеракција со известувањата и да пристапува до дозволите за „Телефон“, SMS, „Контакти“, „Календар“, „Евиденција на повици“ и „Уреди во близина“."</string> - <string name="summary_watch_single_device" msgid="3173948915947011333">"Оваа апликација ќе има дозвола да ги синхронизира податоците како што се имињата на јавувачите и да пристапува до следниве дозволи на вашиот <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string> + <string name="chooser_title_non_profile" msgid="6035023914517087400">"Изберете уред со којшто ќе управува <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>"</string> + <string name="chooser_title" msgid="2235819929238267637">"Изберете <xliff:g id="PROFILE_NAME">%1$s</xliff:g> за поставување"</string> + <string name="summary_watch" msgid="7962014927042971830">"Апликацијава ќе има дозвола да ги синхронизира податоците како што се имињата на јавувачите и да пристапува до следниве дозволи на вашиот <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="confirmation_title_glasses" msgid="8288346850537727333">"Ќе дозволите <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> да управува со <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> - <string name="profile_name_glasses" msgid="8488394059007275998">"очила"</string> - <string name="summary_glasses_multi_device" msgid="615259525961937348">"Апликацијава е потребна за управување со <xliff:g id="DEVICE_NAME">%1$s</xliff:g>. <xliff:g id="APP_NAME">%2$s</xliff:g> ќе може да остварува интеракција со известувањата и да пристапува до дозволите за „Телефон“, SMS, „Контакти“, „Микрофон“ и „Уреди во близина“."</string> - <string name="summary_glasses_single_device" msgid="3000909894067413398">"Апликацијава ќе може да пристапува до овие дозволи на <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string> + <string name="profile_name_glasses" msgid="3506504967216601277">"уред"</string> + <string name="summary_glasses" msgid="2872254734959842579">"Апликацијава ќе може да пристапува до овие дозволи на <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="title_app_streaming" msgid="2270331024626446950">"Овозможете <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> да пристапува до овие податоци на телефонот"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Повеќенаменски услуги"</string> <string name="helper_summary_app_streaming" msgid="2396773196949578425">"<xliff:g id="APP_NAME">%1$s</xliff:g> бара дозвола во име на вашиот <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> за да стримува апликации помеѓу вашите уреди"</string> @@ -38,10 +37,10 @@ <string name="title_nearby_device_streaming" msgid="7269956847378799794">"Ќе дозволите <strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> да го преземе ова дејство?"</string> <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<xliff:g id="APP_NAME">%1$s</xliff:g> бара дозвола во име на вашиот <xliff:g id="DEVICE_NAME">%2$s</xliff:g> за да стримува апликации и други системски функции на уредите во близина"</string> <string name="profile_name_generic" msgid="6851028682723034988">"уред"</string> - <string name="summary_generic_single_device" msgid="4181180669689590417">"Оваа апликација ќе може да ги синхронизира податоците како што се имињата на јавувачите помеѓу вашиот телефон и <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="summary_generic" msgid="1761976003668044801">"Оваа апликација ќе може да ги синхронизира податоците како што се имињата на јавувачите помеѓу вашиот телефон и избраниот уред"</string> <string name="consent_yes" msgid="8344487259618762872">"Дозволи"</string> <string name="consent_no" msgid="2640796915611404382">"Не дозволувај"</string> + <string name="consent_cancel" msgid="5655005528379285841">"Откажи"</string> <string name="consent_back" msgid="2560683030046918882">"Назад"</string> <string name="permission_expand" msgid="893185038020887411">"Прошири <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string> <string name="permission_collapse" msgid="3320833884220844084">"Собери <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string> diff --git a/packages/CompanionDeviceManager/res/values-ml/strings.xml b/packages/CompanionDeviceManager/res/values-ml/strings.xml index 21dcfa1c84be..1703742d0511 100644 --- a/packages/CompanionDeviceManager/res/values-ml/strings.xml +++ b/packages/CompanionDeviceManager/res/values-ml/strings.xml @@ -17,15 +17,14 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="4470785958457506021">"കമ്പാനിയൻ ഉപകരണ മാനേജർ"</string> - <string name="confirmation_title" msgid="4593465730772390351">"<strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> ആക്സസ് ചെയ്യാൻ <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> എന്നതിനെ അനുവദിക്കണോ?"</string> + <string name="confirmation_title" msgid="2244241995958340998">"<strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> ആക്സസ് ചെയ്യാൻ <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> എന്ന ആപ്പിനെ അനുവദിക്കണോ?"</string> <string name="profile_name_watch" msgid="576290739483672360">"വാച്ച്"</string> - <string name="chooser_title" msgid="2262294130493605839">"<strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> ഉപയോഗിച്ച് മാനേജ് ചെയ്യുന്നതിന് ഒരു <xliff:g id="PROFILE_NAME">%1$s</xliff:g> തിരഞ്ഞെടുക്കുക"</string> - <string name="summary_watch" msgid="898569637110705523">"നിങ്ങളുടെ <xliff:g id="DEVICE_NAME">%1$s</xliff:g> മാനേജ് ചെയ്യാൻ ഈ ആപ്പ് ആവശ്യമാണ്. വിളിക്കുന്നയാളുടെ പേര് പോലുള്ള വിവരങ്ങൾ സമന്വയിപ്പിക്കുന്നതിനും നിങ്ങളുടെ അറിയിപ്പുകളുമായി സംവദിക്കാനും നിങ്ങളുടെ ഫോൺ, SMS, Contacts, Calendar, കോൾ ചരിത്രം, സമീപമുള്ള ഉപകരണങ്ങളുടെ അനുമതികൾ എന്നിവ ആക്സസ് ചെയ്യാനും <xliff:g id="APP_NAME">%2$s</xliff:g> ആപ്പിനെ അനുവദിക്കും."</string> - <string name="summary_watch_single_device" msgid="3173948915947011333">"വിളിക്കുന്നയാളുടെ പേര് പോലുള്ള വിവരങ്ങൾ സമന്വയിപ്പിക്കാനും നിങ്ങളുടെ <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> എന്നതിൽ ഈ അനുമതികൾ ആക്സസ് ചെയ്യാനും ഈ ആപ്പിനെ അനുവദിക്കും"</string> + <string name="chooser_title_non_profile" msgid="6035023914517087400">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ഉപയോഗിച്ച് മാനേജ് ചെയ്യുന്നതിന് ഒരു ഉപകരണം തിരഞ്ഞെടുക്കുക"</string> + <string name="chooser_title" msgid="2235819929238267637">"സജ്ജീകരിക്കാൻ <xliff:g id="PROFILE_NAME">%1$s</xliff:g> തിരഞ്ഞെടുക്കുക"</string> + <string name="summary_watch" msgid="7962014927042971830">"വിളിക്കുന്നയാളുടെ പേര് പോലുള്ള വിവരങ്ങൾ സമന്വയിപ്പിക്കാനും നിങ്ങളുടെ <xliff:g id="DEVICE_NAME">%1$s</xliff:g> എന്നതിൽ ഈ അനുമതികൾ ആക്സസ് ചെയ്യാനും ഈ ആപ്പിനെ അനുവദിക്കും"</string> <string name="confirmation_title_glasses" msgid="8288346850537727333">"<strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>? മാനേജ് ചെയ്യാൻ, <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> എന്നതിനെ അനുവദിക്കുക"</string> - <string name="profile_name_glasses" msgid="8488394059007275998">"ഗ്ലാസുകൾ"</string> - <string name="summary_glasses_multi_device" msgid="615259525961937348">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> മാനേജ് ചെയ്യാൻ ഈ ആപ്പ് ആവശ്യമാണ്. നിങ്ങളുടെ അറിയിപ്പുകളുമായി ഇടപഴകാനും ഫോൺ, SMS, കോൺടാക്റ്റുകൾ, മൈക്രോഫോൺ, സമീപമുള്ള ഉപകരണങ്ങളുടെ അനുമതികൾ എന്നിവ ആക്സസ് ചെയ്യാനും <xliff:g id="APP_NAME">%2$s</xliff:g> എന്നതിനെ അനുവദിക്കും."</string> - <string name="summary_glasses_single_device" msgid="3000909894067413398">"നിങ്ങളുടെ <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> എന്നതിൽ ഇനിപ്പറയുന്ന അനുമതികൾ ആക്സസ് ചെയ്യാൻ ഈ ആപ്പിനെ അനുവദിക്കും"</string> + <string name="profile_name_glasses" msgid="3506504967216601277">"ഉപകരണം"</string> + <string name="summary_glasses" msgid="2872254734959842579">"നിങ്ങളുടെ <xliff:g id="DEVICE_NAME">%1$s</xliff:g> എന്നതിൽ ഇനിപ്പറയുന്ന അനുമതികൾ ആക്സസ് ചെയ്യാൻ ഈ ആപ്പിനെ അനുവദിക്കും"</string> <string name="title_app_streaming" msgid="2270331024626446950">"നിങ്ങളുടെ ഫോണിൽ നിന്ന് ഈ വിവരങ്ങൾ ആക്സസ് ചെയ്യാൻ <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ആപ്പിനെ അനുവദിക്കുക"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"ക്രോസ്-ഉപകരണ സേവനങ്ങൾ"</string> <string name="helper_summary_app_streaming" msgid="2396773196949578425">"നിങ്ങളുടെ ഉപകരണങ്ങളിൽ ഒന്നിൽ നിന്ന് അടുത്തതിലേക്ക് ആപ്പുകൾ സ്ട്രീം ചെയ്യാൻ <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> എന്ന ഉപകരണത്തിന് വേണ്ടി <xliff:g id="APP_NAME">%1$s</xliff:g> എന്നത് അനുമതി അഭ്യർത്ഥിക്കുന്നു"</string> @@ -38,10 +37,10 @@ <string name="title_nearby_device_streaming" msgid="7269956847378799794">"ഈ പ്രവർത്തനം നടത്താൻ <strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> എന്നതിനെ അനുവദിക്കണോ?"</string> <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"സമീപമുള്ള ഉപകരണങ്ങളിൽ ആപ്പുകളും മറ്റ് സിസ്റ്റം ഫീച്ചറുകളും സ്ട്രീം ചെയ്യാൻ നിങ്ങളുടെ <xliff:g id="DEVICE_NAME">%2$s</xliff:g> എന്നതിന് വേണ്ടി <xliff:g id="APP_NAME">%1$s</xliff:g> അനുമതി അഭ്യർത്ഥിക്കുന്നു"</string> <string name="profile_name_generic" msgid="6851028682723034988">"ഉപകരണം"</string> - <string name="summary_generic_single_device" msgid="4181180669689590417">"വിളിക്കുന്നയാളുടെ പേര് പോലുള്ള വിവരങ്ങൾ നിങ്ങളുടെ ഫോണിനും <xliff:g id="DEVICE_NAME">%1$s</xliff:g> എന്നതിനും ഇടയിൽ സമന്വയിപ്പിക്കുന്നതിന് ഈ ആപ്പിന് കഴിയും"</string> <string name="summary_generic" msgid="1761976003668044801">"വിളിക്കുന്നയാളുടെ പേര് പോലുള്ള വിവരങ്ങൾ നിങ്ങളുടെ ഫോണിനും തിരഞ്ഞെടുത്ത ഉപകരണത്തിനും ഇടയിൽ സമന്വയിപ്പിക്കുന്നതിന് ഈ ആപ്പിന് കഴിയും"</string> <string name="consent_yes" msgid="8344487259618762872">"അനുവദിക്കുക"</string> <string name="consent_no" msgid="2640796915611404382">"അനുവദിക്കരുത്"</string> + <string name="consent_cancel" msgid="5655005528379285841">"റദ്ദാക്കുക"</string> <string name="consent_back" msgid="2560683030046918882">"മടങ്ങുക"</string> <string name="permission_expand" msgid="893185038020887411">"<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g> വികസിപ്പിക്കുക"</string> <string name="permission_collapse" msgid="3320833884220844084">"<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g> ചുരുക്കുക"</string> diff --git a/packages/CompanionDeviceManager/res/values-mn/strings.xml b/packages/CompanionDeviceManager/res/values-mn/strings.xml index 0c31abf58c98..613c9aa63b64 100644 --- a/packages/CompanionDeviceManager/res/values-mn/strings.xml +++ b/packages/CompanionDeviceManager/res/values-mn/strings.xml @@ -17,15 +17,14 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string> - <string name="confirmation_title" msgid="4593465730772390351">"<strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>-д <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>-д хандахыг зөвшөөрөх үү?"</string> + <string name="confirmation_title" msgid="2244241995958340998">"<strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>-д <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> аппыг хандахыг зөвшөөрөх үү?"</string> <string name="profile_name_watch" msgid="576290739483672360">"цаг"</string> - <string name="chooser_title" msgid="2262294130493605839">"<strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>-н удирдах<xliff:g id="PROFILE_NAME">%1$s</xliff:g>-г сонгоно уу"</string> - <string name="summary_watch" msgid="898569637110705523">"Энэ апп таны <xliff:g id="DEVICE_NAME">%1$s</xliff:g>-г удирдахад шаардлагатай. <xliff:g id="APP_NAME">%2$s</xliff:g>-д залгаж буй хүний нэр зэрэг мэдээллийг синк хийх, таны мэдэгдэлтэй харилцан үйлдэл хийх, Утас, SMS, Харилцагчид, Календарь, Дуудлагын жагсаалт болон Ойролцоох төхөөрөмжүүдийн зөвшөөрөлд хандахыг зөвшөөрнө."</string> - <string name="summary_watch_single_device" msgid="3173948915947011333">"Энэ аппад залгаж буй хүний нэр зэрэг мэдээллийг синк хийх болон таны <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>-н эдгээр зөвшөөрөлд хандахыг зөвшөөрнө"</string> + <string name="chooser_title_non_profile" msgid="6035023914517087400">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>-н удирдах төхөөрөмжийг сонгоно уу"</string> + <string name="chooser_title" msgid="2235819929238267637">"Тохируулахын тулд <xliff:g id="PROFILE_NAME">%1$s</xliff:g> сонгоно уу"</string> + <string name="summary_watch" msgid="7962014927042971830">"Энэ аппад залгаж буй хүний нэр зэрэг мэдээллийг синк хийх болон таны <xliff:g id="DEVICE_NAME">%1$s</xliff:g>-н эдгээр зөвшөөрөлд хандахыг зөвшөөрнө"</string> <string name="confirmation_title_glasses" msgid="8288346850537727333">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>-д <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>-г удирдахыг зөвшөөрөх үү?"</string> - <string name="profile_name_glasses" msgid="8488394059007275998">"нүдний шил"</string> - <string name="summary_glasses_multi_device" msgid="615259525961937348">"Энэ апп <xliff:g id="DEVICE_NAME">%1$s</xliff:g>-г удирдахад шаардлагатай. <xliff:g id="APP_NAME">%2$s</xliff:g>-д таны мэдэгдэлтэй харилцан үйлдэл хийх, Утас, SMS, Харилцагчид, Микрофон болон Ойролцоох төхөөрөмжүүдийн зөвшөөрөлд хандахыг зөвшөөрнө."</string> - <string name="summary_glasses_single_device" msgid="3000909894067413398">"Энэ апп таны <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>-н эдгээр зөвшөөрөлд хандах эрхтэй байх болно"</string> + <string name="profile_name_glasses" msgid="3506504967216601277">"төхөөрөмж"</string> + <string name="summary_glasses" msgid="2872254734959842579">"Энэ апп таны <xliff:g id="DEVICE_NAME">%1$s</xliff:g>-н эдгээр зөвшөөрөлд хандах эрхтэй байх болно"</string> <string name="title_app_streaming" msgid="2270331024626446950">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>-д таны утаснаас энэ мэдээлэлд хандахыг зөвшөөрнө үү"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Төхөөрөмж хоорондын үйлчилгээ"</string> <string name="helper_summary_app_streaming" msgid="2396773196949578425">"Таны төхөөрөмжүүд хооронд апп дамжуулахын тулд <xliff:g id="APP_NAME">%1$s</xliff:g> таны <xliff:g id="DISPLAY_NAME">%2$s</xliff:g>-н өмнөөс зөвшөөрөл хүсэж байна"</string> @@ -38,10 +37,10 @@ <string name="title_nearby_device_streaming" msgid="7269956847378799794">"<strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong>-д энэ үйлдлийг хийхийг зөвшөөрөх үү?"</string> <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<xliff:g id="APP_NAME">%1$s</xliff:g> таны <xliff:g id="DEVICE_NAME">%2$s</xliff:g>-н өмнөөс аппууд болон системийн бусад онцлогийг ойролцоох төхөөрөмжүүд рүү дамжуулах зөвшөөрөл хүсэж байна"</string> <string name="profile_name_generic" msgid="6851028682723034988">"төхөөрөмж"</string> - <string name="summary_generic_single_device" msgid="4181180669689590417">"Энэ апп залгаж буй хүний нэр зэрэг мэдээллийг таны утас болон <xliff:g id="DEVICE_NAME">%1$s</xliff:g>-н хооронд синк хийх боломжтой болно"</string> <string name="summary_generic" msgid="1761976003668044801">"Энэ апп залгаж буй хүний нэр зэрэг мэдээллийг таны утас болон сонгосон төхөөрөмжийн хооронд синк хийх боломжтой болно"</string> <string name="consent_yes" msgid="8344487259618762872">"Зөвшөөрөх"</string> <string name="consent_no" msgid="2640796915611404382">"Бүү зөвшөөр"</string> + <string name="consent_cancel" msgid="5655005528379285841">"Цуцлах"</string> <string name="consent_back" msgid="2560683030046918882">"Буцах"</string> <string name="permission_expand" msgid="893185038020887411">"<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>-г дэлгэх"</string> <string name="permission_collapse" msgid="3320833884220844084">"<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>-г хураах"</string> diff --git a/packages/CompanionDeviceManager/res/values-mr/strings.xml b/packages/CompanionDeviceManager/res/values-mr/strings.xml index b94cfac65698..50ab307c9ca0 100644 --- a/packages/CompanionDeviceManager/res/values-mr/strings.xml +++ b/packages/CompanionDeviceManager/res/values-mr/strings.xml @@ -17,15 +17,14 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="4470785958457506021">"सहयोगी डिव्हाइस व्यवस्थापक"</string> - <string name="confirmation_title" msgid="4593465730772390351">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ला <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> अॅक्सेस करण्याची अनुमती द्यायची आहे का?"</string> + <string name="confirmation_title" msgid="2244241995958340998">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> अॅपला <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> अॅक्सेस करण्याची अनुमती द्यायची आहे का?"</string> <string name="profile_name_watch" msgid="576290739483672360">"वॉच"</string> - <string name="chooser_title" msgid="2262294130493605839">"<strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> द्वारे व्यवस्थापित करण्यासाठी <xliff:g id="PROFILE_NAME">%1$s</xliff:g> निवडा"</string> - <string name="summary_watch" msgid="898569637110705523">"तुमचे <xliff:g id="DEVICE_NAME">%1$s</xliff:g> व्यवस्थापित करण्यासाठी हे ॲप आवश्यक आहे. <xliff:g id="APP_NAME">%2$s</xliff:g> ला कॉल करत असलेल्या एखाद्या व्यक्तीचे नाव यासारखी माहिती सिंक करण्याची, तुमच्या सूचनांसोबत संवाद साधण्याची आणि तुमचा फोन, एसएमएस, संपर्क, कॅलेंडर, कॉल लॉग व जवळपासच्या डिव्हाइसच्या परवानग्या अॅक्सेस करण्याची अनुमती मिळेल."</string> - <string name="summary_watch_single_device" msgid="3173948915947011333">"या अॅपला कॉल करत असलेल्या एखाद्या व्यक्तीचे नाव यासारखी माहिती सिंक करण्याची आणि तुमच्या <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> वर पुढील परवानग्या अॅक्सेस करण्याची अनुमती दिली जाईल"</string> + <string name="chooser_title_non_profile" msgid="6035023914517087400">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> द्वारे व्यवस्थापित करण्यासाठी डिव्हाइस निवडा"</string> + <string name="chooser_title" msgid="2235819929238267637">"सेट करण्यासाठी <xliff:g id="PROFILE_NAME">%1$s</xliff:g> निवडा"</string> + <string name="summary_watch" msgid="7962014927042971830">"या अॅपला कॉल करत असलेल्या एखाद्या व्यक्तीचे नाव यासारखी माहिती सिंक करण्याची आणि तुमच्या <xliff:g id="DEVICE_NAME">%1$s</xliff:g> वर या परवानग्या अॅक्सेस करण्याची अनुमती असेल"</string> <string name="confirmation_title_glasses" msgid="8288346850537727333">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ला <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> व्यवस्थापित करण्याची अनुमती द्यायची आहे?"</string> - <string name="profile_name_glasses" msgid="8488394059007275998">"Glasses"</string> - <string name="summary_glasses_multi_device" msgid="615259525961937348">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> व्यवस्थापित करण्यासाठी हे ॲप आवश्यक आहे. <xliff:g id="APP_NAME">%2$s</xliff:g> ला तुमच्या सूचनांसोबत संवाद साधण्याची आणि तुमचा फोन, एसएमएस, संपर्क, मायक्रोफोन व जवळपासच्या डिव्हाइसच्या परवानग्या अॅक्सेस करण्याची अनुमती मिळेल."</string> - <string name="summary_glasses_single_device" msgid="3000909894067413398">"या अॅपला तुमच्या <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> वर या परवानग्या अॅक्सेस करण्याची अनुमती दिली जाईल"</string> + <string name="profile_name_glasses" msgid="3506504967216601277">"डिव्हाइस"</string> + <string name="summary_glasses" msgid="2872254734959842579">"या अॅपला तुमच्या <xliff:g id="DEVICE_NAME">%1$s</xliff:g> वर या परवानग्या अॅक्सेस करण्याची अनुमती असेल"</string> <string name="title_app_streaming" msgid="2270331024626446950">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ला ही माहिती तुमच्या फोनवरून अॅक्सेस करण्यासाठी अनुमती द्या"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"क्रॉस-डिव्हाइस सेवा"</string> <string name="helper_summary_app_streaming" msgid="2396773196949578425">"तुमच्या डिव्हाइसदरम्यान ॲप्स स्ट्रीम करण्यासाठी <xliff:g id="APP_NAME">%1$s</xliff:g> हे तुमच्या <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> च्या वतीने परवानगीची विनंती करत आहे"</string> @@ -38,10 +37,10 @@ <string name="title_nearby_device_streaming" msgid="7269956847378799794">"<strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> ला ही कृती करण्याची अनुमती द्यायची आहे का?"</string> <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<xliff:g id="APP_NAME">%1$s</xliff:g> हे जवळपासच्या डिव्हाइसवर अॅप्स आणि इतर सिस्टीम वैशिष्ट्ये स्ट्रीम करण्यासाठी तुमच्या <xliff:g id="DEVICE_NAME">%2$s</xliff:g> च्या वतीने परवानगीची विनंती करा"</string> <string name="profile_name_generic" msgid="6851028682723034988">"डिव्हाइस"</string> - <string name="summary_generic_single_device" msgid="4181180669689590417">"हे ॲप तुमचा फोन आणि <xliff:g id="DEVICE_NAME">%1$s</xliff:g> दरम्यान कॉल करत असलेल्या एखाद्या व्यक्तीचे नाव यासारखी माहिती सिंक करू शकेल"</string> <string name="summary_generic" msgid="1761976003668044801">"हे ॲप तुमचा फोन आणि निवडलेल्या डिव्हाइसदरम्यान कॉल करत असलेल्या एखाद्या व्यक्तीचे नाव यासारखी माहिती सिंक करू शकेल"</string> <string name="consent_yes" msgid="8344487259618762872">"अनुमती द्या"</string> <string name="consent_no" msgid="2640796915611404382">"अनुमती देऊ नका"</string> + <string name="consent_cancel" msgid="5655005528379285841">"रद्द करा"</string> <string name="consent_back" msgid="2560683030046918882">"मागे जा"</string> <string name="permission_expand" msgid="893185038020887411">"<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g> चा विस्तार करा"</string> <string name="permission_collapse" msgid="3320833884220844084">"<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g> कोलॅप्स करा"</string> diff --git a/packages/CompanionDeviceManager/res/values-ms/strings.xml b/packages/CompanionDeviceManager/res/values-ms/strings.xml index 93745c8a3666..ef110b61c9a2 100644 --- a/packages/CompanionDeviceManager/res/values-ms/strings.xml +++ b/packages/CompanionDeviceManager/res/values-ms/strings.xml @@ -17,15 +17,14 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="4470785958457506021">"Pengurus Peranti Rakan"</string> - <string name="confirmation_title" msgid="4593465730772390351">"Benarkan <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> untuk mengakses <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> + <string name="confirmation_title" msgid="2244241995958340998">"Benarkan apl <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> mengakses <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> <string name="profile_name_watch" msgid="576290739483672360">"jam tangan"</string> - <string name="chooser_title" msgid="2262294130493605839">"Pilih <xliff:g id="PROFILE_NAME">%1$s</xliff:g> untuk diurus oleh <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> - <string name="summary_watch" msgid="898569637110705523">"Apl ini diperlukan untuk mengurus <xliff:g id="DEVICE_NAME">%1$s</xliff:g> anda. <xliff:g id="APP_NAME">%2$s</xliff:g> akan dibenarkan untuk menyegerakkan maklumat seperti nama individu yang memanggil, berinteraksi dengan pemberitahuan anda dan mengakses kebenaran Telefon, SMS, Kenalan, Kalendar, Log panggilan dan Peranti berdekatan anda."</string> - <string name="summary_watch_single_device" msgid="3173948915947011333">"Apl ini akan dibenarkan untuk menyegerakkan maklumat seperti nama seseorang yang membuat panggilan dan mengakses kebenaran ini pada <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> anda"</string> + <string name="chooser_title_non_profile" msgid="6035023914517087400">"Pilih peranti untuk diurus oleh <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>"</string> + <string name="chooser_title" msgid="2235819929238267637">"Pilih <xliff:g id="PROFILE_NAME">%1$s</xliff:g> untuk disediakan"</string> + <string name="summary_watch" msgid="7962014927042971830">"Apl ini akan dibenarkan untuk menyegerakkan maklumat seperti nama individu yang membuat panggilan dan mengakses kebenaran ini pada <xliff:g id="DEVICE_NAME">%1$s</xliff:g> anda"</string> <string name="confirmation_title_glasses" msgid="8288346850537727333">"Benarkan <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> mengurus <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> - <string name="profile_name_glasses" msgid="8488394059007275998">"cermin mata"</string> - <string name="summary_glasses_multi_device" msgid="615259525961937348">"Apl ini diperlukan untuk mengurus <xliff:g id="DEVICE_NAME">%1$s</xliff:g>. <xliff:g id="APP_NAME">%2$s</xliff:g> akan dibenarkan untuk berinteraksi dengan pemberitahuan anda dan mengakses kebenaran Telefon, SMS, Kenalan, Mikrofon dan Peranti berdekatan anda."</string> - <string name="summary_glasses_single_device" msgid="3000909894067413398">"Apl ini akan dibenarkan untuk mengakses kebenaran yang berikut pada <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> anda"</string> + <string name="profile_name_glasses" msgid="3506504967216601277">"peranti"</string> + <string name="summary_glasses" msgid="2872254734959842579">"Apl ini akan dibenarkan untuk mengakses kebenaran yang berikut pada <xliff:g id="DEVICE_NAME">%1$s</xliff:g> anda"</string> <string name="title_app_streaming" msgid="2270331024626446950">"Benarkan <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> mengakses maklumat ini daripada telefon anda"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Perkhidmatan silang peranti"</string> <string name="helper_summary_app_streaming" msgid="2396773196949578425">"<xliff:g id="APP_NAME">%1$s</xliff:g> sedang meminta kebenaran bagi pihak <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> anda untuk menstrim apl antara peranti anda"</string> @@ -38,10 +37,10 @@ <string name="title_nearby_device_streaming" msgid="7269956847378799794">"Benarkan <strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> mengambil tindakan ini?"</string> <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<xliff:g id="APP_NAME">%1$s</xliff:g> sedang meminta kebenaran bagi pihak <xliff:g id="DEVICE_NAME">%2$s</xliff:g> anda untuk menstrim apl dan ciri sistem yang lain pada peranti berdekatan"</string> <string name="profile_name_generic" msgid="6851028682723034988">"peranti"</string> - <string name="summary_generic_single_device" msgid="4181180669689590417">"Apl ini akan dapat menyegerakkan maklumat seperti nama individu yang memanggil, antara telefon anda dengan <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="summary_generic" msgid="1761976003668044801">"Apl ini akan dapat menyegerakkan maklumat seperti nama individu yang memanggil, antara telefon anda dengan peranti yang dipilih"</string> <string name="consent_yes" msgid="8344487259618762872">"Benarkan"</string> <string name="consent_no" msgid="2640796915611404382">"Jangan benarkan"</string> + <string name="consent_cancel" msgid="5655005528379285841">"Batal"</string> <string name="consent_back" msgid="2560683030046918882">"Kembali"</string> <string name="permission_expand" msgid="893185038020887411">"Kembangkan <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string> <string name="permission_collapse" msgid="3320833884220844084">"Kuncupkan <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string> diff --git a/packages/CompanionDeviceManager/res/values-my/strings.xml b/packages/CompanionDeviceManager/res/values-my/strings.xml index 3f298a00bc4c..57f2e23ca936 100644 --- a/packages/CompanionDeviceManager/res/values-my/strings.xml +++ b/packages/CompanionDeviceManager/res/values-my/strings.xml @@ -17,15 +17,14 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="4470785958457506021">"တွဲဖက်ကိရိယာ မန်နေဂျာ"</string> - <string name="confirmation_title" msgid="4593465730772390351">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> အား <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>? သုံးခွင့်ပြုခြင်း"</string> + <string name="confirmation_title" msgid="2244241995958340998">"<strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> ကို သုံးရန် <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> အက်ပ်ကို ခွင့်ပြုမလား။"</string> <string name="profile_name_watch" msgid="576290739483672360">"နာရီ"</string> - <string name="chooser_title" msgid="2262294130493605839">"<strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> က စီမံခန့်ခွဲရန် <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ကို ရွေးချယ်ပါ"</string> - <string name="summary_watch" msgid="898569637110705523">"သင်၏ <xliff:g id="DEVICE_NAME">%1$s</xliff:g> ကို စီမံခန့်ခွဲရန် ဤအက်ပ်လိုအပ်သည်။ ခေါ်ဆိုသူ၏အမည်ကဲ့သို့ အချက်အလက်ကို စင့်ခ်လုပ်ရန်၊ သင်၏ဖုန်း၊ SMS စာတိုစနစ်၊ အဆက်အသွယ်များ၊ ပြက္ခဒိန်၊ ခေါ်ဆိုမှတ်တမ်းနှင့် အနီးတစ်ဝိုက်ရှိ စက်များဆိုင်ရာ ခွင့်ပြုချက်များသုံးရန်၊ အကြောင်းကြားချက်များနှင့် ပြန်လှန်တုံ့ပြန်ရန် <xliff:g id="APP_NAME">%2$s</xliff:g> ကို ခွင့်ပြုမည်။"</string> - <string name="summary_watch_single_device" msgid="3173948915947011333">"ခေါ်ဆိုသူ၏အမည်ကဲ့သို့ အချက်အလက်ကို စင့်ခ်လုပ်ရန်နှင့် သင့် <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> တွင် ၎င်းခွင့်ပြုချက်များရယူရန် ဤအက်ပ်ကိုခွင့်ပြုမည်"</string> + <string name="chooser_title_non_profile" msgid="6035023914517087400">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> က စီမံခန့်ခွဲရန် စက်တစ်ခုကို ရွေးပါ"</string> + <string name="chooser_title" msgid="2235819929238267637">"စနစ်ထည့်သွင်းရန် <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ကို ရွေးပါ"</string> + <string name="summary_watch" msgid="7962014927042971830">"ခေါ်ဆိုသူ၏အမည်ကဲ့သို့ အချက်အလက်ကို စင့်ခ်လုပ်ရန်နှင့် သင့် <xliff:g id="DEVICE_NAME">%1$s</xliff:g> တွင် ၎င်းခွင့်ပြုချက်များရယူရန် ဤအက်ပ်ကိုခွင့်ပြုမည်"</string> <string name="confirmation_title_glasses" msgid="8288346850537727333">"<strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> ကို <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> အား စီမံခွင့်ပြုမလား။"</string> - <string name="profile_name_glasses" msgid="8488394059007275998">"မျက်မှန်"</string> - <string name="summary_glasses_multi_device" msgid="615259525961937348">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> ကို စီမံခန့်ခွဲရန် ဤအက်ပ်လိုအပ်သည်။ သင်၏ဖုန်း၊ SMS စာတိုစနစ်၊ အဆက်အသွယ်များ၊ မိုက်ခရိုဖုန်းနှင့် အနီးတစ်ဝိုက်ရှိ စက်များဆိုင်ရာ ခွင့်ပြုချက်များသုံးရန်၊ အကြောင်းကြားချက်များနှင့် ပြန်လှန်တုံ့ပြန်ရန် <xliff:g id="APP_NAME">%2$s</xliff:g> ကို ခွင့်ပြုမည်။"</string> - <string name="summary_glasses_single_device" msgid="3000909894067413398">"သင့် <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> တွင် ၎င်းခွင့်ပြုချက်များရယူရန် ဤအက်ပ်ကိုခွင့်ပြုမည်"</string> + <string name="profile_name_glasses" msgid="3506504967216601277">"စက်"</string> + <string name="summary_glasses" msgid="2872254734959842579">"သင့် <xliff:g id="DEVICE_NAME">%1$s</xliff:g> တွင် ၎င်းခွင့်ပြုချက်များရယူရန် ဤအက်ပ်ကိုခွင့်ပြုမည်"</string> <string name="title_app_streaming" msgid="2270331024626446950">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ကို သင့်ဖုန်းမှ ဤအချက်အလက် သုံးခွင့်ပြုမည်"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"စက်များကြားသုံး ဝန်ဆောင်မှုများ"</string> <string name="helper_summary_app_streaming" msgid="2396773196949578425">"<xliff:g id="APP_NAME">%1$s</xliff:g> သည် သင်၏စက်များအကြား အက်ပ်များတိုက်ရိုက်လွှင့်ရန် <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> ကိုယ်စား ခွင့်ပြုချက်တောင်းနေသည်"</string> @@ -38,10 +37,10 @@ <string name="title_nearby_device_streaming" msgid="7269956847378799794">"<strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> ကို ဤသို့လုပ်ဆောင်ခွင့်ပြုမလား။"</string> <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<xliff:g id="APP_NAME">%1$s</xliff:g> သည် အနီးတစ်ဝိုက်ရှိ အက်ပ်များနှင့် အခြားစနစ်အင်္ဂါရပ်များကို တိုက်ရိုက်ဖွင့်ရန် သင့် <xliff:g id="DEVICE_NAME">%2$s</xliff:g> ကိုယ်စား ခွင့်ပြုချက်တောင်းနေသည်"</string> <string name="profile_name_generic" msgid="6851028682723034988">"စက်"</string> - <string name="summary_generic_single_device" msgid="4181180669689590417">"ဤအက်ပ်သည် သင့်ဖုန်းနှင့် <xliff:g id="DEVICE_NAME">%1$s</xliff:g> အကြား ခေါ်ဆိုသူ၏အမည်ကဲ့သို့ အချက်အလက်ကို စင့်ခ်လုပ်နိုင်ပါမည်"</string> <string name="summary_generic" msgid="1761976003668044801">"ဤအက်ပ်သည် သင့်ဖုန်းနှင့် ရွေးထားသောစက်အကြား ခေါ်ဆိုသူ၏အမည်ကဲ့သို့ အချက်အလက်ကို စင့်ခ်လုပ်နိုင်ပါမည်"</string> <string name="consent_yes" msgid="8344487259618762872">"ခွင့်ပြုရန်"</string> <string name="consent_no" msgid="2640796915611404382">"ခွင့်မပြုပါ"</string> + <string name="consent_cancel" msgid="5655005528379285841">"မလုပ်တော့"</string> <string name="consent_back" msgid="2560683030046918882">"နောက်သို့"</string> <string name="permission_expand" msgid="893185038020887411">"<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g> ကို ပိုပြရန်"</string> <string name="permission_collapse" msgid="3320833884220844084">"<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g> ကို လျှော့ပြရန်"</string> diff --git a/packages/CompanionDeviceManager/res/values-nb/strings.xml b/packages/CompanionDeviceManager/res/values-nb/strings.xml index 2c794d9c4b46..083ec99b0286 100644 --- a/packages/CompanionDeviceManager/res/values-nb/strings.xml +++ b/packages/CompanionDeviceManager/res/values-nb/strings.xml @@ -17,15 +17,14 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string> - <string name="confirmation_title" msgid="4593465730772390351">"Gi <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> tilgang til <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> + <string name="confirmation_title" msgid="2244241995958340998">"Vil du gi <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>-appen tilgang til <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> <string name="profile_name_watch" msgid="576290739483672360">"klokke"</string> - <string name="chooser_title" msgid="2262294130493605839">"Velg <xliff:g id="PROFILE_NAME">%1$s</xliff:g> som skal administreres av <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> - <string name="summary_watch" msgid="898569637110705523">"Denne appen kreves for å administrere <xliff:g id="DEVICE_NAME">%1$s</xliff:g>. <xliff:g id="APP_NAME">%2$s</xliff:g> får tillatelse til å synkronisere informasjon som navnet til noen som ringer, og samhandle med varslene dine, og får tilgang til tillatelsene for telefon, SMS, kontakter, kalender, samtalelogger og enheter i nærheten."</string> - <string name="summary_watch_single_device" msgid="3173948915947011333">"Denne appen får tillatelse til å synkronisere informasjon som navnet til noen som ringer, og har disse tillatelsene på din/ditt <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string> + <string name="chooser_title_non_profile" msgid="6035023914517087400">"Velg en enhet som skal administreres av <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>"</string> + <string name="chooser_title" msgid="2235819929238267637">"Velg <xliff:g id="PROFILE_NAME">%1$s</xliff:g> som skal konfigureres"</string> + <string name="summary_watch" msgid="7962014927042971830">"Denne appen får tillatelse til å synkronisere informasjon som navnet til noen som ringer, og har disse tillatelsene på <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="confirmation_title_glasses" msgid="8288346850537727333">"Vil du la <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> administrere <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> - <string name="profile_name_glasses" msgid="8488394059007275998">"briller"</string> - <string name="summary_glasses_multi_device" msgid="615259525961937348">"Denne appen kreves for å administrere <xliff:g id="DEVICE_NAME">%1$s</xliff:g>. <xliff:g id="APP_NAME">%2$s</xliff:g> får tilgang til varslene dine og får tillatelsene for telefon, SMS, kontakter, mikrofon og enheter i nærheten."</string> - <string name="summary_glasses_single_device" msgid="3000909894067413398">"Denne appen får disse tillatelsene på din/ditt <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string> + <string name="profile_name_glasses" msgid="3506504967216601277">"enheten"</string> + <string name="summary_glasses" msgid="2872254734959842579">"Denne appen får disse tillatelsene på <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="title_app_streaming" msgid="2270331024626446950">"Gi <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> tilgang til denne informasjonen fra telefonen din"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Tjenester på flere enheter"</string> <string name="helper_summary_app_streaming" msgid="2396773196949578425">"<xliff:g id="APP_NAME">%1$s</xliff:g> ber om tillatelse til å strømme apper mellom enhetene dine, på vegne av <xliff:g id="DISPLAY_NAME">%2$s</xliff:g>"</string> @@ -38,10 +37,10 @@ <string name="title_nearby_device_streaming" msgid="7269956847378799794">"Vil du la <strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> gjøre dette?"</string> <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<xliff:g id="APP_NAME">%1$s</xliff:g> ber om tillatelse på vegne av <xliff:g id="DEVICE_NAME">%2$s</xliff:g> til å strømme apper og andre systemfunksjoner til enheter i nærheten"</string> <string name="profile_name_generic" msgid="6851028682723034988">"enhet"</string> - <string name="summary_generic_single_device" msgid="4181180669689590417">"Denne appen kan synkronisere informasjon som navnet til noen som ringer, mellom telefonen og <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="summary_generic" msgid="1761976003668044801">"Denne appen kan synkronisere informasjon som navnet til noen som ringer, mellom telefonen og den valgte enheten"</string> <string name="consent_yes" msgid="8344487259618762872">"Tillat"</string> <string name="consent_no" msgid="2640796915611404382">"Ikke tillat"</string> + <string name="consent_cancel" msgid="5655005528379285841">"Avbryt"</string> <string name="consent_back" msgid="2560683030046918882">"Tilbake"</string> <string name="permission_expand" msgid="893185038020887411">"Vis <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string> <string name="permission_collapse" msgid="3320833884220844084">"Skjul <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string> diff --git a/packages/CompanionDeviceManager/res/values-ne/strings.xml b/packages/CompanionDeviceManager/res/values-ne/strings.xml index 1f4506a17c18..7a1df5f7b2be 100644 --- a/packages/CompanionDeviceManager/res/values-ne/strings.xml +++ b/packages/CompanionDeviceManager/res/values-ne/strings.xml @@ -17,15 +17,14 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="4470785958457506021">"सहयोगी डिभाइसको प्रबन्धक"</string> - <string name="confirmation_title" msgid="4593465730772390351">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> लाई <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> प्रयोग गर्ने अनुमति दिने हो?"</string> + <string name="confirmation_title" msgid="2244241995958340998">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> एपलाई <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> प्रयोग गर्ने अनुमति दिने हो?"</string> <string name="profile_name_watch" msgid="576290739483672360">"घडी"</string> - <string name="chooser_title" msgid="2262294130493605839">"आफूले <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> प्रयोग गरी व्यवस्थापन गर्न चाहेको <xliff:g id="PROFILE_NAME">%1$s</xliff:g> चयन गर्नुहोस्"</string> - <string name="summary_watch" msgid="898569637110705523">"तपाईंको <xliff:g id="DEVICE_NAME">%1$s</xliff:g> व्यवस्थापन गर्न यो एप चाहिन्छ। <xliff:g id="APP_NAME">%2$s</xliff:g> लाई कल गर्ने व्यक्तिको नाम जस्ता जानकारी सिंक गर्ने, तपाईंका सूचना हेर्ने र फोन, SMS, कन्ट्याक्ट, पात्रो, कल लग तथा नजिकैका डिभाइससम्बन्धी अनुमतिहरू हेर्ने तथा प्रयोग गर्ने अनुमति दिइने छ।"</string> - <string name="summary_watch_single_device" msgid="3173948915947011333">"तपाईंको <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> मा यो एपलाई कल गर्ने व्यक्तिको नाम जस्ता जानकारी सिंक गर्ने र यी कुराहरू गर्ने अनुमति दिइने छ"</string> + <string name="chooser_title_non_profile" msgid="6035023914517087400">"आफूले <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> प्रयोग गरी व्यवस्थापन गर्न चाहेको डिभाइस चयन गर्नुहोस्"</string> + <string name="chooser_title" msgid="2235819929238267637">"सेट अप गर्नका लागि <xliff:g id="PROFILE_NAME">%1$s</xliff:g> छनौट गर्नुहोस्"</string> + <string name="summary_watch" msgid="7962014927042971830">"तपाईंको <xliff:g id="DEVICE_NAME">%1$s</xliff:g> मा यो एपलाई कल गर्ने व्यक्तिको नाम जस्ता जानकारी सिंक गर्ने र यी कुराहरू गर्ने अनुमति दिइने छ"</string> <string name="confirmation_title_glasses" msgid="8288346850537727333">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> लाई <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> व्यवस्थापन गर्ने अनुमति दिने हो?"</string> - <string name="profile_name_glasses" msgid="8488394059007275998">"चस्मा"</string> - <string name="summary_glasses_multi_device" msgid="615259525961937348">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> व्यवस्थापन गर्न यो एप चाहिन्छ। <xliff:g id="APP_NAME">%2$s</xliff:g> लाई तपाईंका सूचना हेर्ने र फोन, SMS, कन्ट्याक्ट, माइक्रोफोन तथा नजिकैका डिभाइससम्बन्धी अनुमतिहरू हेर्ने तथा प्रयोग गर्ने अनुमति दिइने छ।"</string> - <string name="summary_glasses_single_device" msgid="3000909894067413398">"तपाईंको <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> मा यो एपलाई निम्न अनुमति दिइने छ:"</string> + <string name="profile_name_glasses" msgid="3506504967216601277">"डिभाइस"</string> + <string name="summary_glasses" msgid="2872254734959842579">"तपाईंको <xliff:g id="DEVICE_NAME">%1$s</xliff:g> मा यो एपलाई निम्न अनुमति दिइने छ:"</string> <string name="title_app_streaming" msgid="2270331024626446950">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> लाई तपाईंको फोनमा भएको यो जानकारी हेर्ने तथा प्रयोग गर्ने अनुमति दिनुहोस्"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"क्रस-डिभाइस सेवाहरू"</string> <string name="helper_summary_app_streaming" msgid="2396773196949578425">"<xliff:g id="APP_NAME">%1$s</xliff:g> तपाईंको डिभाइस <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> को तर्फबाट तपाईंका कुनै एउटा डिभाइसबाट अर्को डिभाइसमा एप स्ट्रिम गर्ने अनुमति माग्दै छ"</string> @@ -38,10 +37,10 @@ <string name="title_nearby_device_streaming" msgid="7269956847378799794">"<strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> लाई यो कार्य गर्ने अनुमति दिने हो?"</string> <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<xliff:g id="APP_NAME">%1$s</xliff:g> तपाईंको डिभाइस <xliff:g id="DEVICE_NAME">%2$s</xliff:g> को तर्फबाट नजिकैका डिभाइसहरूमा एप र सिस्टमका अन्य सुविधाहरू स्ट्रिम गर्ने अनुमति माग्दै छ"</string> <string name="profile_name_generic" msgid="6851028682723034988">"यन्त्र"</string> - <string name="summary_generic_single_device" msgid="4181180669689590417">"यो एपले तपाईंको फोन र तपाईंले छनौट गर्ने <xliff:g id="DEVICE_NAME">%1$s</xliff:g> का बिचमा कल गर्ने व्यक्तिको नाम जस्ता जानकारी सिंक गर्न सक्ने छ।"</string> <string name="summary_generic" msgid="1761976003668044801">"यो एपले तपाईंको फोन र तपाईंले छनौट गर्ने डिभाइसका बिचमा कल गर्ने व्यक्तिको नाम जस्ता जानकारी सिंक गर्न सक्ने छ।"</string> <string name="consent_yes" msgid="8344487259618762872">"अनुमति दिनुहोस्"</string> <string name="consent_no" msgid="2640796915611404382">"अनुमति नदिनुहोस्"</string> + <string name="consent_cancel" msgid="5655005528379285841">"रद्द गर्नुहोस्"</string> <string name="consent_back" msgid="2560683030046918882">"पछाडि"</string> <string name="permission_expand" msgid="893185038020887411">"<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g> एक्स्पान्ड गर्नुहोस्"</string> <string name="permission_collapse" msgid="3320833884220844084">"<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g> कोल्याप्स गर्नुहोस्"</string> diff --git a/packages/CompanionDeviceManager/res/values-nl/strings.xml b/packages/CompanionDeviceManager/res/values-nl/strings.xml index 7c79cb4039a2..71d14e7411ed 100644 --- a/packages/CompanionDeviceManager/res/values-nl/strings.xml +++ b/packages/CompanionDeviceManager/res/values-nl/strings.xml @@ -17,15 +17,14 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string> - <string name="confirmation_title" msgid="4593465730772390351">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> toegang geven tot <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> + <string name="confirmation_title" msgid="2244241995958340998">"De app <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> toegang geven tot <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> <string name="profile_name_watch" msgid="576290739483672360">"smartwatch"</string> - <string name="chooser_title" msgid="2262294130493605839">"Een <xliff:g id="PROFILE_NAME">%1$s</xliff:g> kiezen om te beheren met <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> - <string name="summary_watch" msgid="898569637110705523">"Deze app is vereist om je <xliff:g id="DEVICE_NAME">%1$s</xliff:g> te beheren. <xliff:g id="APP_NAME">%2$s</xliff:g> mag informatie (zoals de naam van iemand die belt) synchroniseren, mag interactie hebben met je meldingen en krijgt toegang tot de rechten Telefoon, Sms, Contacten, Agenda, Gesprekslijsten en Apparaten in de buurt."</string> - <string name="summary_watch_single_device" msgid="3173948915947011333">"Deze app kan informatie synchroniseren (zoals de naam van iemand die belt) en krijgt toegang tot deze rechten op je <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string> + <string name="chooser_title_non_profile" msgid="6035023914517087400">"Een apparaat kiezen om te beheren met <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>"</string> + <string name="chooser_title" msgid="2235819929238267637">"Een <xliff:g id="PROFILE_NAME">%1$s</xliff:g> kiezen om in te stellen"</string> + <string name="summary_watch" msgid="7962014927042971830">"Deze app kan informatie synchroniseren (zoals de naam van iemand die belt) en krijgt toegang tot deze rechten op je <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="confirmation_title_glasses" msgid="8288346850537727333">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> toestaan <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> te beheren?"</string> - <string name="profile_name_glasses" msgid="8488394059007275998">"brillen"</string> - <string name="summary_glasses_multi_device" msgid="615259525961937348">"Deze app is nodig om <xliff:g id="DEVICE_NAME">%1$s</xliff:g> te beheren. <xliff:g id="APP_NAME">%2$s</xliff:g> mag interactie hebben met je meldingen en krijgt toegang tot de rechten voor Telefoon, Sms, Contacten, Microfoon en Apparaten in de buurt."</string> - <string name="summary_glasses_single_device" msgid="3000909894067413398">"Deze app krijgt toegang tot deze rechten op je <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string> + <string name="profile_name_glasses" msgid="3506504967216601277">"apparaat"</string> + <string name="summary_glasses" msgid="2872254734959842579">"Deze app krijgt toegang tot deze rechten op je <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="title_app_streaming" msgid="2270331024626446950">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> toegang geven tot deze informatie op je telefoon"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Cross-device-services"</string> <string name="helper_summary_app_streaming" msgid="2396773196949578425">"<xliff:g id="APP_NAME">%1$s</xliff:g> vraagt namens jouw <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> toestemming om apps te streamen tussen je apparaten"</string> @@ -38,10 +37,10 @@ <string name="title_nearby_device_streaming" msgid="7269956847378799794">"Toestaan dat <strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> deze actie uitvoert?"</string> <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<xliff:g id="APP_NAME">%1$s</xliff:g> vraagt namens je <xliff:g id="DEVICE_NAME">%2$s</xliff:g> toestemming om apps en andere systeemfuncties naar apparaten in de buurt te streamen"</string> <string name="profile_name_generic" msgid="6851028682723034988">"apparaat"</string> - <string name="summary_generic_single_device" msgid="4181180669689590417">"Deze app kan informatie, zoals de naam van iemand die belt, synchroniseren tussen je telefoon en <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="summary_generic" msgid="1761976003668044801">"Deze app kan informatie, zoals de naam van iemand die belt, synchroniseren tussen je telefoon en het gekozen apparaat"</string> <string name="consent_yes" msgid="8344487259618762872">"Toestaan"</string> <string name="consent_no" msgid="2640796915611404382">"Niet toestaan"</string> + <string name="consent_cancel" msgid="5655005528379285841">"Annuleren"</string> <string name="consent_back" msgid="2560683030046918882">"Terug"</string> <string name="permission_expand" msgid="893185038020887411">"<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g> uitvouwen"</string> <string name="permission_collapse" msgid="3320833884220844084">"<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g> samenvouwen"</string> diff --git a/packages/CompanionDeviceManager/res/values-or/strings.xml b/packages/CompanionDeviceManager/res/values-or/strings.xml index dbff4e213690..a058374dd0d4 100644 --- a/packages/CompanionDeviceManager/res/values-or/strings.xml +++ b/packages/CompanionDeviceManager/res/values-or/strings.xml @@ -17,15 +17,14 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="4470785958457506021">"ସହଯୋଗୀ ଡିଭାଇସ୍ ପରିଚାଳକ"</string> - <string name="confirmation_title" msgid="4593465730772390351">"<strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>କୁ ଆକ୍ସେସ କରିବା ପାଇଁ <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>କୁ ଅନୁମତି ଦେବେ?"</string> + <string name="confirmation_title" msgid="2244241995958340998">"<strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>କୁ ଆକ୍ସେସ କରିବା ପାଇଁ <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ଆପକୁ ଅନୁମତି ଦେବେ?"</string> <string name="profile_name_watch" msgid="576290739483672360">"ୱାଚ୍"</string> - <string name="chooser_title" msgid="2262294130493605839">"<strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> ଦ୍ୱାରା ପରିଚାଳିତ ହେବା ପାଇଁ ଏକ <xliff:g id="PROFILE_NAME">%1$s</xliff:g>କୁ ବାଛନ୍ତୁ"</string> - <string name="summary_watch" msgid="898569637110705523">"ଆପଣଙ୍କ <xliff:g id="DEVICE_NAME">%1$s</xliff:g>କୁ ପରିଚାଳନା କରିବା ପାଇଁ ଏହି ଆପ ଆବଶ୍ୟକ। କଲ କରୁଥିବା ଯେ କୌଣସି ବ୍ୟକ୍ତିଙ୍କ ନାମ ପରି ସୂଚନା ସିଙ୍କ କରିବା, ଆପଣଙ୍କ ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକ ସହ ଇଣ୍ଟରାକ୍ଟ କରିବା ଏବଂ ଆପଣଙ୍କର ଫୋନ, SMS, କଣ୍ଟାକ୍ଟ, କେଲେଣ୍ଡର, କଲ ଲଗ ଓ ଆଖପାଖର ଡିଭାଇସ ଅନୁମତିଗୁଡ଼ିକୁ ଆକ୍ସେସ କରିବା ପାଇଁ <xliff:g id="APP_NAME">%2$s</xliff:g>କୁ ଅନୁମତି ଦିଆଯିବ।"</string> - <string name="summary_watch_single_device" msgid="3173948915947011333">"କଲ କରୁଥିବା ଯେ କୌଣସି ବ୍ୟକ୍ତିଙ୍କ ନାମ ପରି ସୂଚନା ସିଙ୍କ କରିବାକୁ ଏବଂ ଆପଣଙ୍କ <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>ରେ ଏହି ଅନୁମତିଗୁଡ଼ିକୁ ଆକ୍ସେସ କରିବା ପାଇଁ ଏହି ଆପକୁ ଅନୁମତି ଦିଆଯିବ"</string> + <string name="chooser_title_non_profile" msgid="6035023914517087400">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ଦ୍ୱାରା ପରିଚାଳିତ ହେବା ପାଇଁ ଏକ ଡିଭାଇସ ବାଛନ୍ତୁ"</string> + <string name="chooser_title" msgid="2235819929238267637">"ସେଟ ଅପ କରିବାକୁ ଏକ <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ବାଛନ୍ତୁ"</string> + <string name="summary_watch" msgid="7962014927042971830">"କଲ କରୁଥିବା ଯେ କୌଣସି ବ୍ୟକ୍ତିଙ୍କ ନାମ ପରି ସୂଚନା ସିଙ୍କ କରିବାକୁ ଏବଂ ଆପଣଙ୍କ <xliff:g id="DEVICE_NAME">%1$s</xliff:g>ରେ ଏହି ଅନୁମତିଗୁଡ଼ିକୁ ଆକ୍ସେସ କରିବା ପାଇଁ ଏହି ଆପକୁ ଅନୁମତି ଦିଆଯିବ"</string> <string name="confirmation_title_glasses" msgid="8288346850537727333">"<strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>କୁ ପରିଚାଳନା କରିବା ପାଇଁ <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>କୁ ଅନୁମତି ଦେବେ?"</string> - <string name="profile_name_glasses" msgid="8488394059007275998">"ଚଷମା"</string> - <string name="summary_glasses_multi_device" msgid="615259525961937348">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>କୁ ପରିଚାଳନା କରିବା ପାଇଁ ଏହି ଆପ ଆବଶ୍ୟକ। ଆପଣଙ୍କ ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକ ସହ ଇଣ୍ଟରାକ୍ଟ କରିବା ଏବଂ ଆପଣଙ୍କର ଫୋନ, SMS, କଣ୍ଟାକ୍ଟ, ମାଇକ୍ରୋଫୋନ ଓ ଆଖପାଖର ଡିଭାଇସ ଅନୁମତିଗୁଡ଼ିକୁ ଆକ୍ସେସ କରିବା ପାଇଁ <xliff:g id="APP_NAME">%2$s</xliff:g>କୁ ଅନୁମତି ଦିଆଯିବ।"</string> - <string name="summary_glasses_single_device" msgid="3000909894067413398">"ଆପଣଙ୍କ <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>ରେ ଏହି ଅନୁମତିଗୁଡ଼ିକୁ ଆକ୍ସେସ କରିବା ପାଇଁ ଏହି ଆପକୁ ଅନୁମତି ଦିଆଯିବ"</string> + <string name="profile_name_glasses" msgid="3506504967216601277">"ଡିଭାଇସ"</string> + <string name="summary_glasses" msgid="2872254734959842579">"ଆପଣଙ୍କ <xliff:g id="DEVICE_NAME">%1$s</xliff:g>ରେ ଏହି ଅନୁମତିଗୁଡ଼ିକୁ ଆକ୍ସେସ କରିବା ପାଇଁ ଏହି ଆପକୁ ଅନୁମତି ଦିଆଯିବ"</string> <string name="title_app_streaming" msgid="2270331024626446950">"ଆପଣଙ୍କ ଫୋନରୁ ଏହି ସୂଚନାକୁ ଆକ୍ସେସ କରିବା ପାଇଁ <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>କୁ ଅନୁମତି ଦିଅନ୍ତୁ"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"କ୍ରସ-ଡିଭାଇସ ସେବାଗୁଡ଼ିକ"</string> <string name="helper_summary_app_streaming" msgid="2396773196949578425">"ଆପଣଙ୍କ ଡିଭାଇସଗୁଡ଼ିକ ମଧ୍ୟରେ ଆପ୍ସକୁ ଷ୍ଟ୍ରିମ କରିବା ପାଇଁ <xliff:g id="APP_NAME">%1$s</xliff:g> ଆପଣଙ୍କର <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> ତରଫରୁ ଅନୁମତି ପାଇଁ ଅନୁରୋଧ କରୁଛି"</string> @@ -38,10 +37,10 @@ <string name="title_nearby_device_streaming" msgid="7269956847378799794">"ଏହି ପଦକ୍ଷେପ ନେବା ପାଇଁ <strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong>କୁ ଅନୁମତି ଦେବେ?"</string> <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"ଆଖପାଖର ଡିଭାଇସଗୁଡ଼ିକରେ ଆପ୍ସ ଏବଂ ଅନ୍ୟ ସିଷ୍ଟମ ଫିଚରଗୁଡ଼ିକୁ ଷ୍ଟ୍ରିମ କରିବା ପାଇଁ <xliff:g id="APP_NAME">%1$s</xliff:g> ଆପଣଙ୍କ <xliff:g id="DEVICE_NAME">%2$s</xliff:g> ତରଫରୁ ଅନୁମତି ପାଇଁ ଅନୁରୋଧ କରୁଛି"</string> <string name="profile_name_generic" msgid="6851028682723034988">"ଡିଭାଇସ୍"</string> - <string name="summary_generic_single_device" msgid="4181180669689590417">"ଆପଣଙ୍କ ଫୋନ ଏବଂ <xliff:g id="DEVICE_NAME">%1$s</xliff:g> ମଧ୍ୟରେ, କଲ କରୁଥିବା ଯେ କୌଣସି ବ୍ୟକ୍ତିଙ୍କ ନାମ ପରି ସୂଚନା ସିଙ୍କ କରିବାକୁ ଏହି ଆପ ସକ୍ଷମ ହେବ"</string> <string name="summary_generic" msgid="1761976003668044801">"ଆପଣଙ୍କ ଫୋନ ଏବଂ ବଛାଯାଇଥିବା ଡିଭାଇସ ମଧ୍ୟରେ, କଲ କରୁଥିବା ଯେ କୌଣସି ବ୍ୟକ୍ତିଙ୍କ ନାମ ପରି ସୂଚନା ସିଙ୍କ କରିବାକୁ ଏହି ଆପ ସକ୍ଷମ ହେବ"</string> <string name="consent_yes" msgid="8344487259618762872">"ଅନୁମତି ଦିଅନ୍ତୁ"</string> <string name="consent_no" msgid="2640796915611404382">"ଅନୁମତି ଦିଅନ୍ତୁ ନାହିଁ"</string> + <string name="consent_cancel" msgid="5655005528379285841">"ବାତିଲ କରନ୍ତୁ"</string> <string name="consent_back" msgid="2560683030046918882">"ପଛକୁ ଫେରନ୍ତୁ"</string> <string name="permission_expand" msgid="893185038020887411">"<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>କୁ ବିସ୍ତାର କରନ୍ତୁ"</string> <string name="permission_collapse" msgid="3320833884220844084">"<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>କୁ ସଙ୍କୁଚିତ କରନ୍ତୁ"</string> diff --git a/packages/CompanionDeviceManager/res/values-pa/strings.xml b/packages/CompanionDeviceManager/res/values-pa/strings.xml index 91b5264f1334..984ec4884dbb 100644 --- a/packages/CompanionDeviceManager/res/values-pa/strings.xml +++ b/packages/CompanionDeviceManager/res/values-pa/strings.xml @@ -17,15 +17,14 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="4470785958457506021">"ਸੰਬੰਧੀ ਡੀਵਾਈਸ ਪ੍ਰਬੰਧਕ"</string> - <string name="confirmation_title" msgid="4593465730772390351">"ਕੀ <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ਨੂੰ <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> ਤੱਕ ਪਹੁੰਚ ਕਰਨ ਦੀ ਆਗਿਆ ਦੇਣੀ ਹੈ?"</string> + <string name="confirmation_title" msgid="2244241995958340998">"ਕੀ <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ਐਪ ਨੂੰ <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> ਤੱਕ ਪਹੁੰਚ ਕਰਨ ਦੀ ਆਗਿਆ ਦੇਣੀ ਹੈ?"</string> <string name="profile_name_watch" msgid="576290739483672360">"ਸਮਾਰਟ-ਵਾਚ"</string> - <string name="chooser_title" msgid="2262294130493605839">"<strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> ਵੱਲੋਂ ਪ੍ਰਬੰਧਿਤ ਕੀਤੇ ਜਾਣ ਲਈ <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ਚੁਣੋ"</string> - <string name="summary_watch" msgid="898569637110705523">"ਇਹ ਐਪ ਤੁਹਾਡੇ <xliff:g id="DEVICE_NAME">%1$s</xliff:g> ਦਾ ਪ੍ਰਬੰਧਨ ਕਰਨ ਲਈ ਲੋੜੀਂਦੀ ਹੈ। <xliff:g id="APP_NAME">%2$s</xliff:g> ਨੂੰ ਕਾਲਰ ਦੇ ਨਾਮ ਵਰਗੀ ਜਾਣਕਾਰੀ ਨੂੰ ਸਿੰਕ ਕਰਨ, ਤੁਹਾਡੀਆਂ ਸੂਚਨਾਵਾਂ ਨਾਲ ਅੰਤਰਕਿਰਿਆ ਕਰਨ ਅਤੇ ਤੁਹਾਡੇ ਫ਼ੋਨ, SMS, ਸੰਪਰਕਾਂ, ਕੈਲੰਡਰ, ਕਾਲ ਲੌਗਾਂ ਅਤੇ ਨਜ਼ਦੀਕੀ ਡੀਵਾਈਸਾਂ ਸੰਬੰਧੀ ਇਜਾਜ਼ਤਾਂ ਤੱਕ ਪਹੁੰਚ ਕਰਨ ਦੀ ਆਗਿਆ ਹੋਵੇਗੀ।"</string> - <string name="summary_watch_single_device" msgid="3173948915947011333">"ਇਸ ਐਪ ਨੂੰ ਤੁਹਾਡੇ <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> \'ਤੇ ਕਾਲਰ ਦੇ ਨਾਮ ਵਰਗੀ ਜਾਣਕਾਰੀ ਨੂੰ ਸਿੰਕ ਕਰਨ ਅਤੇ ਇਨ੍ਹਾਂ ਇਜਾਜ਼ਤਾਂ ਤੱਕ ਪਹੁੰਚ ਕਰਨ ਦੀ ਆਗਿਆ ਹੋਵੇਗੀ"</string> + <string name="chooser_title_non_profile" msgid="6035023914517087400">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ਵੱਲੋਂ ਪ੍ਰਬੰਧਿਤ ਕੀਤੇ ਜਾਣ ਲਈ ਕੋਈ ਡੀਵਾਈਸ ਚੁਣੋ"</string> + <string name="chooser_title" msgid="2235819929238267637">"ਸੈੱਟਅੱਪ ਕਰਨ ਲਈ <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ਚੁਣੋ"</string> + <string name="summary_watch" msgid="7962014927042971830">"ਇਸ ਐਪ ਨੂੰ ਤੁਹਾਡੇ <xliff:g id="DEVICE_NAME">%1$s</xliff:g> \'ਤੇ ਕਾਲਰ ਦੇ ਨਾਮ ਵਰਗੀ ਜਾਣਕਾਰੀ ਨੂੰ ਸਿੰਕ ਕਰਨ ਅਤੇ ਇਨ੍ਹਾਂ ਇਜਾਜ਼ਤਾਂ ਤੱਕ ਪਹੁੰਚ ਕਰਨ ਦੀ ਆਗਿਆ ਹੋਵੇਗੀ"</string> <string name="confirmation_title_glasses" msgid="8288346850537727333">"ਕੀ <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ਨੂੰ <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> ਦਾ ਪ੍ਰਬੰਧਨ ਕਰਨ ਦੀ ਆਗਿਆ ਦੇਣੀ ਹੈ?"</string> - <string name="profile_name_glasses" msgid="8488394059007275998">"ਐਨਕਾਂ"</string> - <string name="summary_glasses_multi_device" msgid="615259525961937348">"ਇਹ ਐਪ <xliff:g id="DEVICE_NAME">%1$s</xliff:g> ਦਾ ਪ੍ਰਬੰਧਨ ਕਰਨ ਲਈ ਲੋੜੀਂਦੀ ਹੈ। <xliff:g id="APP_NAME">%2$s</xliff:g> ਨੂੰ ਤੁਹਾਡੀਆਂ ਸੂਚਨਾਵਾਂ ਨਾਲ ਅੰਤਰਕਿਰਿਆ ਕਰਨ ਅਤੇ ਤੁਹਾਡੇ ਫ਼ੋਨ, SMS, ਸੰਪਰਕਾਂ, ਮਾਈਕ੍ਰੋਫ਼ੋਨ ਅਤੇ ਨਜ਼ਦੀਕੀ ਡੀਵਾਈਸਾਂ ਸੰਬੰਧੀ ਇਜਾਜ਼ਤਾਂ ਤੱਕ ਪਹੁੰਚ ਕਰਨ ਦੀ ਆਗਿਆ ਹੋਵੇਗੀ।"</string> - <string name="summary_glasses_single_device" msgid="3000909894067413398">"ਇਸ ਐਪ ਨੂੰ ਤੁਹਾਡੇ <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> \'ਤੇ ਇਨ੍ਹਾਂ ਇਜਾਜ਼ਤਾਂ ਤੱਕ ਪਹੁੰਚ ਕਰਨ ਦੀ ਆਗਿਆ ਹੋਵੇਗੀ"</string> + <string name="profile_name_glasses" msgid="3506504967216601277">"ਡੀਵਾਈਸ"</string> + <string name="summary_glasses" msgid="2872254734959842579">"ਇਸ ਐਪ ਨੂੰ ਤੁਹਾਡੇ <xliff:g id="DEVICE_NAME">%1$s</xliff:g> \'ਤੇ ਇਨ੍ਹਾਂ ਇਜਾਜ਼ਤਾਂ ਤੱਕ ਪਹੁੰਚ ਕਰਨ ਦੀ ਆਗਿਆ ਹੋਵੇਗੀ"</string> <string name="title_app_streaming" msgid="2270331024626446950">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ਨੂੰ ਤੁਹਾਡੇ ਫ਼ੋਨ ਤੋਂ ਇਸ ਜਾਣਕਾਰੀ ਤੱਕ ਪਹੁੰਚ ਕਰਨ ਦੀ ਆਗਿਆ ਦਿਓ"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"ਕ੍ਰਾਸ-ਡੀਵਾਈਸ ਸੇਵਾਵਾਂ"</string> <string name="helper_summary_app_streaming" msgid="2396773196949578425">"<xliff:g id="APP_NAME">%1$s</xliff:g> ਤੁਹਾਡੇ <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> ਦੀ ਤਰਫ਼ੋਂ ਤੁਹਾਡੇ ਡੀਵਾਈਸਾਂ ਵਿਚਕਾਰ ਐਪਾਂ ਨੂੰ ਸਟ੍ਰੀਮ ਕਰਨ ਦੀ ਇਜਾਜ਼ਤ ਮੰਗ ਰਹੀ ਹੈ"</string> @@ -38,10 +37,10 @@ <string name="title_nearby_device_streaming" msgid="7269956847378799794">"ਕੀ <strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> ਨੂੰ ਇਹ ਕਾਰਵਾਈ ਕਰਨ ਦੀ ਆਗਿਆ ਦੇਣੀ ਹੈ?"</string> <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<xliff:g id="APP_NAME">%1$s</xliff:g> ਤੁਹਾਡੇ <xliff:g id="DEVICE_NAME">%2$s</xliff:g> ਦੀ ਤਰਫ਼ੋਂ ਨਜ਼ਦੀਕੀ ਡੀਵਾਈਸਾਂ \'ਤੇ ਐਪਾਂ ਅਤੇ ਹੋਰ ਸਿਸਟਮ ਸੰਬੰਧੀ ਵਿਸ਼ੇਸ਼ਤਾਵਾਂ ਨੂੰ ਸਟ੍ਰੀਮ ਕਰਨ ਦੀ ਇਜਾਜ਼ਤ ਮੰਗ ਰਹੀ ਹੈ"</string> <string name="profile_name_generic" msgid="6851028682723034988">"ਡੀਵਾਈਸ"</string> - <string name="summary_generic_single_device" msgid="4181180669689590417">"ਇਹ ਐਪ ਤੁਹਾਡੇ ਫ਼ੋਨ ਅਤੇ <xliff:g id="DEVICE_NAME">%1$s</xliff:g> ਵਿਚਕਾਰ ਕਾਲਰ ਦੇ ਨਾਮ ਵਰਗੀ ਜਾਣਕਾਰੀ ਨੂੰ ਸਿੰਕ ਕਰ ਸਕੇਗੀ"</string> <string name="summary_generic" msgid="1761976003668044801">"ਇਹ ਐਪ ਤੁਹਾਡੇ ਫ਼ੋਨ ਅਤੇ ਚੁਣੇ ਗਏ ਡੀਵਾਈਸ ਵਿਚਕਾਰ ਕਾਲਰ ਦੇ ਨਾਮ ਵਰਗੀ ਜਾਣਕਾਰੀ ਨੂੰ ਸਿੰਕ ਕਰ ਸਕੇਗੀ"</string> <string name="consent_yes" msgid="8344487259618762872">"ਆਗਿਆ ਦਿਓ"</string> <string name="consent_no" msgid="2640796915611404382">"ਆਗਿਆ ਨਾ ਦਿਓ"</string> + <string name="consent_cancel" msgid="5655005528379285841">"ਰੱਦ ਕਰੋ"</string> <string name="consent_back" msgid="2560683030046918882">"ਪਿੱਛੇ"</string> <string name="permission_expand" msgid="893185038020887411">"<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g> ਦਾ ਵਿਸਤਾਰ ਕਰੋ"</string> <string name="permission_collapse" msgid="3320833884220844084">"<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g> ਨੂੰ ਸਮੇਟੋ"</string> diff --git a/packages/CompanionDeviceManager/res/values-pl/strings.xml b/packages/CompanionDeviceManager/res/values-pl/strings.xml index 1432a458307d..7ca172e2fbe9 100644 --- a/packages/CompanionDeviceManager/res/values-pl/strings.xml +++ b/packages/CompanionDeviceManager/res/values-pl/strings.xml @@ -17,15 +17,14 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="4470785958457506021">"Menedżer urządzeń towarzyszących"</string> - <string name="confirmation_title" msgid="4593465730772390351">"Zezwolić na dostęp aplikacji <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> do tego urządzenia (<strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>)?"</string> + <string name="confirmation_title" msgid="2244241995958340998">"Zezwolić aplikacji <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> na dostęp do urządzenia <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> <string name="profile_name_watch" msgid="576290739483672360">"zegarek"</string> - <string name="chooser_title" msgid="2262294130493605839">"Wybierz <xliff:g id="PROFILE_NAME">%1$s</xliff:g>, którym ma zarządzać aplikacja <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> - <string name="summary_watch" msgid="898569637110705523">"Ta aplikacja jest niezbędna do zarządzania urządzeniem <xliff:g id="DEVICE_NAME">%1$s</xliff:g>. Aplikacja <xliff:g id="APP_NAME">%2$s</xliff:g> będzie mogła synchronizować informacje takie jak nazwa osoby dzwoniącej, korzystać z powiadomień oraz uprawnień dotyczących telefonu, SMS-ów, kontaktów, kalendarza, rejestrów połączeń i Urządzeń w pobliżu."</string> - <string name="summary_watch_single_device" msgid="3173948915947011333">"Aplikacja będzie mogła synchronizować informacje takie jak nazwa dzwoniącego oraz korzystać z tych uprawnień na Twoim urządzeniu (<xliff:g id="DEVICE_TYPE">%1$s</xliff:g>)"</string> + <string name="chooser_title_non_profile" msgid="6035023914517087400">"Wybierz urządzenie, którym ma zarządzać aplikacja <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>"</string> + <string name="chooser_title" msgid="2235819929238267637">"Wybierz profil <xliff:g id="PROFILE_NAME">%1$s</xliff:g>, aby go skonfigurować"</string> + <string name="summary_watch" msgid="7962014927042971830">"Aplikacja będzie mogła synchronizować informacje takie jak nazwa dzwoniącego oraz korzystać z tych uprawnień na Twoim urządzeniu (<xliff:g id="DEVICE_NAME">%1$s</xliff:g>)"</string> <string name="confirmation_title_glasses" msgid="8288346850537727333">"Zezwolić na dostęp aplikacji <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> do urządzenia <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> - <string name="profile_name_glasses" msgid="8488394059007275998">"Okulary"</string> - <string name="summary_glasses_multi_device" msgid="615259525961937348">"Ta aplikacja jest niezbędna do zarządzania urządzeniem <xliff:g id="DEVICE_NAME">%1$s</xliff:g>. Aplikacja <xliff:g id="APP_NAME">%2$s</xliff:g> będzie mogła wchodzić w interakcję z powiadomieniami i korzystać z uprawnień dotyczących telefonu, SMS-ów, kontaktów, mikrofonu oraz urządzeń w pobliżu."</string> - <string name="summary_glasses_single_device" msgid="3000909894067413398">"Aplikacja będzie miała dostęp do tych uprawnień na Twoim urządzeniu (<xliff:g id="DEVICE_TYPE">%1$s</xliff:g>)"</string> + <string name="profile_name_glasses" msgid="3506504967216601277">"urządzenie"</string> + <string name="summary_glasses" msgid="2872254734959842579">"Aplikacja będzie miała dostęp do tych uprawnień na Twoim urządzeniu (<xliff:g id="DEVICE_NAME">%1$s</xliff:g>)"</string> <string name="title_app_streaming" msgid="2270331024626446950">"Zezwól urządzeniu <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> na dostęp do tych informacji na Twoim telefonie"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Usługi na innym urządzeniu"</string> <string name="helper_summary_app_streaming" msgid="2396773196949578425">"Aplikacja <xliff:g id="APP_NAME">%1$s</xliff:g> prosi w imieniu urządzenia <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> o uprawnienia dotyczące strumieniowego odtwarzania treści z aplikacji na innym urządzeniu"</string> @@ -38,10 +37,10 @@ <string name="title_nearby_device_streaming" msgid="7269956847378799794">"Zezwolić urządzeniu <strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> na wykonanie tego działania?"</string> <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"Aplikacja <xliff:g id="APP_NAME">%1$s</xliff:g> prosi w imieniu urządzenia <xliff:g id="DEVICE_NAME">%2$s</xliff:g> o uprawnienia do strumieniowego odtwarzania treści i innych funkcji systemowych na urządzeniach w pobliżu"</string> <string name="profile_name_generic" msgid="6851028682723034988">"urządzenie"</string> - <string name="summary_generic_single_device" msgid="4181180669689590417">"Ta aplikacja może synchronizować informacje takie jak nazwa osoby dzwoniącej między Twoim telefonem i urządzeniem <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="summary_generic" msgid="1761976003668044801">"Ta aplikacja może synchronizować informacje takie jak nazwa osoby dzwoniącej między Twoim telefonem i wybranym urządzeniem"</string> <string name="consent_yes" msgid="8344487259618762872">"Zezwól"</string> <string name="consent_no" msgid="2640796915611404382">"Nie zezwalaj"</string> + <string name="consent_cancel" msgid="5655005528379285841">"Anuluj"</string> <string name="consent_back" msgid="2560683030046918882">"Wstecz"</string> <string name="permission_expand" msgid="893185038020887411">"Rozwiń sekcję <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string> <string name="permission_collapse" msgid="3320833884220844084">"Zwiń sekcję <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string> diff --git a/packages/CompanionDeviceManager/res/values-pt-rBR/strings.xml b/packages/CompanionDeviceManager/res/values-pt-rBR/strings.xml index c45cda6e0702..26647c8820b8 100644 --- a/packages/CompanionDeviceManager/res/values-pt-rBR/strings.xml +++ b/packages/CompanionDeviceManager/res/values-pt-rBR/strings.xml @@ -17,15 +17,14 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="4470785958457506021">"Gerenciador de dispositivos complementar"</string> - <string name="confirmation_title" msgid="4593465730772390351">"Permitir que o app <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> acesse o dispositivo <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> + <string name="confirmation_title" msgid="2244241995958340998">"Permitir que o app <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> acesse o dispositivo <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> <string name="profile_name_watch" msgid="576290739483672360">"relógio"</string> - <string name="chooser_title" msgid="2262294130493605839">"Escolha um <xliff:g id="PROFILE_NAME">%1$s</xliff:g> para ser gerenciado pelo app <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> - <string name="summary_watch" msgid="898569637110705523">"O app <xliff:g id="APP_NAME">%2$s</xliff:g> é necessário para gerenciar o dispositivo <xliff:g id="DEVICE_NAME">%1$s</xliff:g>. Ele poderá sincronizar informações, como o nome de quem está ligando, interagir com suas notificações e acessar as permissões do Telefone, SMS, contatos, agenda, registro de chamadas e dispositivos por perto."</string> - <string name="summary_watch_single_device" msgid="3173948915947011333">"O app poderá sincronizar informações, como o nome de quem está ligando, e acessar estas permissões no seu <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string> + <string name="chooser_title_non_profile" msgid="6035023914517087400">"Escolha um dispositivo para ser gerenciado pelo app <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>"</string> + <string name="chooser_title" msgid="2235819929238267637">"Escolha um <xliff:g id="PROFILE_NAME">%1$s</xliff:g> para configurar"</string> + <string name="summary_watch" msgid="7962014927042971830">"O app poderá sincronizar informações, como o nome de quem está ligando, e acessar estas permissões no seu <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="confirmation_title_glasses" msgid="8288346850537727333">"Permitir que o app <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> gerencie o dispositivo <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> - <string name="profile_name_glasses" msgid="8488394059007275998">"óculos"</string> - <string name="summary_glasses_multi_device" msgid="615259525961937348">"O app <xliff:g id="APP_NAME">%2$s</xliff:g> é necessário para gerenciar o dispositivo <xliff:g id="DEVICE_NAME">%1$s</xliff:g>. Ele poderá interagir com suas notificações e acessar suas permissões de telefone, SMS, contatos, microfone e dispositivos por perto."</string> - <string name="summary_glasses_single_device" msgid="3000909894067413398">"O app poderá acessar estas permissões no seu <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string> + <string name="profile_name_glasses" msgid="3506504967216601277">"dispositivo"</string> + <string name="summary_glasses" msgid="2872254734959842579">"O app poderá acessar estas permissões no seu <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="title_app_streaming" msgid="2270331024626446950">"Permitir que o app <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> acesse estas informações do smartphone"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Serviços entre dispositivos"</string> <string name="helper_summary_app_streaming" msgid="2396773196949578425">"O app <xliff:g id="APP_NAME">%1$s</xliff:g> está pedindo permissão em nome do seu dispositivo <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> para fazer streaming de apps entre seus dispositivos"</string> @@ -38,10 +37,10 @@ <string name="title_nearby_device_streaming" msgid="7269956847378799794">"Permitir que o dispositivo <strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> realize esta ação?"</string> <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<xliff:g id="APP_NAME">%1$s</xliff:g> está pedindo permissão em nome do seu dispositivo <xliff:g id="DEVICE_NAME">%2$s</xliff:g> para fazer streaming de apps e de outros recursos do sistema para dispositivos por perto"</string> <string name="profile_name_generic" msgid="6851028682723034988">"dispositivo"</string> - <string name="summary_generic_single_device" msgid="4181180669689590417">"O app poderá sincronizar informações, como o nome de quem está ligando, entre seu smartphone e o dispositivo <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="summary_generic" msgid="1761976003668044801">"O app poderá sincronizar informações, como o nome de quem está ligando, entre seu smartphone e o dispositivo escolhido"</string> <string name="consent_yes" msgid="8344487259618762872">"Permitir"</string> <string name="consent_no" msgid="2640796915611404382">"Não permitir"</string> + <string name="consent_cancel" msgid="5655005528379285841">"Cancelar"</string> <string name="consent_back" msgid="2560683030046918882">"Voltar"</string> <string name="permission_expand" msgid="893185038020887411">"Abrir <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string> <string name="permission_collapse" msgid="3320833884220844084">"Fechar <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string> diff --git a/packages/CompanionDeviceManager/res/values-pt-rPT/strings.xml b/packages/CompanionDeviceManager/res/values-pt-rPT/strings.xml index 8619ff43cf1b..ba60f565977a 100644 --- a/packages/CompanionDeviceManager/res/values-pt-rPT/strings.xml +++ b/packages/CompanionDeviceManager/res/values-pt-rPT/strings.xml @@ -17,15 +17,14 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="4470785958457506021">"Gestor de dispositivos associados"</string> - <string name="confirmation_title" msgid="4593465730772390351">"Permitir que a app <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> aceda ao <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> + <string name="confirmation_title" msgid="2244241995958340998">"Permitir que a app <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> aceda ao <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> <string name="profile_name_watch" msgid="576290739483672360">"relógio"</string> - <string name="chooser_title" msgid="2262294130493605839">"Escolha um <xliff:g id="PROFILE_NAME">%1$s</xliff:g> para ser gerido pela app <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> - <string name="summary_watch" msgid="898569637110705523">"Esta app é necessária para gerir o dispositivo <xliff:g id="DEVICE_NAME">%1$s</xliff:g>. A app <xliff:g id="APP_NAME">%2$s</xliff:g> vai poder sincronizar informações, como o nome do autor de uma chamada, interagir com as suas notificações e aceder às autorizações do Telemóvel, SMS, Contactos, Calendário, Registos de chamadas e Dispositivos próximos."</string> - <string name="summary_watch_single_device" msgid="3173948915947011333">"Esta app vai poder sincronizar informações, como o nome do autor de uma chamada, e aceder a estas autorizações no seu <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string> + <string name="chooser_title_non_profile" msgid="6035023914517087400">"Escolha um dispositivo para ser gerido pela app <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>"</string> + <string name="chooser_title" msgid="2235819929238267637">"Escolha um perfil de <xliff:g id="PROFILE_NAME">%1$s</xliff:g> para configurar"</string> + <string name="summary_watch" msgid="7962014927042971830">"Esta app vai poder sincronizar informações, como o nome do autor de uma chamada, e aceder a estas autorizações no seu <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="confirmation_title_glasses" msgid="8288346850537727333">"Permita que a app <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> faça a gestão do dispositivo <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string> - <string name="profile_name_glasses" msgid="8488394059007275998">"óculos"</string> - <string name="summary_glasses_multi_device" msgid="615259525961937348">"Esta app é necessária para gerir o dispositivo <xliff:g id="DEVICE_NAME">%1$s</xliff:g>. A app <xliff:g id="APP_NAME">%2$s</xliff:g> vai poder interagir com as suas notificações e aceder às autorizações do Telemóvel, SMS, Contactos, Microfone e Dispositivos próximos."</string> - <string name="summary_glasses_single_device" msgid="3000909894067413398">"Esta app vai poder aceder a estas autorizações no seu <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string> + <string name="profile_name_glasses" msgid="3506504967216601277">"dispositivo"</string> + <string name="summary_glasses" msgid="2872254734959842579">"Esta app vai poder aceder a estas autorizações no seu <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="title_app_streaming" msgid="2270331024626446950">"Permita que a app <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> aceda a estas informações do seu telemóvel"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Serviços entre dispositivos"</string> <string name="helper_summary_app_streaming" msgid="2396773196949578425">"A app <xliff:g id="APP_NAME">%1$s</xliff:g> está a pedir autorização em nome do seu dispositivo <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> para fazer stream de apps entre os seus dispositivos"</string> @@ -38,10 +37,10 @@ <string name="title_nearby_device_streaming" msgid="7269956847378799794">"Permitir que o dispositivo <strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> faça esta ação?"</string> <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"A app <xliff:g id="APP_NAME">%1$s</xliff:g> está a pedir autorização em nome do dispositivo <xliff:g id="DEVICE_NAME">%2$s</xliff:g> para fazer stream de apps e outras funcionalidades do sistema para dispositivos próximos"</string> <string name="profile_name_generic" msgid="6851028682723034988">"dispositivo"</string> - <string name="summary_generic_single_device" msgid="4181180669689590417">"Esta app vai poder sincronizar informações, como o nome do autor de uma chamada, entre o telemóvel e o dispositivo <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="summary_generic" msgid="1761976003668044801">"Esta app vai poder sincronizar informações, como o nome do autor de uma chamada, entre o telemóvel e o dispositivo escolhido"</string> <string name="consent_yes" msgid="8344487259618762872">"Permitir"</string> <string name="consent_no" msgid="2640796915611404382">"Não permitir"</string> + <string name="consent_cancel" msgid="5655005528379285841">"Cancelar"</string> <string name="consent_back" msgid="2560683030046918882">"Voltar"</string> <string name="permission_expand" msgid="893185038020887411">"Expandir <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string> <string name="permission_collapse" msgid="3320833884220844084">"Reduzir <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string> diff --git a/packages/CompanionDeviceManager/res/values-pt/strings.xml b/packages/CompanionDeviceManager/res/values-pt/strings.xml index c45cda6e0702..26647c8820b8 100644 --- a/packages/CompanionDeviceManager/res/values-pt/strings.xml +++ b/packages/CompanionDeviceManager/res/values-pt/strings.xml @@ -17,15 +17,14 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="4470785958457506021">"Gerenciador de dispositivos complementar"</string> - <string name="confirmation_title" msgid="4593465730772390351">"Permitir que o app <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> acesse o dispositivo <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> + <string name="confirmation_title" msgid="2244241995958340998">"Permitir que o app <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> acesse o dispositivo <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> <string name="profile_name_watch" msgid="576290739483672360">"relógio"</string> - <string name="chooser_title" msgid="2262294130493605839">"Escolha um <xliff:g id="PROFILE_NAME">%1$s</xliff:g> para ser gerenciado pelo app <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> - <string name="summary_watch" msgid="898569637110705523">"O app <xliff:g id="APP_NAME">%2$s</xliff:g> é necessário para gerenciar o dispositivo <xliff:g id="DEVICE_NAME">%1$s</xliff:g>. Ele poderá sincronizar informações, como o nome de quem está ligando, interagir com suas notificações e acessar as permissões do Telefone, SMS, contatos, agenda, registro de chamadas e dispositivos por perto."</string> - <string name="summary_watch_single_device" msgid="3173948915947011333">"O app poderá sincronizar informações, como o nome de quem está ligando, e acessar estas permissões no seu <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string> + <string name="chooser_title_non_profile" msgid="6035023914517087400">"Escolha um dispositivo para ser gerenciado pelo app <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>"</string> + <string name="chooser_title" msgid="2235819929238267637">"Escolha um <xliff:g id="PROFILE_NAME">%1$s</xliff:g> para configurar"</string> + <string name="summary_watch" msgid="7962014927042971830">"O app poderá sincronizar informações, como o nome de quem está ligando, e acessar estas permissões no seu <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="confirmation_title_glasses" msgid="8288346850537727333">"Permitir que o app <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> gerencie o dispositivo <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> - <string name="profile_name_glasses" msgid="8488394059007275998">"óculos"</string> - <string name="summary_glasses_multi_device" msgid="615259525961937348">"O app <xliff:g id="APP_NAME">%2$s</xliff:g> é necessário para gerenciar o dispositivo <xliff:g id="DEVICE_NAME">%1$s</xliff:g>. Ele poderá interagir com suas notificações e acessar suas permissões de telefone, SMS, contatos, microfone e dispositivos por perto."</string> - <string name="summary_glasses_single_device" msgid="3000909894067413398">"O app poderá acessar estas permissões no seu <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string> + <string name="profile_name_glasses" msgid="3506504967216601277">"dispositivo"</string> + <string name="summary_glasses" msgid="2872254734959842579">"O app poderá acessar estas permissões no seu <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="title_app_streaming" msgid="2270331024626446950">"Permitir que o app <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> acesse estas informações do smartphone"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Serviços entre dispositivos"</string> <string name="helper_summary_app_streaming" msgid="2396773196949578425">"O app <xliff:g id="APP_NAME">%1$s</xliff:g> está pedindo permissão em nome do seu dispositivo <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> para fazer streaming de apps entre seus dispositivos"</string> @@ -38,10 +37,10 @@ <string name="title_nearby_device_streaming" msgid="7269956847378799794">"Permitir que o dispositivo <strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> realize esta ação?"</string> <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<xliff:g id="APP_NAME">%1$s</xliff:g> está pedindo permissão em nome do seu dispositivo <xliff:g id="DEVICE_NAME">%2$s</xliff:g> para fazer streaming de apps e de outros recursos do sistema para dispositivos por perto"</string> <string name="profile_name_generic" msgid="6851028682723034988">"dispositivo"</string> - <string name="summary_generic_single_device" msgid="4181180669689590417">"O app poderá sincronizar informações, como o nome de quem está ligando, entre seu smartphone e o dispositivo <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="summary_generic" msgid="1761976003668044801">"O app poderá sincronizar informações, como o nome de quem está ligando, entre seu smartphone e o dispositivo escolhido"</string> <string name="consent_yes" msgid="8344487259618762872">"Permitir"</string> <string name="consent_no" msgid="2640796915611404382">"Não permitir"</string> + <string name="consent_cancel" msgid="5655005528379285841">"Cancelar"</string> <string name="consent_back" msgid="2560683030046918882">"Voltar"</string> <string name="permission_expand" msgid="893185038020887411">"Abrir <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string> <string name="permission_collapse" msgid="3320833884220844084">"Fechar <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string> diff --git a/packages/CompanionDeviceManager/res/values-ro/strings.xml b/packages/CompanionDeviceManager/res/values-ro/strings.xml index f74e08eebd2a..84182e82820c 100644 --- a/packages/CompanionDeviceManager/res/values-ro/strings.xml +++ b/packages/CompanionDeviceManager/res/values-ro/strings.xml @@ -17,15 +17,14 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="4470785958457506021">"Manager de dispozitiv Companion"</string> - <string name="confirmation_title" msgid="4593465730772390351">"Permiți ca <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> să acceseze dispozitivul <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> + <string name="confirmation_title" msgid="2244241995958340998">"Permiți ca <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> să acceseze dispozitivul <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> <string name="profile_name_watch" msgid="576290739483672360">"ceas"</string> - <string name="chooser_title" msgid="2262294130493605839">"Alege un profil <xliff:g id="PROFILE_NAME">%1$s</xliff:g> pe care să îl gestioneze <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> - <string name="summary_watch" msgid="898569637110705523">"Această aplicație este necesară pentru a gestiona <xliff:g id="DEVICE_NAME">%1$s</xliff:g>. <xliff:g id="APP_NAME">%2$s</xliff:g> va putea să sincronizeze informații, cum ar fi numele unui apelant, să interacționeze cu notificările tale și să îți acceseze permisiunile pentru Telefon, SMS, Agendă, Calendar, Jurnale de apeluri și Dispozitive din apropiere."</string> - <string name="summary_watch_single_device" msgid="3173948915947011333">"Aplicația va putea să sincronizeze informații, cum ar fi numele unui apelant, și să acceseze aceste permisiuni pe <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string> + <string name="chooser_title_non_profile" msgid="6035023914517087400">"Alege un dispozitiv pe care să îl gestioneze <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>"</string> + <string name="chooser_title" msgid="2235819929238267637">"Alege un <xliff:g id="PROFILE_NAME">%1$s</xliff:g> de configurat"</string> + <string name="summary_watch" msgid="7962014927042971830">"Aplicația va putea să sincronizeze informații, cum ar fi numele unui apelant, și să acceseze aceste permisiuni pe <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="confirmation_title_glasses" msgid="8288346850537727333">"Permiți ca <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> să gestioneze <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> - <string name="profile_name_glasses" msgid="8488394059007275998">"ochelari"</string> - <string name="summary_glasses_multi_device" msgid="615259525961937348">"Această aplicație este necesară pentru a gestiona <xliff:g id="DEVICE_NAME">%1$s</xliff:g>. <xliff:g id="APP_NAME">%2$s</xliff:g> va putea să interacționeze cu notificările tale și să-ți acceseze permisiunile pentru Telefon, SMS, Agendă, Microfon și Dispozitive din apropiere."</string> - <string name="summary_glasses_single_device" msgid="3000909894067413398">"Aplicația va putea să acceseze următoarele permisiuni pe <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string> + <string name="profile_name_glasses" msgid="3506504967216601277">"dispozitiv"</string> + <string name="summary_glasses" msgid="2872254734959842579">"Aplicația va putea să acceseze următoarele permisiuni pe <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="title_app_streaming" msgid="2270331024626446950">"Permite ca <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> să acceseze aceste informații de pe telefon"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Servicii pe mai multe dispozitive"</string> <string name="helper_summary_app_streaming" msgid="2396773196949578425">"<xliff:g id="APP_NAME">%1$s</xliff:g> solicită permisiunea pentru <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> de a reda în stream aplicații între dispozitivele tale"</string> @@ -38,10 +37,10 @@ <string name="title_nearby_device_streaming" msgid="7269956847378799794">"Permiți ca <strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> să realizeze această acțiune?"</string> <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<xliff:g id="APP_NAME">%1$s</xliff:g> solicită permisiunea pentru <xliff:g id="DEVICE_NAME">%2$s</xliff:g> de a reda în stream conținut din aplicații și alte funcții de sistem pe dispozitivele din apropiere"</string> <string name="profile_name_generic" msgid="6851028682723034988">"dispozitiv"</string> - <string name="summary_generic_single_device" msgid="4181180669689590417">"Aplicația va putea să sincronizeze informații, cum ar fi numele unui apelant, între telefonul tău și <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="summary_generic" msgid="1761976003668044801">"Aplicația va putea să sincronizeze informații, cum ar fi numele unui apelant, între telefonul tău și dispozitivul ales"</string> <string name="consent_yes" msgid="8344487259618762872">"Permite"</string> <string name="consent_no" msgid="2640796915611404382">"Nu permite"</string> + <string name="consent_cancel" msgid="5655005528379285841">"Anulează"</string> <string name="consent_back" msgid="2560683030046918882">"Înapoi"</string> <string name="permission_expand" msgid="893185038020887411">"Extinde <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string> <string name="permission_collapse" msgid="3320833884220844084">"Restrânge <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string> diff --git a/packages/CompanionDeviceManager/res/values-ru/strings.xml b/packages/CompanionDeviceManager/res/values-ru/strings.xml index cdf1668f1fb4..911bddc09fde 100644 --- a/packages/CompanionDeviceManager/res/values-ru/strings.xml +++ b/packages/CompanionDeviceManager/res/values-ru/strings.xml @@ -17,15 +17,14 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="4470785958457506021">"Управление подключенными устройствами"</string> - <string name="confirmation_title" msgid="4593465730772390351">"Предоставить приложению <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> доступ к устройству <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> + <string name="confirmation_title" msgid="2244241995958340998">"Предоставить приложению <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> доступ к устройству <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> <string name="profile_name_watch" msgid="576290739483672360">"часы"</string> - <string name="chooser_title" msgid="2262294130493605839">"Выберите устройство (<xliff:g id="PROFILE_NAME">%1$s</xliff:g>), которым будет управлять приложение <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> - <string name="summary_watch" msgid="898569637110705523">"Это приложение необходимо для управления устройством \"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>\". Приложение \"<xliff:g id="APP_NAME">%2$s</xliff:g>\" сможет синхронизировать данные, например из журнала звонков, а также получит доступ к уведомлениям и разрешениям \"Телефон\", \"Контакты\", \"Календарь\", \"Список вызовов\", \"Устройства поблизости\" и SMS."</string> - <string name="summary_watch_single_device" msgid="3173948915947011333">"Это приложение сможет синхронизировать данные, например имена вызывающих абонентов, а также получит указанные разрешения на <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>."</string> + <string name="chooser_title_non_profile" msgid="6035023914517087400">"Выберите устройство, которым будет управлять приложение <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>"</string> + <string name="chooser_title" msgid="2235819929238267637">"Выберите <xliff:g id="PROFILE_NAME">%1$s</xliff:g> для настройки"</string> + <string name="summary_watch" msgid="7962014927042971830">"Это приложение сможет синхронизировать данные, например имена вызывающих абонентов, а также получит указанные разрешения на <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string> <string name="confirmation_title_glasses" msgid="8288346850537727333">"Разрешить приложению <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> управлять устройством <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> - <string name="profile_name_glasses" msgid="8488394059007275998">"Очки"</string> - <string name="summary_glasses_multi_device" msgid="615259525961937348">"Это приложение необходимо для управления устройством \"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>\". Приложение \"<xliff:g id="APP_NAME">%2$s</xliff:g>\" сможет взаимодействовать с уведомлениями, а также получит разрешения \"Телефон\", SMS, \"Контакты\", \"Микрофон\" и \"Устройства поблизости\"."</string> - <string name="summary_glasses_single_device" msgid="3000909894067413398">"Это приложение получит указанные разрешения на <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>."</string> + <string name="profile_name_glasses" msgid="3506504967216601277">"устройстве"</string> + <string name="summary_glasses" msgid="2872254734959842579">"Это приложение получит указанные разрешения на <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string> <string name="title_app_streaming" msgid="2270331024626446950">"Разрешите приложению <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> получать эту информацию с вашего телефона"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Сервисы стриминга приложений"</string> <string name="helper_summary_app_streaming" msgid="2396773196949578425">"Приложение \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" запрашивает разрешение от имени вашего устройства <xliff:g id="DISPLAY_NAME">%2$s</xliff:g>, чтобы транслировать приложения между устройствами."</string> @@ -38,10 +37,10 @@ <string name="title_nearby_device_streaming" msgid="7269956847378799794">"Разрешить приложению <strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> выполнять это действие?"</string> <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"Приложение \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" от имени вашего устройства \"<xliff:g id="DEVICE_NAME">%2$s</xliff:g>\" запрашивает разрешение транслировать приложения и системные функции на устройства поблизости."</string> <string name="profile_name_generic" msgid="6851028682723034988">"устройство"</string> - <string name="summary_generic_single_device" msgid="4181180669689590417">"Приложение сможет синхронизировать информацию между телефоном и устройством \"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>\", например данные из журнала звонков."</string> <string name="summary_generic" msgid="1761976003668044801">"Приложение сможет синхронизировать информацию между телефоном и выбранным устройством, например данные из журнала звонков."</string> <string name="consent_yes" msgid="8344487259618762872">"Разрешить"</string> <string name="consent_no" msgid="2640796915611404382">"Запретить"</string> + <string name="consent_cancel" msgid="5655005528379285841">"Отмена"</string> <string name="consent_back" msgid="2560683030046918882">"Назад"</string> <string name="permission_expand" msgid="893185038020887411">"Разворачивать список \"<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>\"."</string> <string name="permission_collapse" msgid="3320833884220844084">"Сворачивать список \"<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>\"."</string> diff --git a/packages/CompanionDeviceManager/res/values-si/strings.xml b/packages/CompanionDeviceManager/res/values-si/strings.xml index 41224c6b2b0b..daa20580066b 100644 --- a/packages/CompanionDeviceManager/res/values-si/strings.xml +++ b/packages/CompanionDeviceManager/res/values-si/strings.xml @@ -17,15 +17,14 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="4470785958457506021">"සහායක උපාංග කළමනාකරු"</string> - <string name="confirmation_title" msgid="4593465730772390351">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> හට <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> වෙත ප්රවේශ වීමට ඉඩ දෙන්න ද?"</string> + <string name="confirmation_title" msgid="2244241995958340998">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> යෙදුමට <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> වෙත ප්රවේශ වීමට ඉඩ දෙන්න ද?"</string> <string name="profile_name_watch" msgid="576290739483672360">"ඔරලෝසුව"</string> - <string name="chooser_title" msgid="2262294130493605839">"<strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> මගින් කළමනාකරණය කරනු ලැබීමට <xliff:g id="PROFILE_NAME">%1$s</xliff:g>ක් තෝරන්න"</string> - <string name="summary_watch" msgid="898569637110705523">"මෙම යෙදුමට ඔබේ <xliff:g id="DEVICE_NAME">%1$s</xliff:g> කළමනාකරණය කිරීමට අවශ්යයි. <xliff:g id="APP_NAME">%2$s</xliff:g> හට අමතන කෙනෙකුගේ නම වැනි, තතු සමමුහුර්ත කිරීමට, ඔබේ දැනුම්දීම් සමග අන්තර්ක්රියා කිරීමට සහ ඔබේ දුරකථනය, SMS, සම්බන්ධතා, දින දර්ශනය, ඇමතුම් ලොග සහ අවට උපාංග අවසර වෙත ප්රවේශ වීමට ඉඩ දෙනු ඇත."</string> - <string name="summary_watch_single_device" msgid="3173948915947011333">"මෙම යෙදුමට අමතන කෙනෙකුගේ නම වැනි, තතු සමමුහුර්ත කිරීමට, සහ ඔබේ <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> මත මෙම අවසර වෙත ප්රවේශ වීමට ඉඩ දෙනු ඇත"</string> + <string name="chooser_title_non_profile" msgid="6035023914517087400">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> විසින් කළමනා කරනු ලැබීමට උපාංගයක් තෝරන්න"</string> + <string name="chooser_title" msgid="2235819929238267637">"සැකසීමට <xliff:g id="PROFILE_NAME">%1$s</xliff:g>ක් තෝරන්න"</string> + <string name="summary_watch" msgid="7962014927042971830">"මෙම යෙදුමට අමතන කෙනෙකුගේ නම වැනි, තොරතුරු සමමුහූර්ත කිරීමට, සහ ඔබේ <xliff:g id="DEVICE_NAME">%1$s</xliff:g> මත මෙම අවසර වෙත ප්රවේශ වීමට ඉඩ දෙනු ලැබේ"</string> <string name="confirmation_title_glasses" msgid="8288346850537727333">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> හට <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> කළමනා කිරීමට ඉඩ දෙන්න ද?"</string> - <string name="profile_name_glasses" msgid="8488394059007275998">"කණ්ණාඩි"</string> - <string name="summary_glasses_multi_device" msgid="615259525961937348">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> කළමනා කිරීමට මෙම යෙදුම අවශ්යයි. <xliff:g id="APP_NAME">%2$s</xliff:g> හට ඔබේ දැනුම්දීම් සමග අන්තර්ක්රියා කිරීමට සහ ඔබේ දුරකථනය, කෙටි පණිවුඩය, සම්බන්ධතා, මයික්රොෆෝනය සහ අවට උපාංග අවසර වෙත ප්රවේශ වීමට ඉඩ දෙයි."</string> - <string name="summary_glasses_single_device" msgid="3000909894067413398">"මෙම යෙදුමට ඔබේ <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> මත මෙම අවසර වෙත ප්රවේශ වීමට ඉඩ දෙනු ඇත"</string> + <string name="profile_name_glasses" msgid="3506504967216601277">"උපාංගය"</string> + <string name="summary_glasses" msgid="2872254734959842579">"මෙම යෙදුමට ඔබේ <xliff:g id="DEVICE_NAME">%1$s</xliff:g> මත මෙම අවසර වෙත ප්රවේශ වීමට ඉඩ දෙනු ලැබේ"</string> <string name="title_app_streaming" msgid="2270331024626446950">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> හට ඔබගේ දුරකථනයෙන් මෙම තොරතුරුවලට ප්රවේශ වීමට ඉඩ දෙන්න"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"හරස්-උපාංග සේවා"</string> <string name="helper_summary_app_streaming" msgid="2396773196949578425">"<xliff:g id="APP_NAME">%1$s</xliff:g> ඔබේ <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> වෙනුවෙන් ඔබේ උපාංග අතර යෙදුම් ප්රවාහ කිරීමට අවසරය ඉල්ලමින් සිටියි"</string> @@ -38,10 +37,10 @@ <string name="title_nearby_device_streaming" msgid="7269956847378799794">"මෙම ක්රියාව කිරීමට <strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> හට ඉඩ දෙන්න ද?"</string> <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<xliff:g id="APP_NAME">%1$s</xliff:g> ඔබේ <xliff:g id="DEVICE_NAME">%2$s</xliff:g> වෙනුවෙන් යෙදුම් සහ අනෙකුත් පද්ධති විශේෂාංග අවට උපාංග වෙත ප්රවාහ කිරීමට අවසර ඉල්ලයි"</string> <string name="profile_name_generic" msgid="6851028682723034988">"උපාංගය"</string> - <string name="summary_generic_single_device" msgid="4181180669689590417">"මෙම යෙදුමට ඔබේ දුරකථනය සහ <xliff:g id="DEVICE_NAME">%1$s</xliff:g> අතර, අමතන කෙනෙකුගේ නම වැනි, තතු සමමුහුර්ත කිරීමට හැකි වනු ඇත"</string> <string name="summary_generic" msgid="1761976003668044801">"මෙම යෙදුමට ඔබේ දුරකථනය සහ තෝරා ගත් උපාංගය අතර, අමතන කෙනෙකුගේ නම වැනි, තතු සමමුහුර්ත කිරීමට හැකි වනු ඇත"</string> <string name="consent_yes" msgid="8344487259618762872">"ඉඩ දෙන්න"</string> <string name="consent_no" msgid="2640796915611404382">"ඉඩ නොදෙන්න"</string> + <string name="consent_cancel" msgid="5655005528379285841">"අවලංගු කරන්න"</string> <string name="consent_back" msgid="2560683030046918882">"ආපසු"</string> <string name="permission_expand" msgid="893185038020887411">"<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g> විදහන්න"</string> <string name="permission_collapse" msgid="3320833884220844084">"<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g> හකුළන්න"</string> diff --git a/packages/CompanionDeviceManager/res/values-sk/strings.xml b/packages/CompanionDeviceManager/res/values-sk/strings.xml index 220f00e10e06..929b56c7daf0 100644 --- a/packages/CompanionDeviceManager/res/values-sk/strings.xml +++ b/packages/CompanionDeviceManager/res/values-sk/strings.xml @@ -17,15 +17,14 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="4470785958457506021">"Správca sprievodných zariadení"</string> - <string name="confirmation_title" msgid="4593465730772390351">"Chcete povoliť aplikácii <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> prístup k zariadeniu <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> + <string name="confirmation_title" msgid="2244241995958340998">"Chcete povoliť aplikácii <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> prístup k zariadeniu <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> <string name="profile_name_watch" msgid="576290739483672360">"hodinky"</string> - <string name="chooser_title" msgid="2262294130493605839">"Vyberte profil <xliff:g id="PROFILE_NAME">%1$s</xliff:g>, ktorý bude spravovať aplikácia <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> - <string name="summary_watch" msgid="898569637110705523">"Táto aplikácia sa vyžaduje na správu zariadenia <xliff:g id="DEVICE_NAME">%1$s</xliff:g>. <xliff:g id="APP_NAME">%2$s</xliff:g> bude môcť synchronizovať informácie, napríklad meno volajúceho, interagovať s vašimi upozorneniami a získavať prístup k povoleniam telefónu, SMS, kontaktov, kalendára, zoznamu hovorov a zariadení v okolí."</string> - <string name="summary_watch_single_device" msgid="3173948915947011333">"Táto aplikácia bude môcť synchronizovať informácie, napríklad meno volajúceho, a získavať prístup k týmto povoleniam v zariadení <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string> + <string name="chooser_title_non_profile" msgid="6035023914517087400">"Vyberte zariadenie, ktoré bude spravovať aplikácia <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>"</string> + <string name="chooser_title" msgid="2235819929238267637">"Vyberte profil <xliff:g id="PROFILE_NAME">%1$s</xliff:g>, ktorý nastavíte"</string> + <string name="summary_watch" msgid="7962014927042971830">"Táto aplikácia bude môcť synchronizovať informácie, napríklad meno volajúceho, a získavať prístup k týmto povoleniam v zariadení <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="confirmation_title_glasses" msgid="8288346850537727333">"Chcete povoliť aplikácii <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> spravovať zariadenie <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> - <string name="profile_name_glasses" msgid="8488394059007275998">"okuliare"</string> - <string name="summary_glasses_multi_device" msgid="615259525961937348">"Táto aplikácia sa vyžaduje na správu zariadenia <xliff:g id="DEVICE_NAME">%1$s</xliff:g>. <xliff:g id="APP_NAME">%2$s</xliff:g> bude môcť interagovať s vašimi upozorneniami a získa prístup k povoleniam pre telefón, SMS, kontakty, mikrofón a zariadenia v okolí."</string> - <string name="summary_glasses_single_device" msgid="3000909894067413398">"Táto aplikácia bude mať prístup k týmto povoleniam v zariadení <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string> + <string name="profile_name_glasses" msgid="3506504967216601277">"zariadenie"</string> + <string name="summary_glasses" msgid="2872254734959842579">"Táto aplikácia bude mať prístup k týmto povoleniam v zariadení <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="title_app_streaming" msgid="2270331024626446950">"Povoľte aplikácii <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> prístup k týmto informáciám z vášho telefónu"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Služby pre viacero zariadení"</string> <string name="helper_summary_app_streaming" msgid="2396773196949578425">"<xliff:g id="APP_NAME">%1$s</xliff:g> vyžaduje pre zariadenie <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> povolenie streamovať aplikácie medzi vašimi zariadeniami."</string> @@ -38,10 +37,10 @@ <string name="title_nearby_device_streaming" msgid="7269956847378799794">"Chcete povoliť zariadeniu <strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> vykonať túto akciu?"</string> <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<xliff:g id="APP_NAME">%1$s</xliff:g> vyžaduje pre zariadenie <xliff:g id="DEVICE_NAME">%2$s</xliff:g> povolenie streamovať aplikácie a ďalšie systémové funkcie do zariadení v okolí"</string> <string name="profile_name_generic" msgid="6851028682723034988">"zariadenie"</string> - <string name="summary_generic_single_device" msgid="4181180669689590417">"Táto aplikácia bude môcť synchronizovať informácie, napríklad meno volajúceho, medzi telefónom a zariadením <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="summary_generic" msgid="1761976003668044801">"Táto aplikácia bude môcť synchronizovať informácie, napríklad meno volajúceho, medzi telefónom a vybraným zariadením"</string> <string name="consent_yes" msgid="8344487259618762872">"Povoliť"</string> <string name="consent_no" msgid="2640796915611404382">"Nepovoliť"</string> + <string name="consent_cancel" msgid="5655005528379285841">"Zrušiť"</string> <string name="consent_back" msgid="2560683030046918882">"Späť"</string> <string name="permission_expand" msgid="893185038020887411">"Rozbaliť sekciu <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string> <string name="permission_collapse" msgid="3320833884220844084">"Zbaliť sekciu <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string> diff --git a/packages/CompanionDeviceManager/res/values-sl/strings.xml b/packages/CompanionDeviceManager/res/values-sl/strings.xml index 562e47428963..b1b1e9112154 100644 --- a/packages/CompanionDeviceManager/res/values-sl/strings.xml +++ b/packages/CompanionDeviceManager/res/values-sl/strings.xml @@ -17,15 +17,14 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="4470785958457506021">"Upravitelj spremljevalnih naprav"</string> - <string name="confirmation_title" msgid="4593465730772390351">"Želite aplikaciji <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> dovoliti dostop do naprave <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> + <string name="confirmation_title" msgid="2244241995958340998">"Želite aplikaciji <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> dovoliti dostop do naprave <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> <string name="profile_name_watch" msgid="576290739483672360">"ura"</string> - <string name="chooser_title" msgid="2262294130493605839">"Izbira profila »<xliff:g id="PROFILE_NAME">%1$s</xliff:g>«, ki ga bo upravljala aplikacija <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> - <string name="summary_watch" msgid="898569637110705523">"Ta aplikacija je potrebna za upravljanje naprave »<xliff:g id="DEVICE_NAME">%1$s</xliff:g>«. Aplikaciji <xliff:g id="APP_NAME">%2$s</xliff:g> bodo omogočene sinhronizacija podatkov, na primer imena klicatelja, interakcija z obvestili in uporaba dovoljenj Telefon, SMS, Stiki, Koledar, Dnevniki klicev in Naprave v bližini."</string> - <string name="summary_watch_single_device" msgid="3173948915947011333">"Ta aplikacija bo lahko sinhronizirala podatke, na primer ime klicatelja, in dostopala do teh dovoljenj v napravi »<xliff:g id="DEVICE_TYPE">%1$s</xliff:g>«."</string> + <string name="chooser_title_non_profile" msgid="6035023914517087400">"Izbira naprave, ki jo bo upravljala aplikacija <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>"</string> + <string name="chooser_title" msgid="2235819929238267637">"Izberite profil naprave »<xliff:g id="PROFILE_NAME">%1$s</xliff:g>« za nastavitev"</string> + <string name="summary_watch" msgid="7962014927042971830">"Ta aplikacija bo lahko sinhronizirala podatke, na primer ime klicatelja, in dostopala do teh dovoljenj v napravi »<xliff:g id="DEVICE_NAME">%1$s</xliff:g>«."</string> <string name="confirmation_title_glasses" msgid="8288346850537727333">"Želite aplikaciji <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> dovoliti upravljanje naprave <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> - <string name="profile_name_glasses" msgid="8488394059007275998">"očala"</string> - <string name="summary_glasses_multi_device" msgid="615259525961937348">"Ta aplikacija je potrebna za upravljanje naprave »<xliff:g id="DEVICE_NAME">%1$s</xliff:g>«. Aplikaciji <xliff:g id="APP_NAME">%2$s</xliff:g> bosta omogočeni interakcija z obvestili in uporaba dovoljenj Telefon, SMS, Stiki, Mikrofon in Naprave v bližini."</string> - <string name="summary_glasses_single_device" msgid="3000909894067413398">"Ta aplikacija bo lahko dostopala do teh dovoljenj v napravi »<xliff:g id="DEVICE_TYPE">%1$s</xliff:g>«."</string> + <string name="profile_name_glasses" msgid="3506504967216601277">"naprava"</string> + <string name="summary_glasses" msgid="2872254734959842579">"Ta aplikacija bo lahko dostopala do teh dovoljenj v napravi »<xliff:g id="DEVICE_NAME">%1$s</xliff:g>«."</string> <string name="title_app_streaming" msgid="2270331024626446950">"Dovolite, da <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> dostopa do teh podatkov v vašem telefonu"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Storitve za zunanje naprave"</string> <string name="helper_summary_app_streaming" msgid="2396773196949578425">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> v imenu naprave »<xliff:g id="DISPLAY_NAME">%2$s</xliff:g>« zahteva dovoljenje za pretočno predvajanje aplikacij v vaših napravah."</string> @@ -38,10 +37,10 @@ <string name="title_nearby_device_streaming" msgid="7269956847378799794">"Ali napravi <strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> dovolite izvedbo tega dejanja?"</string> <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> v imenu naprave »<xliff:g id="DEVICE_NAME">%2$s</xliff:g>« zahteva dovoljenje za pretočno predvajanje aplikacij in drugih sistemskih funkcij v napravah v bližini."</string> <string name="profile_name_generic" msgid="6851028682723034988">"naprava"</string> - <string name="summary_generic_single_device" msgid="4181180669689590417">"Ta aplikacija bo lahko sinhronizirala podatke, na primer ime klicatelja, v telefonu in napravi »<xliff:g id="DEVICE_NAME">%1$s</xliff:g>«."</string> <string name="summary_generic" msgid="1761976003668044801">"Ta aplikacija bo lahko sinhronizirala podatke, na primer ime klicatelja, v telefonu in izbrani napravi."</string> <string name="consent_yes" msgid="8344487259618762872">"Dovoli"</string> <string name="consent_no" msgid="2640796915611404382">"Ne dovoli"</string> + <string name="consent_cancel" msgid="5655005528379285841">"Prekliči"</string> <string name="consent_back" msgid="2560683030046918882">"Nazaj"</string> <string name="permission_expand" msgid="893185038020887411">"Razširi dovoljenje »<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>«"</string> <string name="permission_collapse" msgid="3320833884220844084">"Strni dovoljenje »<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>«"</string> diff --git a/packages/CompanionDeviceManager/res/values-sq/strings.xml b/packages/CompanionDeviceManager/res/values-sq/strings.xml index 8e32ec700395..037a7073aa56 100644 --- a/packages/CompanionDeviceManager/res/values-sq/strings.xml +++ b/packages/CompanionDeviceManager/res/values-sq/strings.xml @@ -17,15 +17,14 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="4470785958457506021">"Menaxheri i pajisjes shoqëruese"</string> - <string name="confirmation_title" msgid="4593465730772390351">"T\'i lejohet <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> qasja te <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> + <string name="confirmation_title" msgid="2244241995958340998">"T\'i lejohet aplikacionit <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> qasja te <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> <string name="profile_name_watch" msgid="576290739483672360">"ora inteligjente"</string> - <string name="chooser_title" msgid="2262294130493605839">"Zgjidh që <xliff:g id="PROFILE_NAME">%1$s</xliff:g> të menaxhohet nga <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> - <string name="summary_watch" msgid="898569637110705523">"Ky aplikacion nevojitet për të menaxhuar <xliff:g id="DEVICE_NAME">%1$s</xliff:g>. <xliff:g id="APP_NAME">%2$s</xliff:g> do të lejohet të sinkronizojë informacione, si p.sh. emrin e dikujt që po telefonon, të ndërveprojë me njoftimet e tua dhe të ketë qasje te lejet e \"Telefonit\", \"SMS-ve\", \"Kontakteve\", \"Kalendarit\", \"Evidencave të telefonatave\" dhe \"Pajisjeve në afërsi\"."</string> - <string name="summary_watch_single_device" msgid="3173948915947011333">"Këtij aplikacioni do t\'i lejohet të sinkronizojë informacione, si p.sh. emrin e dikujt që po telefonon, si dhe të ketë qasje në këto leje në <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string> + <string name="chooser_title_non_profile" msgid="6035023914517087400">"Zgjidh një pajisje që do të menaxhohet nga <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>"</string> + <string name="chooser_title" msgid="2235819929238267637">"Zgjidh një <xliff:g id="PROFILE_NAME">%1$s</xliff:g> për konfigurimin"</string> + <string name="summary_watch" msgid="7962014927042971830">"Këtij aplikacioni do t\'i lejohet të sinkronizojë informacione, si p.sh. emrin e dikujt që po telefonon, si dhe të ketë qasje në këto leje në <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="confirmation_title_glasses" msgid="8288346850537727333">"Të lejohet që <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> të menaxhojë <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> - <string name="profile_name_glasses" msgid="8488394059007275998">"syzet"</string> - <string name="summary_glasses_multi_device" msgid="615259525961937348">"Ky aplikacion nevojitet për të menaxhuar <xliff:g id="DEVICE_NAME">%1$s</xliff:g>. <xliff:g id="APP_NAME">%2$s</xliff:g> do të lejohet të ndërveprojë me njoftimet e tua dhe të ketë qasje te lejet e \"Telefonit\", \"SMS-ve\", \"Kontakteve\", \"Mikrofonit\" dhe të \"Pajisjeve në afërsi\"."</string> - <string name="summary_glasses_single_device" msgid="3000909894067413398">"Këtij aplikacioni do t\'i lejohet qasja te këto leje në <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string> + <string name="profile_name_glasses" msgid="3506504967216601277">"pajisje"</string> + <string name="summary_glasses" msgid="2872254734959842579">"Këtij aplikacioni do t\'i lejohet qasja te këto leje në <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="title_app_streaming" msgid="2270331024626446950">"Lejo që <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> të ketë qasje në këtë informacion nga telefoni yt"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Shërbimet mes pajisjeve"</string> <string name="helper_summary_app_streaming" msgid="2396773196949578425">"<xliff:g id="APP_NAME">%1$s</xliff:g> po kërkon leje në emër të <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> për të transmetuar aplikacione ndërmjet pajisjeve të tua"</string> @@ -38,10 +37,10 @@ <string name="title_nearby_device_streaming" msgid="7269956847378799794">"Të lejohet që <strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> të ndërmarrë këtë veprim?"</string> <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<xliff:g id="APP_NAME">%1$s</xliff:g> po kërkon leje në emër të (<xliff:g id="DEVICE_NAME">%2$s</xliff:g>) tënde për të transmetuar aplikacione dhe veçori të tjera të sistemit te pajisjet në afërsi"</string> <string name="profile_name_generic" msgid="6851028682723034988">"pajisja"</string> - <string name="summary_generic_single_device" msgid="4181180669689590417">"Ky aplikacion do të mund të sinkronizojë informacione, si p.sh emrin i dikujt që po telefonon, mes telefonit tënd dhe <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string> <string name="summary_generic" msgid="1761976003668044801">"Ky aplikacion do të mund të sinkronizojë informacione, si p.sh emrin e dikujt që po telefonon, mes telefonit tënd dhe pajisjes së zgjedhur."</string> <string name="consent_yes" msgid="8344487259618762872">"Lejo"</string> <string name="consent_no" msgid="2640796915611404382">"Mos lejo"</string> + <string name="consent_cancel" msgid="5655005528379285841">"Anulo"</string> <string name="consent_back" msgid="2560683030046918882">"Pas"</string> <string name="permission_expand" msgid="893185038020887411">"Zgjero: <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string> <string name="permission_collapse" msgid="3320833884220844084">"Palos: <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string> diff --git a/packages/CompanionDeviceManager/res/values-sr/strings.xml b/packages/CompanionDeviceManager/res/values-sr/strings.xml index 34d9ca25e22a..3817d58b67ea 100644 --- a/packages/CompanionDeviceManager/res/values-sr/strings.xml +++ b/packages/CompanionDeviceManager/res/values-sr/strings.xml @@ -17,15 +17,14 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="4470785958457506021">"Менаџер придруженог уређаја"</string> - <string name="confirmation_title" msgid="4593465730772390351">"Дозволите да <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> приступа уређају <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string> + <string name="confirmation_title" msgid="2244241995958340998">"Дозволите да апликација <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> приступа уређају <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string> <string name="profile_name_watch" msgid="576290739483672360">"сат"</string> - <string name="chooser_title" msgid="2262294130493605839">"Одаберите <xliff:g id="PROFILE_NAME">%1$s</xliff:g> којим ће управљати апликација <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> - <string name="summary_watch" msgid="898569637110705523">"Ова апликација је потребна за управљање уређајем <xliff:g id="DEVICE_NAME">%1$s</xliff:g>. <xliff:g id="APP_NAME">%2$s</xliff:g> ће добити дозволу за синхронизовање информација, попут особе која упућује позив, за интеракцију са обавештењима и приступ дозволама за телефон, SMS, контакте, календар, евиденције позива и уређаје у близини."</string> - <string name="summary_watch_single_device" msgid="3173948915947011333">"Овој апликацији ће бити дозвољено да синхронизује податке, попут имена особе која упућује позив, и приступа тим дозволама на вашем уређају (<xliff:g id="DEVICE_TYPE">%1$s</xliff:g>)"</string> + <string name="chooser_title_non_profile" msgid="6035023914517087400">"Одаберите уређај којим ће управљати апликација <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>"</string> + <string name="chooser_title" msgid="2235819929238267637">"Одаберите профил <xliff:g id="PROFILE_NAME">%1$s</xliff:g> који желите да подесите"</string> + <string name="summary_watch" msgid="7962014927042971830">"Овој апликацији ће бити дозвољено да синхронизује податке, попут имена особе која упућује позив, и приступа тим дозволама на вашем уређају (<xliff:g id="DEVICE_NAME">%1$s</xliff:g>)"</string> <string name="confirmation_title_glasses" msgid="8288346850537727333">"Желите ли да дозволите да <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> управља уређајем <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> - <string name="profile_name_glasses" msgid="8488394059007275998">"наочаре"</string> - <string name="summary_glasses_multi_device" msgid="615259525961937348">"Ова апликација је потребна за управљање уређајем <xliff:g id="DEVICE_NAME">%1$s</xliff:g>. <xliff:g id="APP_NAME">%2$s</xliff:g> ће добити дозволу за интеракцију са обавештењима и приступ дозволама за телефон, SMS, контакте, микрофон и уређаје у близини."</string> - <string name="summary_glasses_single_device" msgid="3000909894067413398">"Овој апликацији ће бити дозвољено да приступа овим дозволама на вашем уређају (<xliff:g id="DEVICE_TYPE">%1$s</xliff:g>)"</string> + <string name="profile_name_glasses" msgid="3506504967216601277">"уређај"</string> + <string name="summary_glasses" msgid="2872254734959842579">"Овој апликацији ће бити дозвољено да приступа овим дозволама на вашем уређају (<xliff:g id="DEVICE_NAME">%1$s</xliff:g>)"</string> <string name="title_app_streaming" msgid="2270331024626446950">"Дозволите да <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> приступа овим информацијама са телефона"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Услуге на више уређаја"</string> <string name="helper_summary_app_streaming" msgid="2396773196949578425">"<xliff:g id="APP_NAME">%1$s</xliff:g> захтева дозволу у име уређаја <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> за стримовање апликација између уређаја"</string> @@ -38,10 +37,10 @@ <string name="title_nearby_device_streaming" msgid="7269956847378799794">"Желите ли да дозволите да <strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> обави ову радњу?"</string> <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"Апликација <xliff:g id="APP_NAME">%1$s</xliff:g> захтева дозволу у име уређаја <xliff:g id="DEVICE_NAME">%2$s</xliff:g> да стримује апликације и друге системске функције на уређаје у близини"</string> <string name="profile_name_generic" msgid="6851028682723034988">"уређај"</string> - <string name="summary_generic_single_device" msgid="4181180669689590417">"Ова апликација ће моћи да синхронизује податке, попут имена особе која упућује позив, између телефона и уређаја <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="summary_generic" msgid="1761976003668044801">"Ова апликација ће моћи да синхронизује податке, попут имена особе која упућује позив, између телефона и одабраног уређаја"</string> <string name="consent_yes" msgid="8344487259618762872">"Дозволи"</string> <string name="consent_no" msgid="2640796915611404382">"Не дозволи"</string> + <string name="consent_cancel" msgid="5655005528379285841">"Откажи"</string> <string name="consent_back" msgid="2560683030046918882">"Назад"</string> <string name="permission_expand" msgid="893185038020887411">"Прошири <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string> <string name="permission_collapse" msgid="3320833884220844084">"Скупи <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string> diff --git a/packages/CompanionDeviceManager/res/values-sv/strings.xml b/packages/CompanionDeviceManager/res/values-sv/strings.xml index 54e2d18ea451..f3be3becf16c 100644 --- a/packages/CompanionDeviceManager/res/values-sv/strings.xml +++ b/packages/CompanionDeviceManager/res/values-sv/strings.xml @@ -17,15 +17,14 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string> - <string name="confirmation_title" msgid="4593465730772390351">"Vill du tillåta att <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> får åtkomst till <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> + <string name="confirmation_title" msgid="2244241995958340998">"Vill du tillåta att appen <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> får åtkomst till <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> <string name="profile_name_watch" msgid="576290739483672360">"klocka"</string> - <string name="chooser_title" msgid="2262294130493605839">"Välj en <xliff:g id="PROFILE_NAME">%1$s</xliff:g> för hantering av <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> - <string name="summary_watch" msgid="898569637110705523">"Appen behövs för att hantera <xliff:g id="DEVICE_NAME">%1$s</xliff:g>. <xliff:g id="APP_NAME">%2$s</xliff:g> får tillåtelse att synkronisera information, till exempel namnet på någon som ringer, interagera med dina aviseringar och får åtkomst till behörigheterna Telefon, Sms, Kontakter, Kalender, Samtalsloggar och Enheter i närheten."</string> - <string name="summary_watch_single_device" msgid="3173948915947011333">"Appen får tillåtelse att synkronisera information, till exempel namnet på någon som ringer, och få tillgång till dessa behörigheter på din <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string> + <string name="chooser_title_non_profile" msgid="6035023914517087400">"Välj en enhet för hantering av <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>"</string> + <string name="chooser_title" msgid="2235819929238267637">"Välj en <xliff:g id="PROFILE_NAME">%1$s</xliff:g> för konfigurering"</string> + <string name="summary_watch" msgid="7962014927042971830">"Appen får tillåtelse att synkronisera information, till exempel namnet på någon som ringer, och få tillgång till dessa behörigheter på din <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="confirmation_title_glasses" msgid="8288346850537727333">"Tillåt att <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> hanterar <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string> - <string name="profile_name_glasses" msgid="8488394059007275998">"glasögon"</string> - <string name="summary_glasses_multi_device" msgid="615259525961937348">"Appen behövs för att hantera <xliff:g id="DEVICE_NAME">%1$s</xliff:g>. <xliff:g id="APP_NAME">%2$s</xliff:g> får tillåtelse att interagera med dina aviseringar och får åtkomst till behörigheterna Telefon, Sms, Kontakter, Mikrofon och Enheter i närheten."</string> - <string name="summary_glasses_single_device" msgid="3000909894067413398">"Appen får tillåtelse att använda dessa behörigheter på din <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string> + <string name="profile_name_glasses" msgid="3506504967216601277">"enhet"</string> + <string name="summary_glasses" msgid="2872254734959842579">"Appen får tillåtelse att använda dessa behörigheter på din <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="title_app_streaming" msgid="2270331024626446950">"Ge <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> åtkomstbehörighet till denna information på telefonen"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Tjänster för flera enheter"</string> <string name="helper_summary_app_streaming" msgid="2396773196949578425">"<xliff:g id="APP_NAME">%1$s</xliff:g> begär behörighet att låta <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> streama appar mellan enheter"</string> @@ -38,10 +37,10 @@ <string name="title_nearby_device_streaming" msgid="7269956847378799794">"Vill du tillåta att <strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> utför denna åtgärd?"</string> <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<xliff:g id="APP_NAME">%1$s</xliff:g> begär behörighet att streama appar och andra systemfunktioner till enheter i närheten för din <xliff:g id="DEVICE_NAME">%2$s</xliff:g>"</string> <string name="profile_name_generic" msgid="6851028682723034988">"enhet"</string> - <string name="summary_generic_single_device" msgid="4181180669689590417">"Den här appen kommer att kunna synkronisera information mellan telefonen och <xliff:g id="DEVICE_NAME">%1$s</xliff:g>, till exempel namnet på någon som ringer"</string> <string name="summary_generic" msgid="1761976003668044801">"Den här appen kommer att kunna synkronisera information mellan telefonen och den valda enheten, till exempel namnet på någon som ringer"</string> <string name="consent_yes" msgid="8344487259618762872">"Tillåt"</string> <string name="consent_no" msgid="2640796915611404382">"Tillåt inte"</string> + <string name="consent_cancel" msgid="5655005528379285841">"Avbryt"</string> <string name="consent_back" msgid="2560683030046918882">"Tillbaka"</string> <string name="permission_expand" msgid="893185038020887411">"Utöka <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string> <string name="permission_collapse" msgid="3320833884220844084">"Komprimera <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string> diff --git a/packages/CompanionDeviceManager/res/values-sw/strings.xml b/packages/CompanionDeviceManager/res/values-sw/strings.xml index 6e8c607f3fdf..7b94239fddb8 100644 --- a/packages/CompanionDeviceManager/res/values-sw/strings.xml +++ b/packages/CompanionDeviceManager/res/values-sw/strings.xml @@ -17,15 +17,14 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="4470785958457506021">"Kidhibiti cha Vifaa Visaidizi"</string> - <string name="confirmation_title" msgid="4593465730772390351">"Ungependa kuruhusu <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ifikie <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> + <string name="confirmation_title" msgid="2244241995958340998">"Ungependa kuruhusu <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ifikie <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> <string name="profile_name_watch" msgid="576290739483672360">"saa"</string> - <string name="chooser_title" msgid="2262294130493605839">"Chagua <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ili idhibitiwe na <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> - <string name="summary_watch" msgid="898569637110705523">"Programu hii inahitajika ili udhibiti <xliff:g id="DEVICE_NAME">%1$s</xliff:g> yako. <xliff:g id="APP_NAME">%2$s</xliff:g> itaruhusiwa kusawazisha maelezo, kama vile jina la mtu anayepiga simu, kutumia arifa zako na ruhusa zako za Simu, SMS, Anwani, Maikrofoni na vifaa vilivyo Karibu."</string> - <string name="summary_watch_single_device" msgid="3173948915947011333">"Programu hii itaruhusiwa kusawazisha maelezo, kama vile jina la mtu anayepiga simu na kufikia ruhusa hizi kwenye <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> yako"</string> + <string name="chooser_title_non_profile" msgid="6035023914517087400">"Chagua kifaa cha kudhibitiwa na <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>"</string> + <string name="chooser_title" msgid="2235819929238267637">"Chagua <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ili uweke mipangilio"</string> + <string name="summary_watch" msgid="7962014927042971830">"Programu hii itaruhusiwa kusawazisha maelezo, kama vile jina la mtu anayepiga simu na kufikia ruhusa hizi kwenye <xliff:g id="DEVICE_NAME">%1$s</xliff:g> yako"</string> <string name="confirmation_title_glasses" msgid="8288346850537727333">"Ungependa kuruhusu <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> idhibiti <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> - <string name="profile_name_glasses" msgid="8488394059007275998">"miwani"</string> - <string name="summary_glasses_multi_device" msgid="615259525961937348">"Programu hii inahitajika ili udhibiti <xliff:g id="DEVICE_NAME">%1$s</xliff:g>. <xliff:g id="APP_NAME">%2$s</xliff:g> itaruhusiwa kutumia arifa zako na kufikia ruhusa zako za Simu, SMS, Anwani, Maikrofoni na Vifaa vilivyo Karibu."</string> - <string name="summary_glasses_single_device" msgid="3000909894067413398">"Programu hii itaruhusiwa kufikia ruhusa hizi kwenye <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> yako"</string> + <string name="profile_name_glasses" msgid="3506504967216601277">"kifaa"</string> + <string name="summary_glasses" msgid="2872254734959842579">"Programu hii itaruhusiwa kufikia ruhusa hizi kwenye <xliff:g id="DEVICE_NAME">%1$s</xliff:g> yako"</string> <string name="title_app_streaming" msgid="2270331024626446950">"Ruhusu <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ifikie maelezo haya kutoka kwenye simu yako"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Huduma za kifaa kilichounganishwa kwingine"</string> <string name="helper_summary_app_streaming" msgid="2396773196949578425">"Programu ya <xliff:g id="APP_NAME">%1$s</xliff:g> inaomba ruhusa kwa niaba ya <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> yako ili itiririshe programu kati ya vifaa vyako"</string> @@ -38,10 +37,10 @@ <string name="title_nearby_device_streaming" msgid="7269956847378799794">"Ungependa kuruhusu <strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> itekeleze kitendo hiki?"</string> <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<xliff:g id="APP_NAME">%1$s</xliff:g> inaomba ruhusa kwa niaba ya <xliff:g id="DEVICE_NAME">%2$s</xliff:g> chako ili itiririshe programu na vipengele vingine vya mfumo kwenye vifaa vilivyo karibu"</string> <string name="profile_name_generic" msgid="6851028682723034988">"kifaa"</string> - <string name="summary_generic_single_device" msgid="4181180669689590417">"Programu hii itaweza kusawazisha maelezo, kama vile jina la mtu anayepiga simu, kati ya simu na <xliff:g id="DEVICE_NAME">%1$s</xliff:g> yako"</string> <string name="summary_generic" msgid="1761976003668044801">"Programu hii itaweza kusawazisha maelezo, kama vile jina la mtu anayepiga simu, kati ya simu yako na kifaa ulichochagua"</string> <string name="consent_yes" msgid="8344487259618762872">"Ruhusu"</string> <string name="consent_no" msgid="2640796915611404382">"Usiruhusu"</string> + <string name="consent_cancel" msgid="5655005528379285841">"Ghairi"</string> <string name="consent_back" msgid="2560683030046918882">"Nyuma"</string> <string name="permission_expand" msgid="893185038020887411">"Panua <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string> <string name="permission_collapse" msgid="3320833884220844084">"Kunja <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string> diff --git a/packages/CompanionDeviceManager/res/values-ta/strings.xml b/packages/CompanionDeviceManager/res/values-ta/strings.xml index d049953420fc..c1b835299e8d 100644 --- a/packages/CompanionDeviceManager/res/values-ta/strings.xml +++ b/packages/CompanionDeviceManager/res/values-ta/strings.xml @@ -17,15 +17,14 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="4470785958457506021">"கம்பேனியன் சாதன நிர்வாகி"</string> - <string name="confirmation_title" msgid="4593465730772390351">"<strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> சாதனத்தை அணுக <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ஆப்ஸை அனுமதிக்கவா?"</string> + <string name="confirmation_title" msgid="2244241995958340998">"<strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> சாதனத்தை அணுக <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ஆப்ஸை அனுமதிக்கவா?"</string> <string name="profile_name_watch" msgid="576290739483672360">"வாட்ச்"</string> - <string name="chooser_title" msgid="2262294130493605839">"<strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> ஆப்ஸ் நிர்வகிக்கக்கூடிய <xliff:g id="PROFILE_NAME">%1$s</xliff:g> தேர்ந்தெடுக்கப்பட வேண்டும்"</string> - <string name="summary_watch" msgid="898569637110705523">"உங்கள் <xliff:g id="DEVICE_NAME">%1$s</xliff:g> சாதனத்தை நிர்வகிக்க இந்த ஆப்ஸ் தேவை. அழைப்பவரின் பெயர் போன்ற தகவலை ஒத்திசைத்தல், உங்கள் அறிவிப்புகளைப் பார்த்தல், உங்கள் மொபைல், மெசேஜ், தொடர்புகள், கேலெண்டர், அழைப்புப் பதிவுகள், அருகிலுள்ள சாதனங்களை அணுகுதல் ஆகியவற்றுக்கு <xliff:g id="APP_NAME">%2$s</xliff:g> அனுமதிக்கப்படும்."</string> - <string name="summary_watch_single_device" msgid="3173948915947011333">"அழைப்பவரின் பெயர் போன்ற தகவல்களை ஒத்திசைக்கவும் உங்கள் <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> சாதனத்தில் இந்த அனுமதிகளை அணுகவும் இந்த ஆப்ஸ் அனுமதிக்கப்படும்"</string> + <string name="chooser_title_non_profile" msgid="6035023914517087400">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ஆப்ஸால் நிர்வகிக்கப்பட வேண்டிய சாதனத்தைத் தேர்வுசெய்யுங்கள்"</string> + <string name="chooser_title" msgid="2235819929238267637">"அமைக்க <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ஐத் தேர்வுசெய்யவும்"</string> + <string name="summary_watch" msgid="7962014927042971830">"அழைப்பவரின் பெயர் போன்ற தகவல்களை ஒத்திசைக்கவும் உங்கள் <xliff:g id="DEVICE_NAME">%1$s</xliff:g> சாதனத்தில் இந்த அனுமதிகளை அணுகவும் இந்த ஆப்ஸ் அனுமதிக்கப்படும்"</string> <string name="confirmation_title_glasses" msgid="8288346850537727333">"<strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong&gt சாதனத்தை நிர்வகிக்க <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ஆப்ஸை அனுமதிக்கவா?"</string> - <string name="profile_name_glasses" msgid="8488394059007275998">"கிளாஸஸ்"</string> - <string name="summary_glasses_multi_device" msgid="615259525961937348">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> சாதனத்தை நிர்வகிக்க இந்த ஆப்ஸ் தேவை. உங்கள் மொபைல், மெசேஜ், தொடர்புகள், மைக்ரோஃபோன், அருகிலுள்ள சாதனங்கள் ஆகியவற்றுக்கான அணுகலையும் உங்கள் அறிவிப்புகளைப் பார்ப்பதற்கான அனுமதியையும் <xliff:g id="APP_NAME">%2$s</xliff:g> பெறும்."</string> - <string name="summary_glasses_single_device" msgid="3000909894067413398">"உங்கள் <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> சாதனத்தில் இந்த அனுமதிகளை அணுக இந்த ஆப்ஸ் அனுமதிக்கப்படும்"</string> + <string name="profile_name_glasses" msgid="3506504967216601277">"சாதனம்"</string> + <string name="summary_glasses" msgid="2872254734959842579">"உங்கள் <xliff:g id="DEVICE_NAME">%1$s</xliff:g> சாதனத்தில் இந்த அனுமதிகளை அணுக இந்த ஆப்ஸ் அனுமதிக்கப்படும்"</string> <string name="title_app_streaming" msgid="2270331024626446950">"மொபைலில் உள்ள இந்தத் தகவல்களை அணுக, <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ஆப்ஸை அனுமதிக்கவும்"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"பன்முக சாதன சேவைகள்"</string> <string name="helper_summary_app_streaming" msgid="2396773196949578425">"உங்கள் சாதனங்களுக்கு இடையே ஆப்ஸை ஸ்ட்ரீம் செய்ய உங்கள் <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> சார்பாக <xliff:g id="APP_NAME">%1$s</xliff:g> ஆப்ஸ் அனுமதியைக் கோருகிறது"</string> @@ -38,10 +37,10 @@ <string name="title_nearby_device_streaming" msgid="7269956847378799794">"இந்தச் செயலைச் செய்ய <strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> ஐ அனுமதிக்கவா?"</string> <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"அருகிலுள்ள சாதனங்களுக்கு ஆப்ஸையும் பிற சிஸ்டம் அம்சங்களையும் ஸ்ட்ரீம் செய்ய உங்கள் <xliff:g id="DEVICE_NAME">%2$s</xliff:g> சார்பாக <xliff:g id="APP_NAME">%1$s</xliff:g> அனுமதி கோருகிறது"</string> <string name="profile_name_generic" msgid="6851028682723034988">"சாதனம்"</string> - <string name="summary_generic_single_device" msgid="4181180669689590417">"அழைப்பவரின் பெயர் போன்ற தகவலை உங்கள் மொபைல் மற்றும் <xliff:g id="DEVICE_NAME">%1$s</xliff:g> சாதனத்திற்கு இடையில் இந்த ஆப்ஸால் ஒத்திசைக்க முடியும்"</string> <string name="summary_generic" msgid="1761976003668044801">"அழைப்பவரின் பெயர் போன்ற தகவலை உங்கள் மொபைல் மற்றும் தேர்வுசெய்த சாதனத்திற்கு இடையில் இந்த ஆப்ஸால் ஒத்திசைக்க முடியும்"</string> <string name="consent_yes" msgid="8344487259618762872">"அனுமதி"</string> <string name="consent_no" msgid="2640796915611404382">"அனுமதிக்க வேண்டாம்"</string> + <string name="consent_cancel" msgid="5655005528379285841">"ரத்துசெய்"</string> <string name="consent_back" msgid="2560683030046918882">"பின்செல்"</string> <string name="permission_expand" msgid="893185038020887411">"<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g> ஐ விரிவாக்கும்"</string> <string name="permission_collapse" msgid="3320833884220844084">"<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g> ஐச் சுருக்கும்"</string> diff --git a/packages/CompanionDeviceManager/res/values-te/strings.xml b/packages/CompanionDeviceManager/res/values-te/strings.xml index 5e91614b9b59..52316cdc6227 100644 --- a/packages/CompanionDeviceManager/res/values-te/strings.xml +++ b/packages/CompanionDeviceManager/res/values-te/strings.xml @@ -17,15 +17,14 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="4470785958457506021">"సహచర పరికర మేనేజర్"</string> - <string name="confirmation_title" msgid="4593465730772390351">"<strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>ను యాక్సెస్ చేయడానికి <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>ను అనుమతించాలా?"</string> + <string name="confirmation_title" msgid="2244241995958340998">"<strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>ను యాక్సెస్ చేయడానికి <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> యాప్ను అనుమతించాలా?"</string> <string name="profile_name_watch" msgid="576290739483672360">"వాచ్"</string> - <string name="chooser_title" msgid="2262294130493605839">"<strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> ద్వారా మేనేజ్ చేయబడటానికి ఒక <xliff:g id="PROFILE_NAME">%1$s</xliff:g>ను ఎంచుకోండి"</string> - <string name="summary_watch" msgid="898569637110705523">"మీ <xliff:g id="DEVICE_NAME">%1$s</xliff:g>ను మేనేజ్ చేయడానికి ఈ యాప్ అవసరం. కాల్ చేస్తున్న వారి పేరు వంటి సమాచారాన్ని సింక్ చేయడానికి, మీ నోటిఫికేషన్లతో ఇంటరాక్ట్ అవ్వడానికి, అలాగే మీ ఫోన్, SMS, కాంటాక్ట్లు, క్యాలెండర్, కాల్ లాగ్లు, సమీపంలోని పరికరాల అనుమతులను యాక్సెస్ చేయడానికి <xliff:g id="APP_NAME">%2$s</xliff:g> అనుమతించబడుతుంది."</string> - <string name="summary_watch_single_device" msgid="3173948915947011333">"కాల్ చేస్తున్న వారి పేరు వంటి సమాచారాన్ని సింక్ చేయడానికి, మీ <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>లో ఈ అనుమతులను యాక్సెస్ చేయడానికి ఈ యాప్ అనుమతించబడుతుంది"</string> + <string name="chooser_title_non_profile" msgid="6035023914517087400">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ద్వారా మేనేజ్ చేయబడే పరికరాన్ని ఎంచుకోండి"</string> + <string name="chooser_title" msgid="2235819929238267637">"సెటప్ చేయడానికి <xliff:g id="PROFILE_NAME">%1$s</xliff:g>ను ఎంచుకోండి"</string> + <string name="summary_watch" msgid="7962014927042971830">"కాల్ చేస్తున్న వారి పేరు వంటి సమాచారాన్ని సింక్ చేయడానికి, మీ <xliff:g id="DEVICE_NAME">%1$s</xliff:g>లో ఈ అనుమతులను యాక్సెస్ చేయడానికి ఈ యాప్ అనుమతించబడుతుంది"</string> <string name="confirmation_title_glasses" msgid="8288346850537727333">"<strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>ను మేనేజ్ చేయడానికి <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>ను అనుమతించాలా?"</string> - <string name="profile_name_glasses" msgid="8488394059007275998">"గ్లాసెస్"</string> - <string name="summary_glasses_multi_device" msgid="615259525961937348">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>ను మేనేజ్ చేయడానికి ఈ యాప్ అవసరం. మీ నోటిఫికేషన్లతో ఇంటరాక్ట్ అవ్వడానికి, అలాగే మీ ఫోన్, SMS, కాంటాక్ట్లు, మైక్రోఫోన్, సమీపంలోని పరికరాల అనుమతులను యాక్సెస్ చేయడానికి <xliff:g id="APP_NAME">%2$s</xliff:g> అనుమతించబడుతుంది."</string> - <string name="summary_glasses_single_device" msgid="3000909894067413398">"మీ <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>లో ఈ అనుమతులను యాక్సెస్ చేయడానికి ఈ యాప్ అనుమతించబడుతుంది"</string> + <string name="profile_name_glasses" msgid="3506504967216601277">"పరికరం"</string> + <string name="summary_glasses" msgid="2872254734959842579">"మీ <xliff:g id="DEVICE_NAME">%1$s</xliff:g>లో ఈ అనుమతులను యాక్సెస్ చేయడానికి ఈ యాప్ అనుమతించబడుతుంది"</string> <string name="title_app_streaming" msgid="2270331024626446950">"మీ ఫోన్ నుండి ఈ సమాచారాన్ని యాక్సెస్ చేయడానికి <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> యాప్ను అనుమతించండి"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Cross-device services"</string> <string name="helper_summary_app_streaming" msgid="2396773196949578425">"మీ పరికరాల మధ్య యాప్లను స్ట్రీమ్ చేయడానికి <xliff:g id="APP_NAME">%1$s</xliff:g> మీ <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> తరఫున అనుమతిని రిక్వెస్ట్ చేస్తోంది"</string> @@ -38,10 +37,10 @@ <string name="title_nearby_device_streaming" msgid="7269956847378799794">"ఈ చర్యను అమలు చేయడానికి <strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong>ను అనుమతించాలా?"</string> <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"సమీపంలోని పరికరాలకు యాప్లను, ఇతర సిస్టమ్ ఫీచర్లను స్ట్రీమ్ చేయడానికి <xliff:g id="APP_NAME">%1$s</xliff:g> మీ <xliff:g id="DEVICE_NAME">%2$s</xliff:g> తరఫున అనుమతిని రిక్వెస్ట్ చేస్తోంది"</string> <string name="profile_name_generic" msgid="6851028682723034988">"పరికరం"</string> - <string name="summary_generic_single_device" msgid="4181180669689590417">"కాల్ చేస్తున్న వారి పేరు వంటి సమాచారాన్ని ఈ యాప్ మీ ఫోన్కి, <xliff:g id="DEVICE_NAME">%1$s</xliff:g>కి మధ్య సింక్ చేయగలుగుతుంది"</string> <string name="summary_generic" msgid="1761976003668044801">"కాల్ చేస్తున్న వారి పేరు వంటి సమాచారాన్ని ఈ యాప్ మీ ఫోన్ కు, ఎంచుకున్న పరికరానికీ మధ్య సింక్ చేయగలుగుతుంది"</string> <string name="consent_yes" msgid="8344487259618762872">"అనుమతించండి"</string> <string name="consent_no" msgid="2640796915611404382">"అనుమతించవద్దు"</string> + <string name="consent_cancel" msgid="5655005528379285841">"రద్దు చేయండి"</string> <string name="consent_back" msgid="2560683030046918882">"వెనుకకు"</string> <string name="permission_expand" msgid="893185038020887411">"<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>ను విస్తరించండి"</string> <string name="permission_collapse" msgid="3320833884220844084">"<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>ను కుదించండి"</string> diff --git a/packages/CompanionDeviceManager/res/values-th/strings.xml b/packages/CompanionDeviceManager/res/values-th/strings.xml index 46e631cecb88..cb01f2d0d9ac 100644 --- a/packages/CompanionDeviceManager/res/values-th/strings.xml +++ b/packages/CompanionDeviceManager/res/values-th/strings.xml @@ -17,15 +17,14 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string> - <string name="confirmation_title" msgid="4593465730772390351">"อนุญาตให้ <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> เข้าถึง <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string> + <string name="confirmation_title" msgid="2244241995958340998">"อนุญาตให้แอป <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> เข้าถึง <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> ใช่ไหม"</string> <string name="profile_name_watch" msgid="576290739483672360">"นาฬิกา"</string> - <string name="chooser_title" msgid="2262294130493605839">"เลือก<xliff:g id="PROFILE_NAME">%1$s</xliff:g>ที่จะให้มีการจัดการโดย <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> - <string name="summary_watch" msgid="898569637110705523">"ต้องใช้แอปนี้ในการจัดการ<xliff:g id="DEVICE_NAME">%1$s</xliff:g> <xliff:g id="APP_NAME">%2$s</xliff:g> จะได้รับอนุญาตให้ซิงค์ข้อมูล เช่น ชื่อของบุคคลที่โทรเข้ามา โต้ตอบกับการแจ้งเตือน รวมถึงมีสิทธิ์เข้าถึงโทรศัพท์, SMS, รายชื่อติดต่อ, ปฏิทิน, บันทึกการโทร และอุปกรณ์ที่อยู่ใกล้เคียง"</string> - <string name="summary_watch_single_device" msgid="3173948915947011333">"แอปนี้จะได้รับอนุญาตให้ซิงค์ข้อมูล เช่น ชื่อของบุคคลที่โทรเข้ามา และมีสิทธิ์เข้าถึงข้อมูลเหล่านี้ใน<xliff:g id="DEVICE_TYPE">%1$s</xliff:g>ของคุณ"</string> + <string name="chooser_title_non_profile" msgid="6035023914517087400">"เลือกอุปกรณ์ที่จะให้มีการจัดการโดย <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>"</string> + <string name="chooser_title" msgid="2235819929238267637">"เลือก<xliff:g id="PROFILE_NAME">%1$s</xliff:g>ที่จะตั้งค่า"</string> + <string name="summary_watch" msgid="7962014927042971830">"แอปนี้จะได้รับอนุญาตให้ซิงค์ข้อมูล เช่น ชื่อของบุคคลที่โทรเข้ามา และมีสิทธิ์เข้าถึงข้อมูลเหล่านี้ใน<xliff:g id="DEVICE_NAME">%1$s</xliff:g>ของคุณ"</string> <string name="confirmation_title_glasses" msgid="8288346850537727333">"อนุญาตให้ <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> จัดการ <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> ไหม"</string> - <string name="profile_name_glasses" msgid="8488394059007275998">"แว่นตา"</string> - <string name="summary_glasses_multi_device" msgid="615259525961937348">"ต้องใช้แอปนี้ในการจัดการ<xliff:g id="DEVICE_NAME">%1$s</xliff:g> <xliff:g id="APP_NAME">%2$s</xliff:g> จะได้รับอนุญาตให้โต้ตอบกับการแจ้งเตือนและมีสิทธิ์เข้าถึงโทรศัพท์, SMS, รายชื่อติดต่อ, ไมโครโฟน และอุปกรณ์ที่อยู่ใกล้เคียง"</string> - <string name="summary_glasses_single_device" msgid="3000909894067413398">"แอปนี้จะได้รับสิทธิ์ดังต่อไปนี้ใน<xliff:g id="DEVICE_TYPE">%1$s</xliff:g>ของคุณ"</string> + <string name="profile_name_glasses" msgid="3506504967216601277">"อุปกรณ์"</string> + <string name="summary_glasses" msgid="2872254734959842579">"แอปนี้จะได้รับสิทธิ์ดังต่อไปนี้ใน<xliff:g id="DEVICE_NAME">%1$s</xliff:g>ของคุณ"</string> <string name="title_app_streaming" msgid="2270331024626446950">"อนุญาตให้ <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> เข้าถึงข้อมูลนี้จากโทรศัพท์ของคุณ"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"บริการหลายอุปกรณ์"</string> <string name="helper_summary_app_streaming" msgid="2396773196949578425">"<xliff:g id="APP_NAME">%1$s</xliff:g> กำลังขอสิทธิ์ในนามของ <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> เพื่อสตรีมแอประหว่างอุปกรณ์ต่างๆ ของคุณ"</string> @@ -38,10 +37,10 @@ <string name="title_nearby_device_streaming" msgid="7269956847378799794">"อนุญาตให้ <strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> ทำงานนี้ไหม"</string> <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<xliff:g id="APP_NAME">%1$s</xliff:g> กำลังขอสิทธิ์ในนามของ <xliff:g id="DEVICE_NAME">%2$s</xliff:g> เพื่อสตรีมแอปและฟีเจอร์อื่นๆ ของระบบไปยังอุปกรณ์ที่อยู่ใกล้เคียง"</string> <string name="profile_name_generic" msgid="6851028682723034988">"อุปกรณ์"</string> - <string name="summary_generic_single_device" msgid="4181180669689590417">"แอปนี้จะสามารถซิงค์ข้อมูล เช่น ชื่อของบุคคลที่โทรเข้ามา ระหว่างโทรศัพท์ของคุณและ<xliff:g id="DEVICE_NAME">%1$s</xliff:g>ได้"</string> <string name="summary_generic" msgid="1761976003668044801">"แอปนี้จะสามารถซิงค์ข้อมูล เช่น ชื่อของบุคคลที่โทรเข้ามา ระหว่างโทรศัพท์ของคุณและอุปกรณ์ที่เลือกไว้ได้"</string> <string name="consent_yes" msgid="8344487259618762872">"อนุญาต"</string> <string name="consent_no" msgid="2640796915611404382">"ไม่อนุญาต"</string> + <string name="consent_cancel" msgid="5655005528379285841">"ยกเลิก"</string> <string name="consent_back" msgid="2560683030046918882">"กลับ"</string> <string name="permission_expand" msgid="893185038020887411">"ขยาย <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string> <string name="permission_collapse" msgid="3320833884220844084">"ยุบ <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string> diff --git a/packages/CompanionDeviceManager/res/values-tl/strings.xml b/packages/CompanionDeviceManager/res/values-tl/strings.xml index b358870ae956..43d599666649 100644 --- a/packages/CompanionDeviceManager/res/values-tl/strings.xml +++ b/packages/CompanionDeviceManager/res/values-tl/strings.xml @@ -17,15 +17,14 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="4470785958457506021">"Kasamang Device Manager"</string> - <string name="confirmation_title" msgid="4593465730772390351">"Payagan ang <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> na i-access ang <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> + <string name="confirmation_title" msgid="2244241995958340998">"Payagan ang app na <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> na i-access ang <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> <string name="profile_name_watch" msgid="576290739483672360">"relo"</string> - <string name="chooser_title" msgid="2262294130493605839">"Pumili ng <xliff:g id="PROFILE_NAME">%1$s</xliff:g> para pamahalaan ng <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> - <string name="summary_watch" msgid="898569637110705523">"Kailangan ang app na ito para mapamahalaan ang iyong <xliff:g id="DEVICE_NAME">%1$s</xliff:g>. Papayagan ang <xliff:g id="APP_NAME">%2$s</xliff:g> na mag-sync ng impormasyon, tulad ng pangalan ng isang taong tumatawag, makipag-ugnayan sa mga notification mo, at ma-access ang iyong mga pahintulot sa Telepono, SMS, Mga Contact, Kalendaryo, Mga log ng tawag, at Mga kalapit na device."</string> - <string name="summary_watch_single_device" msgid="3173948915947011333">"Papayagan ang app na ito na mag-sync ng impormasyon, tulad ng pangalan ng isang taong tumatawag, at i-access ang mga pahintulot na ito sa iyong <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string> + <string name="chooser_title_non_profile" msgid="6035023914517087400">"Pumili ng device na papamahalaan ng <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>"</string> + <string name="chooser_title" msgid="2235819929238267637">"Pumili ng <xliff:g id="PROFILE_NAME">%1$s</xliff:g> para mag-set up"</string> + <string name="summary_watch" msgid="7962014927042971830">"Papayagan ang app na ito na mag-sync ng impormasyon, tulad ng pangalan ng taong tumatawag, at i-access ang mga pahintulot na ito sa iyong <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="confirmation_title_glasses" msgid="8288346850537727333">"Payagan ang <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> na pamahalaan ang <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> - <string name="profile_name_glasses" msgid="8488394059007275998">"salamin"</string> - <string name="summary_glasses_multi_device" msgid="615259525961937348">"Kailangan ang app na ito para mapamahalaan ang <xliff:g id="DEVICE_NAME">%1$s</xliff:g>. Papayagan ang <xliff:g id="APP_NAME">%2$s</xliff:g> na makipag-ugnayan sa mga notification mo at i-access ang iyong mga pahintulot sa Telepono, SMS, Mga Contact, Mikropono, at Mga kalapit na device."</string> - <string name="summary_glasses_single_device" msgid="3000909894067413398">"Papayagan ang app na ito na i-access ang mga pahintulot na ito sa iyong <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string> + <string name="profile_name_glasses" msgid="3506504967216601277">"device"</string> + <string name="summary_glasses" msgid="2872254734959842579">"Papayagan ang app na ito na i-access ang mga pahintulot na ito sa iyong <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="title_app_streaming" msgid="2270331024626446950">"Payagan ang <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> na i-access ang impormasyong ito sa iyong telepono"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Mga cross-device na serbisyo"</string> <string name="helper_summary_app_streaming" msgid="2396773196949578425">"Ang <xliff:g id="APP_NAME">%1$s</xliff:g> ay humihiling ng pahintulot sa ngalan ng iyong <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> para mag-stream ng mga app sa pagitan ng mga device mo"</string> @@ -38,10 +37,10 @@ <string name="title_nearby_device_streaming" msgid="7269956847378799794">"Payagan ang <strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> na gawin ang pagkilos na ito?"</string> <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"Humihiling ang <xliff:g id="APP_NAME">%1$s</xliff:g> ng pahintulot sa ngalan ng iyong <xliff:g id="DEVICE_NAME">%2$s</xliff:g> para mag-stream ng mga app at iba pang feature ng system sa mga kalapit na device"</string> <string name="profile_name_generic" msgid="6851028682723034988">"device"</string> - <string name="summary_generic_single_device" msgid="4181180669689590417">"Magagawa ng app na ito na mag-sync ng impormasyon, tulad ng pangalan ng isang taong tumatawag, sa pagitan ng iyong telepono at ng <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="summary_generic" msgid="1761976003668044801">"Magagawa ng app na ito na mag-sync ng impormasyon, tulad ng pangalan ng isang taong tumatawag, sa pagitan ng iyong telepono at ng napiling device"</string> <string name="consent_yes" msgid="8344487259618762872">"Payagan"</string> <string name="consent_no" msgid="2640796915611404382">"Huwag payagan"</string> + <string name="consent_cancel" msgid="5655005528379285841">"Kanselahin"</string> <string name="consent_back" msgid="2560683030046918882">"Bumalik"</string> <string name="permission_expand" msgid="893185038020887411">"I-expand ang <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string> <string name="permission_collapse" msgid="3320833884220844084">"I-collapse ang <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string> diff --git a/packages/CompanionDeviceManager/res/values-tr/strings.xml b/packages/CompanionDeviceManager/res/values-tr/strings.xml index c5229ab6d747..1e8e842b28ba 100644 --- a/packages/CompanionDeviceManager/res/values-tr/strings.xml +++ b/packages/CompanionDeviceManager/res/values-tr/strings.xml @@ -17,15 +17,14 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string> - <string name="confirmation_title" msgid="4593465730772390351">"<strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> cihazına erişmesi için <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> uygulamasına izin verin"</string> + <string name="confirmation_title" msgid="2244241995958340998">"<strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> cihazına erişmesi için <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> uygulamasına izin verin"</string> <string name="profile_name_watch" msgid="576290739483672360">"saat"</string> - <string name="chooser_title" msgid="2262294130493605839">"<strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> tarafından yönetilecek bir <xliff:g id="PROFILE_NAME">%1$s</xliff:g> seçin"</string> - <string name="summary_watch" msgid="898569637110705523">"Bu uygulama, <xliff:g id="DEVICE_NAME">%1$s</xliff:g> cihazınızın yönetilmesi için gereklidir. <xliff:g id="APP_NAME">%2$s</xliff:g> adlı uygulamanın arayan kişinin adı gibi bilgileri senkronize etmesine, bildirimlerinizle etkileşimde bulunup Telefon, SMS, Kişiler, Takvim, Arama kayıtları ve Yakındaki cihazlar izinlerine erişmesine izin verilir."</string> - <string name="summary_watch_single_device" msgid="3173948915947011333">"Bu uygulamanın arayan kişinin adı gibi bilgileri senkronize etmesine ve <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> cihazınızda aşağıdaki izinlere erişmesine izin verilir"</string> + <string name="chooser_title_non_profile" msgid="6035023914517087400">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> tarafından yönetilecek bir cihaz seçin"</string> + <string name="chooser_title" msgid="2235819929238267637">"Ayarlamak için bir <xliff:g id="PROFILE_NAME">%1$s</xliff:g> seçin"</string> + <string name="summary_watch" msgid="7962014927042971830">"Bu uygulamanın arayan kişinin adı gibi bilgileri senkronize etmesine ve <xliff:g id="DEVICE_NAME">%1$s</xliff:g> cihazınızda aşağıdaki izinlere erişmesine izin verilir"</string> <string name="confirmation_title_glasses" msgid="8288346850537727333">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> uygulamasına <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> cihazını yönetmesi için izin verilsin mi?"</string> - <string name="profile_name_glasses" msgid="8488394059007275998">"glasses"</string> - <string name="summary_glasses_multi_device" msgid="615259525961937348">"Bu uygulama, <xliff:g id="DEVICE_NAME">%1$s</xliff:g> cihazının yönetilmesi için gereklidir. <xliff:g id="APP_NAME">%2$s</xliff:g> adlı uygulamanın bildirimlerinizle etkileşimde bulunup Telefon, SMS, Kişiler, Mikrofon ve Yakındaki cihazlar izinlerine erişmesine izin verilir."</string> - <string name="summary_glasses_single_device" msgid="3000909894067413398">"Bu uygulamanın <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> cihazınızda şu izinlere erişmesine izin verilecek:"</string> + <string name="profile_name_glasses" msgid="3506504967216601277">"Cihaz"</string> + <string name="summary_glasses" msgid="2872254734959842579">"Bu uygulamanın <xliff:g id="DEVICE_NAME">%1$s</xliff:g> cihazınızda şu izinlere erişmesine izin verilecek:"</string> <string name="title_app_streaming" msgid="2270331024626446950">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> uygulamasının, telefonunuzdaki bu bilgilere erişmesine izin verin"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Cihazlar arası hizmetler"</string> <string name="helper_summary_app_streaming" msgid="2396773196949578425">"<xliff:g id="APP_NAME">%1$s</xliff:g>, cihazlarınız arasında uygulama akışı gerçekleştirmek için <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> cihazınız adına izin istiyor"</string> @@ -38,10 +37,10 @@ <string name="title_nearby_device_streaming" msgid="7269956847378799794">"<strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> cihazının bu işlemi yapmasına izin verilsin mi?"</string> <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<xliff:g id="APP_NAME">%1$s</xliff:g> uygulaması <xliff:g id="DEVICE_NAME">%2$s</xliff:g> cihazınız adına uygulamaları ve diğer sistem özelliklerini yakındaki cihazlara aktarmak için izin istiyor"</string> <string name="profile_name_generic" msgid="6851028682723034988">"cihaz"</string> - <string name="summary_generic_single_device" msgid="4181180669689590417">"Bu uygulama, arayan kişinin adı gibi bilgileri telefonunuz ve <xliff:g id="DEVICE_NAME">%1$s</xliff:g> adlı cihaz arasında senkronize edebilir"</string> <string name="summary_generic" msgid="1761976003668044801">"Bu uygulama, arayan kişinin adı gibi bilgileri telefonunuz ve seçili cihaz arasında senkronize edebilir"</string> <string name="consent_yes" msgid="8344487259618762872">"İzin ver"</string> <string name="consent_no" msgid="2640796915611404382">"İzin verme"</string> + <string name="consent_cancel" msgid="5655005528379285841">"İptal"</string> <string name="consent_back" msgid="2560683030046918882">"Geri"</string> <string name="permission_expand" msgid="893185038020887411">"<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g> panelini genişlet"</string> <string name="permission_collapse" msgid="3320833884220844084">"<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g> panelini daralt"</string> diff --git a/packages/CompanionDeviceManager/res/values-uk/strings.xml b/packages/CompanionDeviceManager/res/values-uk/strings.xml index 221a86926d38..39a2b1b19897 100644 --- a/packages/CompanionDeviceManager/res/values-uk/strings.xml +++ b/packages/CompanionDeviceManager/res/values-uk/strings.xml @@ -17,15 +17,14 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="4470785958457506021">"Диспетчер супутніх пристроїв"</string> - <string name="confirmation_title" msgid="4593465730772390351">"Надати додатку <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> доступ до інформації на <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> + <string name="confirmation_title" msgid="2244241995958340998">"Дозволити додатку <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> доступ до інформації на пристрої <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> <string name="profile_name_watch" msgid="576290739483672360">"годинник"</string> - <string name="chooser_title" msgid="2262294130493605839">"Виберіть <xliff:g id="PROFILE_NAME">%1$s</xliff:g>, яким керуватиме додаток <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> - <string name="summary_watch" msgid="898569637110705523">"Цей додаток потрібен, щоб керувати пристроєм \"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>\". Додаток <xliff:g id="APP_NAME">%2$s</xliff:g> зможе синхронізувати інформацію (наприклад, ім’я абонента, який викликає), взаємодіяти з вашими сповіщеннями й отримає дозволи \"Телефон\", \"SMS\", \"Контакти\", \"Календар\", \"Журнали викликів\" і \"Пристрої поблизу\"."</string> - <string name="summary_watch_single_device" msgid="3173948915947011333">"Цей додаток зможе синхронізувати інформацію (наприклад, ім’я абонента, який викликає) і отримає доступ до перелічених нижче дозволів на вашому <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string> + <string name="chooser_title_non_profile" msgid="6035023914517087400">"Виберіть пристрій, яким керуватиме додаток <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>"</string> + <string name="chooser_title" msgid="2235819929238267637">"Виберіть <xliff:g id="PROFILE_NAME">%1$s</xliff:g> для налаштування"</string> + <string name="summary_watch" msgid="7962014927042971830">"Цей додаток зможе синхронізувати інформацію (наприклад, ім’я абонента, який викликає) і отримає доступ до перелічених нижче дозволів на вашому <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="confirmation_title_glasses" msgid="8288346850537727333">"Дозволити додатку <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> керувати пристроєм <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> - <string name="profile_name_glasses" msgid="8488394059007275998">"окуляри"</string> - <string name="summary_glasses_multi_device" msgid="615259525961937348">"Цей додаток потрібен, щоб керувати пристроєм \"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>\". Додаток <xliff:g id="APP_NAME">%2$s</xliff:g> зможе взаємодіяти з вашими сповіщеннями й отримає дозволи \"Телефон\", \"SMS\", \"Контакти\", \"Мікрофон\" і \"Пристрої поблизу\"."</string> - <string name="summary_glasses_single_device" msgid="3000909894067413398">"Цей додаток матиме доступ до перелічених нижче дозволів на вашому <xliff:g id="DEVICE_TYPE">%1$s</xliff:g>"</string> + <string name="profile_name_glasses" msgid="3506504967216601277">"пристрій"</string> + <string name="summary_glasses" msgid="2872254734959842579">"Цей додаток матиме доступ до перелічених нижче дозволів на вашому <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="title_app_streaming" msgid="2270331024626446950">"Надайте додатку <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> доступ до цієї інформації з телефона"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Сервіси для кількох пристроїв"</string> <string name="helper_summary_app_streaming" msgid="2396773196949578425">"Додаток <xliff:g id="APP_NAME">%1$s</xliff:g> від імені вашого пристрою \"<xliff:g id="DISPLAY_NAME">%2$s</xliff:g>\" запитує дозвіл на трансляцію додатків між вашими пристроями"</string> @@ -38,10 +37,10 @@ <string name="title_nearby_device_streaming" msgid="7269956847378799794">"Дозволити додатку <strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> виконувати цю дію?"</string> <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"Додаток <xliff:g id="APP_NAME">%1$s</xliff:g> від імені вашого пристрою (<xliff:g id="DEVICE_NAME">%2$s</xliff:g>) запитує дозвіл на трансляцію додатків та інших системних функцій на пристрої поблизу"</string> <string name="profile_name_generic" msgid="6851028682723034988">"пристрій"</string> - <string name="summary_generic_single_device" msgid="4181180669689590417">"Цей додаток зможе синхронізувати інформацію (наприклад, ім’я абонента, який викликає) між телефоном і пристроєм \"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>\""</string> <string name="summary_generic" msgid="1761976003668044801">"Цей додаток зможе синхронізувати інформацію (наприклад, ім’я абонента, який викликає) між телефоном і вибраним пристроєм"</string> <string name="consent_yes" msgid="8344487259618762872">"Дозволити"</string> <string name="consent_no" msgid="2640796915611404382">"Не дозволяти"</string> + <string name="consent_cancel" msgid="5655005528379285841">"Скасувати"</string> <string name="consent_back" msgid="2560683030046918882">"Назад"</string> <string name="permission_expand" msgid="893185038020887411">"Розгорнути дозвіл \"<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>\""</string> <string name="permission_collapse" msgid="3320833884220844084">"Згорнути дозвіл \"<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>\""</string> diff --git a/packages/CompanionDeviceManager/res/values-ur/strings.xml b/packages/CompanionDeviceManager/res/values-ur/strings.xml index 8acfc29b32f6..ce44941653b9 100644 --- a/packages/CompanionDeviceManager/res/values-ur/strings.xml +++ b/packages/CompanionDeviceManager/res/values-ur/strings.xml @@ -17,15 +17,14 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="4470785958457506021">"ساتھی آلہ مینیجر"</string> - <string name="confirmation_title" msgid="4593465730772390351">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> کو <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> تک رسائی کی اجازت دیں؟"</string> + <string name="confirmation_title" msgid="2244241995958340998">"ایپ <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> کو <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> تک رسائی کی اجازت دیں؟"</string> <string name="profile_name_watch" msgid="576290739483672360">"دیکھیں"</string> - <string name="chooser_title" msgid="2262294130493605839">"<strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> کے ذریعے نظم کئے جانے کیلئے <xliff:g id="PROFILE_NAME">%1$s</xliff:g> کو منتخب کریں"</string> - <string name="summary_watch" msgid="898569637110705523">"آپ کے <xliff:g id="DEVICE_NAME">%1$s</xliff:g> کا نظم کرنے کے لیے اس ایپ کی ضرورت ہے۔ <xliff:g id="APP_NAME">%2$s</xliff:g> کو کسی کال کرنے والے کے نام جیسی معلومات کی مطابقت پذیری کرنے، آپ کی اطلاعات کے ساتھ تعامل کرنے، آپ کے فون، SMS، رابطے، کیلنڈر، کال لاگز اور قریبی آلات کی اجازتوں تک رسائی حاصل کرنے کی اجازت ہوگی۔"</string> - <string name="summary_watch_single_device" msgid="3173948915947011333">"اس ایپ کو آپ کے <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> پر کسی کال کرنے والے کے نام جیسی معلومات کی مطابقت پذیری کرنے اور ان اجازتوں تک رسائی کی اجازت ہوگی"</string> + <string name="chooser_title_non_profile" msgid="6035023914517087400">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> کے ذریعے منتخب کیے جانے کیلئے آلہ منتخب کریں"</string> + <string name="chooser_title" msgid="2235819929238267637">"سیٹ اپ کرنے کے لیے <xliff:g id="PROFILE_NAME">%1$s</xliff:g> کا انتخاب کریں"</string> + <string name="summary_watch" msgid="7962014927042971830">"اس ایپ کو آپ کے <xliff:g id="DEVICE_NAME">%1$s</xliff:g> پر کسی کال کرنے والے کے نام جیسی معلومات کی مطابقت پذیری کرنے اور ان اجازتوں تک رسائی کی اجازت ہوگی"</string> <string name="confirmation_title_glasses" msgid="8288346850537727333">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> کو <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> کا نظم کرنے کی اجازت دیں؟"</string> - <string name="profile_name_glasses" msgid="8488394059007275998">"گلاسز"</string> - <string name="summary_glasses_multi_device" msgid="615259525961937348">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> کا نظم کرنے کے لیے، اس ایپ کی ضرورت ہے۔ <xliff:g id="APP_NAME">%2$s</xliff:g> کو آپ کی اطلاعات کے ساتھ تعامل کرنے اور آپ کے فون، SMS، رابطوں، مائیکروفون اور قریبی آلات کی اجازتوں تک رسائی کی اجازت ہوگی۔"</string> - <string name="summary_glasses_single_device" msgid="3000909894067413398">"اس ایپ کو آپ کے <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> پر ان اجازتوں تک رسائی کی اجازت ہوگی"</string> + <string name="profile_name_glasses" msgid="3506504967216601277">"آلہ"</string> + <string name="summary_glasses" msgid="2872254734959842579">"اس ایپ کو آپ کے <xliff:g id="DEVICE_NAME">%1$s</xliff:g> پر ان اجازتوں تک رسائی کی اجازت ہوگی"</string> <string name="title_app_streaming" msgid="2270331024626446950">"اپنے فون سے ان معلومات تک رسائی حاصل کرنے کی <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> کو اجازت دیں"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"کراس ڈیوائس سروسز"</string> <string name="helper_summary_app_streaming" msgid="2396773196949578425">"<xliff:g id="APP_NAME">%1$s</xliff:g> ایپ آپ کے <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> کی جانب سے آپ کے آلات کے درمیان ایپس کی سلسلہ بندی کرنے کی اجازت کی درخواست کر رہی ہے"</string> @@ -38,10 +37,10 @@ <string name="title_nearby_device_streaming" msgid="7269956847378799794">"<strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> کو یہ کارروائی انجام دینے کی اجازت دیں؟"</string> <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<xliff:g id="APP_NAME">%1$s</xliff:g> آپ کے <xliff:g id="DEVICE_NAME">%2$s</xliff:g> کی جانب سے ایپس اور سسٹم کی دیگر خصوصیات کی سلسلہ بندی قریبی آلات پر کرنے کی اجازت طلب کر رہی ہے"</string> <string name="profile_name_generic" msgid="6851028682723034988">"آلہ"</string> - <string name="summary_generic_single_device" msgid="4181180669689590417">"یہ ایپ آپ کے فون اور <xliff:g id="DEVICE_NAME">%1$s</xliff:g> کے درمیان معلومات، جیسے کسی کال کرنے والے کے نام، کی مطابقت پذیری کر سکے گی"</string> <string name="summary_generic" msgid="1761976003668044801">"یہ ایپ آپ کے فون اور منتخب کردہ آلے کے درمیان معلومات، جیسے کسی کال کرنے والے کے نام، کی مطابقت پذیری کر سکے گی"</string> <string name="consent_yes" msgid="8344487259618762872">"اجازت دیں"</string> <string name="consent_no" msgid="2640796915611404382">"اجازت نہ دیں"</string> + <string name="consent_cancel" msgid="5655005528379285841">"منسوخ کریں"</string> <string name="consent_back" msgid="2560683030046918882">"پیچھے"</string> <string name="permission_expand" msgid="893185038020887411">"<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g> کو پھیلائیں"</string> <string name="permission_collapse" msgid="3320833884220844084">"<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g> کو سکیڑیں"</string> diff --git a/packages/CompanionDeviceManager/res/values-uz/strings.xml b/packages/CompanionDeviceManager/res/values-uz/strings.xml index 95d5b1b73aa5..82b69375cf09 100644 --- a/packages/CompanionDeviceManager/res/values-uz/strings.xml +++ b/packages/CompanionDeviceManager/res/values-uz/strings.xml @@ -17,15 +17,14 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string> - <string name="confirmation_title" msgid="4593465730772390351">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ilovasiga <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> qurilmasidan foydalanishga ruxsat berilsinmi?"</string> + <string name="confirmation_title" msgid="2244241995958340998">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ilovasiga <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> qurilmasidan foydalanishga ruxsat berilsinmi?"</string> <string name="profile_name_watch" msgid="576290739483672360">"soat"</string> - <string name="chooser_title" msgid="2262294130493605839">"<strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> boshqaradigan <xliff:g id="PROFILE_NAME">%1$s</xliff:g> qurilmasini tanlang"</string> - <string name="summary_watch" msgid="898569637110705523">"Bu ilova <xliff:g id="DEVICE_NAME">%1$s</xliff:g> profilini boshqarish uchun kerak. <xliff:g id="APP_NAME">%2$s</xliff:g> ilovasiga chaqiruvchining ismi, bildirishnomalar bilan ishlash va telefon, SMS, kontaktlar, taqvim, chaqiruvlar jurnali va yaqin-atrofdagi qurilmalarni aniqlash kabi maʼlumotlarni sinxronlashga ruxsat beriladi."</string> - <string name="summary_watch_single_device" msgid="3173948915947011333">"Bu ilovaga chaqiruvchining ismi kabi maʼlumotlarni sinxronlash va <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> qurilmasida quyidagi amallarni bajarishga ruxsat beriladi"</string> + <string name="chooser_title_non_profile" msgid="6035023914517087400">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> boshqaradigan qurilmani tanlang"</string> + <string name="chooser_title" msgid="2235819929238267637">"Sozlash uchun <xliff:g id="PROFILE_NAME">%1$s</xliff:g> profilini tanlang"</string> + <string name="summary_watch" msgid="7962014927042971830">"Bu ilovaga chaqiruvchining ismi kabi maʼlumotlarni sinxronlash va <xliff:g id="DEVICE_NAME">%1$s</xliff:g> qurilmasida quyidagi amallarni bajarishga ruxsat beriladi"</string> <string name="confirmation_title_glasses" msgid="8288346850537727333">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ilovasiga <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> qurilmasini boshqarish uchun ruxsat berilsinmi?"</string> - <string name="profile_name_glasses" msgid="8488394059007275998">"koʻzoynak"</string> - <string name="summary_glasses_multi_device" msgid="615259525961937348">"Bu ilova <xliff:g id="DEVICE_NAME">%1$s</xliff:g> qurilmasini boshqarish uchun kerak. <xliff:g id="APP_NAME">%2$s</xliff:g> ilovasiga bildirishnomalar bilan ishlash va telefon, SMS, kontaktlar, mikrofon va yaqin-atrofdagi qurilmalarga kirishga ruxsat beriladi."</string> - <string name="summary_glasses_single_device" msgid="3000909894067413398">"Bu ilova <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> qurilmasida quyidagi ruxsatlarni oladi"</string> + <string name="profile_name_glasses" msgid="3506504967216601277">"qurilma"</string> + <string name="summary_glasses" msgid="2872254734959842579">"Bu ilova <xliff:g id="DEVICE_NAME">%1$s</xliff:g> qurilmasida quyidagi ruxsatlarni oladi"</string> <string name="title_app_streaming" msgid="2270331024626446950">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ilovasiga telefondagi ushbu maʼlumot uchun ruxsat bering"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Qurilmalararo xizmatlar"</string> <string name="helper_summary_app_streaming" msgid="2396773196949578425">"Qurilamalararo ilovalar strimingi uchun <xliff:g id="APP_NAME">%1$s</xliff:g> ilovasi <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> nomidan ruxsat soʻramoqda"</string> @@ -38,10 +37,10 @@ <string name="title_nearby_device_streaming" msgid="7269956847378799794">"<strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> ilovasiga bu amalni bajarish uchun ruxsat berilsinmi?"</string> <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<xliff:g id="APP_NAME">%1$s</xliff:g> ilovasi <xliff:g id="DEVICE_NAME">%2$s</xliff:g> qurilmangizdan nomidan atrofdagi qurilmalarga ilova va boshqa tizim funksiyalarini uzatish uchun ruxsat olmoqchi"</string> <string name="profile_name_generic" msgid="6851028682723034988">"qurilma"</string> - <string name="summary_generic_single_device" msgid="4181180669689590417">"Bu ilova telefoningiz va <xliff:g id="DEVICE_NAME">%1$s</xliff:g> qurilmasida chaqiruvchining ismi kabi maʼlumotlarni sinxronlay oladi"</string> <string name="summary_generic" msgid="1761976003668044801">"Bu ilova telefoningiz va tanlangan qurilmada chaqiruvchining ismi kabi maʼlumotlarni sinxronlay oladi"</string> <string name="consent_yes" msgid="8344487259618762872">"Ruxsat"</string> <string name="consent_no" msgid="2640796915611404382">"Ruxsat berilmasin"</string> + <string name="consent_cancel" msgid="5655005528379285841">"Bekor qilish"</string> <string name="consent_back" msgid="2560683030046918882">"Orqaga"</string> <string name="permission_expand" msgid="893185038020887411">"Yoyish: <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string> <string name="permission_collapse" msgid="3320833884220844084">"Yopish: <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string> diff --git a/packages/CompanionDeviceManager/res/values-vi/strings.xml b/packages/CompanionDeviceManager/res/values-vi/strings.xml index 24fdd4add637..951f42984018 100644 --- a/packages/CompanionDeviceManager/res/values-vi/strings.xml +++ b/packages/CompanionDeviceManager/res/values-vi/strings.xml @@ -17,15 +17,14 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="4470785958457506021">"Trình quản lý thiết bị đồng hành"</string> - <string name="confirmation_title" msgid="4593465730772390351">"Cho phép <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> truy cập vào <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> + <string name="confirmation_title" msgid="2244241995958340998">"Cho phép ứng dụng <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> truy cập vào <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> <string name="profile_name_watch" msgid="576290739483672360">"đồng hồ"</string> - <string name="chooser_title" msgid="2262294130493605839">"Chọn một <xliff:g id="PROFILE_NAME">%1$s</xliff:g> sẽ do <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> quản lý"</string> - <string name="summary_watch" msgid="898569637110705523">"Cần ứng dụng này để quản lý <xliff:g id="DEVICE_NAME">%1$s</xliff:g> của bạn. <xliff:g id="APP_NAME">%2$s</xliff:g> được phép đồng bộ hoá thông tin (ví dụ: tên người gọi), tương tác với thông báo cũng như có các quyền truy cập Điện thoại, Tin nhắn SMS, Danh bạ, Lịch, Nhật ký cuộc gọi và Thiết bị ở gần."</string> - <string name="summary_watch_single_device" msgid="3173948915947011333">"Ứng dụng này sẽ được phép đồng bộ hoá thông tin (chẳng hạn như tên của người đang gọi điện) và dùng những quyền sau trên <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> của bạn"</string> + <string name="chooser_title_non_profile" msgid="6035023914517087400">"Chọn một thiết bị sẽ do <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> quản lý"</string> + <string name="chooser_title" msgid="2235819929238267637">"Chọn một <xliff:g id="PROFILE_NAME">%1$s</xliff:g> để thiết lập"</string> + <string name="summary_watch" msgid="7962014927042971830">"Ứng dụng này sẽ được phép đồng bộ hoá thông tin (chẳng hạn như tên của người đang gọi điện) và dùng những quyền sau trên <xliff:g id="DEVICE_NAME">%1$s</xliff:g> của bạn"</string> <string name="confirmation_title_glasses" msgid="8288346850537727333">"Cho phép <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> quản lý <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> - <string name="profile_name_glasses" msgid="8488394059007275998">"kính"</string> - <string name="summary_glasses_multi_device" msgid="615259525961937348">"Bạn cần có ứng dụng này để quản lý <xliff:g id="DEVICE_NAME">%1$s</xliff:g>. <xliff:g id="APP_NAME">%2$s</xliff:g> sẽ được phép tương tác với thông báo của bạn, cũng như sử dụng các quyền đối với Điện thoại, SMS, Danh bạ, Micrô và Thiết bị ở gần."</string> - <string name="summary_glasses_single_device" msgid="3000909894067413398">"Ứng dụng này sẽ được phép dùng những quyền sau trên <xliff:g id="DEVICE_TYPE">%1$s</xliff:g> của bạn"</string> + <string name="profile_name_glasses" msgid="3506504967216601277">"thiết bị"</string> + <string name="summary_glasses" msgid="2872254734959842579">"Ứng dụng này sẽ được phép dùng những quyền sau trên <xliff:g id="DEVICE_NAME">%1$s</xliff:g> của bạn"</string> <string name="title_app_streaming" msgid="2270331024626446950">"Cho phép <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> truy cập vào thông tin này trên điện thoại của bạn"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Dịch vụ trên nhiều thiết bị"</string> <string name="helper_summary_app_streaming" msgid="2396773196949578425">"<xliff:g id="APP_NAME">%1$s</xliff:g> đang yêu cầu quyền thay cho <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> để truyền trực tuyến ứng dụng giữa các thiết bị của bạn"</string> @@ -38,10 +37,10 @@ <string name="title_nearby_device_streaming" msgid="7269956847378799794">"Cho phép <strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong> thực hiện hành động này?"</string> <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"<xliff:g id="APP_NAME">%1$s</xliff:g> đang thay <xliff:g id="DEVICE_NAME">%2$s</xliff:g> yêu cầu quyền truyền trực tuyến ứng dụng và các tính năng khác của hệ thống đến các thiết bị ở gần"</string> <string name="profile_name_generic" msgid="6851028682723034988">"thiết bị"</string> - <string name="summary_generic_single_device" msgid="4181180669689590417">"Ứng dụng này sẽ đồng bộ hoá thông tin (ví dụ: tên người gọi) giữa điện thoại của bạn và <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="summary_generic" msgid="1761976003668044801">"Ứng dụng này sẽ đồng bộ hoá thông tin (ví dụ: tên người gọi) giữa điện thoại của bạn và thiết bị bạn chọn"</string> <string name="consent_yes" msgid="8344487259618762872">"Cho phép"</string> <string name="consent_no" msgid="2640796915611404382">"Không cho phép"</string> + <string name="consent_cancel" msgid="5655005528379285841">"Huỷ"</string> <string name="consent_back" msgid="2560683030046918882">"Quay lại"</string> <string name="permission_expand" msgid="893185038020887411">"Mở rộng <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string> <string name="permission_collapse" msgid="3320833884220844084">"Thu gọn <xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string> diff --git a/packages/CompanionDeviceManager/res/values-zh-rCN/strings.xml b/packages/CompanionDeviceManager/res/values-zh-rCN/strings.xml index 401a263505bf..5edf9c03b276 100644 --- a/packages/CompanionDeviceManager/res/values-zh-rCN/strings.xml +++ b/packages/CompanionDeviceManager/res/values-zh-rCN/strings.xml @@ -17,15 +17,14 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="4470785958457506021">"配套设备管理器"</string> - <string name="confirmation_title" msgid="4593465730772390351">"允许<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>访问<strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> + <string name="confirmation_title" msgid="2244241995958340998">"允许应用<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>访问<strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> <string name="profile_name_watch" msgid="576290739483672360">"手表"</string> - <string name="chooser_title" msgid="2262294130493605839">"选择要由<strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>管理的<xliff:g id="PROFILE_NAME">%1$s</xliff:g>"</string> - <string name="summary_watch" msgid="898569637110705523">"需要使用此应用才能管理您的设备“<xliff:g id="DEVICE_NAME">%1$s</xliff:g>”。<xliff:g id="APP_NAME">%2$s</xliff:g>将能同步信息(例如来电者的姓名)、与通知交互,并能获得对电话、短信、通讯录、日历、通话记录和附近设备的访问权限。"</string> - <string name="summary_watch_single_device" msgid="3173948915947011333">"该应用将可以同步信息(例如来电者的姓名),并可以获得您<xliff:g id="DEVICE_TYPE">%1$s</xliff:g>上的以下权限"</string> + <string name="chooser_title_non_profile" msgid="6035023914517087400">"选择要由<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>管理的设备"</string> + <string name="chooser_title" msgid="2235819929238267637">"选择 <xliff:g id="PROFILE_NAME">%1$s</xliff:g> 进行设置"</string> + <string name="summary_watch" msgid="7962014927042971830">"该应用将可以同步信息(例如来电者的姓名),并可以获得您<xliff:g id="DEVICE_NAME">%1$s</xliff:g>上的以下权限"</string> <string name="confirmation_title_glasses" msgid="8288346850537727333">"允许<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>管理<strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> - <string name="profile_name_glasses" msgid="8488394059007275998">"眼镜"</string> - <string name="summary_glasses_multi_device" msgid="615259525961937348">"需要使用此应用才能管理<xliff:g id="DEVICE_NAME">%1$s</xliff:g>。“<xliff:g id="APP_NAME">%2$s</xliff:g>”将能与通知交互,并可获得电话、短信、通讯录、麦克风和附近设备的访问权限。"</string> - <string name="summary_glasses_single_device" msgid="3000909894067413398">"该应用将可以获得您<xliff:g id="DEVICE_TYPE">%1$s</xliff:g>上的以下权限"</string> + <string name="profile_name_glasses" msgid="3506504967216601277">"设备"</string> + <string name="summary_glasses" msgid="2872254734959842579">"该应用将可以获得您<xliff:g id="DEVICE_NAME">%1$s</xliff:g>上的以下权限"</string> <string name="title_app_streaming" msgid="2270331024626446950">"允许“<xliff:g id="APP_NAME">%1$s</xliff:g>”<strong></strong>访问您手机中的这项信息"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"跨设备服务"</string> <string name="helper_summary_app_streaming" msgid="2396773196949578425">"“<xliff:g id="APP_NAME">%1$s</xliff:g>”正代表您的<xliff:g id="DISPLAY_NAME">%2$s</xliff:g>请求在您的设备之间流式传输应用内容"</string> @@ -38,10 +37,10 @@ <string name="title_nearby_device_streaming" msgid="7269956847378799794">"允许<strong><xliff:g id="DEVICE_NAME">%1$s</xliff:g></strong>进行此操作?"</string> <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"“<xliff:g id="APP_NAME">%1$s</xliff:g>”正代表您的<xliff:g id="DEVICE_NAME">%2$s</xliff:g>请求将应用和其他系统功能流式传输到附近的设备"</string> <string name="profile_name_generic" msgid="6851028682723034988">"设备"</string> - <string name="summary_generic_single_device" msgid="4181180669689590417">"此应用将能在您的手机和“<xliff:g id="DEVICE_NAME">%1$s</xliff:g>”之间同步信息,例如来电者的姓名"</string> <string name="summary_generic" msgid="1761976003668044801">"此应用将能在您的手机和所选设备之间同步信息,例如来电者的姓名"</string> <string name="consent_yes" msgid="8344487259618762872">"允许"</string> <string name="consent_no" msgid="2640796915611404382">"不允许"</string> + <string name="consent_cancel" msgid="5655005528379285841">"取消"</string> <string name="consent_back" msgid="2560683030046918882">"返回"</string> <string name="permission_expand" msgid="893185038020887411">"展开<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string> <string name="permission_collapse" msgid="3320833884220844084">"收起<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string> diff --git a/packages/CompanionDeviceManager/res/values-zh-rHK/strings.xml b/packages/CompanionDeviceManager/res/values-zh-rHK/strings.xml index 52230cb810ec..0878c239646b 100644 --- a/packages/CompanionDeviceManager/res/values-zh-rHK/strings.xml +++ b/packages/CompanionDeviceManager/res/values-zh-rHK/strings.xml @@ -17,15 +17,14 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="4470785958457506021">"隨附裝置管理工具"</string> - <string name="confirmation_title" msgid="4593465730772390351">"要允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」<strong></strong>存取「<xliff:g id="DEVICE_NAME">%2$s</xliff:g>」<strong></strong>嗎?"</string> + <string name="confirmation_title" msgid="2244241995958340998">"要允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」應用程式<strong></strong>存取「<xliff:g id="DEVICE_NAME">%2$s</xliff:g>」<strong></strong>嗎?"</string> <string name="profile_name_watch" msgid="576290739483672360">"手錶"</string> - <string name="chooser_title" msgid="2262294130493605839">"選擇由 <strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> 管理的<xliff:g id="PROFILE_NAME">%1$s</xliff:g>"</string> - <string name="summary_watch" msgid="898569637110705523">"必須使用此應用程式,才能管理「<xliff:g id="DEVICE_NAME">%1$s</xliff:g>」。「<xliff:g id="APP_NAME">%2$s</xliff:g>」將可同步資訊 (例如來電者的名稱)、透過通知與你互動,並存取電話、短訊、通訊錄、日曆、通話記錄和附近的裝置權限。"</string> - <string name="summary_watch_single_device" msgid="3173948915947011333">"此應用程式將可同步資訊 (例如來電者的名稱),並可在<xliff:g id="DEVICE_TYPE">%1$s</xliff:g>上取得以下權限"</string> + <string name="chooser_title_non_profile" msgid="6035023914517087400">"選擇要讓 <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> 管理的裝置"</string> + <string name="chooser_title" msgid="2235819929238267637">"選擇要設定的<xliff:g id="PROFILE_NAME">%1$s</xliff:g>"</string> + <string name="summary_watch" msgid="7962014927042971830">"此應用程式將可同步資訊 (例如來電者的名稱),並可在<xliff:g id="DEVICE_NAME">%1$s</xliff:g>上取得以下權限"</string> <string name="confirmation_title_glasses" msgid="8288346850537727333">"要允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」<strong></strong>管理「<xliff:g id="DEVICE_NAME">%2$s</xliff:g>」<strong></strong>嗎?"</string> - <string name="profile_name_glasses" msgid="8488394059007275998">"眼鏡"</string> - <string name="summary_glasses_multi_device" msgid="615259525961937348">"必須使用此應用程式,才能管理「<xliff:g id="DEVICE_NAME">%1$s</xliff:g>」。「<xliff:g id="APP_NAME">%2$s</xliff:g>」將可透過通知與你互動,並存取電話、短訊、通訊錄、麥克風和附近的裝置權限。"</string> - <string name="summary_glasses_single_device" msgid="3000909894067413398">"此應用程式將可在<xliff:g id="DEVICE_TYPE">%1$s</xliff:g>上取得以下權限"</string> + <string name="profile_name_glasses" msgid="3506504967216601277">"裝置"</string> + <string name="summary_glasses" msgid="2872254734959842579">"此應用程式將可在<xliff:g id="DEVICE_NAME">%1$s</xliff:g>上取得以下權限"</string> <string name="title_app_streaming" msgid="2270331024626446950">"允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」<strong></strong>存取你手機中的這項資料"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"跨裝置服務"</string> <string name="helper_summary_app_streaming" msgid="2396773196949578425">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」正在代表 <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> 要求權限,以便在裝置間串流應用程式的內容"</string> @@ -38,10 +37,10 @@ <string name="title_nearby_device_streaming" msgid="7269956847378799794">"要允許「<xliff:g id="DEVICE_NAME">%1$s</xliff:g>」<strong></strong>執行此操作嗎?"</string> <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」正在代表「<xliff:g id="DEVICE_NAME">%2$s</xliff:g>」要求權限,才能在附近的裝置上串流播放應用程式和其他系統功能"</string> <string name="profile_name_generic" msgid="6851028682723034988">"裝置"</string> - <string name="summary_generic_single_device" msgid="4181180669689590417">"此應用程式將可同步手機和「<xliff:g id="DEVICE_NAME">%1$s</xliff:g>」的資訊,例如來電者的名稱"</string> <string name="summary_generic" msgid="1761976003668044801">"此應用程式將可同步手機和所選裝置的資訊,例如來電者的名稱"</string> <string name="consent_yes" msgid="8344487259618762872">"允許"</string> <string name="consent_no" msgid="2640796915611404382">"不允許"</string> + <string name="consent_cancel" msgid="5655005528379285841">"取消"</string> <string name="consent_back" msgid="2560683030046918882">"返回"</string> <string name="permission_expand" msgid="893185038020887411">"展開<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string> <string name="permission_collapse" msgid="3320833884220844084">"收合<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string> diff --git a/packages/CompanionDeviceManager/res/values-zh-rTW/strings.xml b/packages/CompanionDeviceManager/res/values-zh-rTW/strings.xml index c6801578acfb..4f50ef505df8 100644 --- a/packages/CompanionDeviceManager/res/values-zh-rTW/strings.xml +++ b/packages/CompanionDeviceManager/res/values-zh-rTW/strings.xml @@ -17,15 +17,14 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="4470785958457506021">"隨附裝置管理工具"</string> - <string name="confirmation_title" msgid="4593465730772390351">"要允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」<strong></strong>存取「<xliff:g id="DEVICE_NAME">%2$s</xliff:g>」<strong></strong>嗎?"</string> + <string name="confirmation_title" msgid="2244241995958340998">"要允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」<strong></strong>應用程式存取「<xliff:g id="DEVICE_NAME">%2$s</xliff:g>」<strong></strong>嗎?"</string> <string name="profile_name_watch" msgid="576290739483672360">"手錶"</string> - <string name="chooser_title" msgid="2262294130493605839">"選擇要讓「<xliff:g id="APP_NAME">%2$s</xliff:g>」<strong></strong>管理的<xliff:g id="PROFILE_NAME">%1$s</xliff:g>"</string> - <string name="summary_watch" msgid="898569637110705523">"你必須使用這個應用程式,才能管理<xliff:g id="DEVICE_NAME">%1$s</xliff:g>。「<xliff:g id="APP_NAME">%2$s</xliff:g>」將可同步資訊 (例如來電者名稱)、存取通知及在通知上執行操作,並取得電話、簡訊、聯絡人、日曆、通話記錄、麥克風和鄰近裝置權限。"</string> - <string name="summary_watch_single_device" msgid="3173948915947011333">"這個應用程式將可同步處理資訊 (例如來電者名稱)、取得<xliff:g id="DEVICE_TYPE">%1$s</xliff:g>上的這些權限"</string> + <string name="chooser_title_non_profile" msgid="6035023914517087400">"選擇要讓「<xliff:g id="APP_NAME">%1$s</xliff:g>」<strong></strong>管理的裝置"</string> + <string name="chooser_title" msgid="2235819929238267637">"選擇要設定的<xliff:g id="PROFILE_NAME">%1$s</xliff:g>"</string> + <string name="summary_watch" msgid="7962014927042971830">"這個應用程式將可同步處理資訊 (例如來電者名稱)、取得<xliff:g id="DEVICE_NAME">%1$s</xliff:g>上的這些權限"</string> <string name="confirmation_title_glasses" msgid="8288346850537727333">"要允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」<strong></strong>管理「<xliff:g id="DEVICE_NAME">%2$s</xliff:g>」<strong></strong>嗎?"</string> - <string name="profile_name_glasses" msgid="8488394059007275998">"眼鏡"</string> - <string name="summary_glasses_multi_device" msgid="615259525961937348">"你必須使用這個應用程式,才能管理「<xliff:g id="DEVICE_NAME">%1$s</xliff:g>」。「<xliff:g id="APP_NAME">%2$s</xliff:g>」將可存取通知及在通知上執行操作,並取得電話、簡訊、聯絡人、麥克風和鄰近裝置權限。"</string> - <string name="summary_glasses_single_device" msgid="3000909894067413398">"這個應用程式將可取得<xliff:g id="DEVICE_TYPE">%1$s</xliff:g>上的這些權限"</string> + <string name="profile_name_glasses" msgid="3506504967216601277">"裝置"</string> + <string name="summary_glasses" msgid="2872254734959842579">"這個應用程式將可取得<xliff:g id="DEVICE_NAME">%1$s</xliff:g>上的這些權限"</string> <string name="title_app_streaming" msgid="2270331024626446950">"允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」<strong></strong>存取手機中的這項資訊"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"跨裝置服務"</string> <string name="helper_summary_app_streaming" msgid="2396773196949578425">"為了在裝置間串流傳輸應用程式內容,「<xliff:g id="APP_NAME">%1$s</xliff:g>」正在代表 <xliff:g id="DISPLAY_NAME">%2$s</xliff:g> 要求相關權限"</string> @@ -38,10 +37,10 @@ <string name="title_nearby_device_streaming" msgid="7269956847378799794">"要允許「<xliff:g id="DEVICE_NAME">%1$s</xliff:g>」<strong></strong>執行這項操作嗎?"</string> <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」正在代表「<xliff:g id="DEVICE_NAME">%2$s</xliff:g>」要求必要權限,才能在鄰近裝置上串流播放應用程式和其他系統功能"</string> <string name="profile_name_generic" msgid="6851028682723034988">"裝置"</string> - <string name="summary_generic_single_device" msgid="4181180669689590417">"這個應用程式將可在手機和「<xliff:g id="DEVICE_NAME">%1$s</xliff:g>」之間同步資訊,例如來電者名稱"</string> <string name="summary_generic" msgid="1761976003668044801">"這個應用程式將可在手機和指定裝置間同步資訊,例如來電者名稱"</string> <string name="consent_yes" msgid="8344487259618762872">"允許"</string> <string name="consent_no" msgid="2640796915611404382">"不允許"</string> + <string name="consent_cancel" msgid="5655005528379285841">"取消"</string> <string name="consent_back" msgid="2560683030046918882">"返回"</string> <string name="permission_expand" msgid="893185038020887411">"展開<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string> <string name="permission_collapse" msgid="3320833884220844084">"收合<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string> diff --git a/packages/CompanionDeviceManager/res/values-zu/strings.xml b/packages/CompanionDeviceManager/res/values-zu/strings.xml index 22495e285795..7fb5eced4b68 100644 --- a/packages/CompanionDeviceManager/res/values-zu/strings.xml +++ b/packages/CompanionDeviceManager/res/values-zu/strings.xml @@ -17,15 +17,14 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="app_label" msgid="4470785958457506021">"Isiphathi sedivayisi esihambisanayo"</string> - <string name="confirmation_title" msgid="4593465730772390351">"Vumela <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ukufinyelela <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> + <string name="confirmation_title" msgid="2244241995958340998">"Vumela i-app <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ukufinyelela <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>?"</string> <string name="profile_name_watch" msgid="576290739483672360">"buka"</string> - <string name="chooser_title" msgid="2262294130493605839">"Khetha i-<xliff:g id="PROFILE_NAME">%1$s</xliff:g> ezophathwa yi-<strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong>"</string> - <string name="summary_watch" msgid="898569637110705523">"Le app iyadingeka ukuphatha i-<xliff:g id="DEVICE_NAME">%1$s</xliff:g> yakho. I-<xliff:g id="APP_NAME">%2$s</xliff:g> izovunyelwa ukuvumelanisa ulwazi, njengegama lomuntu othile ofonayo, ukusebenzisana nezaziso zakho futhi ufinyelele Ifoni yakho, i-SMS, Oxhumana Nabo, Ikhalenda, Amarekhodi Amakholi nezimvume zamadivayisi aseduze."</string> - <string name="summary_watch_single_device" msgid="3173948915947011333">"Le-app izovunyelwa ukuvumelanisa ulwazi, olufana negama lomuntu ofonayo, iphinde ifinyelele lezi zimvume ku-<xliff:g id="DEVICE_TYPE">%1$s</xliff:g> yakho"</string> + <string name="chooser_title_non_profile" msgid="6035023914517087400">"Khetha idivayisi engaphathwa nge-<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>"</string> + <string name="chooser_title" msgid="2235819929238267637">"Khetha i-<xliff:g id="PROFILE_NAME">%1$s</xliff:g> ukusetha"</string> + <string name="summary_watch" msgid="7962014927042971830">"Le-app izovunyelwa ukuvumelanisa ulwazi, olufana negama lomuntu ofonayo, iphinde ifinyelele lezi zimvume ku-<xliff:g id="DEVICE_NAME">%1$s</xliff:g> yakho"</string> <string name="confirmation_title_glasses" msgid="8288346850537727333">"Vumela i-<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ukuthi ifinyelele i-<strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>"</string> - <string name="profile_name_glasses" msgid="8488394059007275998">"Izingilazi"</string> - <string name="summary_glasses_multi_device" msgid="615259525961937348">"Le app iyadingeka ukuphatha i-<xliff:g id="DEVICE_NAME">%1$s</xliff:g>. I-<xliff:g id="APP_NAME">%2$s</xliff:g> izovunyelwa ukuthi ihlanganyele nezaziso zakho futhi ifinyelele kufoni yakho, i-SMS, Oxhumana nabo, Imakrofoni Nezimvume zamadivayisi aseduze."</string> - <string name="summary_glasses_single_device" msgid="3000909894067413398">"Le-app izovunyelwa ukufinyelela lezi zimvume ku-<xliff:g id="DEVICE_TYPE">%1$s</xliff:g> yakho"</string> + <string name="profile_name_glasses" msgid="3506504967216601277">"idivayisi"</string> + <string name="summary_glasses" msgid="2872254734959842579">"Le-app izovunyelwa ukufinyelela lezi zimvume ku-<xliff:g id="DEVICE_NAME">%1$s</xliff:g> yakho"</string> <string name="title_app_streaming" msgid="2270331024626446950">"Vumela i-<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ifinyelele lolu lwazi kusukela efonini yakho"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Amasevisi amadivayisi amaningi"</string> <string name="helper_summary_app_streaming" msgid="2396773196949578425">"I-<xliff:g id="APP_NAME">%1$s</xliff:g> icela imvume esikhundleni se-<xliff:g id="DISPLAY_NAME">%2$s</xliff:g> yakho ukuze isakaze-bukhoma ama-app phakathi kwamadivayisi akho"</string> @@ -38,10 +37,10 @@ <string name="title_nearby_device_streaming" msgid="7269956847378799794">"Vumela i-<xliff:g id="DEVICE_NAME">%1$s</xliff:g> ukwenza lesi senzo?"</string> <string name="helper_summary_nearby_device_streaming" msgid="2063965070936844876">"I-<xliff:g id="APP_NAME">%1$s</xliff:g> icela imvume esikhundleni se-<xliff:g id="DEVICE_NAME">%2$s</xliff:g> ukusakaza ama-app nezinye izakhi zesistimu kumadivayisi aseduze"</string> <string name="profile_name_generic" msgid="6851028682723034988">"idivayisi"</string> - <string name="summary_generic_single_device" msgid="4181180669689590417">"Le app izokwazi ukuvumelanisa ulwazi, njengegama lomuntu othile ofonayo, phakathi kwefoni yakho ne-<xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string> <string name="summary_generic" msgid="1761976003668044801">"Le app izokwazi ukuvumelanisa ulwazi, njengegama lomuntu othile ofonayo, phakathi kwefoni yakho nedivayisi ekhethiwe"</string> <string name="consent_yes" msgid="8344487259618762872">"Vumela"</string> <string name="consent_no" msgid="2640796915611404382">"Ungavumeli"</string> + <string name="consent_cancel" msgid="5655005528379285841">"Khansela"</string> <string name="consent_back" msgid="2560683030046918882">"Emuva"</string> <string name="permission_expand" msgid="893185038020887411">"Nweba i-<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string> <string name="permission_collapse" msgid="3320833884220844084">"Goqa i-<xliff:g id="PERMISSION_TYPE">%1$s</xliff:g>"</string> diff --git a/packages/CompanionDeviceManager/res/values/strings.xml b/packages/CompanionDeviceManager/res/values/strings.xml index 2502bbf7b40b..7a6fad4b6d51 100644 --- a/packages/CompanionDeviceManager/res/values/strings.xml +++ b/packages/CompanionDeviceManager/res/values/strings.xml @@ -20,7 +20,7 @@ <string name="app_label">Companion Device Manager</string> <!-- Title of the device association confirmation dialog. --> - <string name="confirmation_title">Allow <strong><xliff:g id="app_name" example="Android Wear">%1$s</xliff:g></strong> to access <strong><xliff:g id="device_name" example="ASUS ZenWatch 2">%2$s</xliff:g></strong>?</string> + <string name="confirmation_title">Allow the app <strong><xliff:g id="app_name" example="Android Wear">%1$s</xliff:g></strong> to access <strong><xliff:g id="device_name" example="ASUS ZenWatch 2">%2$s</xliff:g></strong>?</string> <!-- ================= DEVICE_PROFILE_WATCH and null profile ================= --> @@ -28,13 +28,13 @@ <string name="profile_name_watch">watch</string> <!-- Title of the device selection dialog. --> - <string name="chooser_title">Choose a <xliff:g id="profile_name" example="watch">%1$s</xliff:g> to be managed by <strong><xliff:g id="app_name" example="Android Wear">%2$s</xliff:g></strong></string> + <string name="chooser_title_non_profile">Choose a device to be managed by <strong><xliff:g id="app_name" example="Android Wear">%1$s</xliff:g></strong></string> - <!-- Description of the privileges the application will get if associated with the companion device of WATCH profile (type) [CHAR LIMIT=NONE] --> - <string name="summary_watch">This app is needed to manage your <xliff:g id="device_name" example="My Watch">%1$s</xliff:g>. <xliff:g id="app_name" example="Android Wear">%2$s</xliff:g> will be allowed to sync info, like the name of someone calling, interact with your notifications and access your Phone, SMS, Contacts, Calendar, Call logs and Nearby devices permissions.</string> + <!-- Tile of the multiple devices' dialog. --> + <string name="chooser_title">Choose a <xliff:g id="profile_name" example="watch">%1$s</xliff:g> to set up</string> - <!-- Description of the privileges the application will get if associated with the companion device of WATCH profile for singleDevice(type) [CHAR LIMIT=NONE] --> - <string name="summary_watch_single_device">This app will be allowed to sync info, like the name of someone calling, and access these permissions on your <xliff:g id="device_type" example="phone">%1$s</xliff:g></string> + <!-- Description of the privileges the application will get if associated with the companion device of WATCH profile [CHAR LIMIT=NONE] --> + <string name="summary_watch">This app will be allowed to sync info, like the name of someone calling, and access these permissions on your <xliff:g id="device_name" example="phone">%1$s</xliff:g></string> <!-- ================= DEVICE_PROFILE_GLASSES ================= --> @@ -42,13 +42,10 @@ <string name="confirmation_title_glasses">Allow <strong><xliff:g id="app_name" example="Android Wear">%1$s</xliff:g></strong> to manage <strong><xliff:g id="device_name" example="Glasses">%2$s</xliff:g></strong>?</string> <!-- The name of the "glasses" device type [CHAR LIMIT=30] --> - <string name="profile_name_glasses">glasses</string> + <string name="profile_name_glasses">device</string> - <!-- Description of the privileges the application will get if associated with the companion device of GLASSES profile (type) [CHAR LIMIT=NONE] --> - <string name="summary_glasses_multi_device">This app is needed to manage <xliff:g id="device_name" example="My Glasses">%1$s</xliff:g>. <xliff:g id="app_name" example="Glasses">%2$s</xliff:g> will be allowed to interact with your notifications and access your Phone, SMS, Contacts, Microphone and Nearby devices permissions.</string> - - <!-- Description of the privileges the application will get if associated with the companion device of GLASSES profile for singleDevice(type) [CHAR LIMIT=NONE] --> - <string name="summary_glasses_single_device">This app will be allowed to access these permissions on your <xliff:g id="device_type" example="phone">%1$s</xliff:g></string> + <!-- Description of the privileges the application will get if associated with the companion device of GLASSES profile [CHAR LIMIT=NONE] --> + <string name="summary_glasses">This app will be allowed to access these permissions on your <xliff:g id="device_name" example="phone">%1$s</xliff:g></string> <!-- ================= DEVICE_PROFILE_APP_STREAMING ================= --> @@ -97,9 +94,6 @@ <string name="profile_name_generic">device</string> <!-- Description of the privileges the application will get if associated with the companion device of unspecified profile (type) [CHAR LIMIT=NONE] --> - <string name="summary_generic_single_device">This app will be able to sync info, like the name of someone calling, between your phone and <xliff:g id="device_name" example="My Watch">%1$s</xliff:g></string> - - <!-- Description of the privileges the application will get if associated with the companion device of unspecified profile (type) [CHAR LIMIT=NONE] --> <string name="summary_generic">This app will be able to sync info, like the name of someone calling, between your phone and the chosen device</string> <!-- ================= Buttons ================= --> @@ -110,6 +104,9 @@ <!-- Negative button for the device-app association consent dialog [CHAR LIMIT=30] --> <string name="consent_no">Don\u2019t allow</string> + <!-- Cancel button for the device chooser dialog [CHAR LIMIT=30] --> + <string name="consent_cancel">Cancel</string> + <!-- Back button for the helper consent dialog [CHAR LIMIT=30] --> <string name="consent_back">Back</string> diff --git a/packages/CompanionDeviceManager/res/values/styles.xml b/packages/CompanionDeviceManager/res/values/styles.xml index e85190be0e1e..222877bbe9e9 100644 --- a/packages/CompanionDeviceManager/res/values/styles.xml +++ b/packages/CompanionDeviceManager/res/values/styles.xml @@ -69,11 +69,13 @@ <style name="PositiveButton" parent="@android:style/Widget.Material.Button.Borderless.Colored"> - <item name="android:layout_width">300dp</item> - <item name="android:layout_height">56dp</item> + <item name="android:layout_width">match_parent</item> + <item name="android:layout_height">wrap_content</item> <item name="android:layout_marginBottom">2dp</item> <item name="android:textAllCaps">false</item> <item name="android:textSize">14sp</item> + <item name="android:layout_marginStart">32dp</item> + <item name="android:layout_marginEnd">32dp</item> <item name="android:textColor">@android:color/system_neutral1_900</item> <item name="android:textAppearance">@android:style/TextAppearance.DeviceDefault.Medium</item> <item name="android:background">@drawable/btn_positive_bottom</item> @@ -81,11 +83,13 @@ <style name="NegativeButton" parent="@android:style/Widget.Material.Button.Borderless.Colored"> - <item name="android:layout_width">300dp</item> - <item name="android:layout_height">56dp</item> + <item name="android:layout_width">match_parent</item> + <item name="android:layout_height">wrap_content</item> <item name="android:layout_marginTop">2dp</item> <item name="android:textAllCaps">false</item> <item name="android:textSize">14sp</item> + <item name="android:layout_marginStart">32dp</item> + <item name="android:layout_marginEnd">32dp</item> <item name="android:textColor">@android:color/system_neutral1_900</item> <item name="android:layout_marginTop">4dp</item> <item name="android:textAppearance">@android:style/TextAppearance.DeviceDefault.Medium</item> diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java index 4154029b6d41..97016f5384f6 100644 --- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java +++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java @@ -27,10 +27,8 @@ import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTE import static com.android.companiondevicemanager.CompanionDeviceDiscoveryService.DiscoveryState; import static com.android.companiondevicemanager.CompanionDeviceDiscoveryService.DiscoveryState.FINISHED_TIMEOUT; -import static com.android.companiondevicemanager.CompanionDeviceResources.MULTI_DEVICES_SUMMARIES; import static com.android.companiondevicemanager.CompanionDeviceResources.PERMISSION_TYPES; import static com.android.companiondevicemanager.CompanionDeviceResources.PROFILES_NAME; -import static com.android.companiondevicemanager.CompanionDeviceResources.PROFILES_NAME_MULTI; import static com.android.companiondevicemanager.CompanionDeviceResources.PROFILE_ICON; import static com.android.companiondevicemanager.CompanionDeviceResources.SUMMARIES; import static com.android.companiondevicemanager.CompanionDeviceResources.SUPPORTED_PROFILES; @@ -121,6 +119,9 @@ public class CompanionDeviceActivity extends FragmentActivity implements private IAssociationRequestCallback mAppCallback; private ResultReceiver mCdmServiceReceiver; + // Present for application's name. + private CharSequence mAppLabel; + // Always present widgets. private TextView mTitle; private TextView mSummary; @@ -165,8 +166,7 @@ public class CompanionDeviceActivity extends FragmentActivity implements private @Nullable RecyclerView mDeviceListRecyclerView; private @Nullable DeviceListAdapter mDeviceAdapter; - - // The recycler view is only shown for selfManaged and singleDevice association request. + // The recycler view is shown for non-null profile association request. private @Nullable RecyclerView mPermissionListRecyclerView; private @Nullable PermissionListAdapter mPermissionListAdapter; @@ -178,8 +178,6 @@ public class CompanionDeviceActivity extends FragmentActivity implements // onActivityResult() after the association is created. private @Nullable DeviceFilterPair<?> mSelectedDevice; - private @Nullable List<Integer> mPermissionTypes; - private LinearLayoutManager mPermissionsLayoutManager = new LinearLayoutManager(this); @Override @@ -302,6 +300,8 @@ public class CompanionDeviceActivity extends FragmentActivity implements setContentView(R.layout.activity_confirmation); + mAppLabel = appLabel; + mConstraintList = findViewById(R.id.constraint_list); mAssociationConfirmationDialog = findViewById(R.id.association_confirmation); mVendorHeader = findViewById(R.id.vendor_header); @@ -322,7 +322,6 @@ public class CompanionDeviceActivity extends FragmentActivity implements mMultipleDeviceSpinner = findViewById(R.id.spinner_multiple_device); mSingleDeviceSpinner = findViewById(R.id.spinner_single_device); - mDeviceAdapter = new DeviceListAdapter(this, this::onListItemClick); mPermissionListRecyclerView = findViewById(R.id.permission_list); mPermissionListAdapter = new PermissionListAdapter(this); @@ -468,8 +467,6 @@ public class CompanionDeviceActivity extends FragmentActivity implements throw new RuntimeException("Unsupported profile " + deviceProfile); } - mPermissionTypes = new ArrayList<>(); - try { vendorIcon = getVendorHeaderIcon(this, packageName, userId); vendorName = getVendorHeaderName(this, packageName, userId); @@ -486,17 +483,13 @@ public class CompanionDeviceActivity extends FragmentActivity implements } title = getHtmlFromResources(this, TITLES.get(deviceProfile), deviceName); - mPermissionTypes.addAll(PERMISSION_TYPES.get(deviceProfile)); + setupPermissionList(deviceProfile); // Summary is not needed for selfManaged dialog. mSummary.setVisibility(View.GONE); - - setupPermissionList(); - mTitle.setText(title); mVendorHeaderName.setText(vendorName); mVendorHeader.setVisibility(View.VISIBLE); - mVendorHeader.setVisibility(View.VISIBLE); mProfileIcon.setVisibility(View.GONE); mDeviceListRecyclerView.setVisibility(View.GONE); // Top and bottom borders should be gone for selfManaged dialog. @@ -509,7 +502,9 @@ public class CompanionDeviceActivity extends FragmentActivity implements final String deviceProfile = mRequest.getDeviceProfile(); - mPermissionTypes = new ArrayList<>(); + if (!SUPPORTED_PROFILES.contains(deviceProfile)) { + throw new RuntimeException("Unsupported profile " + deviceProfile); + } CompanionDeviceDiscoveryService.getScanResult().observe(this, deviceFilterPairs -> updateSingleDeviceUi( @@ -529,75 +524,40 @@ public class CompanionDeviceActivity extends FragmentActivity implements if (deviceFilterPairs.isEmpty()) return; mSelectedDevice = requireNonNull(deviceFilterPairs.get(0)); - // No need to show user consent dialog if it is a singleDevice - // and isSkipPrompt(true) AssociationRequest. - // See AssociationRequestsProcessor#mayAssociateWithoutPrompt. - if (mRequest.isSkipPrompt()) { - mSingleDeviceSpinner.setVisibility(View.GONE); - onUserSelectedDevice(mSelectedDevice); - return; - } - - final String deviceName = mSelectedDevice.getDisplayName(); - final Spanned title; - final Spanned summary; - final Drawable profileIcon; - if (!SUPPORTED_PROFILES.contains(deviceProfile)) { - throw new RuntimeException("Unsupported profile " + deviceProfile); - } + final Drawable profileIcon = getIcon(this, PROFILE_ICON.get(deviceProfile)); - if (deviceProfile == null) { - summary = getHtmlFromResources(this, SUMMARIES.get(null), deviceName); - mConstraintList.setVisibility(View.GONE); - } else { - summary = getHtmlFromResources( - this, SUMMARIES.get(deviceProfile), getString(R.string.device_type)); - mPermissionTypes.addAll(PERMISSION_TYPES.get(deviceProfile)); - setupPermissionList(); - } - - title = getHtmlFromResources(this, TITLES.get(deviceProfile), appLabel, deviceName); - profileIcon = getIcon(this, PROFILE_ICON.get(deviceProfile)); + updatePermissionUi(); - mTitle.setText(title); - mSummary.setText(summary); mProfileIcon.setImageDrawable(profileIcon); - mSingleDeviceSpinner.setVisibility(View.GONE); mAssociationConfirmationDialog.setVisibility(View.VISIBLE); + mSingleDeviceSpinner.setVisibility(View.GONE); } private void initUiForMultipleDevices(CharSequence appLabel) { if (DEBUG) Log.i(TAG, "initUiFor_MultipleDevices()"); - final String deviceProfile = mRequest.getDeviceProfile(); - - final String profileName; - final String profileNameMulti; - final Spanned summary; final Drawable profileIcon; - final int summaryResourceId; + final Spanned title; + final String deviceProfile = mRequest.getDeviceProfile(); if (!SUPPORTED_PROFILES.contains(deviceProfile)) { throw new RuntimeException("Unsupported profile " + deviceProfile); } - profileName = getString(PROFILES_NAME.get(deviceProfile)); - profileNameMulti = getString(PROFILES_NAME_MULTI.get(deviceProfile)); profileIcon = getIcon(this, PROFILE_ICON.get(deviceProfile)); - summaryResourceId = MULTI_DEVICES_SUMMARIES.get(deviceProfile); if (deviceProfile == null) { - summary = getHtmlFromResources(this, summaryResourceId); + title = getHtmlFromResources(this, R.string.chooser_title_non_profile, appLabel); + mButtonNotAllowMultipleDevices.setText(R.string.consent_no); } else { - summary = getHtmlFromResources(this, summaryResourceId, profileName, appLabel); + title = getHtmlFromResources(this, + R.string.chooser_title, getString(PROFILES_NAME.get(deviceProfile))); } - final Spanned title = getHtmlFromResources( - this, R.string.chooser_title, profileNameMulti, appLabel); + mDeviceAdapter = new DeviceListAdapter(this, this::onDeviceClicked); mTitle.setText(title); - mSummary.setText(summary); mProfileIcon.setImageDrawable(profileIcon); mDeviceListRecyclerView.setAdapter(mDeviceAdapter); @@ -613,6 +573,7 @@ public class CompanionDeviceActivity extends FragmentActivity implements mDeviceAdapter.setDevices(deviceFilterPairs); }); + mSummary.setVisibility(View.GONE); // "Remove" consent button: users would need to click on the list item. mButtonAllow.setVisibility(View.GONE); mButtonNotAllow.setVisibility(View.GONE); @@ -623,11 +584,9 @@ public class CompanionDeviceActivity extends FragmentActivity implements mMultipleDeviceSpinner.setVisibility(View.VISIBLE); } - private void onListItemClick(int position) { - if (DEBUG) Log.d(TAG, "onListItemClick() " + position); - + private void onDeviceClicked(int position) { final DeviceFilterPair<?> selectedDevice = mDeviceAdapter.getItem(position); - + // To prevent double tap on the selected device. if (mSelectedDevice != null) { if (DEBUG) Log.w(TAG, "Already selected."); return; @@ -637,7 +596,47 @@ public class CompanionDeviceActivity extends FragmentActivity implements mSelectedDevice = requireNonNull(selectedDevice); - onUserSelectedDevice(selectedDevice); + Log.d(TAG, "onDeviceClicked(): " + mSelectedDevice.toShortString()); + + updatePermissionUi(); + + mSummary.setVisibility(View.VISIBLE); + mButtonAllow.setVisibility(View.VISIBLE); + mButtonNotAllow.setVisibility(View.VISIBLE); + mDeviceListRecyclerView.setVisibility(View.GONE); + mNotAllowMultipleDevicesLayout.setVisibility(View.GONE); + } + + private void updatePermissionUi() { + final String deviceProfile = mRequest.getDeviceProfile(); + final int summaryResourceId = SUMMARIES.get(deviceProfile); + final String remoteDeviceName = mSelectedDevice.getDisplayName(); + final Spanned title = getHtmlFromResources( + this, TITLES.get(deviceProfile), mAppLabel, remoteDeviceName); + final Spanned summary; + + // No need to show permission consent dialog if it is a isSkipPrompt(true) + // AssociationRequest. See AssociationRequestsProcessor#mayAssociateWithoutPrompt. + if (mRequest.isSkipPrompt()) { + mSingleDeviceSpinner.setVisibility(View.GONE); + onUserSelectedDevice(mSelectedDevice); + return; + } + + if (deviceProfile == null && mRequest.isSingleDevice()) { + summary = getHtmlFromResources(this, summaryResourceId, remoteDeviceName); + mConstraintList.setVisibility(View.GONE); + } else if (deviceProfile == null) { + onUserSelectedDevice(mSelectedDevice); + return; + } else { + summary = getHtmlFromResources( + this, summaryResourceId, getString(R.string.device_type)); + setupPermissionList(deviceProfile); + } + + mTitle.setText(title); + mSummary.setText(summary); } private void onPositiveButtonClick(View v) { @@ -680,8 +679,9 @@ public class CompanionDeviceActivity extends FragmentActivity implements // initiate the layoutManager for the recyclerview, add listeners for monitoring the scrolling // and when mPermissionListRecyclerView is fully populated. // Lastly, disable the Allow and Don't allow buttons. - private void setupPermissionList() { - mPermissionListAdapter.setPermissionType(mPermissionTypes); + private void setupPermissionList(String deviceProfile) { + final List<Integer> permissionTypes = new ArrayList<>(PERMISSION_TYPES.get(deviceProfile)); + mPermissionListAdapter.setPermissionType(permissionTypes); mPermissionListRecyclerView.setAdapter(mPermissionListAdapter); mPermissionListRecyclerView.setLayoutManager(mPermissionsLayoutManager); diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceResources.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceResources.java index 7aed13960b08..060c03213bcd 100644 --- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceResources.java +++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceResources.java @@ -86,21 +86,11 @@ final class CompanionDeviceResources { static final Map<String, Integer> SUMMARIES; static { final Map<String, Integer> map = new ArrayMap<>(); - map.put(DEVICE_PROFILE_WATCH, R.string.summary_watch_single_device); - map.put(DEVICE_PROFILE_GLASSES, R.string.summary_glasses_single_device); - map.put(null, R.string.summary_generic_single_device); - - SUMMARIES = unmodifiableMap(map); - } - - static final Map<String, Integer> MULTI_DEVICES_SUMMARIES; - static { - final Map<String, Integer> map = new ArrayMap<>(); map.put(DEVICE_PROFILE_WATCH, R.string.summary_watch); - map.put(DEVICE_PROFILE_GLASSES, R.string.summary_glasses_multi_device); + map.put(DEVICE_PROFILE_GLASSES, R.string.summary_glasses); map.put(null, R.string.summary_generic); - MULTI_DEVICES_SUMMARIES = unmodifiableMap(map); + SUMMARIES = unmodifiableMap(map); } static final Map<String, Integer> PROFILES_NAME; diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java index b265a425d2e7..e1eb36ac276c 100644 --- a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java +++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java @@ -16,11 +16,10 @@ package com.android.dynsystem; -import static android.os.AsyncTask.Status.FINISHED; -import static android.os.AsyncTask.Status.PENDING; import static android.os.AsyncTask.Status.RUNNING; import static android.os.image.DynamicSystemClient.ACTION_HIDE_NOTIFICATION; import static android.os.image.DynamicSystemClient.ACTION_NOTIFY_IF_IN_USE; +import static android.os.image.DynamicSystemClient.ACTION_NOTIFY_KEYGUARD_DISMISSED; import static android.os.image.DynamicSystemClient.ACTION_START_INSTALL; import static android.os.image.DynamicSystemClient.CAUSE_ERROR_EXCEPTION; import static android.os.image.DynamicSystemClient.CAUSE_ERROR_INVALID_URL; @@ -234,6 +233,8 @@ public class DynamicSystemInstallationService extends Service executeNotifyIfInUseCommand(); } else if (ACTION_HIDE_NOTIFICATION.equals(action)) { executeHideNotificationCommand(); + } else if (ACTION_NOTIFY_KEYGUARD_DISMISSED.equals(action)) { + executeNotifyKeyguardDismissed(); } return Service.START_NOT_STICKY; @@ -423,7 +424,10 @@ public class DynamicSystemInstallationService extends Service Log.e(TAG, "It's already running in normal system."); return; } - + if (mDynSystem.getActiveDsuSlot().endsWith(".lock")) { + Log.e(TAG, "Ignore the reboot intent for a locked DSU slot"); + return; + } if (!mDynSystem.setEnable(/* enable = */ false, /* oneShot = */ false)) { Log.e(TAG, "Failed to disable DynamicSystem."); @@ -474,6 +478,10 @@ public class DynamicSystemInstallationService extends Service } } + private void executeNotifyKeyguardDismissed() { + postStatus(STATUS_NOT_STARTED, CAUSE_INSTALL_CANCELLED, null); + } + private void resetTaskAndStop() { resetTaskAndStop(/* removeNotification= */ false); } diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/VerificationActivity.java b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/VerificationActivity.java index b52272961e4b..7401691cd59e 100644 --- a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/VerificationActivity.java +++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/VerificationActivity.java @@ -16,6 +16,7 @@ package com.android.dynsystem; +import static android.os.image.DynamicSystemClient.ACTION_NOTIFY_KEYGUARD_DISMISSED; import static android.os.image.DynamicSystemClient.KEY_KEYGUARD_USE_DEFAULT_STRINGS; import android.app.Activity; @@ -83,11 +84,19 @@ public class VerificationActivity extends Activity { protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == REQUEST_CODE && resultCode == RESULT_OK) { startInstallationService(); + } else { + notifyKeyguardDismissed(); } finish(); } + private void notifyKeyguardDismissed() { + Intent intent = new Intent(this, DynamicSystemInstallationService.class); + intent.setAction(ACTION_NOTIFY_KEYGUARD_DISMISSED); + startServiceAsUser(intent, UserHandle.SYSTEM); + } + private void startInstallationService() { // retrieve data from calling intent Intent callingIntent = getIntent(); diff --git a/packages/EasterEgg/res/values/q_puzzles.xml b/packages/EasterEgg/res/values/q_puzzles.xml index 7c2eff152ffe..2a477f5ee18f 100644 --- a/packages/EasterEgg/res/values/q_puzzles.xml +++ b/packages/EasterEgg/res/values/q_puzzles.xml @@ -44,7 +44,6 @@ <item>android:drawable/ic_audio_alarm</item> <item>android:drawable/ic_audio_alarm_mute</item> - <item>android:drawable/ic_bluetooth_share_icon</item> <item>android:drawable/ic_bt_headphones_a2dp</item> <item>android:drawable/ic_bt_headset_hfp</item> <item>android:drawable/ic_bt_hearing_aid</item> diff --git a/packages/PackageInstaller/res/values/strings.xml b/packages/PackageInstaller/res/values/strings.xml index a2118fac4231..c52fde6364a0 100644 --- a/packages/PackageInstaller/res/values/strings.xml +++ b/packages/PackageInstaller/res/values/strings.xml @@ -38,7 +38,11 @@ <!-- Message for updating an existing app [CHAR LIMIT=NONE] --> <string name="install_confirm_question_update">Do you want to update this app?</string> <!-- Message for updating an existing app with update owner reminder [CHAR LIMIT=NONE] --> - <string name="install_confirm_question_update_owner_reminder">Update this app from <xliff:g id="new_update_owner">%1$s</xliff:g>?\n\nThis app normally receives updates from <xliff:g id="existing_update_owner">%2$s</xliff:g>. By updating from a different source, you may receive future updates from any source on your phone. App functionality may change.</string> + <string name="install_confirm_question_update_owner_reminder" product="tablet">Update this app from <xliff:g id="new_update_owner">%1$s</xliff:g>?\n\nThis app normally receives updates from <xliff:g id="existing_update_owner">%2$s</xliff:g>. By updating from a different source, you may receive future updates from any source on your tablet. App functionality may change.</string> + <!-- Message for updating an existing app with update owner reminder [CHAR LIMIT=NONE] --> + <string name="install_confirm_question_update_owner_reminder" product="tv">Update this app from <xliff:g id="new_update_owner">%1$s</xliff:g>?\n\nThis app normally receives updates from <xliff:g id="existing_update_owner">%2$s</xliff:g>. By updating from a different source, you may receive future updates from any source on your TV. App functionality may change.</string> + <!-- Message for updating an existing app with update owner reminder [CHAR LIMIT=NONE] --> + <string name="install_confirm_question_update_owner_reminder" product="default">Update this app from <xliff:g id="new_update_owner">%1$s</xliff:g>?\n\nThis app normally receives updates from <xliff:g id="existing_update_owner">%2$s</xliff:g>. By updating from a different source, you may receive future updates from any source on your phone. App functionality may change.</string> <!-- [CHAR LIMIT=100] --> <string name="install_failed">App not installed.</string> <!-- Reason displayed when installation fails because the package was blocked diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/DeleteStagedFileOnResult.java b/packages/PackageInstaller/src/com/android/packageinstaller/DeleteStagedFileOnResult.java index 7b17cbdd3a1e..19d74b33e034 100644 --- a/packages/PackageInstaller/src/com/android/packageinstaller/DeleteStagedFileOnResult.java +++ b/packages/PackageInstaller/src/com/android/packageinstaller/DeleteStagedFileOnResult.java @@ -16,6 +16,8 @@ package com.android.packageinstaller; +import static android.content.Intent.CATEGORY_LAUNCHER; + import static com.android.packageinstaller.PackageInstallerActivity.EXTRA_STAGED_SESSION_ID; import android.app.Activity; @@ -45,6 +47,9 @@ public class DeleteStagedFileOnResult extends Activity { protected void onActivityResult(int requestCode, int resultCode, Intent data) { setResult(resultCode, data); finish(); + if (data != null && data.hasCategory(CATEGORY_LAUNCHER)) { + startActivity(data); + } } @Override diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/InstallSuccess.java b/packages/PackageInstaller/src/com/android/packageinstaller/InstallSuccess.java index 73c03a57cade..ff991d2f7ee3 100644 --- a/packages/PackageInstaller/src/com/android/packageinstaller/InstallSuccess.java +++ b/packages/PackageInstaller/src/com/android/packageinstaller/InstallSuccess.java @@ -17,7 +17,6 @@ package com.android.packageinstaller; import android.app.Activity; -import android.content.ActivityNotFoundException; import android.content.DialogInterface; import android.content.Intent; import android.content.pm.ApplicationInfo; @@ -123,11 +122,7 @@ public class InstallSuccess extends AlertActivity { Button launchButton = mAlert.getButton(DialogInterface.BUTTON_POSITIVE); if (enabled) { launchButton.setOnClickListener(view -> { - try { - startActivity(mLaunchIntent); - } catch (ActivityNotFoundException | SecurityException e) { - Log.e(LOG_TAG, "Could not start activity", e); - } + setResult(Activity.RESULT_OK, mLaunchIntent); finish(); }); } else { diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java index d1541569bc55..e071c111d617 100644 --- a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java +++ b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java @@ -16,8 +16,9 @@ */ package com.android.packageinstaller; +import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP; import static android.content.Intent.FLAG_ACTIVITY_NO_HISTORY; -import static android.content.Intent.FLAG_ACTIVITY_REORDER_TO_FRONT; +import static android.content.Intent.FLAG_ACTIVITY_SINGLE_TOP; import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS; import android.Manifest; @@ -789,7 +790,8 @@ public class PackageInstallerActivity extends AlertActivity { } new Handler(Looper.getMainLooper()).postDelayed(() -> { if (!isDestroyed()) { - startActivity(getIntent().addFlags(FLAG_ACTIVITY_REORDER_TO_FRONT)); + startActivity(getIntent().addFlags( + FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_SINGLE_TOP)); } }, 500); diff --git a/packages/SettingsLib/Spa/build.gradle.kts b/packages/SettingsLib/Spa/build.gradle.kts new file mode 100644 index 000000000000..64b67d7fe4f1 --- /dev/null +++ b/packages/SettingsLib/Spa/build.gradle.kts @@ -0,0 +1,66 @@ +/* + * 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. + */ + +import com.android.build.gradle.BaseExtension +import com.android.build.gradle.api.AndroidBasePlugin +import org.jetbrains.kotlin.gradle.tasks.KotlinCompile + +plugins { + alias(libs.plugins.android.application) apply false + alias(libs.plugins.android.library) apply false + alias(libs.plugins.kotlin.android) apply false +} + +allprojects { + extra["jetpackComposeVersion"] = "1.4.0-beta01" +} + +subprojects { + plugins.withType<AndroidBasePlugin> { + configure<BaseExtension> { + compileSdkVersion(33) + + defaultConfig { + minSdk = 21 + targetSdk = 34 + } + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 + } + } + } + + afterEvaluate { + plugins.withType<AndroidBasePlugin> { + configure<BaseExtension> { + if (buildFeatures.compose == true) { + composeOptions { + kotlinCompilerExtensionVersion = "1.4.4" + } + } + } + } + } + + tasks.withType<KotlinCompile> { + kotlinOptions { + jvmTarget = "17" + freeCompilerArgs = listOf("-Xjvm-default=all") + } + } +} diff --git a/packages/SettingsLib/Spa/gallery/build.gradle b/packages/SettingsLib/Spa/gallery/build.gradle deleted file mode 100644 index 212aa7b7851d..000000000000 --- a/packages/SettingsLib/Spa/gallery/build.gradle +++ /dev/null @@ -1,58 +0,0 @@ -/* - * 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. - */ - -plugins { - id 'com.android.application' - id 'kotlin-android' -} - -android { - namespace 'com.android.settingslib.spa.gallery' - compileSdk TARGET_SDK - buildToolsVersion = BUILD_TOOLS_VERSION - - defaultConfig { - applicationId "com.android.settingslib.spa.gallery" - minSdk MIN_SDK - targetSdk TARGET_SDK - versionCode 1 - versionName "1.0" - } - - sourceSets { - main { - kotlin { - srcDir "src" - } - res.srcDirs = ["res"] - manifest.srcFile "AndroidManifest.xml" - } - } - compileOptions { - sourceCompatibility JavaVersion.VERSION_17 - targetCompatibility JavaVersion.VERSION_17 - } - buildFeatures { - compose true - } - composeOptions { - kotlinCompilerExtensionVersion jetpack_compose_compiler_version - } -} - -dependencies { - implementation project(":spa") -} diff --git a/packages/SettingsLib/Spa/build.gradle b/packages/SettingsLib/Spa/gallery/build.gradle.kts index e68ef85cd43d..7f689c16b7ed 100644 --- a/packages/SettingsLib/Spa/build.gradle +++ b/packages/SettingsLib/Spa/gallery/build.gradle.kts @@ -14,27 +14,32 @@ * limitations under the License. */ -import org.jetbrains.kotlin.gradle.tasks.KotlinCompile - -buildscript { - ext { - BUILD_TOOLS_VERSION = "30.0.3" - MIN_SDK = 21 - TARGET_SDK = 33 - jetpack_compose_version = '1.4.0-beta01' - jetpack_compose_compiler_version = '1.4.4' - } -} plugins { - id 'com.android.application' version '8.0.0' apply false - id 'com.android.library' version '8.0.0' apply false - id 'org.jetbrains.kotlin.android' version '1.8.10' apply false + alias(libs.plugins.android.application) + alias(libs.plugins.kotlin.android) } -subprojects { - tasks.withType(KotlinCompile).configureEach { - kotlinOptions { - jvmTarget = "17" - freeCompilerArgs = ["-Xjvm-default=all"] + +android { + namespace = "com.android.settingslib.spa.gallery" + + defaultConfig { + applicationId = "com.android.settingslib.spa.gallery" + versionCode = 1 + versionName = "1.0" + } + + sourceSets { + sourceSets.getByName("main") { + java.setSrcDirs(listOf("src")) + res.setSrcDirs(listOf("res")) + manifest.srcFile("AndroidManifest.xml") } } + buildFeatures { + compose = true + } +} + +dependencies { + implementation(project(":spa")) } diff --git a/packages/SettingsLib/Spa/gradle/libs.versions.toml b/packages/SettingsLib/Spa/gradle/libs.versions.toml new file mode 100644 index 000000000000..9a16df8c834c --- /dev/null +++ b/packages/SettingsLib/Spa/gradle/libs.versions.toml @@ -0,0 +1,30 @@ +# +# 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. +# + +[versions] +agp = "8.0.2" +dexmaker-mockito = "2.28.3" +kotlin = "1.8.10" +truth = "1.1" + +[libraries] +dexmaker-mockito = { module = "com.linkedin.dexmaker:dexmaker-mockito", version.ref = "dexmaker-mockito" } +truth = { module = "com.google.truth:truth", version.ref = "truth" } + +[plugins] +android-application = { id = "com.android.application", version.ref = "agp" } +android-library = { id = "com.android.library", version.ref = "agp" } +kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } diff --git a/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.properties b/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.properties index ed85e33ca8a6..33f49e33a47e 100644 --- a/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.properties +++ b/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.properties @@ -14,9 +14,8 @@ # limitations under the License. # -#Thu Jul 14 10:36:06 CST 2022 distributionBase=GRADLE_USER_HOME -distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.1-bin.zip distributionPath=wrapper/dists zipStorePath=wrapper/dists zipStoreBase=GRADLE_USER_HOME diff --git a/packages/SettingsLib/Spa/settings.gradle b/packages/SettingsLib/Spa/settings.gradle.kts index 1c5a1ceda34a..9909781b0623 100644 --- a/packages/SettingsLib/Spa/settings.gradle +++ b/packages/SettingsLib/Spa/settings.gradle.kts @@ -16,20 +16,27 @@ pluginManagement { repositories { - gradlePluginPortal() google() mavenCentral() + gradlePluginPortal() } } dependencyResolutionManagement { repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) + rulesMode.set(RulesMode.FAIL_ON_PROJECT_RULES) + repositories { google() mavenCentral() - maven { url "https://jitpack.io"} + maven { + url = uri("https://jitpack.io") + content { + includeGroup("com.github.PhilJay") + } + } } } rootProject.name = "SpaLib" -include ':spa' -include ':gallery' -include ':testutils' +include(":spa") +include(":gallery") +include(":testutils") diff --git a/packages/SettingsLib/Spa/spa/build.gradle b/packages/SettingsLib/Spa/spa/build.gradle deleted file mode 100644 index a591366cd63f..000000000000 --- a/packages/SettingsLib/Spa/spa/build.gradle +++ /dev/null @@ -1,119 +0,0 @@ -/* - * 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. - */ - -plugins { - id 'com.android.library' - id 'kotlin-android' -} - -android { - namespace 'com.android.settingslib.spa' - compileSdk TARGET_SDK - buildToolsVersion = BUILD_TOOLS_VERSION - - defaultConfig { - minSdk MIN_SDK - targetSdk TARGET_SDK - - testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" - } - - sourceSets { - main { - kotlin { - srcDir "src" - } - res.srcDirs = ["res"] - manifest.srcFile "AndroidManifest.xml" - } - androidTest { - kotlin { - srcDir "../tests/src" - } - res.srcDirs = ["../tests/res"] - manifest.srcFile "../tests/AndroidManifest.xml" - } - } - compileOptions { - sourceCompatibility JavaVersion.VERSION_17 - targetCompatibility JavaVersion.VERSION_17 - } - buildFeatures { - compose true - } - composeOptions { - kotlinCompilerExtensionVersion jetpack_compose_compiler_version - } - buildTypes { - debug { - testCoverageEnabled = true - } - } -} - -dependencies { - api "androidx.appcompat:appcompat:1.7.0-alpha02" - api "androidx.slice:slice-builders:1.1.0-alpha02" - api "androidx.slice:slice-core:1.1.0-alpha02" - api "androidx.slice:slice-view:1.1.0-alpha02" - api "androidx.compose.material3:material3:1.1.0-alpha06" - api "androidx.compose.material:material-icons-extended:$jetpack_compose_version" - api "androidx.compose.runtime:runtime-livedata:$jetpack_compose_version" - api "androidx.compose.ui:ui-tooling-preview:$jetpack_compose_version" - api "androidx.lifecycle:lifecycle-livedata-ktx" - api "androidx.lifecycle:lifecycle-runtime-compose" - api "androidx.navigation:navigation-compose:2.6.0-alpha08" - api "com.github.PhilJay:MPAndroidChart:v3.1.0-alpha" - api "com.google.android.material:material:1.7.0-alpha03" - debugApi "androidx.compose.ui:ui-tooling:$jetpack_compose_version" - implementation "com.airbnb.android:lottie-compose:5.2.0" - - androidTestImplementation project(":testutils") - androidTestImplementation 'androidx.lifecycle:lifecycle-runtime-testing' - androidTestImplementation "com.linkedin.dexmaker:dexmaker-mockito:2.28.3" -} - -task coverageReport(type: JacocoReport, dependsOn: "connectedDebugAndroidTest") { - group = "Reporting" - description = "Generate Jacoco coverage reports after running tests." - - sourceDirectories.from = files("src") - classDirectories.from = fileTree( - dir: "$buildDir/tmp/kotlin-classes/debug", - excludes: [ - "com/android/settingslib/spa/debug/**", - - // Excludes files forked from AndroidX. - "com/android/settingslib/spa/widget/scaffold/CustomizedAppBar*", - "com/android/settingslib/spa/widget/scaffold/TopAppBarColors*", - - // Excludes files forked from Accompanist. - "com/android/settingslib/spa/framework/compose/DrawablePainter*", - - // Excludes inline functions, which is not covered in Jacoco reports. - "com/android/settingslib/spa/framework/util/Collections*", - "com/android/settingslib/spa/framework/util/Flows*", - - // Excludes debug functions - "com/android/settingslib/spa/framework/compose/TimeMeasurer*", - - // Excludes slice demo presenter & provider - "com/android/settingslib/spa/slice/presenter/Demo*", - "com/android/settingslib/spa/slice/provider/Demo*", - ], - ) - executionData.from = fileTree(dir: "$buildDir/outputs/code_coverage/debugAndroidTest/connected") -} diff --git a/packages/SettingsLib/Spa/spa/build.gradle.kts b/packages/SettingsLib/Spa/spa/build.gradle.kts new file mode 100644 index 000000000000..52896351d92f --- /dev/null +++ b/packages/SettingsLib/Spa/spa/build.gradle.kts @@ -0,0 +1,111 @@ +/* + * 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. + */ + +plugins { + alias(libs.plugins.android.library) + alias(libs.plugins.kotlin.android) + jacoco +} + +val jetpackComposeVersion: String? by extra + +android { + namespace = "com.android.settingslib.spa" + + defaultConfig { + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + } + + sourceSets { + sourceSets.getByName("main") { + kotlin.setSrcDirs(listOf("src")) + res.setSrcDirs(listOf("res")) + manifest.srcFile("AndroidManifest.xml") + } + sourceSets.getByName("androidTest") { + kotlin.setSrcDirs(listOf("../tests/src")) + res.setSrcDirs(listOf("../tests/res")) + manifest.srcFile("../tests/AndroidManifest.xml") + } + } + buildFeatures { + compose = true + } + buildTypes { + getByName("debug") { + enableAndroidTestCoverage = true + } + } +} + +dependencies { + api("androidx.appcompat:appcompat:1.7.0-alpha02") + api("androidx.slice:slice-builders:1.1.0-alpha02") + api("androidx.slice:slice-core:1.1.0-alpha02") + api("androidx.slice:slice-view:1.1.0-alpha02") + api("androidx.compose.material3:material3:1.1.0-alpha06") + api("androidx.compose.material:material-icons-extended:$jetpackComposeVersion") + api("androidx.compose.runtime:runtime-livedata:$jetpackComposeVersion") + api("androidx.compose.ui:ui-tooling-preview:$jetpackComposeVersion") + api("androidx.lifecycle:lifecycle-livedata-ktx") + api("androidx.lifecycle:lifecycle-runtime-compose") + api("androidx.navigation:navigation-compose:2.6.0-alpha08") + api("com.github.PhilJay:MPAndroidChart:v3.1.0-alpha") + api("com.google.android.material:material:1.7.0-alpha03") + debugApi("androidx.compose.ui:ui-tooling:$jetpackComposeVersion") + implementation("com.airbnb.android:lottie-compose:5.2.0") + + androidTestImplementation(project(":testutils")) + androidTestImplementation("androidx.lifecycle:lifecycle-runtime-testing") + androidTestImplementation(libs.dexmaker.mockito) +} + +tasks.register<JacocoReport>("coverageReport") { + group = "Reporting" + description = "Generate Jacoco coverage reports after running tests." + dependsOn("connectedDebugAndroidTest") + sourceDirectories.setFrom(files("src")) + classDirectories.setFrom( + fileTree(layout.buildDirectory.dir("tmp/kotlin-classes/debug")) { + setExcludes( + listOf( + "com/android/settingslib/spa/debug/**", + + // Excludes files forked from AndroidX. + "com/android/settingslib/spa/widget/scaffold/CustomizedAppBar*", + "com/android/settingslib/spa/widget/scaffold/TopAppBarColors*", + + // Excludes files forked from Accompanist. + "com/android/settingslib/spa/framework/compose/DrawablePainter*", + + // Excludes inline functions, which is not covered in Jacoco reports. + "com/android/settingslib/spa/framework/util/Collections*", + "com/android/settingslib/spa/framework/util/Flows*", + + // Excludes debug functions + "com/android/settingslib/spa/framework/compose/TimeMeasurer*", + + // Excludes slice demo presenter & provider + "com/android/settingslib/spa/slice/presenter/Demo*", + "com/android/settingslib/spa/slice/provider/Demo*", + ) + ) + } + ) + executionData.setFrom( + fileTree(layout.buildDirectory.dir("outputs/code_coverage/debugAndroidTest/connected")) + ) +} diff --git a/packages/SettingsLib/Spa/testutils/build.gradle b/packages/SettingsLib/Spa/testutils/build.gradle deleted file mode 100644 index 23a9add79d50..000000000000 --- a/packages/SettingsLib/Spa/testutils/build.gradle +++ /dev/null @@ -1,60 +0,0 @@ -/* - * 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. - */ - -plugins { - id 'com.android.library' - id 'kotlin-android' -} - -android { - namespace 'com.android.settingslib.spa.testutils' - compileSdk TARGET_SDK - buildToolsVersion = BUILD_TOOLS_VERSION - - defaultConfig { - minSdk MIN_SDK - targetSdk TARGET_SDK - } - - sourceSets { - main { - kotlin { - srcDir "src" - } - manifest.srcFile "AndroidManifest.xml" - } - } - compileOptions { - sourceCompatibility JavaVersion.VERSION_17 - targetCompatibility JavaVersion.VERSION_17 - } - buildFeatures { - compose true - } - composeOptions { - kotlinCompilerExtensionVersion jetpack_compose_compiler_version - } -} - -dependencies { - api project(":spa") - - api "androidx.arch.core:core-testing:2.2.0-alpha01" - api "androidx.compose.ui:ui-test-junit4:$jetpack_compose_version" - api "com.google.truth:truth:1.1.3" - api "org.mockito:mockito-core:2.21.0" - debugApi "androidx.compose.ui:ui-test-manifest:$jetpack_compose_version" -} diff --git a/packages/SettingsLib/Spa/testutils/build.gradle.kts b/packages/SettingsLib/Spa/testutils/build.gradle.kts new file mode 100644 index 000000000000..6df0226a94f9 --- /dev/null +++ b/packages/SettingsLib/Spa/testutils/build.gradle.kts @@ -0,0 +1,46 @@ +/* + * 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. + */ + +plugins { + alias(libs.plugins.android.library) + alias(libs.plugins.kotlin.android) +} + +val jetpackComposeVersion: String? by extra + +android { + namespace = "com.android.settingslib.spa.testutils" + + sourceSets { + sourceSets.getByName("main") { + java.setSrcDirs(listOf("src")) + manifest.srcFile("AndroidManifest.xml") + } + } + buildFeatures { + compose = true + } +} + +dependencies { + api(project(":spa")) + + api("androidx.arch.core:core-testing:2.2.0-alpha01") + api("androidx.compose.ui:ui-test-junit4:$jetpackComposeVersion") + api(libs.truth) + api("org.mockito:mockito-core:2.21.0") + debugApi("androidx.compose.ui:ui-test-manifest:$jetpackComposeVersion") +} diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppListRepository.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppListRepository.kt index 2b38b4cefe3e..8e0cf894bb28 100644 --- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppListRepository.kt +++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppListRepository.kt @@ -34,11 +34,11 @@ import kotlinx.coroutines.runBlocking /** * The repository to load the App List data. */ -internal interface AppListRepository { +interface AppListRepository { /** Loads the list of [ApplicationInfo]. */ suspend fun loadApps( userId: Int, - showInstantApps: Boolean = false, + loadInstantApps: Boolean = false, matchAnyUserForAdmin: Boolean = false, ): List<ApplicationInfo> @@ -50,6 +50,9 @@ internal interface AppListRepository { /** Gets the system app package names. */ fun getSystemPackageNamesBlocking(userId: Int): Set<String> + + /** Loads the list of [ApplicationInfo], and filter base on `isSystemApp`. */ + suspend fun loadAndFilterApps(userId: Int, isSystemApp: Boolean): List<ApplicationInfo> } /** @@ -62,13 +65,13 @@ object AppListRepositoryUtil { AppListRepositoryImpl(context).getSystemPackageNamesBlocking(userId) } -internal class AppListRepositoryImpl(private val context: Context) : AppListRepository { +class AppListRepositoryImpl(private val context: Context) : AppListRepository { private val packageManager = context.packageManager private val userManager = context.userManager override suspend fun loadApps( userId: Int, - showInstantApps: Boolean, + loadInstantApps: Boolean, matchAnyUserForAdmin: Boolean, ): List<ApplicationInfo> = coroutineScope { val hiddenSystemModulesDeferred = async { @@ -86,7 +89,7 @@ internal class AppListRepositoryImpl(private val context: Context) : AppListRepo val hiddenSystemModules = hiddenSystemModulesDeferred.await() val hideWhenDisabledPackages = hideWhenDisabledPackagesDeferred.await() installedApplicationsAsUser.filter { app -> - app.isInAppList(showInstantApps, hiddenSystemModules, hideWhenDisabledPackages) + app.isInAppList(loadInstantApps, hiddenSystemModules, hideWhenDisabledPackages) } } @@ -136,17 +139,17 @@ internal class AppListRepositoryImpl(private val context: Context) : AppListRepo ): Flow<(app: ApplicationInfo) -> Boolean> = userIdFlow.combine(showSystemFlow, ::showSystemPredicate) - override fun getSystemPackageNamesBlocking(userId: Int) = - runBlocking { getSystemPackageNames(userId) } + override fun getSystemPackageNamesBlocking(userId: Int) = runBlocking { + loadAndFilterApps(userId = userId, isSystemApp = true).map { it.packageName }.toSet() + } - private suspend fun getSystemPackageNames(userId: Int): Set<String> = - coroutineScope { - val loadAppsDeferred = async { loadApps(userId) } - val homeOrLauncherPackages = loadHomeOrLauncherPackages(userId) - val showSystemPredicate = - { app: ApplicationInfo -> isSystemApp(app, homeOrLauncherPackages) } - loadAppsDeferred.await().filter(showSystemPredicate).map { it.packageName }.toSet() + override suspend fun loadAndFilterApps(userId: Int, isSystemApp: Boolean) = coroutineScope { + val loadAppsDeferred = async { loadApps(userId) } + val homeOrLauncherPackages = loadHomeOrLauncherPackages(userId) + loadAppsDeferred.await().filter { app -> + isSystemApp(app, homeOrLauncherPackages) == isSystemApp } + } private suspend fun showSystemPredicate( userId: Int, diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListPage.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListPage.kt index 030b70a75a8b..07e42350233c 100644 --- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListPage.kt +++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListPage.kt @@ -42,7 +42,6 @@ fun <T : AppRecord> AppListPage( showInstantApps: Boolean = false, noMoreOptions: Boolean = false, matchAnyUserForAdmin: Boolean = false, - primaryUserOnly: Boolean = false, noItemMessage: String? = null, moreOptions: @Composable MoreOptionsScope.() -> Unit = {}, header: @Composable () -> Unit = {}, @@ -60,7 +59,7 @@ fun <T : AppRecord> AppListPage( } }, ) { bottomPadding, searchQuery -> - UserProfilePager(primaryUserOnly) { userGroup -> + UserProfilePager { userGroup -> val appListInput = AppListInput( config = AppListConfig( userIds = userGroup.userInfos.map { it.id }, diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/common/UserProfilePager.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/common/UserProfilePager.kt index b5a4929c47f4..6a76c93ac9a9 100644 --- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/common/UserProfilePager.kt +++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/common/UserProfilePager.kt @@ -38,14 +38,9 @@ data class UserGroup( ) @Composable -fun UserProfilePager( - primaryUserOnly: Boolean = false, - content: @Composable (userGroup: UserGroup) -> Unit, -) { +fun UserProfilePager(content: @Composable (userGroup: UserGroup) -> Unit) { val context = LocalContext.current - val userGroups = remember { - context.userManager.getUserGroups(primaryUserOnly) - } + val userGroups = remember { context.userManager.getUserGroups() } val titles = remember { val enterpriseRepository = EnterpriseRepository(context) userGroups.map { userGroup -> @@ -60,10 +55,9 @@ fun UserProfilePager( } } -private fun UserManager.getUserGroups(primaryUserOnly: Boolean): List<UserGroup> { +private fun UserManager.getUserGroups(): List<UserGroup> { val userGroupList = mutableListOf<UserGroup>() val profileToShowInSettingsList = getProfiles(UserHandle.myUserId()) - .filter { userInfo -> !primaryUserOnly || userInfo.isPrimary } .map { userInfo -> userInfo to getUserProperties(userInfo.userHandle).showInSettings } profileToShowInSettingsList.filter { it.second == UserProperties.SHOW_IN_SETTINGS_WITH_PARENT } diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/AppListRepositoryTest.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/AppListRepositoryTest.kt index 302f78081626..375ed60e17cf 100644 --- a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/AppListRepositoryTest.kt +++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/AppListRepositoryTest.kt @@ -108,7 +108,7 @@ class AppListRepositoryTest { val appList = repository.loadApps( userId = ADMIN_USER_ID, - showInstantApps = false, + loadInstantApps = false, ) assertThat(appList).containsExactly(NORMAL_APP) @@ -120,7 +120,7 @@ class AppListRepositoryTest { val appList = repository.loadApps( userId = ADMIN_USER_ID, - showInstantApps = true, + loadInstantApps = true, ) assertThat(appList).containsExactly(NORMAL_APP, INSTANT_APP) @@ -325,6 +325,21 @@ class AppListRepositoryTest { assertThat(systemPackageNames).containsExactly(SYSTEM_APP.packageName) } + @Test + fun loadAndFilterApps_loadNonSystemApp_returnExpectedValues() = runTest { + mockInstalledApplications( + apps = listOf( + NORMAL_APP, INSTANT_APP, SYSTEM_APP, UPDATED_SYSTEM_APP, HOME_APP, IN_LAUNCHER_APP + ), + userId = ADMIN_USER_ID, + ) + + val appList = repository.loadAndFilterApps(userId = ADMIN_USER_ID, isSystemApp = false) + + assertThat(appList) + .containsExactly(NORMAL_APP, UPDATED_SYSTEM_APP, HOME_APP, IN_LAUNCHER_APP) + } + private suspend fun getShowSystemPredicate(showSystem: Boolean) = repository.showSystemPredicate( userIdFlow = flowOf(ADMIN_USER_ID), diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/AppListViewModelTest.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/AppListViewModelTest.kt index 6889e5d21ac0..9b224976e080 100644 --- a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/AppListViewModelTest.kt +++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/AppListViewModelTest.kt @@ -87,7 +87,7 @@ class AppListViewModelTest { private object FakeAppListRepository : AppListRepository { override suspend fun loadApps( userId: Int, - showInstantApps: Boolean, + loadInstantApps: Boolean, matchAnyUserForAdmin: Boolean, ) = listOf(APP) @@ -97,6 +97,9 @@ class AppListViewModelTest { ): Flow<(app: ApplicationInfo) -> Boolean> = flowOf { true } override fun getSystemPackageNamesBlocking(userId: Int): Set<String> = emptySet() + + override suspend fun loadAndFilterApps(userId: Int, isSystemApp: Boolean) = + emptyList<ApplicationInfo>() } private object FakeAppRepository : AppRepository { diff --git a/packages/SettingsLib/res/drawable/dialog_btn_filled.xml b/packages/SettingsLib/res/drawable/dialog_btn_filled.xml index 14cb1de9fa2d..0614f9dbff8c 100644 --- a/packages/SettingsLib/res/drawable/dialog_btn_filled.xml +++ b/packages/SettingsLib/res/drawable/dialog_btn_filled.xml @@ -22,12 +22,12 @@ <item android:id="@android:id/mask"> <shape android:shape="rectangle"> <solid android:color="@android:color/white"/> - <corners android:radius="?android:attr/buttonCornerRadius"/> + <corners android:radius="@dimen/button_corner_radius"/> </shape> </item> <item> <shape android:shape="rectangle"> - <corners android:radius="?android:attr/buttonCornerRadius"/> + <corners android:radius="@dimen/button_corner_radius"/> <solid android:color="?androidprv:attr/colorAccentPrimary"/> <padding android:left="@dimen/dialog_button_horizontal_padding" android:top="@dimen/dialog_button_vertical_padding" diff --git a/packages/SettingsLib/res/drawable/dialog_btn_outline.xml b/packages/SettingsLib/res/drawable/dialog_btn_outline.xml index 1e7775980243..a920b50585ae 100644 --- a/packages/SettingsLib/res/drawable/dialog_btn_outline.xml +++ b/packages/SettingsLib/res/drawable/dialog_btn_outline.xml @@ -22,16 +22,15 @@ <item android:id="@android:id/mask"> <shape android:shape="rectangle"> <solid android:color="@android:color/white"/> - <corners android:radius="?android:attr/buttonCornerRadius"/> + <corners android:radius="@dimen/button_corner_radius"/> </shape> </item> <item> <shape android:shape="rectangle"> - <corners android:radius="?android:attr/buttonCornerRadius"/> + <corners android:radius="@dimen/button_corner_radius"/> <solid android:color="@android:color/transparent"/> <stroke android:color="?androidprv:attr/colorAccentPrimaryVariant" - android:width="1dp" - /> + android:width="1dp"/> <padding android:left="@dimen/dialog_button_horizontal_padding" android:top="@dimen/dialog_button_vertical_padding" android:right="@dimen/dialog_button_horizontal_padding" diff --git a/packages/SettingsLib/res/layout/dialog_with_icon.xml b/packages/SettingsLib/res/layout/dialog_with_icon.xml index 54f8096b87bf..2f30508ca504 100644 --- a/packages/SettingsLib/res/layout/dialog_with_icon.xml +++ b/packages/SettingsLib/res/layout/dialog_with_icon.xml @@ -41,7 +41,6 @@ android:id="@+id/dialog_with_icon_message" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:padding="10dp" android:gravity="center" style="@style/TextAppearanceSmall"/> @@ -64,7 +63,6 @@ android:id="@+id/button_cancel" style="@style/DialogButtonNegative" android:layout_width="wrap_content" - android:buttonCornerRadius="28dp" android:layout_height="wrap_content" android:visibility="gone"/> @@ -79,7 +77,6 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" style="@style/DialogButtonNegative" - android:buttonCornerRadius="40dp" android:visibility="gone"/> <Space @@ -93,8 +90,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" style="@style/DialogButtonPositive" - android:visibility="gone" - /> + android:visibility="gone"/> </LinearLayout> </LinearLayout> </ScrollView> diff --git a/packages/SettingsLib/res/layout/edit_user_info_dialog_content.xml b/packages/SettingsLib/res/layout/edit_user_info_dialog_content.xml index 4ffaf1b0c3e4..2ded3c6e82eb 100644 --- a/packages/SettingsLib/res/layout/edit_user_info_dialog_content.xml +++ b/packages/SettingsLib/res/layout/edit_user_info_dialog_content.xml @@ -14,62 +14,48 @@ limitations under the License. --> -<ScrollView +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/user_info_editor" android:layout_width="match_parent" android:layout_height="match_parent" - android:id="@+id/user_info_scroll" - android:padding="16dp"> - - <LinearLayout - android:layout_width="match_parent" + android:baselineAligned="false" + android:orientation="vertical"> + <FrameLayout + android:layout_width="wrap_content" android:layout_height="wrap_content" - android:baselineAligned="false" - android:orientation="vertical"> - <TextView - android:id="@+id/user_info_title" - android:gravity="center" - android:layout_width="match_parent" - android:layout_height="wrap_content" - style="@style/EditUserDialogTitle" - android:text="@string/user_info_settings_title" - android:textDirection="locale"/> - <FrameLayout - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_gravity="center"> - <ImageView - android:id="@+id/user_photo" - android:layout_width="@dimen/user_photo_size_in_user_info_dialog" - android:layout_height="@dimen/user_photo_size_in_user_info_dialog" - android:contentDescription="@string/user_image_photo_selector" - android:scaleType="fitCenter"/> - <ImageView - android:id="@+id/add_a_photo_icon" - android:layout_width="@dimen/add_a_photo_icon_size_in_user_info_dialog" - android:layout_height="@dimen/add_a_photo_icon_size_in_user_info_dialog" - android:src="@drawable/add_a_photo_circled" - android:layout_gravity="bottom|right"/> - </FrameLayout> - - <EditText - android:id="@+id/user_name" - android:layout_width="match_parent" - android:layout_height="@dimen/user_name_height_in_user_info_dialog" - android:layout_gravity="center" - android:minWidth="200dp" - android:layout_marginStart="6dp" - android:minHeight="@dimen/min_tap_target_size" - android:ellipsize="end" - android:singleLine="true" - android:textAppearance="?android:attr/textAppearanceMedium" - android:textAlignment="viewStart" - android:inputType="text|textCapWords" - android:selectAllOnFocus="true" - android:hint="@string/user_nickname" - android:maxLength="100"/> - - </LinearLayout> + android:layout_gravity="center"> + <ImageView + android:id="@+id/user_photo" + android:layout_width="@dimen/user_photo_size_in_user_info_dialog" + android:layout_height="@dimen/user_photo_size_in_user_info_dialog" + android:contentDescription="@string/user_image_photo_selector" + android:scaleType="fitCenter"/> + <ImageView + android:id="@+id/add_a_photo_icon" + android:layout_width="@dimen/add_a_photo_icon_size_in_user_info_dialog" + android:layout_height="@dimen/add_a_photo_icon_size_in_user_info_dialog" + android:src="@drawable/add_a_photo_circled" + android:layout_gravity="bottom|right"/> + </FrameLayout> + + <EditText + android:id="@+id/user_name" + android:layout_width="match_parent" + android:layout_height="@dimen/user_name_height_in_user_info_dialog" + android:layout_gravity="center" + android:minWidth="200dp" + android:layout_marginStart="6dp" + android:minHeight="@dimen/min_tap_target_size" + android:ellipsize="end" + android:singleLine="true" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textAlignment="viewStart" + android:inputType="text|textCapWords" + android:selectAllOnFocus="true" + android:hint="@string/user_nickname" + android:maxLength="100"/> + +</LinearLayout> -</ScrollView> diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml index 98ad6e33ce3d..a0056938e73c 100644 --- a/packages/SettingsLib/res/values-af/strings.xml +++ b/packages/SettingsLib/res/values-af/strings.xml @@ -114,9 +114,9 @@ <string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM-toegang"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD-oudio: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD oudio"</string> - <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Gehoortoestelle"</string> + <string name="bluetooth_profile_hearing_aid" msgid="2607867572569689732">"Gehoortoestelle"</string> <string name="bluetooth_profile_le_audio" msgid="1725521360076451751">"LE-oudio"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"Gekoppel aan gehoortoestelle"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="5757754050938807276">"Gekoppel aan gehoortoestelle"</string> <string name="bluetooth_le_audio_profile_summary_connected" msgid="6916226974453480650">"Gekoppel aan LE-oudio"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"Gekoppel aan media-oudio"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"Gekoppel aan foonoudio"</string> @@ -134,7 +134,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="808970643123744170">"Gebruik vir foonoudio"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Gebruik vir lêeroordrag"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Gebruik vir invoer"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Gebruik vir gehoortoestelle"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="3374057355721486932">"Gebruik vir gehoortoestelle"</string> <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"Gebruik vir LE_AUDIO"</string> <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Bind saam"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"BIND SAAM"</string> @@ -355,6 +355,8 @@ <string name="pointer_location_summary" msgid="957120116989798464">"Skermlaag wys huidige raakdata"</string> <string name="show_touches" msgid="8437666942161289025">"Wys tikke"</string> <string name="show_touches_summary" msgid="3692861665994502193">"Wys visuele terugvoer vir tikke"</string> + <string name="show_key_presses" msgid="6360141722735900214">"Wys sleuteldrukke"</string> + <string name="show_key_presses_summary" msgid="725387457373015024">"Wys visuele terugvoer vir fisieke sleuteldrukke"</string> <string name="show_screen_updates" msgid="2078782895825535494">"Wys oppervlakopdaterings"</string> <string name="show_screen_updates_summary" msgid="2126932969682087406">"Flits totale vensteroppervlakke wanneer dit opdateer"</string> <string name="show_hw_screen_updates" msgid="2021286231267747506">"Wys aansigopdaterings"</string> diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml index b8b1b69b8054..28ca2bb1b852 100644 --- a/packages/SettingsLib/res/values-am/strings.xml +++ b/packages/SettingsLib/res/values-am/strings.xml @@ -114,9 +114,9 @@ <string name="bluetooth_profile_sap" msgid="8304170950447934386">"የሲም መዳረሻ"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"ኤችዲ ኦዲዮ፦ <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"ኤችዲ ኦዲዮ"</string> - <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"አጋዥ መስሚያዎች"</string> + <string name="bluetooth_profile_hearing_aid" msgid="2607867572569689732">"መስሚያ አጋዥ መሣሪያዎች"</string> <string name="bluetooth_profile_le_audio" msgid="1725521360076451751">"LE ኦዲዮ"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"ከአጋዥ መስሚያዎች ጋር ተገናኝቷል"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="5757754050938807276">"ከመስሚያ አጋዥ መሣሪያዎች ጋር ተገናኝቷል"</string> <string name="bluetooth_le_audio_profile_summary_connected" msgid="6916226974453480650">"ከLE ኦዲዮ ጋር ተገናኝቷል"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"ወደ ማህደረ መረጃ አውዲዮ ተያይዟል"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"ወደ ስልክ አውዲዮ ተያይዟል"</string> @@ -134,7 +134,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="808970643123744170">"ለስልክ ድምፅ ተጠቀም"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"ለፋይል ዝውውር ተጠቀም"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"ለውፅአት ተጠቀም"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"ለአጋዥ መስሚያዎች ይጠቀሙ"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="3374057355721486932">"ለመስሚያ አጋዥ መሣሪያዎች ይጠቀሙ"</string> <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"ለLE_AUDIO ይጠቀሙ"</string> <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"አጣምር"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"አጣምር"</string> @@ -355,6 +355,8 @@ <string name="pointer_location_summary" msgid="957120116989798464">"የማያ ተደራቢ የአሁኑን የCPU አጠቃቀም እያሳየ ነው።"</string> <string name="show_touches" msgid="8437666942161289025">"ነካ ማድረጎችን አሳይ"</string> <string name="show_touches_summary" msgid="3692861665994502193">"ለነካ ማድረጎች ምስላዊ ግብረመልስን አሳይ"</string> + <string name="show_key_presses" msgid="6360141722735900214">"የቁልፍ ጭነቶችን አሳይ"</string> + <string name="show_key_presses_summary" msgid="725387457373015024">"ለአካላዊ የቁልፍ ጭነቶች የሚታይ ግብረመልስን አሳይ"</string> <string name="show_screen_updates" msgid="2078782895825535494">"የወለል ዝማኔዎችን አሳይ"</string> <string name="show_screen_updates_summary" msgid="2126932969682087406">"የመስኮት ወለሎች ሲዘምኑ መላ መስኮቱን አብለጭልጭ"</string> <string name="show_hw_screen_updates" msgid="2021286231267747506">"የእይታ ዝማኔዎችን አሳይ"</string> diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml index 18dc07f92b52..aff5c81ea860 100644 --- a/packages/SettingsLib/res/values-ar/strings.xml +++ b/packages/SettingsLib/res/values-ar/strings.xml @@ -114,9 +114,9 @@ <string name="bluetooth_profile_sap" msgid="8304170950447934386">"الوصول إلى شريحة SIM"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"صوت عالي الدقة: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"صوت عالي الدقة"</string> - <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"سماعات الأذن الطبية"</string> + <string name="bluetooth_profile_hearing_aid" msgid="2607867572569689732">"سماعات الأذن الطبية"</string> <string name="bluetooth_profile_le_audio" msgid="1725521360076451751">"LE Audio"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"تمّ التوصيل بسماعات الأذن الطبية"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="5757754050938807276">"تم توصيل سماعات الأذن الطبية"</string> <string name="bluetooth_le_audio_profile_summary_connected" msgid="6916226974453480650">"متصل بـ LE Audio"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"متصل بالإعدادات الصوتية للوسائط"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"متصل بالإعدادات الصوتية للهاتف"</string> @@ -134,7 +134,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="808970643123744170">"الاستخدام لإعدادات الهاتف الصوتية"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"استخدامه لنقل الملفات"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"استخدام للإدخال"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"استخدام سماعات الأذن الطبية"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="3374057355721486932">"استخدام سماعات الأذن الطبية"</string> <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"الاستخدام لـ LE_AUDIO"</string> <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"إقران"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"إقران"</string> @@ -355,6 +355,8 @@ <string name="pointer_location_summary" msgid="957120116989798464">"عرض بيانات اللمس الحالية فوق المحتوى على الشاشة"</string> <string name="show_touches" msgid="8437666942161289025">"عرض النقرات"</string> <string name="show_touches_summary" msgid="3692861665994502193">"عرض التعليقات المرئية للنقرات"</string> + <string name="show_key_presses" msgid="6360141722735900214">"عرض الضغطات على المفاتيح"</string> + <string name="show_key_presses_summary" msgid="725387457373015024">"عرض الملاحظات المرئية للضغطات الفعلية على المفاتيح"</string> <string name="show_screen_updates" msgid="2078782895825535494">"عرض تحديثات السطح"</string> <string name="show_screen_updates_summary" msgid="2126932969682087406">"وميض أسطح النوافذ بالكامل عندما يتم تحديثها"</string> <string name="show_hw_screen_updates" msgid="2021286231267747506">"إظهار تحديثات العرض"</string> diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml index 725a0572e70d..c1128aed2fc6 100644 --- a/packages/SettingsLib/res/values-as/strings.xml +++ b/packages/SettingsLib/res/values-as/strings.xml @@ -114,9 +114,9 @@ <string name="bluetooth_profile_sap" msgid="8304170950447934386">"ছিমৰ এক্সেছ"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"এইচ্ছডি অডি\'অ: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"এইচ্ছডি অডিঅ’"</string> - <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"শ্ৰৱণ যন্ত্ৰ"</string> + <string name="bluetooth_profile_hearing_aid" msgid="2607867572569689732">"শ্ৰৱণ যন্ত্ৰ"</string> <string name="bluetooth_profile_le_audio" msgid="1725521360076451751">"LE অডিঅ’"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"শ্ৰৱণ যন্ত্ৰলৈ সংযোগ কৰা হৈছে"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="5757754050938807276">"শ্ৰৱণ যন্ত্ৰৰ সৈতে সংযোগ কৰা হৈছে"</string> <string name="bluetooth_le_audio_profile_summary_connected" msgid="6916226974453480650">"LE অডিঅ’ৰ সৈতে সংযোগ কৰক"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"মিডিয়া অডিঅ’লৈ সংযোগ হৈছে"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"ফ’ন অডিঅ\'ৰ লগত সংযোগ কৰা হ’ল"</string> @@ -134,7 +134,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="808970643123744170">"ফ\'ন অডিঅ\'ৰ বাবে ব্যৱহাৰ কৰক"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"ফাইল স্থানান্তৰ কৰিবলৈ ব্যৱহাৰ কৰক"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"ইনপুটৰ বাবে ব্যৱহাৰ কৰক"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"শ্ৰৱণ যন্ত্ৰৰ বাবে ব্যৱহাৰ কৰক"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="3374057355721486932">"শ্ৰৱণ যন্ত্ৰৰ বাবে ব্যৱহাৰ কৰক"</string> <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"LE_AUDIOৰ বাবে ব্যৱহাৰ কৰক"</string> <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"পেয়াৰ কৰক"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"পেয়াৰ কৰক"</string> @@ -355,6 +355,8 @@ <string name="pointer_location_summary" msgid="957120116989798464">"চলিত স্পৰ্শ-বিষয়ক তথ্যসহ স্ক্ৰীন অভাৰলে’"</string> <string name="show_touches" msgid="8437666942161289025">"টেপসমূহ দেখুৱাওক"</string> <string name="show_touches_summary" msgid="3692861665994502193">"টিপিলে দৃশ্যায়িত ফীডবেক দিয়ক"</string> + <string name="show_key_presses" msgid="6360141722735900214">"কী টিপা দেখুৱাওক"</string> + <string name="show_key_presses_summary" msgid="725387457373015024">"ভৌতিক কী টিপাৰ ভিজুৱেল প্ৰতিক্ৰিয়া দেখুৱাওক"</string> <string name="show_screen_updates" msgid="2078782895825535494">"পৃষ্ঠভাগৰ আপডে’ট দেখুৱাওক"</string> <string name="show_screen_updates_summary" msgid="2126932969682087406">"আপডে’ট হওতে গোটেই ৱিণ্ড পৃষ্ঠসমূহ ফ্লাশ্ব কৰক"</string> <string name="show_hw_screen_updates" msgid="2021286231267747506">"ভিউৰ আপডে’ট দেখুৱাওক"</string> diff --git a/packages/SettingsLib/res/values-az/strings.xml b/packages/SettingsLib/res/values-az/strings.xml index ec1b8cfe406c..57b4694e93f7 100644 --- a/packages/SettingsLib/res/values-az/strings.xml +++ b/packages/SettingsLib/res/values-az/strings.xml @@ -114,9 +114,9 @@ <string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM-karta giriş"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD audio: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD audio"</string> - <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Eşitmə cihazları"</string> + <string name="bluetooth_profile_hearing_aid" msgid="2607867572569689732">"Eşitmə aparatları"</string> <string name="bluetooth_profile_le_audio" msgid="1725521360076451751">"LE Audio"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"Eşitmə Aparatlarına qoşuldu"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="5757754050938807276">"Eşitmə aparatlarına qoşuldu"</string> <string name="bluetooth_le_audio_profile_summary_connected" msgid="6916226974453480650">"LE audiosuna qoşulub"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"Media audioya birləşdirilib"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"Telefon audiosuna qoşulu"</string> @@ -134,7 +134,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="808970643123744170">"Telefon audiosu istifadə edin"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Fayl transferi üçün istifadə edin"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Daxiletmə üçün istifadə edin"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Eşitmə Aparatları üçün istifadə edin"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="3374057355721486932">"Eşitmə aparatları üçün istifadə edin"</string> <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"LE_AUDIO üçün istifadə edin"</string> <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Qoşulsun"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"QOŞULSUN"</string> @@ -355,6 +355,8 @@ <string name="pointer_location_summary" msgid="957120116989798464">"Toxunuş və jest datası göstərilsin"</string> <string name="show_touches" msgid="8437666942161289025">"Vizual reaksiya"</string> <string name="show_touches_summary" msgid="3692861665994502193">"Toxunuşa vizual reaksiya verilsin"</string> + <string name="show_key_presses" msgid="6360141722735900214">"Düyməyə basma göstərilsin"</string> + <string name="show_key_presses_summary" msgid="725387457373015024">"Fiziki düymə basılmaları üçün vizual rəy göstərin"</string> <string name="show_screen_updates" msgid="2078782895825535494">"Səth yenilənməsi göstərilsin"</string> <string name="show_screen_updates_summary" msgid="2126932969682087406">"Pəncərə səthi təzələnəndə işıqlansın"</string> <string name="show_hw_screen_updates" msgid="2021286231267747506">"Baxış yenilənməsi göstərilsin"</string> diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml index 91ca8a66a9e1..e9e0e90ed9dc 100644 --- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml +++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml @@ -114,9 +114,9 @@ <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Pristup SIM kartici"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD zvuk: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD zvuk"</string> - <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Slušni aparati"</string> + <string name="bluetooth_profile_hearing_aid" msgid="2607867572569689732">"Slušni aparati"</string> <string name="bluetooth_profile_le_audio" msgid="1725521360076451751">"LE Audio"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"Povezano sa slušnim aparatima"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="5757754050938807276">"Povezano sa slušnim aparatima"</string> <string name="bluetooth_le_audio_profile_summary_connected" msgid="6916226974453480650">"Povezano sa LE audio"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"Povezano sa zvukom medija"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"Povezano sa zvukom telefona"</string> @@ -134,7 +134,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="808970643123744170">"Korišćenje za audio telefona"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Korišćenje za prenos datoteka"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Koristi za ulaz"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Koristi za slušne aparate"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="3374057355721486932">"Koristi za slušne aparate"</string> <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"Koristite za LE_AUDIO"</string> <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Upari"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"UPARI"</string> @@ -355,6 +355,8 @@ <string name="pointer_location_summary" msgid="957120116989798464">"Preklopni element sa trenutnim podacima o dodiru"</string> <string name="show_touches" msgid="8437666942161289025">"Prikazuj dodire"</string> <string name="show_touches_summary" msgid="3692861665994502193">"Prikazuje vizuelne povratne informacije za dodire"</string> + <string name="show_key_presses" msgid="6360141722735900214">"Prikazuj pritiske tastera"</string> + <string name="show_key_presses_summary" msgid="725387457373015024">"Prikazuje povratne informacije za pritiske tastera"</string> <string name="show_screen_updates" msgid="2078782895825535494">"Prikaži ažuriranja površine"</string> <string name="show_screen_updates_summary" msgid="2126932969682087406">"Osvetljava sve površine prozora kada se ažuriraju"</string> <string name="show_hw_screen_updates" msgid="2021286231267747506">"Prikaži ažuriranja prikaza"</string> diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml index 71aac42b755a..0f6112cd1248 100644 --- a/packages/SettingsLib/res/values-be/strings.xml +++ b/packages/SettingsLib/res/values-be/strings.xml @@ -114,9 +114,9 @@ <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Доступ да SIM-карты"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"Аўдыя ў HD: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"Аўдыя ў HD"</string> - <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Слыхавыя апараты"</string> + <string name="bluetooth_profile_hearing_aid" msgid="2607867572569689732">"Слыхавыя апараты"</string> <string name="bluetooth_profile_le_audio" msgid="1725521360076451751">"LE Audio"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"Падключана да слыхавых апаратаў"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="5757754050938807276">"Слыхавыя апараты падключаны"</string> <string name="bluetooth_le_audio_profile_summary_connected" msgid="6916226974453480650">"Падключана да LE audio"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"Падключана да аўдыё медыа"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"Падключана да аўдыё тэлефона"</string> @@ -134,7 +134,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="808970643123744170">"Выкарыстоўваць для аўдыё тэлефона"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Выкарыстоўваць для перадачы файлаў"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Выкарыстоўваць для ўводу"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Выкарыстоўваць для слыхавых апаратаў"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="3374057355721486932">"Выкарыстоўваць для слыхавых апаратаў"</string> <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"Выкарыстоўваць для LE_AUDIO"</string> <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Спалучыць"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"СПАЛУЧЫЦЬ"</string> @@ -355,6 +355,8 @@ <string name="pointer_location_summary" msgid="957120116989798464">"Паказваць на экране націсканні і жэсты"</string> <string name="show_touches" msgid="8437666942161289025">"Паказваць дакрананні"</string> <string name="show_touches_summary" msgid="3692861665994502193">"Паказваць візуалізацыю дакрананняў"</string> + <string name="show_key_presses" msgid="6360141722735900214">"Паказваць націсканні"</string> + <string name="show_key_presses_summary" msgid="725387457373015024">"Паказваць візуальны водгук пры націсканні клавіш"</string> <string name="show_screen_updates" msgid="2078782895825535494">"Абнаўленне паверхні"</string> <string name="show_screen_updates_summary" msgid="2126932969682087406">"Падсвяціць паверхню акна пры абнаўленні"</string> <string name="show_hw_screen_updates" msgid="2021286231267747506">"Паказаць абнаўленні"</string> diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml index 2779689c41ad..abd90d093463 100644 --- a/packages/SettingsLib/res/values-bg/strings.xml +++ b/packages/SettingsLib/res/values-bg/strings.xml @@ -114,9 +114,9 @@ <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Достъп до SIM картата"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"Висококачествено аудио: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"Висококачествено аудио"</string> - <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Слухови апарати"</string> + <string name="bluetooth_profile_hearing_aid" msgid="2607867572569689732">"Слухови апарати"</string> <string name="bluetooth_profile_le_audio" msgid="1725521360076451751">"LE Audio"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"Установена е връзка със слухов апарат"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="5757754050938807276">"Установена е връзка със слухов апарат"</string> <string name="bluetooth_le_audio_profile_summary_connected" msgid="6916226974453480650">"Свързано с LE Audio"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"Установена е връзка с медийно аудио"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"Връзка със звука на телефона"</string> @@ -134,7 +134,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="808970643123744170">"Използване на телефон за аудио"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Използване на за пренос на файлове"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Да се използва за въвеждане"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Използване за слухови апарати"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="3374057355721486932">"Използване за слухови апарати"</string> <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"Използване за LE_AUDIO"</string> <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Сдвояване"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"СДВОЯВАНЕ"</string> @@ -355,6 +355,8 @@ <string name="pointer_location_summary" msgid="957120116989798464">"Насл. на екран показва текущи данни при докосване"</string> <string name="show_touches" msgid="8437666942161289025">"Показване на докосванията"</string> <string name="show_touches_summary" msgid="3692861665994502193">"Показване на визуална обр. връзка за докосванията"</string> + <string name="show_key_presses" msgid="6360141722735900214">"Видими натиск. на клавиши"</string> + <string name="show_key_presses_summary" msgid="725387457373015024">"Визуална обратна връзка при натискане на клавиши"</string> <string name="show_screen_updates" msgid="2078782895825535494">"Актуализации на повърхн."</string> <string name="show_screen_updates_summary" msgid="2126932969682087406">"Примигв. на целите повърхности на прозорците при актуализирането им"</string> <string name="show_hw_screen_updates" msgid="2021286231267747506">"Актуализации на изгледите"</string> diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml index 68283ca69234..b037b134ac2a 100644 --- a/packages/SettingsLib/res/values-bn/strings.xml +++ b/packages/SettingsLib/res/values-bn/strings.xml @@ -114,9 +114,9 @@ <string name="bluetooth_profile_sap" msgid="8304170950447934386">"সিম অ্যাক্সেস"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD অডিও: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD অডিও"</string> - <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"হিয়ারিং এড"</string> + <string name="bluetooth_profile_hearing_aid" msgid="2607867572569689732">"হিয়ারিং এড"</string> <string name="bluetooth_profile_le_audio" msgid="1725521360076451751">"LE অডিও"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"হিয়ারিং এডের সাথে কানেক্ট করা হয়েছে"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="5757754050938807276">"হিয়ারিং এডের সাথে কানেক্ট করা হয়েছে"</string> <string name="bluetooth_le_audio_profile_summary_connected" msgid="6916226974453480650">"LE অডিও কানেক্ট করা হয়েছে"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"মিডিয়া অডিওতে কানেক্ট রয়েছে"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"ফোন অডিওতে কানেক্ট"</string> @@ -134,7 +134,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="808970643123744170">"ফোন অডিওয়ের জন্য ব্যবহার করুন"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"ফাইল স্থানান্তরের জন্য ব্যবহার করুন"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"ইনপুটের জন্য ব্যবহার করুন"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"হিয়ারিং এডের জন্য ব্যবহার করুন"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="3374057355721486932">"হিয়ারিং এডের জন্য ব্যবহার করুন"</string> <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"LE_AUDIO-এর জন্য ব্যবহার করুন"</string> <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"যুক্ত করুন"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"যুক্ত করুন"</string> @@ -355,6 +355,8 @@ <string name="pointer_location_summary" msgid="957120116989798464">"স্ক্রিন ওভারলে বর্তমান স্পর্শ ডেটা দেখাচ্ছে"</string> <string name="show_touches" msgid="8437666942161289025">"আলতো চাপ দেখান"</string> <string name="show_touches_summary" msgid="3692861665994502193">"আলতো চাপ দিলে ভিজ্যুয়াল প্রতিক্রিয়া দেখান"</string> + <string name="show_key_presses" msgid="6360141722735900214">"প্রেস করা কী দেখুন"</string> + <string name="show_key_presses_summary" msgid="725387457373015024">"প্রেস করা ফিজিকাল কীয়ের জন্য ভিস্যুয়াল মতামত দেখুন"</string> <string name="show_screen_updates" msgid="2078782895825535494">"সারফেস আপডেটগুলি দেখান"</string> <string name="show_screen_updates_summary" msgid="2126932969682087406">"সম্পূর্ণ উইন্ডোর সারফেস আপডেট হয়ে গেলে সেটিকে ফ্ল্যাশ করুন"</string> <string name="show_hw_screen_updates" msgid="2021286231267747506">"ভিউয়ের আপডেট দেখুন"</string> diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml index 8e589f6e78fe..1a37a3fb706b 100644 --- a/packages/SettingsLib/res/values-bs/strings.xml +++ b/packages/SettingsLib/res/values-bs/strings.xml @@ -114,9 +114,9 @@ <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Pristup SIM-u"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD audio: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD audio"</string> - <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Slušni aparati"</string> + <string name="bluetooth_profile_hearing_aid" msgid="2607867572569689732">"Slušni aparati"</string> <string name="bluetooth_profile_le_audio" msgid="1725521360076451751">"LE Audio"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"Povezan na slušne aparate"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="5757754050938807276">"Povezano je sa slušnim aparatima"</string> <string name="bluetooth_le_audio_profile_summary_connected" msgid="6916226974453480650">"Povezano s LE zvukom"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"Povezano sa zvukom medija"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"Povezano na zvuk telefona"</string> @@ -134,7 +134,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="808970643123744170">"Koristi za zvuk telefona"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Koristi za prijenos fajlova"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Koristi kao ulaz"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Korištenje za slušne aparate"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="3374057355721486932">"Korištenje za slušne aparate"</string> <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"Koristi za: LE_AUDIO"</string> <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Upari"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"UPARI"</string> @@ -355,6 +355,8 @@ <string name="pointer_location_summary" msgid="957120116989798464">"Preklapanje ekrana s trenutnim podacima o dodiru"</string> <string name="show_touches" msgid="8437666942161289025">"Prikaži dodire"</string> <string name="show_touches_summary" msgid="3692861665994502193">"Prikaz vizuelnih povratnih informacija za dodire"</string> + <string name="show_key_presses" msgid="6360141722735900214">"Prikaži pritiskanja tipki"</string> + <string name="show_key_presses_summary" msgid="725387457373015024">"Prikaži vizuelne povr. inform. za pritiske tipki"</string> <string name="show_screen_updates" msgid="2078782895825535494">"Prikaži ažuriranja za površinu"</string> <string name="show_screen_updates_summary" msgid="2126932969682087406">"Osvjetljava sve površine prozora kada se ažuriraju"</string> <string name="show_hw_screen_updates" msgid="2021286231267747506">"Prikaži ažuriranja prikaza"</string> diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml index f22b489b839b..233b3e1096b9 100644 --- a/packages/SettingsLib/res/values-ca/strings.xml +++ b/packages/SettingsLib/res/values-ca/strings.xml @@ -114,9 +114,9 @@ <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Accés a la SIM"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"Àudio HD: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"Àudio HD"</string> - <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Audiòfons"</string> + <string name="bluetooth_profile_hearing_aid" msgid="2607867572569689732">"Audiòfons"</string> <string name="bluetooth_profile_le_audio" msgid="1725521360076451751">"LE Audio"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"S\'ha connectat als audiòfons"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="5757754050938807276">"S\'ha connectat als audiòfons"</string> <string name="bluetooth_le_audio_profile_summary_connected" msgid="6916226974453480650">"Connectat a LE Audio"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"Connectat a l\'àudio del mitjà"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"Connectat a àudio del telèfon"</string> @@ -134,7 +134,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="808970643123744170">"Utilitza-ho per a l\'àudio del telèfon"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Utilitza per a la transferència de fitxers"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Utilitza per a entrada"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Utilitza per als audiòfons"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="3374057355721486932">"Utilitza per als audiòfons"</string> <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"Utilitza per a LE_AUDIO"</string> <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Vincula"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"VINCULA"</string> @@ -355,6 +355,8 @@ <string name="pointer_location_summary" msgid="957120116989798464">"Superposa les dades dels tocs a la pantalla"</string> <string name="show_touches" msgid="8437666942161289025">"Mostra els tocs"</string> <string name="show_touches_summary" msgid="3692861665994502193">"Mostra la ubicació visual dels tocs"</string> + <string name="show_key_presses" msgid="6360141722735900214">"Mostra les tecles premudes"</string> + <string name="show_key_presses_summary" msgid="725387457373015024">"Mostra avisos visuals en prémer tecles físiques"</string> <string name="show_screen_updates" msgid="2078782895825535494">"Canvis de superfície"</string> <string name="show_screen_updates_summary" msgid="2126932969682087406">"Il·lumina superfícies de finestres en actualitzar-se"</string> <string name="show_hw_screen_updates" msgid="2021286231267747506">"Actualitzacions de visualitzacions"</string> diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml index 363c22e72aaf..41d3d528f7a7 100644 --- a/packages/SettingsLib/res/values-cs/strings.xml +++ b/packages/SettingsLib/res/values-cs/strings.xml @@ -114,9 +114,9 @@ <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Přístup k SIM kartě"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD zvuk: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD zvuk"</string> - <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Naslouchátka"</string> + <string name="bluetooth_profile_hearing_aid" msgid="2607867572569689732">"Naslouchátka"</string> <string name="bluetooth_profile_le_audio" msgid="1725521360076451751">"LE Audio"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"Připojeno k naslouchátkům"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="5757754050938807276">"Připojeno k naslouchátkům"</string> <string name="bluetooth_le_audio_profile_summary_connected" msgid="6916226974453480650">"Připojeno k LE Audio"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"Připojeno ke zvukovému médiu"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"Připojeno k náhlavní soupravě"</string> @@ -134,7 +134,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="808970643123744170">"Umožňuje připojení náhlavní soupravy"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Použít pro přenos souborů"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Použít pro vstup"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Použít pro naslouchátka"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="3374057355721486932">"Použít pro naslouchátka"</string> <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"Používat pro LE_AUDIO"</string> <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Spárovat"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"SPÁROVAT"</string> @@ -355,6 +355,8 @@ <string name="pointer_location_summary" msgid="957120116989798464">"Překryvná vrstva zobrazuje aktuální data o dotycích"</string> <string name="show_touches" msgid="8437666942161289025">"Zobrazovat klepnutí"</string> <string name="show_touches_summary" msgid="3692861665994502193">"Zobrazovat vizuální zpětnou vazbu pro klepnutí"</string> + <string name="show_key_presses" msgid="6360141722735900214">"Zobrazit stisknutí klávesy"</string> + <string name="show_key_presses_summary" msgid="725387457373015024">"Zobrazit vizuální odezvu při stisknutí fyzické klávesy"</string> <string name="show_screen_updates" msgid="2078782895825535494">"Zobrazit obnovení obsahu"</string> <string name="show_screen_updates_summary" msgid="2126932969682087406">"Rozbliká obsah okna při aktualizaci"</string> <string name="show_hw_screen_updates" msgid="2021286231267747506">"Ukazovat aktualizace zobrazení"</string> diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml index 1df4deeeddd2..30022d377a47 100644 --- a/packages/SettingsLib/res/values-da/strings.xml +++ b/packages/SettingsLib/res/values-da/strings.xml @@ -114,9 +114,9 @@ <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Adgang til SIM-kort"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD-lyd: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD-lyd"</string> - <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Høreapparater"</string> + <string name="bluetooth_profile_hearing_aid" msgid="2607867572569689732">"Høreapparater"</string> <string name="bluetooth_profile_le_audio" msgid="1725521360076451751">"LE-lyd"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"Forbundet til høreapparater"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="5757754050938807276">"Forbundet til høreapparater"</string> <string name="bluetooth_le_audio_profile_summary_connected" msgid="6916226974453480650">"Forbundet med LE-lyd"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"Forbundet til medielyd"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"Forbundet til telefonlyd"</string> @@ -134,7 +134,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="808970643123744170">"Brug til telefonlyd"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Brug til filoverførsel"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Brug til input"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Brug til høreapparater"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="3374057355721486932">"Brug til høreapparater"</string> <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"Brug med LE_AUDIO"</string> <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Par"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"ACCEPTÉR PARRING"</string> @@ -355,6 +355,8 @@ <string name="pointer_location_summary" msgid="957120116989798464">"Skærmoverlejringen viser de aktuelle berøringsdata"</string> <string name="show_touches" msgid="8437666942161289025">"Vis tryk"</string> <string name="show_touches_summary" msgid="3692861665994502193">"Vis visuel feedback ved tryk"</string> + <string name="show_key_presses" msgid="6360141722735900214">"Vis tastetryk"</string> + <string name="show_key_presses_summary" msgid="725387457373015024">"Vis visuel feedback ved fysiske tastetryk"</string> <string name="show_screen_updates" msgid="2078782895825535494">"Vis overfladeopdateringer"</string> <string name="show_screen_updates_summary" msgid="2126932969682087406">"Fremhæv hele vinduesoverflader, når de opdateres"</string> <string name="show_hw_screen_updates" msgid="2021286231267747506">"Se visningsopdateringer"</string> diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml index ac5d152d60ef..e9d885ae2818 100644 --- a/packages/SettingsLib/res/values-de/strings.xml +++ b/packages/SettingsLib/res/values-de/strings.xml @@ -114,9 +114,9 @@ <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Zugriff auf SIM"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD-Audio: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD-Audio"</string> - <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Hörgeräte"</string> + <string name="bluetooth_profile_hearing_aid" msgid="2607867572569689732">"Hörgerät"</string> <string name="bluetooth_profile_le_audio" msgid="1725521360076451751">"LE Audio"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"Mit Hörhilfen verbunden"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="5757754050938807276">"Mit Hörgerät verbunden"</string> <string name="bluetooth_le_audio_profile_summary_connected" msgid="6916226974453480650">"Mit LE Audio verbunden"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"Verbunden mit Medien-Audio"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"Verbunden mit Telefon-Audio"</string> @@ -134,7 +134,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="808970643123744170">"Für Telefon-Audio verwenden"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Für Dateiübertragung verwenden"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Für Eingabe verwenden"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Für Hörhilfen verwenden"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="3374057355721486932">"Für Hörgerät verwenden"</string> <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"Für LE_AUDIO verwenden"</string> <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Koppeln"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"KOPPELN"</string> @@ -355,6 +355,8 @@ <string name="pointer_location_summary" msgid="957120116989798464">"Overlay mit aktuellen Daten zu Tippaktionen anzeigen"</string> <string name="show_touches" msgid="8437666942161289025">"Fingertippen visualisieren"</string> <string name="show_touches_summary" msgid="3692861665994502193">"Bei Fingertippen visuelles Feedback anzeigen"</string> + <string name="show_key_presses" msgid="6360141722735900214">"Tastatureingaben anzeigen"</string> + <string name="show_key_presses_summary" msgid="725387457373015024">"Visuelles Feedback für Tastatureingaben anzeigen"</string> <string name="show_screen_updates" msgid="2078782895825535494">"Oberflächenaktualisierungen hervorheben"</string> <string name="show_screen_updates_summary" msgid="2126932969682087406">"Gesamte Fensteroberflächen blinken bei Aktualisierung"</string> <string name="show_hw_screen_updates" msgid="2021286231267747506">"Aktualisierungen von Ansichten hervorheben"</string> diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml index 8f7518012def..19413c197772 100644 --- a/packages/SettingsLib/res/values-el/strings.xml +++ b/packages/SettingsLib/res/values-el/strings.xml @@ -114,9 +114,9 @@ <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Πρόσβαση SIM"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"Ήχος HD: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"Ήχος HD"</string> - <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Βοηθήματα ακοής"</string> + <string name="bluetooth_profile_hearing_aid" msgid="2607867572569689732">"Βοηθήματα ακοής"</string> <string name="bluetooth_profile_le_audio" msgid="1725521360076451751">"LE Audio"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"Έγινε σύνδεση σε βοηθήματα ακοής"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="5757754050938807276">"Σε σύνδεση με βοηθήματα ακοής"</string> <string name="bluetooth_le_audio_profile_summary_connected" msgid="6916226974453480650">"Συνδέθηκε σε LE Audio"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"Συνδέθηκε σε ήχο πολυμέσων"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"Συνδεδεμένο στον ήχο τηλεφώνου"</string> @@ -134,7 +134,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="808970643123744170">"Χρήση για ήχο τηλεφώνου"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Χρήση για τη μεταφορά αρχείων"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Χρήση για είσοδο"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Χρήση για βοηθήματα ακοής"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="3374057355721486932">"Χρήση για βοηθήματα ακοής"</string> <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"Χρήση για LE_AUDIO"</string> <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Σύζευξη"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"ΣΥΖΕΥΞΗ"</string> @@ -355,6 +355,8 @@ <string name="pointer_location_summary" msgid="957120116989798464">"Επικάλ.οθόνης για προβολή τρεχόντων δεδ/νων αφής"</string> <string name="show_touches" msgid="8437666942161289025">"Εμφάνιση πατημάτων"</string> <string name="show_touches_summary" msgid="3692861665994502193">"Εμφάνιση οπτικών σχολίων για πατήματα"</string> + <string name="show_key_presses" msgid="6360141722735900214">"Εμφάν. πατημάτων πλήκτρων"</string> + <string name="show_key_presses_summary" msgid="725387457373015024">"Εμφ. οπτικής ανάδρασης για πατήμ. φυσικών πλήκτρων"</string> <string name="show_screen_updates" msgid="2078782895825535494">"Εμφάνιση ενημερώσεων επιφάνειας"</string> <string name="show_screen_updates_summary" msgid="2126932969682087406">"Προβολή Flash ολόκλ. των επιφ παραθ. όταν ενημερ."</string> <string name="show_hw_screen_updates" msgid="2021286231267747506">"Εμφάνιση ενημερ. προβολής"</string> diff --git a/packages/SettingsLib/res/values-en-rAU/strings.xml b/packages/SettingsLib/res/values-en-rAU/strings.xml index 3a50fa331696..58ea68162e91 100644 --- a/packages/SettingsLib/res/values-en-rAU/strings.xml +++ b/packages/SettingsLib/res/values-en-rAU/strings.xml @@ -114,9 +114,9 @@ <string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM access"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD audio: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD audio"</string> - <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Hearing Aids"</string> + <string name="bluetooth_profile_hearing_aid" msgid="2607867572569689732">"Hearing aids"</string> <string name="bluetooth_profile_le_audio" msgid="1725521360076451751">"LE Audio"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"Connected to Hearing Aids"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="5757754050938807276">"Connected to hearing aids"</string> <string name="bluetooth_le_audio_profile_summary_connected" msgid="6916226974453480650">"Connected to LE audio"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"Connected to media audio"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"Connected to phone audio"</string> @@ -134,7 +134,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="808970643123744170">"Use for phone audio"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Use for file transfer"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Use for input"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Use for Hearing Aids"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="3374057355721486932">"Use for hearing aids"</string> <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"Use for LE_AUDIO"</string> <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Pair"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"PAIR"</string> @@ -355,6 +355,8 @@ <string name="pointer_location_summary" msgid="957120116989798464">"Screen overlay showing current touch data"</string> <string name="show_touches" msgid="8437666942161289025">"Show taps"</string> <string name="show_touches_summary" msgid="3692861665994502193">"Show visual feedback for taps"</string> + <string name="show_key_presses" msgid="6360141722735900214">"Show key presses"</string> + <string name="show_key_presses_summary" msgid="725387457373015024">"Show visual feedback for physical key presses"</string> <string name="show_screen_updates" msgid="2078782895825535494">"Show surface updates"</string> <string name="show_screen_updates_summary" msgid="2126932969682087406">"Flash entire window surfaces when they update"</string> <string name="show_hw_screen_updates" msgid="2021286231267747506">"Show view updates"</string> diff --git a/packages/SettingsLib/res/values-en-rCA/strings.xml b/packages/SettingsLib/res/values-en-rCA/strings.xml index 9ee30e11a404..aab6224f6b30 100644 --- a/packages/SettingsLib/res/values-en-rCA/strings.xml +++ b/packages/SettingsLib/res/values-en-rCA/strings.xml @@ -114,9 +114,9 @@ <string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM Access"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD audio: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD audio"</string> - <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Hearing Aids"</string> + <string name="bluetooth_profile_hearing_aid" msgid="2607867572569689732">"Hearing aids"</string> <string name="bluetooth_profile_le_audio" msgid="1725521360076451751">"LE Audio"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"Connected to Hearing Aids"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="5757754050938807276">"Connected to hearing aids"</string> <string name="bluetooth_le_audio_profile_summary_connected" msgid="6916226974453480650">"Connected to LE audio"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"Connected to media audio"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"Connected to phone audio"</string> @@ -134,7 +134,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="808970643123744170">"Use for phone audio"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Use for file transfer"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Use for input"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Use for Hearing Aids"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="3374057355721486932">"Use for hearing aids"</string> <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"Use for LE_AUDIO"</string> <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Pair"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"PAIR"</string> @@ -355,6 +355,8 @@ <string name="pointer_location_summary" msgid="957120116989798464">"Screen overlay showing current touch data"</string> <string name="show_touches" msgid="8437666942161289025">"Show taps"</string> <string name="show_touches_summary" msgid="3692861665994502193">"Show visual feedback for taps"</string> + <string name="show_key_presses" msgid="6360141722735900214">"Show key presses"</string> + <string name="show_key_presses_summary" msgid="725387457373015024">"Show visual feedback for physical key presses"</string> <string name="show_screen_updates" msgid="2078782895825535494">"Show surface updates"</string> <string name="show_screen_updates_summary" msgid="2126932969682087406">"Flash entire window surfaces when they update"</string> <string name="show_hw_screen_updates" msgid="2021286231267747506">"Show view updates"</string> diff --git a/packages/SettingsLib/res/values-en-rGB/strings.xml b/packages/SettingsLib/res/values-en-rGB/strings.xml index 3a50fa331696..58ea68162e91 100644 --- a/packages/SettingsLib/res/values-en-rGB/strings.xml +++ b/packages/SettingsLib/res/values-en-rGB/strings.xml @@ -114,9 +114,9 @@ <string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM access"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD audio: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD audio"</string> - <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Hearing Aids"</string> + <string name="bluetooth_profile_hearing_aid" msgid="2607867572569689732">"Hearing aids"</string> <string name="bluetooth_profile_le_audio" msgid="1725521360076451751">"LE Audio"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"Connected to Hearing Aids"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="5757754050938807276">"Connected to hearing aids"</string> <string name="bluetooth_le_audio_profile_summary_connected" msgid="6916226974453480650">"Connected to LE audio"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"Connected to media audio"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"Connected to phone audio"</string> @@ -134,7 +134,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="808970643123744170">"Use for phone audio"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Use for file transfer"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Use for input"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Use for Hearing Aids"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="3374057355721486932">"Use for hearing aids"</string> <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"Use for LE_AUDIO"</string> <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Pair"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"PAIR"</string> @@ -355,6 +355,8 @@ <string name="pointer_location_summary" msgid="957120116989798464">"Screen overlay showing current touch data"</string> <string name="show_touches" msgid="8437666942161289025">"Show taps"</string> <string name="show_touches_summary" msgid="3692861665994502193">"Show visual feedback for taps"</string> + <string name="show_key_presses" msgid="6360141722735900214">"Show key presses"</string> + <string name="show_key_presses_summary" msgid="725387457373015024">"Show visual feedback for physical key presses"</string> <string name="show_screen_updates" msgid="2078782895825535494">"Show surface updates"</string> <string name="show_screen_updates_summary" msgid="2126932969682087406">"Flash entire window surfaces when they update"</string> <string name="show_hw_screen_updates" msgid="2021286231267747506">"Show view updates"</string> diff --git a/packages/SettingsLib/res/values-en-rIN/strings.xml b/packages/SettingsLib/res/values-en-rIN/strings.xml index 3a50fa331696..58ea68162e91 100644 --- a/packages/SettingsLib/res/values-en-rIN/strings.xml +++ b/packages/SettingsLib/res/values-en-rIN/strings.xml @@ -114,9 +114,9 @@ <string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM access"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD audio: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD audio"</string> - <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Hearing Aids"</string> + <string name="bluetooth_profile_hearing_aid" msgid="2607867572569689732">"Hearing aids"</string> <string name="bluetooth_profile_le_audio" msgid="1725521360076451751">"LE Audio"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"Connected to Hearing Aids"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="5757754050938807276">"Connected to hearing aids"</string> <string name="bluetooth_le_audio_profile_summary_connected" msgid="6916226974453480650">"Connected to LE audio"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"Connected to media audio"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"Connected to phone audio"</string> @@ -134,7 +134,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="808970643123744170">"Use for phone audio"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Use for file transfer"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Use for input"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Use for Hearing Aids"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="3374057355721486932">"Use for hearing aids"</string> <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"Use for LE_AUDIO"</string> <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Pair"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"PAIR"</string> @@ -355,6 +355,8 @@ <string name="pointer_location_summary" msgid="957120116989798464">"Screen overlay showing current touch data"</string> <string name="show_touches" msgid="8437666942161289025">"Show taps"</string> <string name="show_touches_summary" msgid="3692861665994502193">"Show visual feedback for taps"</string> + <string name="show_key_presses" msgid="6360141722735900214">"Show key presses"</string> + <string name="show_key_presses_summary" msgid="725387457373015024">"Show visual feedback for physical key presses"</string> <string name="show_screen_updates" msgid="2078782895825535494">"Show surface updates"</string> <string name="show_screen_updates_summary" msgid="2126932969682087406">"Flash entire window surfaces when they update"</string> <string name="show_hw_screen_updates" msgid="2021286231267747506">"Show view updates"</string> diff --git a/packages/SettingsLib/res/values-en-rXC/strings.xml b/packages/SettingsLib/res/values-en-rXC/strings.xml index 9ad8a393f469..d69cda5434ad 100644 --- a/packages/SettingsLib/res/values-en-rXC/strings.xml +++ b/packages/SettingsLib/res/values-en-rXC/strings.xml @@ -114,9 +114,9 @@ <string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM Access"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD audio: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD audio"</string> - <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Hearing Aids"</string> + <string name="bluetooth_profile_hearing_aid" msgid="2607867572569689732">"Hearing aids"</string> <string name="bluetooth_profile_le_audio" msgid="1725521360076451751">"LE Audio"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"Connected to Hearing Aids"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="5757754050938807276">"Connected to hearing aids"</string> <string name="bluetooth_le_audio_profile_summary_connected" msgid="6916226974453480650">"Connected to LE audio"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"Connected to media audio"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"Connected to phone audio"</string> @@ -134,7 +134,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="808970643123744170">"Use for phone audio"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Use for file transfer"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Use for input"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Use for Hearing Aids"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="3374057355721486932">"Use for hearing aids"</string> <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"Use for LE_AUDIO"</string> <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Pair"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"PAIR"</string> @@ -355,6 +355,8 @@ <string name="pointer_location_summary" msgid="957120116989798464">"Screen overlay showing current touch data"</string> <string name="show_touches" msgid="8437666942161289025">"Show taps"</string> <string name="show_touches_summary" msgid="3692861665994502193">"Show visual feedback for taps"</string> + <string name="show_key_presses" msgid="6360141722735900214">"Show key presses"</string> + <string name="show_key_presses_summary" msgid="725387457373015024">"Show visual feedback for physical key presses"</string> <string name="show_screen_updates" msgid="2078782895825535494">"Show surface updates"</string> <string name="show_screen_updates_summary" msgid="2126932969682087406">"Flash entire window surfaces when they update"</string> <string name="show_hw_screen_updates" msgid="2021286231267747506">"Show view updates"</string> diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml index 4e6cd004268f..ae24ff0ce28f 100644 --- a/packages/SettingsLib/res/values-es-rUS/strings.xml +++ b/packages/SettingsLib/res/values-es-rUS/strings.xml @@ -114,9 +114,9 @@ <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Acceso a SIM"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"Audio en HD: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"Audio en HD"</string> - <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Audífonos"</string> + <string name="bluetooth_profile_hearing_aid" msgid="2607867572569689732">"Audífonos"</string> <string name="bluetooth_profile_le_audio" msgid="1725521360076451751">"audio de bajo consumo"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"Conectado a audífonos"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="5757754050938807276">"Conectado a audífonos"</string> <string name="bluetooth_le_audio_profile_summary_connected" msgid="6916226974453480650">"Conectado a audio de bajo consumo"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"Conectado al audio multimedia"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"Conectado al audio del dispositivo"</string> @@ -134,7 +134,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="808970643123744170">"Utilizar para el audio del dispositivo"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Utilizar para la transferencia de archivos"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Utilizar para entrada"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Usar para audífonos"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="3374057355721486932">"Usar para audífonos"</string> <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"Usar para LE_AUDIO"</string> <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Vincular"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"SINCRONIZAR"</string> @@ -355,6 +355,8 @@ <string name="pointer_location_summary" msgid="957120116989798464">"Muestra los datos táctiles en la pantalla"</string> <string name="show_touches" msgid="8437666942161289025">"Mostrar presiones"</string> <string name="show_touches_summary" msgid="3692861665994502193">"Muestra la ubicación de las presiones en la pantalla"</string> + <string name="show_key_presses" msgid="6360141722735900214">"Ver pulsaciones de teclas"</string> + <string name="show_key_presses_summary" msgid="725387457373015024">"Ver coment. visual para pulsac. de teclas físicas"</string> <string name="show_screen_updates" msgid="2078782895825535494">"Ver actualiz. de superficie"</string> <string name="show_screen_updates_summary" msgid="2126932969682087406">"Destello en superficie por actualización"</string> <string name="show_hw_screen_updates" msgid="2021286231267747506">"Mostrar cambios de vista"</string> diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml index 6ffed25cb768..df256e5da742 100644 --- a/packages/SettingsLib/res/values-es/strings.xml +++ b/packages/SettingsLib/res/values-es/strings.xml @@ -114,9 +114,9 @@ <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Acceso a tarjeta SIM"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"Audio HD: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"Audio HD"</string> - <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Audífonos"</string> + <string name="bluetooth_profile_hearing_aid" msgid="2607867572569689732">"Audífonos"</string> <string name="bluetooth_profile_le_audio" msgid="1725521360076451751">"LE Audio"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"Conectado a audífonos"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="5757754050938807276">"Conectado a audífonos"</string> <string name="bluetooth_le_audio_profile_summary_connected" msgid="6916226974453480650">"Conectado a LE Audio"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"Conectado al audio del medio"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"Conectado al audio del teléfono"</string> @@ -134,7 +134,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="808970643123744170">"Utilizar para audio del teléfono"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Uso de la transferencia de archivos"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Usar para entrada"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Usar con audífonos"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="3374057355721486932">"Usar con audífonos"</string> <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"Usar en LE_AUDIO"</string> <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Emparejar"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"EMPAREJAR"</string> @@ -355,6 +355,8 @@ <string name="pointer_location_summary" msgid="957120116989798464">"Superpone los datos de las pulsaciones en la pantalla"</string> <string name="show_touches" msgid="8437666942161289025">"Mostrar toques"</string> <string name="show_touches_summary" msgid="3692861665994502193">"Muestra la ubicación de los toques en la pantalla"</string> + <string name="show_key_presses" msgid="6360141722735900214">"Ver pulsación de teclas"</string> + <string name="show_key_presses_summary" msgid="725387457373015024">"Ver respuestas visuales al pulsar teclas físicas"</string> <string name="show_screen_updates" msgid="2078782895825535494">"Mostrar cambios de superficies"</string> <string name="show_screen_updates_summary" msgid="2126932969682087406">"Hace parpadear todas las superficies de la ventana cuando se actualizan"</string> <string name="show_hw_screen_updates" msgid="2021286231267747506">"Ver actualizaciones de vista"</string> diff --git a/packages/SettingsLib/res/values-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml index 434e7d712cdc..be847b308333 100644 --- a/packages/SettingsLib/res/values-et/strings.xml +++ b/packages/SettingsLib/res/values-et/strings.xml @@ -114,9 +114,9 @@ <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Juurdepääs SIM-ile"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD-heli: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD-heli"</string> - <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Kuuldeaparaadid"</string> + <string name="bluetooth_profile_hearing_aid" msgid="2607867572569689732">"Kuuldeaparaadid"</string> <string name="bluetooth_profile_le_audio" msgid="1725521360076451751">"LE Audio"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"Kuuldeaparaatidega ühendatud"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="5757754050938807276">"Kuuldeaparaatidega ühendatud"</string> <string name="bluetooth_le_audio_profile_summary_connected" msgid="6916226974453480650">"Ühendatud üksusega LE Audio"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"Ühendatud meediumiheliga"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"Ühendatud telefoniheliga"</string> @@ -134,7 +134,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="808970643123744170">"Kasuta telefoniheli jaoks"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Kasutage failide edastamiseks"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Kasutage sisendi jaoks"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Kasuta kuulmisaparaatide puhul"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="3374057355721486932">"Kasutatakse kuuldeaparaatide puhul"</string> <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"Üksuse LE_AUDIO kasutamine"</string> <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Seo"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"SEO"</string> @@ -355,6 +355,8 @@ <string name="pointer_location_summary" msgid="957120116989798464">"Praegusi puuteandmeid kuvav ekraani ülekate"</string> <string name="show_touches" msgid="8437666942161289025">"Kuva puudutused"</string> <string name="show_touches_summary" msgid="3692861665994502193">"Kuvab puudutuste visuaalse tagasiside"</string> + <string name="show_key_presses" msgid="6360141722735900214">"Kuva klahvivajutused"</string> + <string name="show_key_presses_summary" msgid="725387457373015024">"Kuva visuaalset tagasisidet füüsiliste klahvivajutuste kohta"</string> <string name="show_screen_updates" msgid="2078782895825535494">"Näita pinna värskendusi"</string> <string name="show_screen_updates_summary" msgid="2126932969682087406">"Akna pinna värskendamiseks kirjuta kogu akna pind üle"</string> <string name="show_hw_screen_updates" msgid="2021286231267747506">"Kuva ekraanikuva värskendusi"</string> diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml index 591d3cf1e52c..93abed2a16cd 100644 --- a/packages/SettingsLib/res/values-eu/strings.xml +++ b/packages/SettingsLib/res/values-eu/strings.xml @@ -114,9 +114,9 @@ <string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIMerako sarbidea"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"Kalitate handiko audioa: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"Kalitate handiko audioa"</string> - <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Audifonoak"</string> + <string name="bluetooth_profile_hearing_aid" msgid="2607867572569689732">"Audifonoak"</string> <string name="bluetooth_profile_le_audio" msgid="1725521360076451751">"Kontsumo txikiko Bluetooth bidezko audioa"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"Audifonoetara konektatuta"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="5757754050938807276">"Audifonoetara konektatuta"</string> <string name="bluetooth_le_audio_profile_summary_connected" msgid="6916226974453480650">"LE audio-ra konektatuta"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"Euskarriaren audiora konektatuta"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"Telefonoaren audiora konektatuta"</string> @@ -134,7 +134,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="808970643123744170">"Erabili telefonoaren audiorako"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Erabili fitxategi-transferentziarako"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Erabili idazketarako"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Erabili audifonoak"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="3374057355721486932">"Erabili audifonoekin"</string> <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"Erabili LE_AUDIO-rako"</string> <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Parekatu"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"PAREKATU"</string> @@ -355,6 +355,8 @@ <string name="pointer_location_summary" msgid="957120116989798464">"Ukipen-datuak erakusteko pantaila-gainjartzea"</string> <string name="show_touches" msgid="8437666942161289025">"Erakutsi sakatutakoa"</string> <string name="show_touches_summary" msgid="3692861665994502193">"Erakutsi sakatutako elementuak"</string> + <string name="show_key_presses" msgid="6360141722735900214">"Erakutsi tekla-sakatzeak"</string> + <string name="show_key_presses_summary" msgid="725387457373015024">"Erakutsi sakatutako tekla fisikoak"</string> <string name="show_screen_updates" msgid="2078782895825535494">"Erakutsi azaleko aldaketak"</string> <string name="show_screen_updates_summary" msgid="2126932969682087406">"Distirarazi leiho osoen azalak haiek eguneratzean"</string> <string name="show_hw_screen_updates" msgid="2021286231267747506">"Erakutsi ikuspegi-aldaketak"</string> diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml index b4e9b350432f..33d262fcca74 100644 --- a/packages/SettingsLib/res/values-fa/strings.xml +++ b/packages/SettingsLib/res/values-fa/strings.xml @@ -114,9 +114,9 @@ <string name="bluetooth_profile_sap" msgid="8304170950447934386">"دسترسی سیمکارت"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"صدای HD: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"صدای HD"</string> - <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"سمعک"</string> + <string name="bluetooth_profile_hearing_aid" msgid="2607867572569689732">"سمعک"</string> <string name="bluetooth_profile_le_audio" msgid="1725521360076451751">"صدای کممصرف"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"به سمعک متصل شد"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="5757754050938807276">"متصل به سمعک"</string> <string name="bluetooth_le_audio_profile_summary_connected" msgid="6916226974453480650">"به «صدای کممصرف» وصل است"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"به رسانه صوتی متصل شد"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"به تلفن صوتی متصل شد"</string> @@ -134,7 +134,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="808970643123744170">"استفاده برای تلفن صوتی"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"استفاده برای انتقال فایل"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"استفاده برای چاپ"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"استفاده کردن برای سمعک"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="3374057355721486932">"استفاده برای سمعک"</string> <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"استفاده برای LE_AUDIO"</string> <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"مرتبطسازی"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"مرتبطسازی"</string> @@ -355,6 +355,8 @@ <string name="pointer_location_summary" msgid="957120116989798464">"همپوشانی صفحهنمایش با نمایش داده لمسی فعلی"</string> <string name="show_touches" msgid="8437666942161289025">"نمایش ضربهها"</string> <string name="show_touches_summary" msgid="3692861665994502193">"نمایش بازخورد تصویری برای ضربهها"</string> + <string name="show_key_presses" msgid="6360141722735900214">"نمایش فشار کلیدها"</string> + <string name="show_key_presses_summary" msgid="725387457373015024">"نمایش بازخورد بصری برای فشار دادن کلیدهای فیزیکی"</string> <string name="show_screen_updates" msgid="2078782895825535494">"نمایش بهروزرسانی سطح"</string> <string name="show_screen_updates_summary" msgid="2126932969682087406">"هنگام بهروزرسانی سطوح پنجره همه فلش شوند"</string> <string name="show_hw_screen_updates" msgid="2021286231267747506">"نمایش بهروزرسانیهای نما"</string> diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml index b3a7560e128c..6ac22b22bb5c 100644 --- a/packages/SettingsLib/res/values-fi/strings.xml +++ b/packages/SettingsLib/res/values-fi/strings.xml @@ -114,9 +114,9 @@ <string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM-kortin käyttö"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD-ääni: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD-ääni"</string> - <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Kuulolaitteet"</string> + <string name="bluetooth_profile_hearing_aid" msgid="2607867572569689732">"Kuulolaitteet"</string> <string name="bluetooth_profile_le_audio" msgid="1725521360076451751">"LE Audio"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"Yhdistetty kuulolaitteisiin"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="5757754050938807276">"Yhdistetty kuulolaitteisiin"</string> <string name="bluetooth_le_audio_profile_summary_connected" msgid="6916226974453480650">"LE Audio yhdistetty"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"Yhdistetty median ääneen"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"Yhdistetty puhelimen ääneen"</string> @@ -134,7 +134,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="808970643123744170">"Käytä puhelimen äänille"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Käytä tiedostojen siirtoon"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Käytä syöttöön"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Käytä kuulolaitteilla"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="3374057355721486932">"Käytä kuulolaitteilla"</string> <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"Käyttö: LE_AUDIO"</string> <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Muodosta laitepari"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"MUODOSTA LAITEPARI"</string> @@ -355,6 +355,8 @@ <string name="pointer_location_summary" msgid="957120116989798464">"Näytön peittokuva näyttää nykyiset kosketustiedot"</string> <string name="show_touches" msgid="8437666942161289025">"Näytä kosketus"</string> <string name="show_touches_summary" msgid="3692861665994502193">"Anna visuaalista palautetta kosketuksesta"</string> + <string name="show_key_presses" msgid="6360141722735900214">"Näytä näppäinpainallukset"</string> + <string name="show_key_presses_summary" msgid="725387457373015024">"Näytä visuaalista palautetta näppäinpainalluksista"</string> <string name="show_screen_updates" msgid="2078782895825535494">"Näytä pintapäivitykset"</string> <string name="show_screen_updates_summary" msgid="2126932969682087406">"Väläytä koko ikkunoiden pinnat päivitettäessä"</string> <string name="show_hw_screen_updates" msgid="2021286231267747506">"Näytä näyttöpäivitykset"</string> diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml index 8d3eae4003ab..532374dae6db 100644 --- a/packages/SettingsLib/res/values-fr-rCA/strings.xml +++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml @@ -114,9 +114,9 @@ <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Accès à la carte SIM"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"Audio HD : <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"Audio HD"</string> - <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Prothèses auditives"</string> + <string name="bluetooth_profile_hearing_aid" msgid="2607867572569689732">"Prothèses auditives"</string> <string name="bluetooth_profile_le_audio" msgid="1725521360076451751">"LE Audio"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"Connecté aux prothèses auditives"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="5757754050938807276">"Connecté aux prothèses auditives"</string> <string name="bluetooth_le_audio_profile_summary_connected" msgid="6916226974453480650">"Connecté par LE Audio"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"Connecté aux paramètres audio du média"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"Connecté à l\'audio du téléphone"</string> @@ -134,7 +134,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="808970643123744170">"Utiliser pour les paramètres audio du téléphone"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Utiliser pour le transfert de fichiers"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Utiliser comme entrée"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Utiliser avec les prothèses auditives"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="3374057355721486932">"Utiliser avec les prothèses auditives"</string> <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"Utiliser pour LE_AUDIO"</string> <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Associer"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"ASSOCIER"</string> @@ -355,6 +355,8 @@ <string name="pointer_location_summary" msgid="957120116989798464">"Superposition écran indiquant données actuelles"</string> <string name="show_touches" msgid="8437666942161289025">"Afficher éléments sélect."</string> <string name="show_touches_summary" msgid="3692861665994502193">"Afficher repère visuel pour éléments sélectionnés"</string> + <string name="show_key_presses" msgid="6360141722735900214">"Afficher press. de touches"</string> + <string name="show_key_presses_summary" msgid="725387457373015024">"Afficher retour visuel pour press. de touches phys."</string> <string name="show_screen_updates" msgid="2078782895825535494">"Afficher mises à jour surface"</string> <string name="show_screen_updates_summary" msgid="2126932969682087406">"Faire clignoter les surfaces à chaque mise à jour"</string> <string name="show_hw_screen_updates" msgid="2021286231267747506">"Afficher m. à j. affichage"</string> diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml index a4e43c7cb0ef..683cfa562d9b 100644 --- a/packages/SettingsLib/res/values-fr/strings.xml +++ b/packages/SettingsLib/res/values-fr/strings.xml @@ -114,9 +114,9 @@ <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Accès à la SIM"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"Audio HD : <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"Audio HD"</string> - <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Appareils auditifs"</string> + <string name="bluetooth_profile_hearing_aid" msgid="2607867572569689732">"Appareils auditifs"</string> <string name="bluetooth_profile_le_audio" msgid="1725521360076451751">"LE Audio"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"Connexion établie avec les appareils auditifs"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="5757754050938807276">"Connexion établie avec les appareils auditifs"</string> <string name="bluetooth_le_audio_profile_summary_connected" msgid="6916226974453480650">"Connecté à LE Audio"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"Connecté aux paramètres audio du média"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"Connecté aux paramètres audio du téléphone"</string> @@ -134,7 +134,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="808970643123744170">"Utiliser pour les paramètres audio du téléphone"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Utiliser pour le transfert de fichiers"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Utiliser comme entrée"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Utiliser pour les appareils auditifs"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="3374057355721486932">"Utiliser pour les appareils auditifs"</string> <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"Utiliser pour LE_AUDIO"</string> <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Associer"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"ASSOCIER"</string> @@ -355,6 +355,8 @@ <string name="pointer_location_summary" msgid="957120116989798464">"Superposition à l\'écran indiquant l\'emplacement actuel du curseur"</string> <string name="show_touches" msgid="8437666942161289025">"Indicateurs visuels"</string> <string name="show_touches_summary" msgid="3692861665994502193">"Afficher un indicateur visuel là où l\'utilisateur appuie sur l\'écran"</string> + <string name="show_key_presses" msgid="6360141722735900214">"Afficher appuis touche"</string> + <string name="show_key_presses_summary" msgid="725387457373015024">"Afficher retour visuel pour appuis de touches"</string> <string name="show_screen_updates" msgid="2078782895825535494">"Mises à jour de la surface"</string> <string name="show_screen_updates_summary" msgid="2126932969682087406">"Faire clignoter les endroits où des mises à jour sont effectuées"</string> <string name="show_hw_screen_updates" msgid="2021286231267747506">"Mises à jour de fenêtres"</string> diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml index dbc0ba618892..0661d7aeb794 100644 --- a/packages/SettingsLib/res/values-gl/strings.xml +++ b/packages/SettingsLib/res/values-gl/strings.xml @@ -114,9 +114,9 @@ <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Acceso á SIM"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"Audio en HD: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"Audio en HD"</string> - <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Audiófonos"</string> + <string name="bluetooth_profile_hearing_aid" msgid="2607867572569689732">"Audiófonos"</string> <string name="bluetooth_profile_le_audio" msgid="1725521360076451751">"Audio de baixo consumo"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"Conectado a audiófonos"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="5757754050938807276">"Conectado a audiófonos"</string> <string name="bluetooth_le_audio_profile_summary_connected" msgid="6916226974453480650">"Estableceuse conexión co audio de baixo consumo"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"Conectado ao audio multimedia"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"Conectado ao audio do teléfono"</string> @@ -134,7 +134,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="808970643123744170">"Utilízase para o audio do teléfono"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Utilízase para a transferencia de ficheiros"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Utilízase para a entrada"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Utilizar para audiófonos"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="3374057355721486932">"Úsase para audiófonos"</string> <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"Usa esta opción para LE_AUDIO"</string> <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Vincular"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"VINCULAR"</string> @@ -355,6 +355,8 @@ <string name="pointer_location_summary" msgid="957120116989798464">"Superpón os datos dos toques na pantalla"</string> <string name="show_touches" msgid="8437666942161289025">"Mostrar toques"</string> <string name="show_touches_summary" msgid="3692861665994502193">"Mostra a localización dos toques na pantalla"</string> + <string name="show_key_presses" msgid="6360141722735900214">"Mostrar teclas premidas"</string> + <string name="show_key_presses_summary" msgid="725387457373015024">"Mostra información das teclas físicas premidas"</string> <string name="show_screen_updates" msgid="2078782895825535494">"Mostrar cambios de superficie"</string> <string name="show_screen_updates_summary" msgid="2126932969682087406">"Ilumina as superficies de ventás ao actualizarse"</string> <string name="show_hw_screen_updates" msgid="2021286231267747506">"Mostrar actualizacións"</string> diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml index ee44c0aa4a53..b92d2f214626 100644 --- a/packages/SettingsLib/res/values-gu/strings.xml +++ b/packages/SettingsLib/res/values-gu/strings.xml @@ -114,9 +114,9 @@ <string name="bluetooth_profile_sap" msgid="8304170950447934386">"સિમ ઍક્સેસ"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD ઑડિયો: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD ઑડિયો"</string> - <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"શ્રવણ યંત્રો"</string> + <string name="bluetooth_profile_hearing_aid" msgid="2607867572569689732">"સાંભળવામાં મદદ આપતા યંત્રો"</string> <string name="bluetooth_profile_le_audio" msgid="1725521360076451751">"LE ઑડિયો"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"શ્રવણ યંત્રો સાથે કનેક્ટ કરેલું છે"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="5757754050938807276">"સાંભળવામાં મદદ આપતા યંત્રો સાથે કનેક્ટ કરેલું છે"</string> <string name="bluetooth_le_audio_profile_summary_connected" msgid="6916226974453480650">"LE ઑડિયોથી કનેક્ટેડ"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"મીડિયા ઑડિઓ સાથે કનેક્ટ કર્યુ"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"ફોન ઑડિઓ સાથે કનેક્ટ થયાં"</string> @@ -134,7 +134,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="808970643123744170">"ફોન ઑડિઓ માટે ઉપયોગ કરો"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"ફાઇલ સ્થાનાંતર માટે ઉપયોગ કરો"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"ઇનપુટ માટે ઉપયોગ કરો"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"શ્રવણ યંત્રો માટે ઉપયોગ કરો"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="3374057355721486932">"સાંભળવામાં મદદ આપતા યંત્રો માટે ઉપયોગ કરો"</string> <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"LE_AUDIO માટે ઉપયોગ કરો"</string> <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"જોડી કરો"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"જોડી કરો"</string> @@ -355,6 +355,8 @@ <string name="pointer_location_summary" msgid="957120116989798464">"વર્તમાન ટચ ડેટા દર્શાવતું સ્ક્રીન ઓવરલે"</string> <string name="show_touches" msgid="8437666942161289025">"ટૅપ બતાવો"</string> <string name="show_touches_summary" msgid="3692861665994502193">"ટૅપ માટે વિઝ્યુઅલ પ્રતિસાદ બતાવો"</string> + <string name="show_key_presses" msgid="6360141722735900214">"કી દબાવવાની ઘટના બતાવો"</string> + <string name="show_key_presses_summary" msgid="725387457373015024">"વાસ્તિવક રીતે કી દબાવવાના વિઝ્યુઅલ પ્રતિસાદ બતાવો"</string> <string name="show_screen_updates" msgid="2078782895825535494">"સપાટી અપડેટ બતાવો"</string> <string name="show_screen_updates_summary" msgid="2126932969682087406">"તે અપડેટ થાય ત્યારે સમગ્ર વિન્ડો સપાટી ફ્લેશ કરો"</string> <string name="show_hw_screen_updates" msgid="2021286231267747506">"વ્યૂના અપડેટ બતાવો"</string> diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml index b97837bf9eba..21936c8131d5 100644 --- a/packages/SettingsLib/res/values-hi/strings.xml +++ b/packages/SettingsLib/res/values-hi/strings.xml @@ -114,9 +114,9 @@ <string name="bluetooth_profile_sap" msgid="8304170950447934386">"सिम ऐक्सेस"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"एचडी ऑडियो: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"एचडी ऑडियो"</string> - <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"कान की मशीन"</string> + <string name="bluetooth_profile_hearing_aid" msgid="2607867572569689732">"कान की मशीनें"</string> <string name="bluetooth_profile_le_audio" msgid="1725521360076451751">"LE Audio"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"सुनने में मदद करने वाले डिवाइस से कनेक्ट है"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="5757754050938807276">"कान की मशीनों के साथ कनेक्ट किया गया"</string> <string name="bluetooth_le_audio_profile_summary_connected" msgid="6916226974453480650">"LE Audio से कनेक्ट किया गया"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"मीडिया ऑडियो से कनेक्ट किया गया"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"फ़ोन ऑडियो से कनेक्ट किया गया"</string> @@ -134,7 +134,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="808970643123744170">"फ़ोन ऑडियो के लिए उपयोग करें"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"फ़ाइल स्थानांतरण के लिए उपयोग करें"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"इनपुट के लिए उपयोग करें"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"सुनने में मदद करने वाले डिवाइस के लिए इस्तेमाल करें"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="3374057355721486932">"कान की मशीनों के लिए इस्तेमाल करें"</string> <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"LE_AUDIO के लिए इस्तेमाल करें"</string> <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"जोड़ें"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"जोड़ें"</string> @@ -355,6 +355,10 @@ <string name="pointer_location_summary" msgid="957120116989798464">"मौजूदा टच डेटा दिखाने वाला स्क्रीन ओवरले"</string> <string name="show_touches" msgid="8437666942161289025">"टैप दिखाएं"</string> <string name="show_touches_summary" msgid="3692861665994502193">"टैप के लिए विज़ुअल फ़ीडबैक दिखाएं"</string> + <!-- no translation found for show_key_presses (6360141722735900214) --> + <skip /> + <!-- no translation found for show_key_presses_summary (725387457373015024) --> + <skip /> <string name="show_screen_updates" msgid="2078782895825535494">"सर्फ़ेस अपडेट दिखाएं"</string> <string name="show_screen_updates_summary" msgid="2126932969682087406">"अपडेट होने पर पूरे विंडो सर्फ़ेस फ़्लैश करें"</string> <string name="show_hw_screen_updates" msgid="2021286231267747506">"जीपीयू व्यू के अपडेट दिखाएं"</string> diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml index 6acfb9545c7b..7a8507dfb22e 100644 --- a/packages/SettingsLib/res/values-hr/strings.xml +++ b/packages/SettingsLib/res/values-hr/strings.xml @@ -114,9 +114,9 @@ <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Pristup SIM-u"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD audio: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD audio"</string> - <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Slušni aparati"</string> + <string name="bluetooth_profile_hearing_aid" msgid="2607867572569689732">"Slušna pomagala"</string> <string name="bluetooth_profile_le_audio" msgid="1725521360076451751">"LE Audio"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"Povezano sa Slušnim aparatima"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="5757754050938807276">"Povezano sa slušnim pomagalima"</string> <string name="bluetooth_le_audio_profile_summary_connected" msgid="6916226974453480650">"Povezano s profilom LE_AUDIO"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"Povezano s medijskim zvukom"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"Povezano sa telefonskim zvukom"</string> @@ -134,7 +134,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="808970643123744170">"Koristi za telefonski zvuk"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Koristi za prijenos datoteke"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Upotrijebi za ulaz"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Upotrijebi za Slušne aparate"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="3374057355721486932">"Upotrijebi za slušna pomagala"</string> <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"Upotrebljavajte za LE_AUDIO"</string> <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Upari"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"UPARI"</string> @@ -355,6 +355,8 @@ <string name="pointer_location_summary" msgid="957120116989798464">"Na zaslonu se prikazuju podaci o dodirima"</string> <string name="show_touches" msgid="8437666942161289025">"Prikaži dodire"</string> <string name="show_touches_summary" msgid="3692861665994502193">"Prikazuju se vizualne povratne informacije za dodire"</string> + <string name="show_key_presses" msgid="6360141722735900214">"Prikaži pritiske na tipke"</string> + <string name="show_key_presses_summary" msgid="725387457373015024">"Prikaži vizualne povratne informacije za pritiske na tipke"</string> <string name="show_screen_updates" msgid="2078782895825535494">"Prikaži ažuriranja površine"</string> <string name="show_screen_updates_summary" msgid="2126932969682087406">"Sve površine prozora bljeskaju pri ažuriranju"</string> <string name="show_hw_screen_updates" msgid="2021286231267747506">"Prikaži ažuriranja prikaza"</string> diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml index 14ecad0339f0..a67748fa7516 100644 --- a/packages/SettingsLib/res/values-hu/strings.xml +++ b/packages/SettingsLib/res/values-hu/strings.xml @@ -114,9 +114,9 @@ <string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM-elérés"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD audio: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD audio"</string> - <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Hallókészülékek"</string> + <string name="bluetooth_profile_hearing_aid" msgid="2607867572569689732">"Hallókészülék"</string> <string name="bluetooth_profile_le_audio" msgid="1725521360076451751">"Alacsony energiaszintű hangátvitel"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"Hallókészülékhez csatlakoztatva"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="5757754050938807276">"Hallókészülékhez csatlakoztatva"</string> <string name="bluetooth_le_audio_profile_summary_connected" msgid="6916226974453480650">"Csatlakoztatva az alacsony energiaszintű hangátvitelhez"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"Csatlakoztatva az eszköz hangjához"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"Csatlakoztatva a telefon hangjához"</string> @@ -134,7 +134,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="808970643123744170">"Felhasználás a telefon hangjához"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Felhasználás fájlátvitelre"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Használat beviteli eszközként"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Hallókészülékkel való használat"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="3374057355721486932">"Hallókészülékkel való használat"</string> <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"Használat ehhez: LE_AUDIO"</string> <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Párosítás"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"PÁROSÍTÁS"</string> @@ -355,6 +355,8 @@ <string name="pointer_location_summary" msgid="957120116989798464">"A fedvény mutatja az aktuális érintési adatokat"</string> <string name="show_touches" msgid="8437666942161289025">"Koppintások megjelenítése"</string> <string name="show_touches_summary" msgid="3692861665994502193">"Koppintások vizuális visszajelzésének megjelenítése"</string> + <string name="show_key_presses" msgid="6360141722735900214">"Gomblenyomások mutatása"</string> + <string name="show_key_presses_summary" msgid="725387457373015024">"Fizikai gomblenyomások vizuális visszajelzéseinek mutatása"</string> <string name="show_screen_updates" msgid="2078782895825535494">"Felületfrissítések megj."</string> <string name="show_screen_updates_summary" msgid="2126932969682087406">"A teljes ablakfelület villogjon frissítéskor."</string> <string name="show_hw_screen_updates" msgid="2021286231267747506">"Frissítések megjelenítése"</string> diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml index 5083165e5fa7..b72b09488cfa 100644 --- a/packages/SettingsLib/res/values-hy/strings.xml +++ b/packages/SettingsLib/res/values-hy/strings.xml @@ -114,9 +114,9 @@ <string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM քարտի հասանելիություն"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD աուդիո՝ <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD աուդիո"</string> - <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Լսողական ապարատ"</string> + <string name="bluetooth_profile_hearing_aid" msgid="2607867572569689732">"Լսողական սարք"</string> <string name="bluetooth_profile_le_audio" msgid="1725521360076451751">"LE Audio"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"Լսողական ապարատը միացված է"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="5757754050938807276">"Միացված է լսողական սարքին"</string> <string name="bluetooth_le_audio_profile_summary_connected" msgid="6916226974453480650">"Միացած է LE audio-ին"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"Միացված է մեդիա աուդիոյին"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"Միացված է հեռախոսի ձայնային տվյալներին"</string> @@ -134,7 +134,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="808970643123744170">"Օգտագործել հեռախոսի աուդիոյի համար"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Օգտագործել ֆայլի փոխանցման համար"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Օգտագործել ներմուծման համար"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Օգտագործել լսողական ապարատի համար"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="3374057355721486932">"Օգտագործել լսողական սարքի համար"</string> <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"Օգտագործել LE_AUDIO-ի համար"</string> <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Զուգակցել"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"Զուգակցել"</string> @@ -355,6 +355,8 @@ <string name="pointer_location_summary" msgid="957120116989798464">"Էկրանի վերադրումը ցույց է տալիս ընթացիկ հպման տվյալները"</string> <string name="show_touches" msgid="8437666942161289025">"Ցույց տալ հպումները"</string> <string name="show_touches_summary" msgid="3692861665994502193">"Ցույց տալ հպումների տեսանելի արձագանքը"</string> + <string name="show_key_presses" msgid="6360141722735900214">"Ցույց տալ սեղմումները"</string> + <string name="show_key_presses_summary" msgid="725387457373015024">"Տեսողական արձագանք ստեղների սեղմումների համար"</string> <string name="show_screen_updates" msgid="2078782895825535494">"Ցույց տալ մակերեսի թարմացումները"</string> <string name="show_screen_updates_summary" msgid="2126932969682087406">"Թարմացվելիս ընդգծել սարքաշարի ծածկույթները կանաչ գույնով"</string> <string name="show_hw_screen_updates" msgid="2021286231267747506">"Ցուցադրել թարմացումները"</string> diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml index c5e69126b0b3..07dd53ae320b 100644 --- a/packages/SettingsLib/res/values-in/strings.xml +++ b/packages/SettingsLib/res/values-in/strings.xml @@ -114,9 +114,9 @@ <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Akses SIM"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"Audio HD: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"Audio HD"</string> - <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Alat Bantu Dengar"</string> + <string name="bluetooth_profile_hearing_aid" msgid="2607867572569689732">"Alat bantu dengar"</string> <string name="bluetooth_profile_le_audio" msgid="1725521360076451751">"LE Audio"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"Terhubung ke Alat Bantu Dengar"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="5757754050938807276">"Terhubung ke alat bantu dengar"</string> <string name="bluetooth_le_audio_profile_summary_connected" msgid="6916226974453480650">"Terhubung ke LE Audio"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"Terhubung ke media audio"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"Terhubung ke audio ponsel"</string> @@ -134,7 +134,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="808970643123744170">"Gunakan untuk audio ponsel"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Gunakan untuk transfer file"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Gunakan untuk masukan"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Gunakan untuk Alat Bantu Dengar"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="3374057355721486932">"Gunakan untuk alat bantu dengar"</string> <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"Digunakan untuk LE_AUDIO"</string> <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Sambungkan"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"SAMBUNGKAN"</string> @@ -355,6 +355,8 @@ <string name="pointer_location_summary" msgid="957120116989798464">"Overlay layar menampilkan data sentuhan saat ini"</string> <string name="show_touches" msgid="8437666942161289025">"Tampilkan ketukan"</string> <string name="show_touches_summary" msgid="3692861665994502193">"Tampilkan efek visual untuk ketukan"</string> + <string name="show_key_presses" msgid="6360141722735900214">"Tampilkan penekanan tombol"</string> + <string name="show_key_presses_summary" msgid="725387457373015024">"Tampilkan respons visual untuk penekanan tombol fisik"</string> <string name="show_screen_updates" msgid="2078782895825535494">"Tampilkan pembaruan permukaan"</string> <string name="show_screen_updates_summary" msgid="2126932969682087406">"Buat seluruh permukaan jendela berkedip saat diperbarui"</string> <string name="show_hw_screen_updates" msgid="2021286231267747506">"Tampilkan pembaruan tampilan"</string> diff --git a/packages/SettingsLib/res/values-is/strings.xml b/packages/SettingsLib/res/values-is/strings.xml index 5d1893088281..bb7bf7074321 100644 --- a/packages/SettingsLib/res/values-is/strings.xml +++ b/packages/SettingsLib/res/values-is/strings.xml @@ -114,9 +114,9 @@ <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Aðgangur að SIM-korti"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD-hljóð: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD-hljóð"</string> - <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Heyrnartæki"</string> + <string name="bluetooth_profile_hearing_aid" msgid="2607867572569689732">"Heyrnartæki"</string> <string name="bluetooth_profile_le_audio" msgid="1725521360076451751">"LE-hljóð"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"Tengt við heyrnartæki"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="5757754050938807276">"Tengt við heyrnartæki"</string> <string name="bluetooth_le_audio_profile_summary_connected" msgid="6916226974453480650">"Tengt við LE-hljóð"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"Tengt við hljóðspilun efnis"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"Tengt við hljóð símans"</string> @@ -134,7 +134,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="808970643123744170">"Nota fyrir hljóð símans"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Nota við skráaflutning"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Nota fyrir inntak"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Nota fyrir heyrnartæki"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="3374057355721486932">"Nota fyrir heyrnartæki"</string> <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"Nota fyrir LE_AUDIO"</string> <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Para"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"PARA"</string> @@ -355,6 +355,8 @@ <string name="pointer_location_summary" msgid="957120116989798464">"Skjáyfirlögn sem sýnir rauntímagögn um snertingar"</string> <string name="show_touches" msgid="8437666942161289025">"Sýna snertingar"</string> <string name="show_touches_summary" msgid="3692861665994502193">"Sýna snertingar myndrænt"</string> + <string name="show_key_presses" msgid="6360141722735900214">"Sýna „Ýtt á lykil“"</string> + <string name="show_key_presses_summary" msgid="725387457373015024">"Sýna myndsvörun fyrir „Ýtt á raunverulegan lykil“"</string> <string name="show_screen_updates" msgid="2078782895825535494">"Sýna yfirborðsuppfærslur"</string> <string name="show_screen_updates_summary" msgid="2126932969682087406">"Láta allt yfirborð glugga blikka við uppfærslu"</string> <string name="show_hw_screen_updates" msgid="2021286231267747506">"Sýna uppfærslur yfirlits"</string> diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml index 85d4985cc50e..5926a6b5c793 100644 --- a/packages/SettingsLib/res/values-it/strings.xml +++ b/packages/SettingsLib/res/values-it/strings.xml @@ -114,9 +114,9 @@ <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Accesso alla SIM"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"Audio HD: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"Audio HD"</string> - <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Apparecchi acustici"</string> + <string name="bluetooth_profile_hearing_aid" msgid="2607867572569689732">"Apparecchi acustici"</string> <string name="bluetooth_profile_le_audio" msgid="1725521360076451751">"LE audio"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"Connessione con gli apparecchi acustici stabilita"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="5757754050938807276">"Connessione con gli apparecchi acustici stabilita"</string> <string name="bluetooth_le_audio_profile_summary_connected" msgid="6916226974453480650">"Connesso a LE Audio"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"Collegato ad audio media"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"Collegato ad audio telefono"</string> @@ -134,7 +134,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="808970643123744170">"Usa per audio telefono"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Usa per trasferimento file"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Utilizza per l\'input"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Utilizza per gli apparecchi acustici"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="3374057355721486932">"Utilizza per gli apparecchi acustici"</string> <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"Usa per LE_AUDIO"</string> <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Accoppia"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"ACCOPPIA"</string> @@ -355,6 +355,8 @@ <string name="pointer_location_summary" msgid="957120116989798464">"Overlay schermo che mostra i dati touch correnti"</string> <string name="show_touches" msgid="8437666942161289025">"Mostra tocchi"</string> <string name="show_touches_summary" msgid="3692861665994502193">"Mostra feedback visivi per i tocchi"</string> + <string name="show_key_presses" msgid="6360141722735900214">"Mostra pressioni tasti"</string> + <string name="show_key_presses_summary" msgid="725387457373015024">"Mostra feedback visivo per pressioni tasti fisici"</string> <string name="show_screen_updates" msgid="2078782895825535494">"Aggiornamenti superficie"</string> <string name="show_screen_updates_summary" msgid="2126932969682087406">"Fai lampeggiare le superfici delle finestre quando si aggiornano"</string> <string name="show_hw_screen_updates" msgid="2021286231267747506">"Aggiornam. visualizzazione"</string> diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml index af56031e7901..51bda70539b3 100644 --- a/packages/SettingsLib/res/values-iw/strings.xml +++ b/packages/SettingsLib/res/values-iw/strings.xml @@ -114,9 +114,9 @@ <string name="bluetooth_profile_sap" msgid="8304170950447934386">"גישה ל-SIM"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"אודיו באיכות HD: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"אודיו באיכות HD"</string> - <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"מכשירי שמיעה"</string> + <string name="bluetooth_profile_hearing_aid" msgid="2607867572569689732">"מכשירי שמיעה"</string> <string name="bluetooth_profile_le_audio" msgid="1725521360076451751">"LE Audio"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"מחובר אל מכשירי שמיעה"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="5757754050938807276">"מחובר למכשירי השמיעה"</string> <string name="bluetooth_le_audio_profile_summary_connected" msgid="6916226974453480650">"מחובר אל LE audio"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"מחובר לאודיו של מדיה"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"מחובר לאודיו של הטלפון"</string> @@ -134,7 +134,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="808970643123744170">"השתמש עבור האודיו של הטלפון"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"לצורך העברת קבצים"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"שימוש כקלט"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"שימוש בשביל מכשירי שמיעה"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="3374057355721486932">"שימוש למכשירי שמיעה"</string> <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"לשימוש עבור LE_AUDIO"</string> <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"התאמה"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"התאמה"</string> @@ -355,6 +355,8 @@ <string name="pointer_location_summary" msgid="957120116989798464">"שכבת-על של המסך המציגה את נתוני המגע הנוכחיים"</string> <string name="show_touches" msgid="8437666942161289025">"הצגת הקשות"</string> <string name="show_touches_summary" msgid="3692861665994502193">"הצגת משוב ויזואלי להקשות"</string> + <string name="show_key_presses" msgid="6360141722735900214">"הצגת לחיצות המקשים"</string> + <string name="show_key_presses_summary" msgid="725387457373015024">"הצגת משוב חזותי עבור לחיצות פיזיות על מקשים"</string> <string name="show_screen_updates" msgid="2078782895825535494">"הצגת עדכונים על פני השטח"</string> <string name="show_screen_updates_summary" msgid="2126932969682087406">"הבהוב של כל שטחי החלון כשהם מתעדכנים"</string> <string name="show_hw_screen_updates" msgid="2021286231267747506">"הצגת עדכונים של התצוגה"</string> diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml index 0032351abfa9..d15b4effdf09 100644 --- a/packages/SettingsLib/res/values-ja/strings.xml +++ b/packages/SettingsLib/res/values-ja/strings.xml @@ -114,9 +114,9 @@ <string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIMアクセス"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD オーディオ: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD オーディオ"</string> - <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"補聴器"</string> + <string name="bluetooth_profile_hearing_aid" msgid="2607867572569689732">"補聴器"</string> <string name="bluetooth_profile_le_audio" msgid="1725521360076451751">"LE Audio"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"補聴器に接続"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="5757754050938807276">"補聴器に接続"</string> <string name="bluetooth_le_audio_profile_summary_connected" msgid="6916226974453480650">"LE Audio に接続"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"メディアの音声に接続"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"携帯電話の音声に接続"</string> @@ -134,7 +134,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="808970643123744170">"携帯電話の音声に使用"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"ファイル転送に使用"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"入力に使用"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"補聴器に使用"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="3374057355721486932">"補聴器に使用"</string> <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"LE_AUDIO の使用"</string> <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"ペア設定する"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"ペア設定する"</string> @@ -355,6 +355,8 @@ <string name="pointer_location_summary" msgid="957120116989798464">"現在のタップデータをオーバーレイ表示する"</string> <string name="show_touches" msgid="8437666942161289025">"タップを表示"</string> <string name="show_touches_summary" msgid="3692861665994502193">"タップを視覚表示する"</string> + <string name="show_key_presses" msgid="6360141722735900214">"キーの押下を表示"</string> + <string name="show_key_presses_summary" msgid="725387457373015024">"物理キーの押下に関する視覚的フィードバックを表示"</string> <string name="show_screen_updates" msgid="2078782895825535494">"表示面の更新を通知"</string> <string name="show_screen_updates_summary" msgid="2126932969682087406">"更新時にウィンドウの表示面全体を点滅させる"</string> <string name="show_hw_screen_updates" msgid="2021286231267747506">"画面の更新を表示"</string> diff --git a/packages/SettingsLib/res/values-ka/strings.xml b/packages/SettingsLib/res/values-ka/strings.xml index 147512b2cc20..f63d4c23f6cc 100644 --- a/packages/SettingsLib/res/values-ka/strings.xml +++ b/packages/SettingsLib/res/values-ka/strings.xml @@ -114,9 +114,9 @@ <string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM წვდომა"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD აუდიო: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD აუდიო"</string> - <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"სმენის მოწყობილობები"</string> + <string name="bluetooth_profile_hearing_aid" msgid="2607867572569689732">"სმენის მოწყობილობები"</string> <string name="bluetooth_profile_le_audio" msgid="1725521360076451751">"LE-აუდიო"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"დაკავშირებულია სმენის მოწყობილობებთან"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="5757754050938807276">"დაკავშირებულია სმენის მოწყობილობებთან"</string> <string name="bluetooth_le_audio_profile_summary_connected" msgid="6916226974453480650">"დაკავშირებულია LE აუდიოსთან"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"დაკავშირებულია აუდიო მულტიმედიურ სისტემასთან"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"დაკავშირებულია ტელეფონის აუდიო მოწყობილობასთან"</string> @@ -134,7 +134,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="808970643123744170">"გამოიყენეთ ტელეფონის აუდიომოწყობილობაში"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"ფაილების ტრანსფერისათვის გამოყენება"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"შეტანისთვის გამოყენება"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"გამოყენება სმენის მოწყობილობებისთვის"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="3374057355721486932">"გამოყენება სმენის მოწყობილობებისთვის"</string> <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"გამოიყენება შემდეგისთვის: LE_AUDIO"</string> <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"დაწყვილება"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"დაწყვილება"</string> @@ -355,6 +355,8 @@ <string name="pointer_location_summary" msgid="957120116989798464">"ეკრანის გადაფარვა შეხების მონაცემების ჩვენებით"</string> <string name="show_touches" msgid="8437666942161289025">"შეხებების ჩვენება"</string> <string name="show_touches_summary" msgid="3692861665994502193">"შეხებებისთვის ვიზუალური უკუკავშირის ჩვენება"</string> + <string name="show_key_presses" msgid="6360141722735900214">"კლავიშების დაჭერის ჩვენება"</string> + <string name="show_key_presses_summary" msgid="725387457373015024">"ვიზუალური გამოხმაურების ჩვენება კლავიშის დაჭერაზე"</string> <string name="show_screen_updates" msgid="2078782895825535494">"ზედაპირის განახლებების ჩვენება"</string> <string name="show_screen_updates_summary" msgid="2126932969682087406">"ფანჯრის მთელი ზედაპირის აციმციმება მისი განახლებისას"</string> <string name="show_hw_screen_updates" msgid="2021286231267747506">"განახლებების ჩვენება"</string> diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml index 7df9fb329b43..883dea61edc5 100644 --- a/packages/SettingsLib/res/values-kk/strings.xml +++ b/packages/SettingsLib/res/values-kk/strings.xml @@ -114,9 +114,9 @@ <string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM картасына кіру"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD форматты аудио: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD форматты аудио"</string> - <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Есту аппараттары"</string> + <string name="bluetooth_profile_hearing_aid" msgid="2607867572569689732">"Есту аппараттары"</string> <string name="bluetooth_profile_le_audio" msgid="1725521360076451751">"LE Audio"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"Есту аппараттарына жалғанған"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="5757754050938807276">"Есту аппараттарына жалғанған"</string> <string name="bluetooth_le_audio_profile_summary_connected" msgid="6916226974453480650">"LE Audio-ға жалғанды."</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"Медиа аудиосына жалғанған"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"Телефон аудиосына қосылған"</string> @@ -134,7 +134,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="808970643123744170">"Телефон аудиосы үшін қолдану"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Файлды жіберу үшін қолдану"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Кіріс үшін қолдану"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Есту аппараттары үшін пайдалану"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="3374057355721486932">"Есту аппараттары үшін пайдалану"</string> <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"LE_AUDIO үшін пайдалану"</string> <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Жұптау"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"ЖҰПТАУ"</string> @@ -355,6 +355,8 @@ <string name="pointer_location_summary" msgid="957120116989798464">"Экран бетіне түртілген элемент дерегі көрсетіледі"</string> <string name="show_touches" msgid="8437666942161289025">"Түрту қимылын көрсету"</string> <string name="show_touches_summary" msgid="3692861665994502193">"Түрту қимылын экраннан көрсету"</string> + <string name="show_key_presses" msgid="6360141722735900214">"Түйменің басылуын көрсету"</string> + <string name="show_key_presses_summary" msgid="725387457373015024">"Түйменің басылуын экраннан көрсету"</string> <string name="show_screen_updates" msgid="2078782895825535494">"Бедердің жаңарғанын көрсету"</string> <string name="show_screen_updates_summary" msgid="2126932969682087406">"Бедері жаңарғанда, терезені түгелдей жыпылықтату"</string> <string name="show_hw_screen_updates" msgid="2021286231267747506">"Көріністің жаңарғанын көрсету"</string> diff --git a/packages/SettingsLib/res/values-km/strings.xml b/packages/SettingsLib/res/values-km/strings.xml index 81d26692a20e..f7a8bfdaaa80 100644 --- a/packages/SettingsLib/res/values-km/strings.xml +++ b/packages/SettingsLib/res/values-km/strings.xml @@ -114,9 +114,9 @@ <string name="bluetooth_profile_sap" msgid="8304170950447934386">"ការចូលដំណើរការស៊ីម"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"សំឡេងកម្រិត HD៖ <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"សំឡេងកម្រិត HD"</string> - <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"ឧបករណ៍ជំនួយការស្ដាប់"</string> + <string name="bluetooth_profile_hearing_aid" msgid="2607867572569689732">"ឧបករណ៍ជំនួយការស្ដាប់"</string> <string name="bluetooth_profile_le_audio" msgid="1725521360076451751">"LE Audio"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"បានភ្ជាប់ទៅឧបករណ៍ជំនួយការស្ដាប់"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="5757754050938807276">"បានភ្ជាប់ទៅឧបករណ៍ជំនួយការស្ដាប់"</string> <string name="bluetooth_le_audio_profile_summary_connected" msgid="6916226974453480650">"បានភ្ជាប់ទៅ LE audio"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"បានភ្ជាប់ទៅអូឌីយ៉ូមេឌៀ"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"តភ្ជាប់ទៅអូឌីយ៉ូទូរស័ព្ទ"</string> @@ -134,7 +134,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="808970643123744170">"ប្រើសម្រាប់អូឌីយ៉ូទូរស័ព្ទ"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"ប្រើសម្រាប់ផ្ទេរឯកសារ"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"ប្រើសម្រាប់បញ្ចូល"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"ប្រើសម្រាប់ឧបករណ៍ជំនួយការស្តាប់"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="3374057355721486932">"ប្រើសម្រាប់ឧបករណ៍ជំនួយការស្ដាប់"</string> <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"ប្រើសម្រាប់ LE_AUDIO"</string> <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"ផ្គូផ្គង"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"ផ្គូផ្គង"</string> @@ -355,6 +355,8 @@ <string name="pointer_location_summary" msgid="957120116989798464">"អេក្រង់ត្រួតគ្នាបង្ហាញទិន្នន័យប៉ះបច្ចុប្បន្ន"</string> <string name="show_touches" msgid="8437666942161289025">"បង្ហាញការចុច"</string> <string name="show_touches_summary" msgid="3692861665994502193">"បង្ហាញដានចុច នៅពេលចុច"</string> + <string name="show_key_presses" msgid="6360141722735900214">"បង្ហាញការចុចគ្រាប់ចុច"</string> + <string name="show_key_presses_summary" msgid="725387457373015024">"បង្ហាញព័ត៌មានឆ្លើយតបជារូបភាពសម្រាប់ការចុចគ្រាប់ចុចរូបវន្ត"</string> <string name="show_screen_updates" msgid="2078782895825535494">"បង្ហាញបច្ចុប្បន្នភាពផ្ទៃ"</string> <string name="show_screen_updates_summary" msgid="2126932969682087406">"ផ្ទៃវីនដូទាំងមូលបញ្ចេញពន្លឺនៅពេលធ្វើបច្ចុប្បន្នភាព"</string> <string name="show_hw_screen_updates" msgid="2021286231267747506">"បង្ហាញបច្ចុប្បន្នភាពទិដ្ឋភាព"</string> diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml index 134f59149d86..e29e22bb5aa8 100644 --- a/packages/SettingsLib/res/values-kn/strings.xml +++ b/packages/SettingsLib/res/values-kn/strings.xml @@ -114,9 +114,9 @@ <string name="bluetooth_profile_sap" msgid="8304170950447934386">"ಸಿಮ್ ಆ್ಯಕ್ಸೆಸ್"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD ಆಡಿಯೋ: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD ಆಡಿಯೋ"</string> - <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"ಶ್ರವಣ ಸಾಧನಗಳು"</string> + <string name="bluetooth_profile_hearing_aid" msgid="2607867572569689732">"ಶ್ರವಣ ಸಾಧನಗಳು"</string> <string name="bluetooth_profile_le_audio" msgid="1725521360076451751">"LE ಆಡಿಯೋ"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"ಶ್ರವಣ ಸಾಧನಗಳಿಗೆ ಸಂಪರ್ಕಿಸಲಾಗಿದೆ"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="5757754050938807276">"ಶ್ರವಣ ಸಾಧನಗಳಿಗೆ ಕನೆಕ್ಟ್ ಮಾಡಲಾಗಿದೆ"</string> <string name="bluetooth_le_audio_profile_summary_connected" msgid="6916226974453480650">"LE ಆಡಿಯೋಗೆ ಕನೆಕ್ಟ್ ಮಾಡಲಾಗಿದೆ"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"ಮಾಧ್ಯಮ ಆಡಿಯೋಗೆ ಸಂಪರ್ಕಗೊಂಡಿದೆ"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"ಫೋನ್ ಆಡಿಯೋಗೆ ಸಂಪರ್ಕಗೊಂಡಿದೆ"</string> @@ -134,7 +134,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="808970643123744170">"ಫೋನ್ ಆಡಿಯೋಗಾಗಿ ಬಳಕೆ"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"ಫೈಲ್ ವರ್ಗಾವಣೆಗಾಗಿ ಬಳಸು"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"ಇನ್ಪುಟ್ಗಾಗಿ ಬಳಸು"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"ಶ್ರವಣ ಸಾಧನಗಳಿಗಾಗಿ ಬಳಸಿ"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="3374057355721486932">"ಶ್ರವಣ ಸಾಧನಗಳಿಗಾಗಿ ಬಳಸಿ"</string> <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"LE_AUDIO ಗೆ ಬಳಸಿ"</string> <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"ಜೋಡಿಸಿ"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"ಜೋಡಿ ಮಾಡು"</string> @@ -355,6 +355,8 @@ <string name="pointer_location_summary" msgid="957120116989798464">"ಪ್ರಸ್ತುತ ಸ್ಪರ್ಶ ಡೇಟಾ ತೋರಿಸುವ ಪರದೆಯ ಓವರ್ಲೇ"</string> <string name="show_touches" msgid="8437666942161289025">"ಟ್ಯಾಪ್ಗಳನ್ನು ತೋರಿಸಿ"</string> <string name="show_touches_summary" msgid="3692861665994502193">"ಟ್ಯಾಪ್ಗಳಿಗೆ ದೃಶ್ಯ ಪ್ರತಿಕ್ರಿಯೆ ತೋರಿಸು"</string> + <string name="show_key_presses" msgid="6360141722735900214">"ಕೀ ಪ್ರೆಸ್ಗಳನ್ನು ತೋರಿಸಿ"</string> + <string name="show_key_presses_summary" msgid="725387457373015024">"ಭೌತಿಕ ಕೀ ಪ್ರೆಸ್ಗಳ ವಿಷುವಲ್ ಪ್ರತಿಕ್ರಿಯೆಗಾಗಿ ನೋಡಿ"</string> <string name="show_screen_updates" msgid="2078782895825535494">"ಸರ್ಫೇಸ್ ಅಪ್ಡೇಟ್ ತೋರಿಸಿ"</string> <string name="show_screen_updates_summary" msgid="2126932969682087406">"ಅಪ್ಡೇಟ್ ಆಗುವಾಗ ವಿಂಡೋದ ಸರ್ಫೇಸ್ ಫ್ಲ್ಯಾಶ್ ಆಗುತ್ತದೆ"</string> <string name="show_hw_screen_updates" msgid="2021286231267747506">"\'ಅಪ್ಡೇಟ್ಗಳನ್ನು ವೀಕ್ಷಿಸಿ\' ತೋರಿಸಿ"</string> diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml index ffa05cc91bd1..653ac2359cba 100644 --- a/packages/SettingsLib/res/values-ko/strings.xml +++ b/packages/SettingsLib/res/values-ko/strings.xml @@ -114,9 +114,9 @@ <string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM 액세스"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD 오디오: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD 오디오"</string> - <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"보청기"</string> + <string name="bluetooth_profile_hearing_aid" msgid="2607867572569689732">"보청기"</string> <string name="bluetooth_profile_le_audio" msgid="1725521360076451751">"LE 오디오"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"보청기에 연결됨"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="5757754050938807276">"보청기에 연결됨"</string> <string name="bluetooth_le_audio_profile_summary_connected" msgid="6916226974453480650">"LE 오디오에 연결됨"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"미디어 오디오에 연결됨"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"휴대전화 오디오에 연결됨"</string> @@ -134,7 +134,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="808970643123744170">"휴대전화 오디오에 사용"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"파일 전송에 사용"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"입력에 사용"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"보청기로 사용"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="3374057355721486932">"보청기로 사용"</string> <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"LE_AUDIO에 사용"</string> <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"페어링"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"페어링"</string> @@ -355,6 +355,8 @@ <string name="pointer_location_summary" msgid="957120116989798464">"현재 터치 데이터 오버레이 표시"</string> <string name="show_touches" msgid="8437666942161289025">"탭한 항목 표시"</string> <string name="show_touches_summary" msgid="3692861665994502193">"탭한 항목에 대해 시각적인 피드백 표시"</string> + <string name="show_key_presses" msgid="6360141722735900214">"키 누름 표시"</string> + <string name="show_key_presses_summary" msgid="725387457373015024">"물리적 키 누름에 관한 시각적 피드백을 표시합니다."</string> <string name="show_screen_updates" msgid="2078782895825535494">"표면 업데이트 표시"</string> <string name="show_screen_updates_summary" msgid="2126932969682087406">"전체 창 화면이 업데이트되었을 때 플래시 처리"</string> <string name="show_hw_screen_updates" msgid="2021286231267747506">"보기 업데이트 표시"</string> diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml index 7e7d3820f88b..c2aa6520ae46 100644 --- a/packages/SettingsLib/res/values-ky/strings.xml +++ b/packages/SettingsLib/res/values-ky/strings.xml @@ -114,9 +114,9 @@ <string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM картаны пайдалануу мүмкүнчүлүгү"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD форматындагы аудио: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD форматындагы аудио"</string> - <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Угуу аппараттары"</string> + <string name="bluetooth_profile_hearing_aid" msgid="2607867572569689732">"Угуу аппараттары"</string> <string name="bluetooth_profile_le_audio" msgid="1725521360076451751">"LE Audio"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"Угуу аппараттарына туташып турат"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="5757754050938807276">"Угуу аппараттарына туташып турат"</string> <string name="bluetooth_le_audio_profile_summary_connected" msgid="6916226974453480650">"LE аудио менен туташты"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"Медиа аудиого туташты"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"Телефон аудиосуна туташты"</string> @@ -134,7 +134,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="808970643123744170">"Телефон аудиосу үчүн колдонулсун"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Файл өткөрүү үчүн колдонулсун"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Киргизүү үчүн колдонулсун"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Угуу аппараттары үчүн колдонуу"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="3374057355721486932">"Угуу аппараттары үчүн колдонуу"</string> <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"LE_AUDIO үчүн колдонуу"</string> <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Байланыштыруу"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"ЖУПТАШТЫРУУ"</string> @@ -355,6 +355,8 @@ <string name="pointer_location_summary" msgid="957120116989798464">"Басылган жерлер жана жаңсоолор экранда көрүнүп турат"</string> <string name="show_touches" msgid="8437666942161289025">"Басылган жерлерди көрсөтүү"</string> <string name="show_touches_summary" msgid="3692861665994502193">"Экранда басылган жерлер көрүнүп турат"</string> + <string name="show_key_presses" msgid="6360141722735900214">"Баскычтардын басылганын көрсөтүү"</string> + <string name="show_key_presses_summary" msgid="725387457373015024">"Баскычтар басылганда визуалдык сигнал көрүнөт"</string> <string name="show_screen_updates" msgid="2078782895825535494">"Экран жаңыруусун көрсөтүү"</string> <string name="show_screen_updates_summary" msgid="2126932969682087406">"Экран жаңырганда анын үстү жарык болот"</string> <string name="show_hw_screen_updates" msgid="2021286231267747506">"Жаңыртууларды көрсөтүү"</string> diff --git a/packages/SettingsLib/res/values-lo/strings.xml b/packages/SettingsLib/res/values-lo/strings.xml index 28fd87e96c7d..0a429fd5d5be 100644 --- a/packages/SettingsLib/res/values-lo/strings.xml +++ b/packages/SettingsLib/res/values-lo/strings.xml @@ -114,9 +114,9 @@ <string name="bluetooth_profile_sap" msgid="8304170950447934386">"ການເຂົ້າເຖິງ SIM"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"ສຽງ HD: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"ສຽງ HD"</string> - <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"ອຸປະກອນຊ່ວຍຟັງ"</string> + <string name="bluetooth_profile_hearing_aid" msgid="2607867572569689732">"ເຄື່ອງຊ່ວຍຟັງ"</string> <string name="bluetooth_profile_le_audio" msgid="1725521360076451751">"ສຽງ LE"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"ເຊື່ອມຕໍ່ຫາອຸປະກອນຊ່ວຍຟັງແລ້ວ"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="5757754050938807276">"ເຊື່ອມຕໍ່ກັບເຄື່ອງຊ່ວຍຟັງແລ້ວ"</string> <string name="bluetooth_le_audio_profile_summary_connected" msgid="6916226974453480650">"ເຊື່ອມຕໍ່ຫາສຽງ LE ແລ້ວ"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"ເຊື່ອມຕໍ່ກັບສື່ດ້ານສຽງແລ້ວ"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"ເຊື່ອມຕໍ່ກັບສຽງໂທລະສັບແລ້ວ"</string> @@ -134,7 +134,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="808970643123744170">"ໃຊ້ສຳລັບລະບົບສຽງຂອງໂທລະສັບ"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"ໃຊ້ເພື່ອໂອນຍ້າຍໄຟລ໌"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"ໃຊ້ສຳລັບການປ້ອນຂໍ້ມູນ"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"ໃຊ້ສຳລັບອຸປະກອນຊ່ວຍຟັງ"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="3374057355721486932">"ໃຊ້ສຳລັບເຄື່ອງຊ່ວຍຟັງ"</string> <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"ໃຊ້ສຳລັບ LE_AUDIO"</string> <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"ຈັບຄູ່"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"ຈັບຄູ່"</string> @@ -355,6 +355,8 @@ <string name="pointer_location_summary" msgid="957120116989798464">"ການວາງຊ້ອນໜ້າຈໍກຳລັງສະແດງຂໍ້ມູນການສຳຜັດໃນປັດຈຸບັນ"</string> <string name="show_touches" msgid="8437666942161289025">"ສະແດງການແຕະ"</string> <string name="show_touches_summary" msgid="3692861665994502193">"ສະແດງຄໍາຕິຊົມທາງຮູບພາບສຳລັບການແຕະ"</string> + <string name="show_key_presses" msgid="6360141722735900214">"ສະແດງການກົດປຸ່ມ"</string> + <string name="show_key_presses_summary" msgid="725387457373015024">"ສະແດງຄຳຕິຊົມພາບສຳລັບການກົດປຸ່ມຈິງ"</string> <string name="show_screen_updates" msgid="2078782895825535494">"ສະແດງການອັບເດດພື້ນຜິວ"</string> <string name="show_screen_updates_summary" msgid="2126932969682087406">"ກະພິບໜ້າຈໍທັງໜ້າເມື່ອມີການອັບເດດ"</string> <string name="show_hw_screen_updates" msgid="2021286231267747506">"ສະແດງອັບເດດມຸມມອງ"</string> diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml index 8246c6bfd168..78246dce4713 100644 --- a/packages/SettingsLib/res/values-lt/strings.xml +++ b/packages/SettingsLib/res/values-lt/strings.xml @@ -114,9 +114,9 @@ <string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM prieiga"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD garsas: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD garsas"</string> - <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Klausos aparatai"</string> + <string name="bluetooth_profile_hearing_aid" msgid="2607867572569689732">"Klausos aparatai"</string> <string name="bluetooth_profile_le_audio" msgid="1725521360076451751">"LE Audio"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"Prisijungta prie klausos aparatų"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="5757754050938807276">"Prisijungta prie klausos aparatų"</string> <string name="bluetooth_le_audio_profile_summary_connected" msgid="6916226974453480650">"Prisijungta prie „LE Audio“"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"Prijungta prie medijos garso įrašo"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"Prijungta prie telefono garso"</string> @@ -134,7 +134,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="808970643123744170">"Naudoti telefono garso įrašui"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Naudoti failų perkėlimui"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Naudoti įvedant"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Naudoti su klausos aparatais"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="3374057355721486932">"Naudoti su klausos aparatais"</string> <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"Naudoti LE_AUDIO"</string> <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Susieti"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"SUSIETI"</string> @@ -355,6 +355,8 @@ <string name="pointer_location_summary" msgid="957120116989798464">"Ekrano perdanga rodo dabartinius lietimo duomenis"</string> <string name="show_touches" msgid="8437666942161289025">"Rodyti palietimus"</string> <string name="show_touches_summary" msgid="3692861665994502193">"Rodyti vaizdinius palietimų atsiliepimus"</string> + <string name="show_key_presses" msgid="6360141722735900214">"Rodyt klavišų paspaudimus"</string> + <string name="show_key_presses_summary" msgid="725387457373015024">"Rodyti fizinių klavišų paspaudimų vaizdinį atsiliepimą"</string> <string name="show_screen_updates" msgid="2078782895825535494">"Rodyti paviršiaus naujin."</string> <string name="show_screen_updates_summary" msgid="2126932969682087406">"Naujinant mirginti visus langų paviršius"</string> <string name="show_hw_screen_updates" msgid="2021286231267747506">"Rodyti rodinių naujinius"</string> diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml index 807df3a3f91d..e3fbcdf02d01 100644 --- a/packages/SettingsLib/res/values-lv/strings.xml +++ b/packages/SettingsLib/res/values-lv/strings.xml @@ -114,9 +114,9 @@ <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Piekļuve SIM kartei"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD audio: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD audio"</string> - <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Dzirdes aparāti"</string> + <string name="bluetooth_profile_hearing_aid" msgid="2607867572569689732">"Dzirdes aparāti"</string> <string name="bluetooth_profile_le_audio" msgid="1725521360076451751">"LE Audio"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"Izveidots savienojums ar dzirdes aparātiem"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="5757754050938807276">"Izveidots savienojums ar dzirdes aparātiem"</string> <string name="bluetooth_le_audio_profile_summary_connected" msgid="6916226974453480650">"Izveidots savienojums ar LE Audio"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"Savienots ar multivides audio"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"Savienots ar tālruņa audio"</string> @@ -134,7 +134,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="808970643123744170">"Izmantot tālruņa skaņai"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Izmantot faila pārsūtīšanai"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Izmantot ievadei"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Izmantot dzirdes aparātiem"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="3374057355721486932">"Izmantot dzirdes aparātiem"</string> <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"Izmantot LE_AUDIO profilam"</string> <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Izveidot pāri"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"SAVIENOT PĀRĪ"</string> @@ -355,6 +355,8 @@ <string name="pointer_location_summary" msgid="957120116989798464">"Ekrāna pārklājums ar aktuāliem pieskāriena datiem"</string> <string name="show_touches" msgid="8437666942161289025">"Rādīt pieskārienus"</string> <string name="show_touches_summary" msgid="3692861665994502193">"Rādīt vizuālo reakciju pēc pieskārieniem"</string> + <string name="show_key_presses" msgid="6360141722735900214">"Rādīt taustiņu nospiešanu"</string> + <string name="show_key_presses_summary" msgid="725387457373015024">"Vizuāla reakcija uz fizisku taustiņu nospiešanu"</string> <string name="show_screen_updates" msgid="2078782895825535494">"Rādīt virsmas atjauninājumus WL: 294"</string> <string name="show_screen_updates_summary" msgid="2126932969682087406">"Atjaunināt visa loga virsmas, kad tās tiek atjauninātas WL: 294"</string> <string name="show_hw_screen_updates" msgid="2021286231267747506">"Rādīt skat. atjaunin."</string> diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml index 583b0699e953..6ba8d5c59f79 100644 --- a/packages/SettingsLib/res/values-mk/strings.xml +++ b/packages/SettingsLib/res/values-mk/strings.xml @@ -114,9 +114,9 @@ <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Пристап до SIM"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD-аудио: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD-аудио"</string> - <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Слушни помагала"</string> + <string name="bluetooth_profile_hearing_aid" msgid="2607867572569689732">"Слушни помагала"</string> <string name="bluetooth_profile_le_audio" msgid="1725521360076451751">"LE-аудио"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"Поврзано со слушни помагала"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="5757754050938807276">"Поврзано со слушни помагала"</string> <string name="bluetooth_le_audio_profile_summary_connected" msgid="6916226974453480650">"Поврзано на LE-аудио"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"Поврзан со аудио на медиуми"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"Поврзан со аудио на телефон"</string> @@ -134,7 +134,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="808970643123744170">"Користи за аудио на телефон"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Користи за пренос на датотеки"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Користи за внес"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Користи за слушни помагала"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="3374057355721486932">"Користам слушни помагала"</string> <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"Користи за LE_AUDIO"</string> <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Спари"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"СПАРИ"</string> @@ -355,6 +355,8 @@ <string name="pointer_location_summary" msgid="957120116989798464">"Прекривката на екран ги покажува тековните податоци на допир"</string> <string name="show_touches" msgid="8437666942161289025">"Прикажувај допири"</string> <string name="show_touches_summary" msgid="3692861665994502193">"Прикажувај визуелни повратни информации за допири"</string> + <string name="show_key_presses" msgid="6360141722735900214">"Прикаж. притисн. копчиња"</string> + <string name="show_key_presses_summary" msgid="725387457373015024">"Визуелно го прикажува притискањето на копчињата"</string> <string name="show_screen_updates" msgid="2078782895825535494">"Прикажи ажурир. површина"</string> <string name="show_screen_updates_summary" msgid="2126932969682087406">"Осветли површ. на прозорци при нивно ажурирање"</string> <string name="show_hw_screen_updates" msgid="2021286231267747506">"Покажувај ажурирања на приказот"</string> diff --git a/packages/SettingsLib/res/values-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml index 610f3653e716..0f4d30d04187 100644 --- a/packages/SettingsLib/res/values-ml/strings.xml +++ b/packages/SettingsLib/res/values-ml/strings.xml @@ -114,9 +114,9 @@ <string name="bluetooth_profile_sap" msgid="8304170950447934386">"സിം ആക്സസ്"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD ഓഡിയോ: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD ഓഡിയോ"</string> - <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"ശ്രവണ സഹായികൾ"</string> + <string name="bluetooth_profile_hearing_aid" msgid="2607867572569689732">"ശ്രവണ സഹായികൾ"</string> <string name="bluetooth_profile_le_audio" msgid="1725521360076451751">"LE ഓഡിയോ"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"ശ്രവണ സഹായികളിലേക്ക് കണക്റ്റ് ചെയ്തു"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="5757754050938807276">"ശ്രവണ സഹായികളിലേക്ക് കണക്റ്റ് ചെയ്തു"</string> <string name="bluetooth_le_audio_profile_summary_connected" msgid="6916226974453480650">"LE ഓഡിയോയിലേക്ക് കണക്റ്റ് ചെയ്തു"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"മീഡിയ ഓഡിയോയിലേക്ക് കണക്റ്റുചെയ്തു"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"ഫോൺ ഓഡിയോയിൽ കണക്റ്റുചെയ്തു"</string> @@ -134,7 +134,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="808970643123744170">"ഫോൺ ഓഡിയോയ്ക്കായി ഉപയോഗിക്കുക"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"ഫയൽ കൈമാറ്റത്തിനായി ഉപയോഗിക്കുന്നു"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"ഇൻപുട്ടിനായി ഉപയോഗിക്കുക"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"ശ്രവണ സഹായികൾക്കായി ഉപയോഗിക്കുക"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="3374057355721486932">"ശ്രവണ സഹായികൾക്കായി ഉപയോഗിക്കുക"</string> <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"LE_AUDIO ഉപയോഗിക്കുക"</string> <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"ജോടിയാക്കുക"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"ജോടിയാക്കുക"</string> @@ -355,6 +355,8 @@ <string name="pointer_location_summary" msgid="957120116989798464">"സ്ക്രീൻ ഓവർലേ നിലവിലെ ടച്ച് ഡാറ്റ ദൃശ്യമാക്കുന്നു"</string> <string name="show_touches" msgid="8437666942161289025">"ടാപ്പുകൾ കാണിക്കുക"</string> <string name="show_touches_summary" msgid="3692861665994502193">"ടാപ്പുകൾക്ക് ദൃശ്യ ഫീഡ്ബാക്ക് കാണിക്കുക"</string> + <string name="show_key_presses" msgid="6360141722735900214">"കീ അമർത്തലുകൾ കാണിക്കുക"</string> + <string name="show_key_presses_summary" msgid="725387457373015024">"ഫിസിക്കൽ കീ അമർത്തൽ വിഷ്വൽ ഫീഡ്ബാക്ക് കാണിക്കൂ"</string> <string name="show_screen_updates" msgid="2078782895825535494">"സർഫേസ് അപ്ഡേറ്റ് കാണിക്കൂ"</string> <string name="show_screen_updates_summary" msgid="2126932969682087406">"അപ്ഡേറ്റ് പൂർത്തിയാകുമ്പോൾ മുഴുവൻ വിൻഡോ സർഫേസുകളും ഫ്ലാഷ് ചെയ്യുക"</string> <string name="show_hw_screen_updates" msgid="2021286231267747506">"\'അപ്ഡേറ്റുകൾ കാണുക\' കാണിക്കുക"</string> diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml index 040f2c82c212..29fb6d4b86c0 100644 --- a/packages/SettingsLib/res/values-mn/strings.xml +++ b/packages/SettingsLib/res/values-mn/strings.xml @@ -114,9 +114,9 @@ <string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM Хандалт"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD аудио: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD аудио"</string> - <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Сонсголын төхөөрөмж"</string> + <string name="bluetooth_profile_hearing_aid" msgid="2607867572569689732">"Сонсголын төхөөрөмж"</string> <string name="bluetooth_profile_le_audio" msgid="1725521360076451751">"LE Аудио"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"Сонсголын төхөөрөмжтэй холбосон"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="5757754050938807276">"Сонсголын төхөөрөмжтэй холбосон"</string> <string name="bluetooth_le_audio_profile_summary_connected" msgid="6916226974453480650">"LE аудионд холбогдсон"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"Медиа аудиод холбогдсон"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"Утасны аудид холбогдсон"</string> @@ -134,7 +134,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="808970643123744170">"Утасны аудиод ашиглах"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Файл дамжуулахад ашиглах"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Оруулахад ашиглах"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Сонсголын төхөөрөмжид ашиглах"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="3374057355721486932">"Сонсголын төхөөрөмж болгон ашиглах"</string> <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"LE_АУДИОНД ашиглах"</string> <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Хослуулах"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"ХОСЛУУЛАХ"</string> @@ -355,6 +355,8 @@ <string name="pointer_location_summary" msgid="957120116989798464">"Дэлгэцийн давхаргаар одоогийн хүрэлтийн өгөгдлийг харуулж байна"</string> <string name="show_touches" msgid="8437666942161289025">"Товшилтыг харуулах"</string> <string name="show_touches_summary" msgid="3692861665994502193">"Товшилтын визуал хариу үйлдлийг харуулах"</string> + <string name="show_key_presses" msgid="6360141722735900214">"Түлхүүрийн даралт харуул"</string> + <string name="show_key_presses_summary" msgid="725387457373015024">"Биет түлхүүрийн даралтын визуал санал хүсэлт харуул"</string> <string name="show_screen_updates" msgid="2078782895825535494">"Гадаргын шинэчлэлтүүдийг харуулах"</string> <string name="show_screen_updates_summary" msgid="2126932969682087406">"Шинэчлэгдэх үед цонхны гадаргыг бүхэлд нь анивчуулах"</string> <string name="show_hw_screen_updates" msgid="2021286231267747506">"Шинэчлэлт харахыг харуулах"</string> diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml index 932ef6c9e8dc..e9e0ee48984a 100644 --- a/packages/SettingsLib/res/values-mr/strings.xml +++ b/packages/SettingsLib/res/values-mr/strings.xml @@ -114,9 +114,9 @@ <string name="bluetooth_profile_sap" msgid="8304170950447934386">"सिम अॅक्सेस"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD ऑडिओ: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD ऑडिओ"</string> - <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"श्रवणयंत्रे"</string> + <string name="bluetooth_profile_hearing_aid" msgid="2607867572569689732">"श्रवणयंत्रे"</string> <string name="bluetooth_profile_le_audio" msgid="1725521360076451751">"LE ऑडिओ"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"श्रवण यंत्रांशी कनेक्ट केले आहे"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="5757754050938807276">"श्रवणयंत्रांशी कनेक्ट केले आहे"</string> <string name="bluetooth_le_audio_profile_summary_connected" msgid="6916226974453480650">"LE ऑडिओशी कनेक्ट केले आहे"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"मीडिया ऑडिओवर कनेक्ट केले"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"फोन ऑडिओ वर कनेक्ट केले"</string> @@ -134,7 +134,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="808970643123744170">"फोन ऑडिओसाठी वापरा"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"फाइल स्थानांतरणासाठी वापरा"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"इनपुट साठी वापरा"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"श्रवण यंत्रांसाठी वापरा"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="3374057355721486932">"श्रवणयंत्रांसाठी वापरा"</string> <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"LE_AUDIO साठी वापरले आहे"</string> <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"पेअर करा"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"पेअर करा"</string> @@ -355,6 +355,8 @@ <string name="pointer_location_summary" msgid="957120116989798464">"वर्तमान स्पर्श डेटा दर्शविणारे स्क्रीन ओव्हरले"</string> <string name="show_touches" msgid="8437666942161289025">"टॅप दाखवा"</string> <string name="show_touches_summary" msgid="3692861665994502193">"टॅपसाठी व्हिज्युअल फीडबॅक दाखवा"</string> + <string name="show_key_presses" msgid="6360141722735900214">"की प्रेस दाखवा"</string> + <string name="show_key_presses_summary" msgid="725387457373015024">"प्रत्यक्ष की प्रेससाठी व्हिज्युअल फीडबॅक दाखवा"</string> <string name="show_screen_updates" msgid="2078782895825535494">"सर्फेस अपडेट दाखवा"</string> <string name="show_screen_updates_summary" msgid="2126932969682087406">"संपूर्ण विंडो सर्फेस अपडेट होतात तेव्हा ते फ्लॅश करा"</string> <string name="show_hw_screen_updates" msgid="2021286231267747506">"व्ह्यू अपडेट दाखवा"</string> diff --git a/packages/SettingsLib/res/values-ms/strings.xml b/packages/SettingsLib/res/values-ms/strings.xml index 460eb563adbc..0a41c0f6a554 100644 --- a/packages/SettingsLib/res/values-ms/strings.xml +++ b/packages/SettingsLib/res/values-ms/strings.xml @@ -114,9 +114,9 @@ <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Akses SIM"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"Audio HD: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"Audio HD"</string> - <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Alat Bantu Dengar"</string> + <string name="bluetooth_profile_hearing_aid" msgid="2607867572569689732">"Alat bantu pendengaran"</string> <string name="bluetooth_profile_le_audio" msgid="1725521360076451751">"LE Audio"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"Disambungkan pada Alat Bantu Dengar"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="5757754050938807276">"Disambungkan kepada alat bantu pendengaran"</string> <string name="bluetooth_le_audio_profile_summary_connected" msgid="6916226974453480650">"Disambungkan kepada LE Audio"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"Disambungkan ke audio media"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"Disambungkan ke audio telefon"</string> @@ -134,7 +134,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="808970643123744170">"Gunakan untuk audio telefon"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Gunakan untuk pemindahan fail"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Gunakan untuk input"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Gunakan untuk Alat Bantu Dengar"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="3374057355721486932">"Gunakan untuk alat bantu pendengaran"</string> <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"Gunakan untuk LE_AUDIO"</string> <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Gandingkan"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"JADIKAN PASANGAN"</string> @@ -355,6 +355,8 @@ <string name="pointer_location_summary" msgid="957120116989798464">"Tindihan skrin menunjukkan data sentuh semasa"</string> <string name="show_touches" msgid="8437666942161289025">"Tunjukkan ketikan"</string> <string name="show_touches_summary" msgid="3692861665994502193">"Tunjukkan maklum balas visual untuk ketikan"</string> + <string name="show_key_presses" msgid="6360141722735900214">"Tunjukkan tekanan kekunci"</string> + <string name="show_key_presses_summary" msgid="725387457373015024">"Tunjukkan maklum balas visual untuk tekanan kekunci fizikal"</string> <string name="show_screen_updates" msgid="2078782895825535494">"Tunjuk kemaskinian permukaan"</string> <string name="show_screen_updates_summary" msgid="2126932969682087406">"Denyar permukaan tetingkap apabila dikemas kini"</string> <string name="show_hw_screen_updates" msgid="2021286231267747506">"Tunjuk kemaskinian paparan"</string> diff --git a/packages/SettingsLib/res/values-my/strings.xml b/packages/SettingsLib/res/values-my/strings.xml index 8458a629db79..7346d9158d60 100644 --- a/packages/SettingsLib/res/values-my/strings.xml +++ b/packages/SettingsLib/res/values-my/strings.xml @@ -114,9 +114,9 @@ <string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM အသုံးပြုခြင်း"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD အသံ- <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD အသံ"</string> - <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"နားကြားကိရိယာ"</string> + <string name="bluetooth_profile_hearing_aid" msgid="2607867572569689732">"နားကြားကိရိယာ"</string> <string name="bluetooth_profile_le_audio" msgid="1725521360076451751">"LE Audio"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"နားကြားကိရိယာနှင့် ချိတ်ဆက်ပြီးပါပြီ"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="5757754050938807276">"နားကြားကိရိယာနှင့် ချိတ်ဆက်ပြီးပါပြီ"</string> <string name="bluetooth_le_audio_profile_summary_connected" msgid="6916226974453480650">"LE အသံနှင့် ချိတ်ဆက်ထားသည်"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"မီဒီယာအသံအား ချိတ်ဆက်ရန်"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"ဖုန်းအသံအား ချိတ်ဆက်ရန်"</string> @@ -134,7 +134,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="808970643123744170">"ဖုန်းအသံအားအသုံးပြုရန်"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"ဖိုင်လွဲပြောင်းရန်အတွက်အသုံးပြုရန်"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"ထည့်သွင်းရန်အသုံးပြုသည်"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"နားကြားကိရိယာအတွက် အသုံးပြုသည်"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="3374057355721486932">"နားကြားကိရိယာအတွက် အသုံးပြုသည်"</string> <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"LE_AUDIO အတွက် သုံးသည်"</string> <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"တွဲချိတ်ရန်"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"တွဲချိတ်ရန်"</string> @@ -355,6 +355,8 @@ <string name="pointer_location_summary" msgid="957120116989798464">"လက်ရှိထိတွေ့မှုဒေတာကို ဖန်သားပေါ်တွင်ထပ်၍ ပြသသည်"</string> <string name="show_touches" msgid="8437666942161289025">"တို့ခြင်းများကို ပြပါ"</string> <string name="show_touches_summary" msgid="3692861665994502193">"တို့ခြင်းများအတွက် အမြင်ဖြင့် တုံ့ပြန်မှုပြသည်"</string> + <string name="show_key_presses" msgid="6360141722735900214">"ကီးနှိပ်မှုများ ပြပါ"</string> + <string name="show_key_presses_summary" msgid="725387457373015024">"ပကတိကီးနှိပ်မှုများအတွက် ရုပ်မြင်အကြံပြုချက် ပြပါ"</string> <string name="show_screen_updates" msgid="2078782895825535494">"မျက်နှာပြင်အပ်ဒိတ်ပြရန်"</string> <string name="show_screen_updates_summary" msgid="2126932969682087406">"အပ်ဒိတ်လုပ်စဉ် ဝင်းဒိုးမျက်နှာပြင်တွင် အချက်ပြရန်"</string> <string name="show_hw_screen_updates" msgid="2021286231267747506">"မြင်ကွင်းအပ်ဒိတ်များ ပြခြင်း"</string> diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml index a082be68feac..dc68bf3dd714 100644 --- a/packages/SettingsLib/res/values-nb/strings.xml +++ b/packages/SettingsLib/res/values-nb/strings.xml @@ -114,9 +114,9 @@ <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Tilgang til SIM-kortet"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD-lyd: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD-lyd"</string> - <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Høreapparater"</string> + <string name="bluetooth_profile_hearing_aid" msgid="2607867572569689732">"Høreapparater"</string> <string name="bluetooth_profile_le_audio" msgid="1725521360076451751">"LE-lyd"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"Koblet til høreapparater"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="5757754050938807276">"Koblet til høreapparater"</string> <string name="bluetooth_le_audio_profile_summary_connected" msgid="6916226974453480650">"Koblet til LE-lyd"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"Koblet til medielyd"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"Koblet til telefonlyd"</string> @@ -134,7 +134,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="808970643123744170">"Bruk for telefonlyd"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Bruk til filoverføring"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Bruk for inndata"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Bruk for høreapparater"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="3374057355721486932">"Bruk for høreapparater"</string> <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"Bruk for LE_AUDIO"</string> <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Koble til"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"KOBLE TIL"</string> @@ -355,6 +355,8 @@ <string name="pointer_location_summary" msgid="957120116989798464">"Skjermoverlegg viser aktuelle berøringsdata"</string> <string name="show_touches" msgid="8437666942161289025">"Vis trykk"</string> <string name="show_touches_summary" msgid="3692861665994502193">"Vis visuell tilbakemelding for trykk"</string> + <string name="show_key_presses" msgid="6360141722735900214">"Vis tastetrykk"</string> + <string name="show_key_presses_summary" msgid="725387457373015024">"Vis visuell tilbakemelding for fysiske tastetrykk"</string> <string name="show_screen_updates" msgid="2078782895825535494">"Vis overflateoppdateringer"</string> <string name="show_screen_updates_summary" msgid="2126932969682087406">"Fremhev hele vindusoverflater når de oppdateres"</string> <string name="show_hw_screen_updates" msgid="2021286231267747506">"Vis visningsoppdateringer"</string> diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml index 349b9d3c4d4d..5d611329dd10 100644 --- a/packages/SettingsLib/res/values-ne/strings.xml +++ b/packages/SettingsLib/res/values-ne/strings.xml @@ -114,9 +114,9 @@ <string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM एक्सेस"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD अडियो: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD अडियो"</string> - <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"श्रवण यन्त्रहरू"</string> + <string name="bluetooth_profile_hearing_aid" msgid="2607867572569689732">"श्रवण यन्त्रहरू"</string> <string name="bluetooth_profile_le_audio" msgid="1725521360076451751">"LE अडियो"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"श्रवण यन्त्रहरूमा जडान गरियो"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="5757754050938807276">"श्रवण यन्त्रहरूमा जडान गरियो"</string> <string name="bluetooth_le_audio_profile_summary_connected" msgid="6916226974453480650">"LE अडियोमा कनेक्ट गरिएको छ"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"मिडिया अडियोसँग जडित"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"फोन अडियोमा जडान गरियो"</string> @@ -134,7 +134,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="808970643123744170">"फोन अडियोको लागि प्रयोग गर्नुहोस्"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"फाइल ट्रान्सफरका लागि प्रयोग गर्नुहोस्"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"इनपुटको लागि प्रयोग गर्नुहोस्"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"श्रवण यन्त्रहरूका लागि प्रयोग गर्नुहोस्"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="3374057355721486932">"श्रवण यन्त्रहरूका लागि प्रयोग गर्नुहोस्"</string> <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"LE_AUDIO मा प्रयोग गर्नुहोस्"</string> <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"कनेक्ट गर्नुहोस्"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"जोडी"</string> @@ -355,6 +355,8 @@ <string name="pointer_location_summary" msgid="957120116989798464">"स्क्रिन ओभरलेले हालको टच डेटा देखाउँदै छ"</string> <string name="show_touches" msgid="8437666942161289025">"ट्याप देखाइयोस्"</string> <string name="show_touches_summary" msgid="3692861665994502193">"ट्यापका लागि भिजुअल प्रतिक्रिया देखाइयोस्"</string> + <string name="show_key_presses" msgid="6360141722735900214">"थिचिएका कीहरू देखाइयून्"</string> + <string name="show_key_presses_summary" msgid="725387457373015024">"थिचिएका भौतिक कीसम्बन्धी भिजुअल प्रतिक्रिया देखाइयोस्"</string> <string name="show_screen_updates" msgid="2078782895825535494">"सर्फेस अपडेट देखाइयोस्"</string> <string name="show_screen_updates_summary" msgid="2126932969682087406">"अपडेट हुँदा विन्डोका पूरै सतहमा देखाइयोस्"</string> <string name="show_hw_screen_updates" msgid="2021286231267747506">"GPU भ्युको अपडेट देखाइयोस्"</string> diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml index 1b456b94f355..51fc99a21236 100644 --- a/packages/SettingsLib/res/values-nl/strings.xml +++ b/packages/SettingsLib/res/values-nl/strings.xml @@ -114,9 +114,9 @@ <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Sim-toegang"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD-audio: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD-audio"</string> - <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Hoortoestellen"</string> + <string name="bluetooth_profile_hearing_aid" msgid="2607867572569689732">"Hoortoestellen"</string> <string name="bluetooth_profile_le_audio" msgid="1725521360076451751">"LE Audio"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"Verbonden met hoortoestellen"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="5757754050938807276">"Verbonden met hoortoestellen"</string> <string name="bluetooth_le_audio_profile_summary_connected" msgid="6916226974453480650">"Verbonden met LE Audio"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"Verbonden met audio van medium"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"Verbonden met audio van telefoon"</string> @@ -134,7 +134,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="808970643123744170">"Gebruiken voor audio van telefoon"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Gebruiken voor bestandsoverdracht"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Gebruiken voor invoer"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Gebruiken voor hoortoestellen"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="3374057355721486932">"Gebruiken voor hoortoestellen"</string> <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"Gebruiken voor LE_AUDIO"</string> <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Koppelen"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"KOPPELEN"</string> @@ -355,6 +355,8 @@ <string name="pointer_location_summary" msgid="957120116989798464">"Schermoverlay met huidige aanraakgegevens"</string> <string name="show_touches" msgid="8437666942161289025">"Tikken tonen"</string> <string name="show_touches_summary" msgid="3692861665994502193">"Toon visuele feedback voor tikken"</string> + <string name="show_key_presses" msgid="6360141722735900214">"Toetsaanslagen tonen"</string> + <string name="show_key_presses_summary" msgid="725387457373015024">"Visuele feedback tonen voor fysieke toetsaanslagen"</string> <string name="show_screen_updates" msgid="2078782895825535494">"Oppervlakupdates tonen"</string> <string name="show_screen_updates_summary" msgid="2126932969682087406">"Flash volledige vensteroppervlakken bij updates"</string> <string name="show_hw_screen_updates" msgid="2021286231267747506">"Weergave-updates tonen"</string> diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml index 99bcafef0543..22f923dffaf7 100644 --- a/packages/SettingsLib/res/values-or/strings.xml +++ b/packages/SettingsLib/res/values-or/strings.xml @@ -114,9 +114,9 @@ <string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM ଆକ୍ସେସ"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD ଅଡିଓ: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD ଅଡିଓ"</string> - <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"ଶ୍ରବଣ ଯନ୍ତ୍ର"</string> + <string name="bluetooth_profile_hearing_aid" msgid="2607867572569689732">"ଶ୍ରବଣ ଯନ୍ତ୍ର"</string> <string name="bluetooth_profile_le_audio" msgid="1725521360076451751">"LE ଅଡିଓ"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"ଶ୍ରବଣ ଯନ୍ତ୍ରକୁ ସଂଯୋଗ ହୋଇଛି"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="5757754050938807276">"ଶ୍ରବଣ ଯନ୍ତ୍ର ସହ କନେକ୍ଟ କରାଯାଇଛି"</string> <string name="bluetooth_le_audio_profile_summary_connected" msgid="6916226974453480650">"LE ଅଡିଓ ସହ କନେକ୍ଟ କରାଯାଇଛି"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"ମିଡିଆ ଅଡିଓ ସହ ସଂଯୁକ୍ତ"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"ଫୋନ୍ ଅଡିଓ ସହିତ ସଂଯୁକ୍ତ"</string> @@ -134,7 +134,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="808970643123744170">"ଫୋନ୍ ଅଡିଓ ପାଇଁ ବ୍ୟବହାର କର"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"ଫାଇଲ୍ ଟ୍ରାନ୍ସଫର୍ ପାଇଁ ବ୍ୟବହାର କରନ୍ତୁ"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"ଇନ୍ପୁଟ୍ ପାଇଁ ବ୍ୟବହାର କରନ୍ତୁ"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"ଶ୍ରବଣ ଯନ୍ତ୍ର ପାଇଁ ବ୍ୟବହାର କରନ୍ତୁ"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="3374057355721486932">"ଶ୍ରବଣ ଯନ୍ତ୍ର ପାଇଁ ବ୍ୟବହାର କରନ୍ତୁ"</string> <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"LE_AUDIO ପାଇଁ ବ୍ୟବହାର କରନ୍ତୁ"</string> <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"ପେୟାର୍"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"ପେୟାର୍"</string> @@ -355,6 +355,8 @@ <string name="pointer_location_summary" msgid="957120116989798464">"ଏବେର ଟଚ୍ ଡାଟା ଦେଖାଉଥିବା ସ୍କ୍ରୀନ୍ ଓଭର୍ଲେ"</string> <string name="show_touches" msgid="8437666942161289025">"ଟାପ୍ ଦେଖାନ୍ତୁ"</string> <string name="show_touches_summary" msgid="3692861665994502193">"ଟାପ୍ ପାଇଁ ଭିଜୁଆଲ୍ ମତାମତ ଦେଖାନ୍ତୁ"</string> + <string name="show_key_presses" msgid="6360141722735900214">"ଦବାଯାଇଥିବା କୀ ଦେଖାନ୍ତୁ"</string> + <string name="show_key_presses_summary" msgid="725387457373015024">"ଦବାଯାଇଥିବା ଫିଜିକାଲ କୀ ପାଇଁ ଭିଜୁଆଲ ମତାମତ ଦେଖାନ୍ତୁ"</string> <string name="show_screen_updates" msgid="2078782895825535494">"ସର୍ଫେସ୍ ଅପଡେଟ୍ ଦେଖାନ୍ତୁ"</string> <string name="show_screen_updates_summary" msgid="2126932969682087406">"ସମଗ୍ର ୱିଣ୍ଡୋ ପୃଷ୍ଠ ଅପଡେଟ୍ ହେବା ବେଳେ ସେଗୁଡ଼ିକ ଫ୍ଲାସ୍ କରନ୍ତୁ"</string> <string name="show_hw_screen_updates" msgid="2021286231267747506">"ଭ୍ୟୁ ଅପଡେଟ୍ଗୁଡ଼ିକୁ ଦେଖାନ୍ତୁ"</string> diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml index 3e202010c85f..3c4c4bc811b9 100644 --- a/packages/SettingsLib/res/values-pa/strings.xml +++ b/packages/SettingsLib/res/values-pa/strings.xml @@ -114,9 +114,9 @@ <string name="bluetooth_profile_sap" msgid="8304170950447934386">"ਸਿਮ ਪਹੁੰਚ"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD ਆਡੀਓ: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD ਆਡੀਓ"</string> - <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"ਸੁਣਨ ਦੇ ਸਾਧਨ"</string> + <string name="bluetooth_profile_hearing_aid" msgid="2607867572569689732">"ਸੁਣਨ ਦੇ ਸਾਧਨ"</string> <string name="bluetooth_profile_le_audio" msgid="1725521360076451751">"LE ਆਡੀਓ"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"ਸੁਣਨ ਦੇ ਸਾਧਨਾਂ ਨਾਲ ਕਨੈਕਟ ਕੀਤਾ ਗਿਆ"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="5757754050938807276">"ਸੁਣਨ ਦੇ ਸਾਧਨਾਂ ਨਾਲ ਕਨੈਕਟ ਕੀਤਾ ਗਿਆ"</string> <string name="bluetooth_le_audio_profile_summary_connected" msgid="6916226974453480650">"LE ਆਡੀਓ ਨਾਲ ਕਨੈਕਟ ਕੀਤਾ ਗਿਆ"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"ਮੀਡੀਆ ਆਡੀਓ ਨਾਲ ਕਨੈਕਟ ਕੀਤਾ"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"ਫ਼ੋਨ ਔਡੀਓ ਨਾਲ ਕਨੈਕਟ ਕੀਤਾ"</string> @@ -134,7 +134,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="808970643123744170">"ਫ਼ੋਨ ਔਡੀਓ ਲਈ ਵਰਤੋ"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"ਫਾਈਲ ਟ੍ਰਾਂਸਫਰ ਲਈ ਵਰਤੋ"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"ਇਨਪੁਟ ਲਈ ਵਰਤੋ"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"ਸੁਣਨ ਦੇ ਸਾਧਨਾਂ ਲਈ ਵਰਤੋ"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="3374057355721486932">"ਸੁਣਨ ਦੇ ਸਾਧਨਾਂ ਲਈ ਵਰਤੋ"</string> <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"LE_AUDIO ਲਈ ਵਰਤੋ"</string> <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"ਜੋੜਾਬੱਧ ਕਰੋ"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"ਜੋੜਾਬੱਧ ਕਰੋ"</string> @@ -355,6 +355,8 @@ <string name="pointer_location_summary" msgid="957120116989798464">"ਸਕ੍ਰੀਨ ਓਵਰਲੇ ਮੌਜੂਦਾ ਸਪਰਸ਼ ਡਾਟਾ ਦਿਖਾ ਰਿਹਾ ਹੈ"</string> <string name="show_touches" msgid="8437666942161289025">"ਟੈਪਾਂ ਦਿਖਾਓ"</string> <string name="show_touches_summary" msgid="3692861665994502193">"ਟੈਪਾਂ ਲਈ ਦ੍ਰਿਸ਼ਟੀਗਤ ਪ੍ਰਤੀਕਰਮ ਦਿਖਾਓ"</string> + <string name="show_key_presses" msgid="6360141722735900214">"ਕੁੰਜੀ ਦਬਾਉਣ ਸੰਬੰਧੀ ਜਾਣਕਾਰੀ ਦਿਖਾਓ"</string> + <string name="show_key_presses_summary" msgid="725387457373015024">"ਭੌਤਿਕ ਕੁੰਜੀ ਦਬਾਉਣ ਸੰਬੰਧੀ ਦ੍ਰਿਸ਼ਟੀਗਤ ਵਿਚਾਰ ਦਿਖਾਓ"</string> <string name="show_screen_updates" msgid="2078782895825535494">"ਸਰਫ਼ੇਸ ਅੱਪਡੇਟ ਦਿਖਾਓ"</string> <string name="show_screen_updates_summary" msgid="2126932969682087406">"ਅੱਪਡੇਟ ਹੋਣ \'ਤੇ, ਸਮੁੱਚੀਆਂ ਵਿੰਡੋ ਸਰਫ਼ੇਸਾਂ ਫਲੈਸ਼ ਕਰੋ"</string> <string name="show_hw_screen_updates" msgid="2021286231267747506">"\'ਅੱਪਡੇਟ ਦੇਖੋ\' ਨੂੰ ਦਿਖਾਓ"</string> diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml index 58a89d96e5ac..62f7e81fe361 100644 --- a/packages/SettingsLib/res/values-pl/strings.xml +++ b/packages/SettingsLib/res/values-pl/strings.xml @@ -114,9 +114,9 @@ <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Dostęp do karty SIM"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"Dźwięk HD: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"Dźwięk HD"</string> - <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Aparaty słuchowe"</string> + <string name="bluetooth_profile_hearing_aid" msgid="2607867572569689732">"Aparaty słuchowe"</string> <string name="bluetooth_profile_le_audio" msgid="1725521360076451751">"LE Audio"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"Połączono z aparatami słuchowymi"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="5757754050938807276">"Połączono z aparatami słuchowymi"</string> <string name="bluetooth_le_audio_profile_summary_connected" msgid="6916226974453480650">"Połączono z LE Audio"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"Połączono z funkcją audio multimediów"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"Połączono z funkcją audio telefonu"</string> @@ -134,7 +134,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="808970643123744170">"Użyj dla funkcji audio telefonu"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Użyj do transferu plików"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Użyj do wprowadzania"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Użyj z aparatami słuchowymi"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="3374057355721486932">"Używaj w przypadku aparatów słuchowych"</string> <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"Używaj dla LE_AUDIO"</string> <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Sparuj"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"SPARUJ"</string> @@ -355,6 +355,8 @@ <string name="pointer_location_summary" msgid="957120116989798464">"Nakładka pokazująca dane o dotknięciach ekranu"</string> <string name="show_touches" msgid="8437666942161289025">"Pokazuj dotknięcia"</string> <string name="show_touches_summary" msgid="3692861665994502193">"Pokazuj potwierdzenie wizualne po dotknięciu"</string> + <string name="show_key_presses" msgid="6360141722735900214">"Wyświetl naciśnięcia klawiszy"</string> + <string name="show_key_presses_summary" msgid="725387457373015024">"Wyświetl opinie wizualne dla naciśnięć fizycznego klucza"</string> <string name="show_screen_updates" msgid="2078782895825535494">"Pokazuj zmiany powierzchni"</string> <string name="show_screen_updates_summary" msgid="2126932969682087406">"Podświetlaj całe aktualizowane powierzchnie okien"</string> <string name="show_hw_screen_updates" msgid="2021286231267747506">"Pokazuj aktualizacje widoku"</string> diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml index 678c076aa03b..6880511f4d97 100644 --- a/packages/SettingsLib/res/values-pt-rBR/strings.xml +++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml @@ -114,9 +114,9 @@ <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Acesso ao chip"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"Áudio HD: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"Áudio HD"</string> - <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Aparelhos auditivos"</string> + <string name="bluetooth_profile_hearing_aid" msgid="2607867572569689732">"Aparelhos auditivos"</string> <string name="bluetooth_profile_le_audio" msgid="1725521360076451751">"LE Audio"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"Conectado a aparelhos auditivos"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="5757754050938807276">"Conectado a aparelhos auditivos"</string> <string name="bluetooth_le_audio_profile_summary_connected" msgid="6916226974453480650">"Conectado ao perfil Áudio de baixa energia"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"Conectado ao áudio da mídia"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"Conectado ao áudio do smartphone"</string> @@ -134,7 +134,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="808970643123744170">"Usar para áudio do smartphone"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Usado para transferência de arquivo"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Usar para entrada"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Usar para aparelhos auditivos"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="3374057355721486932">"Usar para aparelhos auditivos"</string> <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"Usar para LE_AUDIO"</string> <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Parear"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"PAREAR"</string> @@ -355,6 +355,8 @@ <string name="pointer_location_summary" msgid="957120116989798464">"Exibir dados de toque"</string> <string name="show_touches" msgid="8437666942161289025">"Mostrar toques"</string> <string name="show_touches_summary" msgid="3692861665994502193">"Mostrar feedback visual para toques"</string> + <string name="show_key_presses" msgid="6360141722735900214">"Mostrar teclas pressionadas"</string> + <string name="show_key_presses_summary" msgid="725387457373015024">"Mostrar feedback visual de teclas pressionadas"</string> <string name="show_screen_updates" msgid="2078782895825535494">"Mostrar superfície atualizada"</string> <string name="show_screen_updates_summary" msgid="2126932969682087406">"Piscar superfícies de toda a janela ao atualizar"</string> <string name="show_hw_screen_updates" msgid="2021286231267747506">"Ver atualizações de exibição"</string> diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml index 847bfb5634e7..49e3f91461f1 100644 --- a/packages/SettingsLib/res/values-pt-rPT/strings.xml +++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml @@ -114,9 +114,9 @@ <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Acesso ao SIM"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"Áudio HD: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"Áudio HD"</string> - <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Aparelhos auditivos"</string> + <string name="bluetooth_profile_hearing_aid" msgid="2607867572569689732">"Aparelhos auditivos"</string> <string name="bluetooth_profile_le_audio" msgid="1725521360076451751">"LE Audio"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"Ligado a aparelhos auditivos"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="5757754050938807276">"Ligado a aparelhos auditivos"</string> <string name="bluetooth_le_audio_profile_summary_connected" msgid="6916226974453480650">"Ligado a LE Audio"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"Ligado ao áudio de multimédia"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"Ligado ao áudio do telefone"</string> @@ -134,7 +134,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="808970643123744170">"Usar para áudio do telefone"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Usar para transferência de ficheiros"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Usar para entrada"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Usar para aparelhos auditivos"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="3374057355721486932">"Usar para aparelhos auditivos"</string> <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"Usar para LE_AUDIO"</string> <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Sincr."</string> <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"SINCRONIZAR"</string> @@ -355,6 +355,8 @@ <string name="pointer_location_summary" msgid="957120116989798464">"Apresentar dados atuais de toque"</string> <string name="show_touches" msgid="8437666942161289025">"Mostrar toques"</string> <string name="show_touches_summary" msgid="3692861665994502193">"Mostrar feedback visual para toques"</string> + <string name="show_key_presses" msgid="6360141722735900214">"Mostrar toques em teclas"</string> + <string name="show_key_presses_summary" msgid="725387457373015024">"Mostrar feedback visual (toques em teclas físicas)"</string> <string name="show_screen_updates" msgid="2078782895825535494">"Mostrar superfície atualizada"</string> <string name="show_screen_updates_summary" msgid="2126932969682087406">"Destacar a superfície da janela ao atualizar"</string> <string name="show_hw_screen_updates" msgid="2021286231267747506">"Ver atualizações de vistas"</string> diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml index 678c076aa03b..6880511f4d97 100644 --- a/packages/SettingsLib/res/values-pt/strings.xml +++ b/packages/SettingsLib/res/values-pt/strings.xml @@ -114,9 +114,9 @@ <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Acesso ao chip"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"Áudio HD: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"Áudio HD"</string> - <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Aparelhos auditivos"</string> + <string name="bluetooth_profile_hearing_aid" msgid="2607867572569689732">"Aparelhos auditivos"</string> <string name="bluetooth_profile_le_audio" msgid="1725521360076451751">"LE Audio"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"Conectado a aparelhos auditivos"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="5757754050938807276">"Conectado a aparelhos auditivos"</string> <string name="bluetooth_le_audio_profile_summary_connected" msgid="6916226974453480650">"Conectado ao perfil Áudio de baixa energia"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"Conectado ao áudio da mídia"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"Conectado ao áudio do smartphone"</string> @@ -134,7 +134,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="808970643123744170">"Usar para áudio do smartphone"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Usado para transferência de arquivo"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Usar para entrada"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Usar para aparelhos auditivos"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="3374057355721486932">"Usar para aparelhos auditivos"</string> <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"Usar para LE_AUDIO"</string> <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Parear"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"PAREAR"</string> @@ -355,6 +355,8 @@ <string name="pointer_location_summary" msgid="957120116989798464">"Exibir dados de toque"</string> <string name="show_touches" msgid="8437666942161289025">"Mostrar toques"</string> <string name="show_touches_summary" msgid="3692861665994502193">"Mostrar feedback visual para toques"</string> + <string name="show_key_presses" msgid="6360141722735900214">"Mostrar teclas pressionadas"</string> + <string name="show_key_presses_summary" msgid="725387457373015024">"Mostrar feedback visual de teclas pressionadas"</string> <string name="show_screen_updates" msgid="2078782895825535494">"Mostrar superfície atualizada"</string> <string name="show_screen_updates_summary" msgid="2126932969682087406">"Piscar superfícies de toda a janela ao atualizar"</string> <string name="show_hw_screen_updates" msgid="2021286231267747506">"Ver atualizações de exibição"</string> diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml index 7fa228bc5be6..1dce0e12cf1e 100644 --- a/packages/SettingsLib/res/values-ro/strings.xml +++ b/packages/SettingsLib/res/values-ro/strings.xml @@ -114,9 +114,9 @@ <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Acces la SIM"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"Audio HD: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"Audio HD"</string> - <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Aparate auditive"</string> + <string name="bluetooth_profile_hearing_aid" msgid="2607867572569689732">"Aparate auditive"</string> <string name="bluetooth_profile_le_audio" msgid="1725521360076451751">"LE Audio"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"Conectat la aparatul auditiv"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="5757754050938807276">"Conectat la aparatul auditiv"</string> <string name="bluetooth_le_audio_profile_summary_connected" msgid="6916226974453480650">"Conectat la LE audio"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"Conectat la profilul pentru conținut media audio"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"Conectat la componenta audio a telefonului"</string> @@ -134,7 +134,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="808970643123744170">"Folosește pentru componenta audio a telefonului"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Folosește pentru transferul de fișiere"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Folosește pentru introducere date"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Folosește pentru aparatele auditive"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="3374057355721486932">"Folosește pentru aparatele auditive"</string> <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"Folosește pentru LE_AUDIO"</string> <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Asociază"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"CONECTEAZĂ"</string> @@ -355,6 +355,8 @@ <string name="pointer_location_summary" msgid="957120116989798464">"Suprapunere care indică date curente pt. atingeri"</string> <string name="show_touches" msgid="8437666942161289025">"Afișează atingerile"</string> <string name="show_touches_summary" msgid="3692861665994502193">"Afișează feedbackul vizual pentru atingeri"</string> + <string name="show_key_presses" msgid="6360141722735900214">"Arată apăsările pe taste"</string> + <string name="show_key_presses_summary" msgid="725387457373015024">"Arată feedback vizual pentru apăsările pe taste"</string> <string name="show_screen_updates" msgid="2078782895825535494">"Actualizări suprafețe"</string> <string name="show_screen_updates_summary" msgid="2126932969682087406">"Iluminarea întregii fereastre la actualizare"</string> <string name="show_hw_screen_updates" msgid="2021286231267747506">"Afiș. actualizări ecran"</string> diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml index a53b67afc002..79db17c02750 100644 --- a/packages/SettingsLib/res/values-ru/strings.xml +++ b/packages/SettingsLib/res/values-ru/strings.xml @@ -114,9 +114,9 @@ <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Доступ к SIM-карте"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD Audio: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD Audio"</string> - <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Слуховые аппараты"</string> + <string name="bluetooth_profile_hearing_aid" msgid="2607867572569689732">"Слуховые аппараты"</string> <string name="bluetooth_profile_le_audio" msgid="1725521360076451751">"LE Audio"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"Слуховой аппарат подключен"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="5757754050938807276">"Слуховой аппарат подключен"</string> <string name="bluetooth_le_audio_profile_summary_connected" msgid="6916226974453480650">"Подключено к LE Audio"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"Подключено к мультимедийному аудиоустройству"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"Подключено к аудиоустройству телефона"</string> @@ -134,7 +134,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="808970643123744170">"Использовать для аудиоустройства телефона"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Используется для передачи файлов"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Использовать для ввода"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Использовать для слухового аппарата"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="3374057355721486932">"Использовать для слухового аппарата"</string> <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"Использовать для LE_AUDIO"</string> <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Добавить"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"ДОБАВИТЬ"</string> @@ -355,6 +355,8 @@ <string name="pointer_location_summary" msgid="957120116989798464">"Показывать данные касаний и жестов"</string> <string name="show_touches" msgid="8437666942161289025">"Показывать нажатия"</string> <string name="show_touches_summary" msgid="3692861665994502193">"Визуальный отклик при нажатии"</string> + <string name="show_key_presses" msgid="6360141722735900214">"Показывать нажатия клавиш"</string> + <string name="show_key_presses_summary" msgid="725387457373015024">"Показывать визуальный отклик при нажатии клавиш"</string> <string name="show_screen_updates" msgid="2078782895825535494">"Показ. обнов. поверхности"</string> <string name="show_screen_updates_summary" msgid="2126932969682087406">"Подсвечивать поверхности окон при обновлении"</string> <string name="show_hw_screen_updates" msgid="2021286231267747506">"Показывать обнов. экрана"</string> diff --git a/packages/SettingsLib/res/values-si/strings.xml b/packages/SettingsLib/res/values-si/strings.xml index ab5d3ceb8705..c43f5dd6426d 100644 --- a/packages/SettingsLib/res/values-si/strings.xml +++ b/packages/SettingsLib/res/values-si/strings.xml @@ -114,9 +114,9 @@ <string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM ප්රවේශය"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD ශ්රව්යය: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD ශ්රව්යය"</string> - <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"ශ්රවණාධාරක"</string> + <string name="bluetooth_profile_hearing_aid" msgid="2607867572569689732">"ශ්රවණාධාරක"</string> <string name="bluetooth_profile_le_audio" msgid="1725521360076451751">"LE ශ්රව්ය"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"ශ්රවණාධාරක වෙත සම්බන්ධ කළා"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="5757754050938807276">"ශ්රවණාධාරක වෙත සම්බන්ධ කළා"</string> <string name="bluetooth_le_audio_profile_summary_connected" msgid="6916226974453480650">"LE ශ්රව්ය වෙත සම්බන්ධ විය"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"මාධ්ය ශ්රව්යට සම්බන්ධ විය"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"දුරකතනයේ ශ්රව්යට සම්බන්ධ විය"</string> @@ -134,7 +134,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="808970643123744170">"දුරකථන ශ්රව්ය සඳහා භාවිතා කෙරේ"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"ගොනු හුවමාරුව සඳහා භාවිතා කරන්න"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"ආදානය සඳහා භාවිතා කරන්න"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"ශ්රවණාධාර සඳහා භාවිත කරන්න"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="3374057355721486932">"ශ්රවණාධාර සඳහා භාවිත කරන්න"</string> <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"LE_AUDIO සඳහා භාවිත කරන්න"</string> <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"යුගල කරන්න"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"යුගල කරන්න"</string> @@ -355,6 +355,8 @@ <string name="pointer_location_summary" msgid="957120116989798464">"තිර උඩැතිරිය වර්තමාන ස්පර්ශ දත්ත පෙන්වයි"</string> <string name="show_touches" msgid="8437666942161289025">"තට්ටු කිරීම් පෙන්වන්න"</string> <string name="show_touches_summary" msgid="3692861665994502193">"තට්ටු කිරීම් සඳහා දෘශ්ය ප්රතිපෝෂණ පෙන්වන්න"</string> + <string name="show_key_presses" msgid="6360141722735900214">"යතුරු එබීම් පෙන්වන්න"</string> + <string name="show_key_presses_summary" msgid="725387457373015024">"භෞතික යතුරු එබීම සඳහා දෘශ්ය ප්රතිපෝෂණය පෙන්වන්න"</string> <string name="show_screen_updates" msgid="2078782895825535494">"පෘෂ්ඨ යාවත්කාලීන පෙන්වන්න"</string> <string name="show_screen_updates_summary" msgid="2126932969682087406">"යාවත්කාලින වනවිට මුළු කවුළු තලයම දැල්වෙන්න"</string> <string name="show_hw_screen_updates" msgid="2021286231267747506">"නැරඹීම් යාවත්කාලීන පෙන්වන්න"</string> diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml index daf9492b351d..ec383f94fa44 100644 --- a/packages/SettingsLib/res/values-sk/strings.xml +++ b/packages/SettingsLib/res/values-sk/strings.xml @@ -114,9 +114,9 @@ <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Prístup k SIM karte"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD zvuk: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD zvuk"</string> - <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Načúvadlá"</string> + <string name="bluetooth_profile_hearing_aid" msgid="2607867572569689732">"Načúvadlá"</string> <string name="bluetooth_profile_le_audio" msgid="1725521360076451751">"LE Audio"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"Pripojené k načúvadlám"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="5757754050938807276">"Pripojené k načúvadlám"</string> <string name="bluetooth_le_audio_profile_summary_connected" msgid="6916226974453480650">"Pripojené k systému LE audio"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"Pripojené ku zvukovému médiu"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"Pripojené ku zvuku telefónu"</string> @@ -134,7 +134,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="808970643123744170">"Použiť pre zvuk telefónu"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Použiť na prenos súborov"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Použiť pre vstup"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Použiť pre načúvadlá"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="3374057355721486932">"Používať pre načúvadlá"</string> <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"Používať s profilom LE_AUDIO"</string> <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Párovať"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"PÁROVAŤ"</string> @@ -355,6 +355,8 @@ <string name="pointer_location_summary" msgid="957120116989798464">"Zobraziť prekryvnú vrstvu s aktuálnymi údajmi o klepnutiach"</string> <string name="show_touches" msgid="8437666942161289025">"Zobrazovať klepnutia"</string> <string name="show_touches_summary" msgid="3692861665994502193">"Vizuálne znázorňovať klepnutia"</string> + <string name="show_key_presses" msgid="6360141722735900214">"Zobrazovať stlač. kláves."</string> + <string name="show_key_presses_summary" msgid="725387457373015024">"Zobr. vizuálnu spätnú väzbu fyz. stlač. klávesov"</string> <string name="show_screen_updates" msgid="2078782895825535494">"Ukazovať obnovenia obsahu"</string> <string name="show_screen_updates_summary" msgid="2126932969682087406">"Rozblikať obsah okna pri aktualizácii"</string> <string name="show_hw_screen_updates" msgid="2021286231267747506">"Ukazovať obnovenia zobrazenia"</string> diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml index 7164da61c5d5..4d0e7a8e297f 100644 --- a/packages/SettingsLib/res/values-sl/strings.xml +++ b/packages/SettingsLib/res/values-sl/strings.xml @@ -114,9 +114,9 @@ <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Dostop do kartice SIM"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"Zvok visoke kakovosti: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"Zvok visoke kakovosti"</string> - <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Slušni pripomočki"</string> + <string name="bluetooth_profile_hearing_aid" msgid="2607867572569689732">"Slušni aparati"</string> <string name="bluetooth_profile_le_audio" msgid="1725521360076451751">"LE zvok"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"Povezava s slušnimi pripomočki je vzpostavljena"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="5757754050938807276">"Povezava s slušnimi aparati je vzpostavljena."</string> <string name="bluetooth_le_audio_profile_summary_connected" msgid="6916226974453480650">"Povezano s profilom LE Audio"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"Povezan s profilom za predstavnostni zvok"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"Povezava s profilom za zvok telefona vzpostavljena"</string> @@ -134,7 +134,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="808970643123744170">"Uporabi za zvok telefona"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Uporabi za prenos datotek"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Uporabi za vnos"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Uporabi za slušne pripomočke"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="3374057355721486932">"Uporabi za slušne aparate"</string> <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"Uporaba za profil LE_AUDIO"</string> <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Seznani"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"SEZNANI"</string> @@ -355,6 +355,8 @@ <string name="pointer_location_summary" msgid="957120116989798464">"Prekrivanje zaslona prikazuje trenutni dotik."</string> <string name="show_touches" msgid="8437666942161289025">"Prikaži dotike"</string> <string name="show_touches_summary" msgid="3692861665994502193">"Prikaži vizualne povratne informacije za dotike."</string> + <string name="show_key_presses" msgid="6360141722735900214">"Pokaži pritiske tipk"</string> + <string name="show_key_presses_summary" msgid="725387457373015024">"Pokaži vizualne povratne informacije za pritiske fizičnih tipk"</string> <string name="show_screen_updates" msgid="2078782895825535494">"Pokaži posodob. površine"</string> <string name="show_screen_updates_summary" msgid="2126932969682087406">"Ob posodobitvi osveži celotne površine oken."</string> <string name="show_hw_screen_updates" msgid="2021286231267747506">"Prikaži posodob. pogleda"</string> diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml index 7fd6ec9ab15f..2fdac37e1dc8 100644 --- a/packages/SettingsLib/res/values-sq/strings.xml +++ b/packages/SettingsLib/res/values-sq/strings.xml @@ -114,9 +114,9 @@ <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Qasje në kartën SIM"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"Audio HD: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"Audio HD"</string> - <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Aparatet e dëgjimit"</string> + <string name="bluetooth_profile_hearing_aid" msgid="2607867572569689732">"Aparatet e dëgjimit"</string> <string name="bluetooth_profile_le_audio" msgid="1725521360076451751">"LE Audio"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"Lidhur me aparatet e dëgjimit"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="5757754050938807276">"Lidhur me aparatet e dëgjimit"</string> <string name="bluetooth_le_audio_profile_summary_connected" msgid="6916226974453480650">"U lidh me audion LE"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"U lidh me audion e medias"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"U lidh me audion e telefonit"</string> @@ -134,7 +134,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="808970643123744170">"Përdor për audion e telefonit"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Përdor për transferimin e skedarëve"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Përdore për hyrjen"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Përdore për aparatet e dëgjimit"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="3374057355721486932">"Përdore për aparatet e dëgjimit"</string> <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"Përdor për LE_AUDIO"</string> <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Çifto"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"ÇIFTO"</string> @@ -355,6 +355,8 @@ <string name="pointer_location_summary" msgid="957120116989798464">"Mbivendosja e ekranit tregon të dhënat e prekjes"</string> <string name="show_touches" msgid="8437666942161289025">"Shfaq trokitjet"</string> <string name="show_touches_summary" msgid="3692861665994502193">"Shfaq reagimet vizuale për trokitjet"</string> + <string name="show_key_presses" msgid="6360141722735900214">"Shfaq shtypjet e tasteve"</string> + <string name="show_key_presses_summary" msgid="725387457373015024">"Shfaq reagime vizuale për shtypjen e tasteve fizike"</string> <string name="show_screen_updates" msgid="2078782895825535494">"Shfaq përditësimet e sipërfaqes"</string> <string name="show_screen_updates_summary" msgid="2126932969682087406">"Ndriço të gjitha sipërfaqet e dritares kur ato të përditësohen"</string> <string name="show_hw_screen_updates" msgid="2021286231267747506">"Shfaq përditësimet e pamjes"</string> diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml index 86675cf6f9ba..dc698ce037ec 100644 --- a/packages/SettingsLib/res/values-sr/strings.xml +++ b/packages/SettingsLib/res/values-sr/strings.xml @@ -114,9 +114,9 @@ <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Приступ SIM картици"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD звук: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD звук"</string> - <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Слушни апарати"</string> + <string name="bluetooth_profile_hearing_aid" msgid="2607867572569689732">"Слушни апарати"</string> <string name="bluetooth_profile_le_audio" msgid="1725521360076451751">"LE Audio"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"Повезано са слушним апаратима"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="5757754050938807276">"Повезано са слушним апаратима"</string> <string name="bluetooth_le_audio_profile_summary_connected" msgid="6916226974453480650">"Повезано са LE audio"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"Повезано са звуком медија"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"Повезано са звуком телефона"</string> @@ -134,7 +134,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="808970643123744170">"Коришћење за аудио телефона"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Коришћење за пренос датотека"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Користи за улаз"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Користи за слушне апарате"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="3374057355721486932">"Користи за слушне апарате"</string> <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"Користите за LE_AUDIO"</string> <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Упари"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"УПАРИ"</string> @@ -355,6 +355,8 @@ <string name="pointer_location_summary" msgid="957120116989798464">"Преклопни елемент са тренутним подацима о додиру"</string> <string name="show_touches" msgid="8437666942161289025">"Приказуј додире"</string> <string name="show_touches_summary" msgid="3692861665994502193">"Приказује визуелне повратне информације за додире"</string> + <string name="show_key_presses" msgid="6360141722735900214">"Приказуј притиске тастера"</string> + <string name="show_key_presses_summary" msgid="725387457373015024">"Приказује повратне информације за притиске тастера"</string> <string name="show_screen_updates" msgid="2078782895825535494">"Прикажи ажурирања површине"</string> <string name="show_screen_updates_summary" msgid="2126932969682087406">"Осветљава све површине прозора када се ажурирају"</string> <string name="show_hw_screen_updates" msgid="2021286231267747506">"Прикажи ажурирања приказа"</string> diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml index 0d71afd98fa9..64412dd5af5a 100644 --- a/packages/SettingsLib/res/values-sv/strings.xml +++ b/packages/SettingsLib/res/values-sv/strings.xml @@ -114,9 +114,9 @@ <string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM-åtkomst"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD-ljud: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD-ljud"</string> - <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Hörapparater"</string> + <string name="bluetooth_profile_hearing_aid" msgid="2607867572569689732">"Hörapparater"</string> <string name="bluetooth_profile_le_audio" msgid="1725521360076451751">"LE Audio"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"Ansluten till hörapparater"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="5757754050938807276">"Ansluten till hörapparater"</string> <string name="bluetooth_le_audio_profile_summary_connected" msgid="6916226974453480650">"Ansluten till LE audio"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"Ansluten till medialjud"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"Ansluten till telefonens ljud"</string> @@ -134,7 +134,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="808970643123744170">"Använd för telefonens ljud"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Använd för filöverföring"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Använd för inmatning"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Använd med hörapparater"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="3374057355721486932">"Använd med hörapparater"</string> <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"Använd för LE_AUDIO"</string> <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Parkoppla"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"PARKOPPLA"</string> @@ -355,6 +355,8 @@ <string name="pointer_location_summary" msgid="957120116989798464">"Överlägg på skärmen med aktuella skärmtryck"</string> <string name="show_touches" msgid="8437666942161289025">"Visa tryck"</string> <string name="show_touches_summary" msgid="3692861665994502193">"Visa visuell feedback för tryck"</string> + <string name="show_key_presses" msgid="6360141722735900214">"Visa tangenttryckningar"</string> + <string name="show_key_presses_summary" msgid="725387457373015024">"Visa visuell feedback för tangenttryckningar"</string> <string name="show_screen_updates" msgid="2078782895825535494">"Visa ytuppdateringar"</string> <string name="show_screen_updates_summary" msgid="2126932969682087406">"Hela fönstret blinkar vid uppdatering"</string> <string name="show_hw_screen_updates" msgid="2021286231267747506">"Visa visningsuppdatering"</string> diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml index 303131e3a5f2..4893fe838967 100644 --- a/packages/SettingsLib/res/values-sw/strings.xml +++ b/packages/SettingsLib/res/values-sw/strings.xml @@ -114,9 +114,9 @@ <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Ufikiaji wa SIM"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"Sauti ya HD: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"Sauti ya HD"</string> - <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Vifaa vya Kusaidia Kusikia"</string> + <string name="bluetooth_profile_hearing_aid" msgid="2607867572569689732">"Visaidizi vya kusikia"</string> <string name="bluetooth_profile_le_audio" msgid="1725521360076451751">"LE Audio"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"Imeunganishwa kwenye Vifaa vya Kusaidia Kusikia"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="5757754050938807276">"Imeunganishwa kwenye visaidizi vya kusikia"</string> <string name="bluetooth_le_audio_profile_summary_connected" msgid="6916226974453480650">"Imeunganishwa kwenye LE audio"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"Imeunganishwa kwenye sikika ya njia ya mawasiliano"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"Imeunganishwa kwenye sauti ya simu"</string> @@ -134,7 +134,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="808970643123744170">"Tumia kwa sauti ya simu"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Tumia kwa hali faili"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Tumia kwa kuingiza"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Tumia kwenye Vifaa vya Kusaidia Kusikia"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="3374057355721486932">"Tumia kwa visaidizi vya kusikia"</string> <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"Tumia kwa ajili ya LE_AUDIO"</string> <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Oanisha"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"OANISHA"</string> @@ -355,6 +355,8 @@ <string name="pointer_location_summary" msgid="957120116989798464">"Kuegeshwa kwa skrini ikionyesha data ya mguso ya sasa"</string> <string name="show_touches" msgid="8437666942161289025">"Onyesha unapogusa"</string> <string name="show_touches_summary" msgid="3692861665994502193">"Onyesha ishara za kuthibitisha unapogusa"</string> + <string name="show_key_presses" msgid="6360141722735900214">"Onyesha mibofyo ya vitufe"</string> + <string name="show_key_presses_summary" msgid="725387457373015024">"Onyesha muhtasari wa mibofyo halisi ya vitufe"</string> <string name="show_screen_updates" msgid="2078782895825535494">"Onyesha masasisho ya sehemu"</string> <string name="show_screen_updates_summary" msgid="2126932969682087406">"Angaza dirisha lote zitakaposasisha"</string> <string name="show_hw_screen_updates" msgid="2021286231267747506">"Onyesha taarifa za kuonekana"</string> diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml index 83aa2aff8a1c..b72c5339db38 100644 --- a/packages/SettingsLib/res/values-ta/strings.xml +++ b/packages/SettingsLib/res/values-ta/strings.xml @@ -114,9 +114,9 @@ <string name="bluetooth_profile_sap" msgid="8304170950447934386">"சிம் அணுகல்"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD ஆடியோ: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD ஆடியோ"</string> - <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"செவித்துணை கருவிகள்"</string> + <string name="bluetooth_profile_hearing_aid" msgid="2607867572569689732">"செவித்துணைக் கருவிகள்"</string> <string name="bluetooth_profile_le_audio" msgid="1725521360076451751">"LE ஆடியோ"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"செவித்துணை கருவிகளுடன் இணைக்கப்பட்டது"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="5757754050938807276">"செவித்துணைக் கருவிகளுடன் இணைக்கப்பட்டது"</string> <string name="bluetooth_le_audio_profile_summary_connected" msgid="6916226974453480650">"LE ஆடியோவுடன் இணைக்கப்பட்டுள்ளது"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"மீடியா ஆடியோவுடன் இணைக்கப்பட்டது"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"மொபைல் ஆடியோவுடன் இணைக்கப்பட்டது"</string> @@ -134,7 +134,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="808970643123744170">"மொபைல் ஆடியோவைப் பயன்படுத்து"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"ஃபைல் பரிமாற்றத்திற்காகப் பயன்படுத்து"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"உள்ளீட்டுக்குப் பயன்படுத்து"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"செவித்துணை கருவிகளுக்குப் பயன்படுத்தவும்"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="3374057355721486932">"செவித்துணைக் கருவிகளுக்காகப் பயன்படுத்தப்படும்"</string> <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"LE_AUDIOவிற்குப் பயன்படுத்தும்"</string> <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"இணை"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"இணை"</string> @@ -355,6 +355,8 @@ <string name="pointer_location_summary" msgid="957120116989798464">"திரையின் மேல் அடுக்கானது தற்போது தொடப்பட்டிருக்கும் தரவைக் காண்பிக்கிறது"</string> <string name="show_touches" msgid="8437666942161289025">"தட்டல்களைக் காட்டு"</string> <string name="show_touches_summary" msgid="3692861665994502193">"தட்டல்களின் போது காட்சி அறிகுறிகளைக் காட்டும்"</string> + <string name="show_key_presses" msgid="6360141722735900214">"பட்டன் அழுத்தத்தை காட்டவா"</string> + <string name="show_key_presses_summary" msgid="725387457373015024">"பட்டன் அழுத்தங்களைக் காட்சி மூலம் உறுதிப்படுத்தும்"</string> <string name="show_screen_updates" msgid="2078782895825535494">"மேலோட்ட புதுப்பிப்புகளைக் காட்டு"</string> <string name="show_screen_updates_summary" msgid="2126932969682087406">"சாளரத்தின் பரப்புநிலைகள் புதுப்பிக்கப்படும்போது, அவற்றை முழுவதுமாகக் காட்டும்"</string> <string name="show_hw_screen_updates" msgid="2021286231267747506">"வியூ அப்டேட்ஸைக் காட்டு"</string> diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml index e7cf933b05ad..32738bb2eacf 100644 --- a/packages/SettingsLib/res/values-te/strings.xml +++ b/packages/SettingsLib/res/values-te/strings.xml @@ -114,9 +114,9 @@ <string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM యాక్సెస్"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD ఆడియో: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD ఆడియో"</string> - <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"వినికిడి మద్దతు ఉపకరణాలు"</string> + <string name="bluetooth_profile_hearing_aid" msgid="2607867572569689732">"వినికిడి పరికరాలు"</string> <string name="bluetooth_profile_le_audio" msgid="1725521360076451751">"LE ఆడియో"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"వినికిడి మద్దతు ఉపకరణాలకు కనెక్ట్ చేయబడింది"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="5757754050938807276">"వినికిడి పరికరాలకు కనెక్ట్ చేయబడింది"</string> <string name="bluetooth_le_audio_profile_summary_connected" msgid="6916226974453480650">"LE ఆడియోకు కనెక్ట్ చేయబడింది"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"మీడియా ఆడియోకు కనెక్ట్ చేయబడింది"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"ఫోన్ ఆడియోకు కనెక్ట్ చేయబడింది"</string> @@ -134,7 +134,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="808970643123744170">"ఫోన్ ఆడియో కోసం ఉపయోగించండి"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"ఫైల్ బదిలీ కోసం ఉపయోగించండి"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"ఇన్పుట్ కోసం ఉపయోగించండి"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"వినికిడి మద్దతు ఉపకరణాలకు ఉపయోగించండి"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="3374057355721486932">"వినికిడి పరికరాల కోసం ఉపయోగించండి"</string> <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"LE_AUDIO కోసం ఉపయోగించండి"</string> <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"జత చేయి"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"జత చేయి"</string> @@ -355,6 +355,8 @@ <string name="pointer_location_summary" msgid="957120116989798464">"ప్రస్తుత టచ్ డేటాను చూపుతోన్న స్క్రీన్"</string> <string name="show_touches" msgid="8437666942161289025">"నొక్కినవి చూపు"</string> <string name="show_touches_summary" msgid="3692861665994502193">"నొక్కినప్పుడు దృశ్యపరమైన ప్రతిస్పందన చూపు"</string> + <string name="show_key_presses" msgid="6360141722735900214">"కీ ప్రెస్లను చూడండి"</string> + <string name="show_key_presses_summary" msgid="725387457373015024">"భౌతిక కీ ప్రెస్ల విజువల్ ఫీడ్బ్యాక్ను చూడండి"</string> <string name="show_screen_updates" msgid="2078782895825535494">"సర్ఫేస్ అప్డేట్లను చూపు"</string> <string name="show_screen_updates_summary" msgid="2126932969682087406">"విండో సర్ఫేస్లన్నీ అప్డేట్ అయితే ఫ్లాష్ చేయి"</string> <string name="show_hw_screen_updates" msgid="2021286231267747506">"వీక్షణ అప్డేట్లను చూపు"</string> diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml index 32629c32f969..14ef3a3703b4 100644 --- a/packages/SettingsLib/res/values-th/strings.xml +++ b/packages/SettingsLib/res/values-th/strings.xml @@ -114,9 +114,9 @@ <string name="bluetooth_profile_sap" msgid="8304170950447934386">"การเข้าถึงซิม"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"เสียง HD: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"เสียง HD"</string> - <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"เครื่องช่วยฟัง"</string> + <string name="bluetooth_profile_hearing_aid" msgid="2607867572569689732">"เครื่องช่วยฟัง"</string> <string name="bluetooth_profile_le_audio" msgid="1725521360076451751">"LE Audio"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"เชื่อมต่อกับเครื่องช่วยฟังแล้ว"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="5757754050938807276">"เชื่อมต่อกับเครื่องช่วยฟังแล้ว"</string> <string name="bluetooth_le_audio_profile_summary_connected" msgid="6916226974453480650">"เชื่อมต่อกับ LE Audio แล้ว"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"เชื่อมต่อกับระบบเสียงของสื่อแล้ว"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"เชื่อมต่อกับระบบเสียงของโทรศัพท์แล้ว"</string> @@ -134,7 +134,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="808970643123744170">"ใช้สำหรับระบบเสียงของโทรศัพท์"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"ใช้สำหรับการโอนไฟล์"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"ใช้สำหรับการป้อนข้อมูล"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"ใช้สำหรับเครื่องช่วยฟัง"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="3374057355721486932">"ใช้สำหรับเครื่องช่วยฟัง"</string> <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"ใช้สำหรับ LE_AUDIO"</string> <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"จับคู่"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"จับคู่อุปกรณ์"</string> @@ -355,6 +355,8 @@ <string name="pointer_location_summary" msgid="957120116989798464">"การวางซ้อนหน้าจอที่แสดงข้อมูลการแตะในปัจจุบัน"</string> <string name="show_touches" msgid="8437666942161289025">"แสดงการแตะ"</string> <string name="show_touches_summary" msgid="3692861665994502193">"แสดงผลตอบสนองแบบภาพเมื่อแตะ"</string> + <string name="show_key_presses" msgid="6360141722735900214">"แสดงการกดแป้น"</string> + <string name="show_key_presses_summary" msgid="725387457373015024">"แสดงฟีดแบ็กที่เป็นภาพสำหรับการกดแป้นพิมพ์"</string> <string name="show_screen_updates" msgid="2078782895825535494">"แสดงการอัปเดตพื้นผิว"</string> <string name="show_screen_updates_summary" msgid="2126932969682087406">"กะพริบหน้าต่างทั้งหมดเมื่อมีการอัปเดต"</string> <string name="show_hw_screen_updates" msgid="2021286231267747506">"แสดงการอัปเดตมุมมอง"</string> diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml index e60faf6bc823..ab4acb650fbc 100644 --- a/packages/SettingsLib/res/values-tl/strings.xml +++ b/packages/SettingsLib/res/values-tl/strings.xml @@ -114,9 +114,9 @@ <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Access sa SIM"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD audio: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD audio"</string> - <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Mga Hearing Aid"</string> + <string name="bluetooth_profile_hearing_aid" msgid="2607867572569689732">"Mga hearing aid"</string> <string name="bluetooth_profile_le_audio" msgid="1725521360076451751">"LE Audio"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"Nakakonekta sa Mga Hearing Aid"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="5757754050938807276">"Nakakonekta sa mga hearing aid"</string> <string name="bluetooth_le_audio_profile_summary_connected" msgid="6916226974453480650">"Nakakonekta sa LE audio"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"Konektado sa media audio"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"Nakakonekta sa audio ng telepono"</string> @@ -134,7 +134,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="808970643123744170">"Ginagamit para sa audio ng telepono"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Ginagamit para sa paglilipat ng file"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Gamitin para sa input"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Gamitin para sa Mga Hearing Aid"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="3374057355721486932">"Gamitin para sa mga hearing aid"</string> <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"Gamitin para sa LE_AUDIO"</string> <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Ipares"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"IPARES"</string> @@ -355,6 +355,8 @@ <string name="pointer_location_summary" msgid="957120116989798464">"Overlay ng screen na nagpapakita ng touch data"</string> <string name="show_touches" msgid="8437666942161289025">"Ipakita ang mga pag-tap"</string> <string name="show_touches_summary" msgid="3692861665994502193">"Ipakita ang visual na feedback para sa mga pag-tap"</string> + <string name="show_key_presses" msgid="6360141722735900214">"Ipakita ang mga key press"</string> + <string name="show_key_presses_summary" msgid="725387457373015024">"Ipakita: visual feedback ng mga physical key press"</string> <string name="show_screen_updates" msgid="2078782895825535494">"Ipakita update sa surface"</string> <string name="show_screen_updates_summary" msgid="2126932969682087406">"I-flash ang buong window surface kapag nag-update"</string> <string name="show_hw_screen_updates" msgid="2021286231267747506">"Ipakita update ng view"</string> diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml index 3ea12adfd3df..abbef53f4ee7 100644 --- a/packages/SettingsLib/res/values-tr/strings.xml +++ b/packages/SettingsLib/res/values-tr/strings.xml @@ -114,9 +114,9 @@ <string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM Erişimi"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD ses: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD ses"</string> - <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"İşitme Cihazları"</string> + <string name="bluetooth_profile_hearing_aid" msgid="2607867572569689732">"İşitme cihazları"</string> <string name="bluetooth_profile_le_audio" msgid="1725521360076451751">"LE Audio"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"İşitme Cihazlarına Bağlandı"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="5757754050938807276">"İşitme cihazlarına bağlandı"</string> <string name="bluetooth_le_audio_profile_summary_connected" msgid="6916226974453480650">"LE Audio\'ya bağlandı"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"Medya sesine bağlanıldı"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"Telefon sesine bağlandı"</string> @@ -134,7 +134,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="808970643123744170">"Telefon sesi için kullan"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Dosya aktarımı için kullan"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Giriş için kullan"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"İşitme Cihazları için kullan"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="3374057355721486932">"İşitme cihazları için kullan"</string> <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"LE_AUDIO kullan"</string> <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Eşle"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"EŞLE"</string> @@ -355,6 +355,8 @@ <string name="pointer_location_summary" msgid="957120116989798464">"Mevcut dokunmatik verilerini gösteren yer paylaşımı"</string> <string name="show_touches" msgid="8437666942161289025">"Dokunmayı göster"</string> <string name="show_touches_summary" msgid="3692861665994502193">"Dokunmalarda görsel geri bildirim göster"</string> + <string name="show_key_presses" msgid="6360141722735900214">"Basılan tuşları göster"</string> + <string name="show_key_presses_summary" msgid="725387457373015024">"Fiziksel tuşlara basınca görsel geri bildirim ver"</string> <string name="show_screen_updates" msgid="2078782895825535494">"Yüzey güncellemelerini göster"</string> <string name="show_screen_updates_summary" msgid="2126932969682087406">"Güncellenirken tüm pencere yüzeylerini yakıp söndür"</string> <string name="show_hw_screen_updates" msgid="2021286231267747506">"Görünüm güncellemelerini göster"</string> diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml index 95ece2660b76..bf297e66c9d8 100644 --- a/packages/SettingsLib/res/values-uk/strings.xml +++ b/packages/SettingsLib/res/values-uk/strings.xml @@ -114,9 +114,9 @@ <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Доступ до SIM-карти"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD-аудіо: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD-аудіо"</string> - <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Слухові апарати"</string> + <string name="bluetooth_profile_hearing_aid" msgid="2607867572569689732">"Слухові апарати"</string> <string name="bluetooth_profile_le_audio" msgid="1725521360076451751">"LE Audio"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"Підключено до слухових апаратів"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="5757754050938807276">"Підключено до слухових апаратів"</string> <string name="bluetooth_le_audio_profile_summary_connected" msgid="6916226974453480650">"Підключено до LE Audio"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"Підключено до аудіоджерела"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"Підключено до звуку телеф."</string> @@ -134,7 +134,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="808970643123744170">"Викор. для звуку тел."</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Викор. для перед. файлів"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Викор. для введ."</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Використовувати для слухових апаратів"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="3374057355721486932">"Використовувати для слухових апаратів"</string> <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"Використовувати для LE_AUDIO"</string> <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Підключити"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"ПІДКЛЮЧИТИСЯ"</string> @@ -355,6 +355,8 @@ <string name="pointer_location_summary" msgid="957120116989798464">"Показувати на екрані жести й натискання"</string> <string name="show_touches" msgid="8437666942161289025">"Показувати дотики"</string> <string name="show_touches_summary" msgid="3692861665994502193">"Показувати візуальну реакцію на торкання"</string> + <string name="show_key_presses" msgid="6360141722735900214">"Показувати натиск. клавіш"</string> + <string name="show_key_presses_summary" msgid="725387457373015024">"Показувати візуальний відгук на натискання клавіш"</string> <string name="show_screen_updates" msgid="2078782895825535494">"Показ. оновлення поверхні"</string> <string name="show_screen_updates_summary" msgid="2126932969682087406">"Підсвічувати вікна повністю під час оновлення"</string> <string name="show_hw_screen_updates" msgid="2021286231267747506">"Показувати оновлення областей"</string> diff --git a/packages/SettingsLib/res/values-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml index 70aa9a3ddbb7..db2bb2615aac 100644 --- a/packages/SettingsLib/res/values-ur/strings.xml +++ b/packages/SettingsLib/res/values-ur/strings.xml @@ -114,9 +114,9 @@ <string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM رسائی"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD آڈیو: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD آڈیو"</string> - <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"سماعتی آلات"</string> + <string name="bluetooth_profile_hearing_aid" msgid="2607867572569689732">"سماعتی آلات"</string> <string name="bluetooth_profile_le_audio" msgid="1725521360076451751">"LE آڈیو"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"سماعتی آلات سے منسلک ہے"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="5757754050938807276">"سماعتی آلات سے منسلک ہے"</string> <string name="bluetooth_le_audio_profile_summary_connected" msgid="6916226974453480650">"LE آڈیو سے منسلک ہے"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"میڈیا آڈیو سے مربوط"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"فون آڈیو سے مربوط"</string> @@ -134,7 +134,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="808970643123744170">"فون آڈیو کیلئے استعمال کریں"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"فائل منتقل کرنے کیلئے استعمال کریں"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"ان پٹ کیلئے استعمال"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"سماعتی آلات کے لیے استعمال کریں"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="3374057355721486932">"سماعتی آلات کیلئے استعمال کریں"</string> <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"LE_AUDIO کے لیے استعمال کریں"</string> <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"جوڑا بنائیں"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"جوڑا بنائیں"</string> @@ -355,6 +355,8 @@ <string name="pointer_location_summary" msgid="957120116989798464">"موجودہ ٹچ ڈیٹا دکھانے والا اسکرین اوور لے"</string> <string name="show_touches" msgid="8437666942161289025">"تھپتھپاہٹیں دکھائیں"</string> <string name="show_touches_summary" msgid="3692861665994502193">"تھپتھپاہٹوں کیلئے بصری تاثرات دکھائیں"</string> + <string name="show_key_presses" msgid="6360141722735900214">"\'کلید کو دبانا\' دکھائیں"</string> + <string name="show_key_presses_summary" msgid="725387457373015024">"\'طبعی کلید کو دبانا\' کیلئے ویژوئل تاثرات دکھائیں"</string> <string name="show_screen_updates" msgid="2078782895825535494">"سطح کے اپ ڈیٹس دکھائیں"</string> <string name="show_screen_updates_summary" msgid="2126932969682087406">"اپ ڈیٹ ہونے پر ونڈو کی پوری سطحیں جھلملائیں"</string> <string name="show_hw_screen_updates" msgid="2021286231267747506">"منظر کے اپ ڈیٹس دکھائیں"</string> diff --git a/packages/SettingsLib/res/values-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml index 5f42969a23f1..96e5d6a085c2 100644 --- a/packages/SettingsLib/res/values-uz/strings.xml +++ b/packages/SettingsLib/res/values-uz/strings.xml @@ -114,9 +114,9 @@ <string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM kartaga kirish"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD audio: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD audio"</string> - <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Eshitish apparatlari"</string> + <string name="bluetooth_profile_hearing_aid" msgid="2607867572569689732">"Eshitish moslamalari"</string> <string name="bluetooth_profile_le_audio" msgid="1725521360076451751">"LE Audio"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"Eshitish apparatlariga ulangan"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="5757754050938807276">"Eshitish moslamalariga ulangan"</string> <string name="bluetooth_le_audio_profile_summary_connected" msgid="6916226974453480650">"LE audioga ulandi"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"Audio qurilmasiga ulangan"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"Telefon karnayiga ulanildi"</string> @@ -134,7 +134,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="808970643123744170">"Dok’dan karnay sifatida foydalanish"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Fayl almashinish uchun foydalanish"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Kiritish qurilmasi sifatida foydalanish"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Eshitish apparatlari uchun foydalanish"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="3374057355721486932">"Eshitish moslamalari uchun foydalanish"</string> <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"LE_AUDIO uchun foydalanish"</string> <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"OK"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"ULANISH"</string> @@ -355,6 +355,8 @@ <string name="pointer_location_summary" msgid="957120116989798464">"Ekranda bosilgan va tegilgan joylarni vizuallashtirish"</string> <string name="show_touches" msgid="8437666942161289025">"Bosishlarni ko‘rsatish"</string> <string name="show_touches_summary" msgid="3692861665994502193">"Ekranda bosilgan joylardagi nuqtalarni ko‘rsatish"</string> + <string name="show_key_presses" msgid="6360141722735900214">"Tugma bosishlarini chiqarish"</string> + <string name="show_key_presses_summary" msgid="725387457373015024">"Jismoniy tugmani bosishlar uchun vizual fikr-mulohazani chiqarish"</string> <string name="show_screen_updates" msgid="2078782895825535494">"Yuza yangilanishlarini ko‘rsatish"</string> <string name="show_screen_updates_summary" msgid="2126932969682087406">"Yangilangandan so‘ng to‘liq oyna sirtlarini miltillatish"</string> <string name="show_hw_screen_updates" msgid="2021286231267747506">"Yangilash oynasini ochish"</string> diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml index d75f1b266283..7035077087ef 100644 --- a/packages/SettingsLib/res/values-vi/strings.xml +++ b/packages/SettingsLib/res/values-vi/strings.xml @@ -114,9 +114,9 @@ <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Truy cập SIM"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"Âm thanh HD: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"Âm thanh HD"</string> - <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Thiết bị trợ thính"</string> + <string name="bluetooth_profile_hearing_aid" msgid="2607867572569689732">"Thiết bị trợ thính"</string> <string name="bluetooth_profile_le_audio" msgid="1725521360076451751">"Âm thanh năng lượng thấp"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"Đã kết nối với Thiết bị trợ thính"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="5757754050938807276">"Đã kết nối với thiết bị trợ thính"</string> <string name="bluetooth_le_audio_profile_summary_connected" msgid="6916226974453480650">"Đã kết nối với âm thanh LE"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"Đã kết nối với âm thanh nội dung nghe nhìn"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"Đã kết nối với âm thanh điện thoại"</string> @@ -134,7 +134,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="808970643123744170">"Sử dụng cho âm thanh điện thoại"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Sử dụng để chuyển tệp"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Sử dụng để nhập"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Dùng cho Thiết bị trợ thính"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="3374057355721486932">"Dùng cho thiết bị trợ thính"</string> <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"Dùng cho LE_AUDIO"</string> <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Ghép nối"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"GHÉP NỐI"</string> @@ -355,6 +355,8 @@ <string name="pointer_location_summary" msgid="957120116989798464">"Lớp phủ màn hình hiển thị dữ liệu chạm hiện tại"</string> <string name="show_touches" msgid="8437666942161289025">"Hiện số lần nhấn"</string> <string name="show_touches_summary" msgid="3692861665994502193">"Hiển thị phản hồi trực quan cho các lần nhấn"</string> + <string name="show_key_presses" msgid="6360141722735900214">"Hiện thao tác nhấn phím"</string> + <string name="show_key_presses_summary" msgid="725387457373015024">"Hiện phản hồi trực quan cho thao tác nhấn phím vật lý"</string> <string name="show_screen_updates" msgid="2078782895825535494">"Hiện bản cập nhật giao diện"</string> <string name="show_screen_updates_summary" msgid="2126932969682087406">"Chuyển nhanh toàn bộ các giao diện cửa sổ khi các giao diện này cập nhật"</string> <string name="show_hw_screen_updates" msgid="2021286231267747506">"Hiện bản cập nhật chế độ xem"</string> diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml index 6787f324b9e5..fabc00483d7e 100644 --- a/packages/SettingsLib/res/values-zh-rCN/strings.xml +++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml @@ -114,9 +114,9 @@ <string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM 卡访问权限"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD 音频:<xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD 音频"</string> - <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"助听器"</string> + <string name="bluetooth_profile_hearing_aid" msgid="2607867572569689732">"助听器"</string> <string name="bluetooth_profile_le_audio" msgid="1725521360076451751">"LE 音频"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"已连接到助听器"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="5757754050938807276">"已连接到助听器"</string> <string name="bluetooth_le_audio_profile_summary_connected" msgid="6916226974453480650">"已连接到 LE 音频"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"已连接到媒体音频"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"已连接到手机音频"</string> @@ -134,7 +134,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="808970643123744170">"用于手机音频"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"用于文件传输"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"用于输入"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"用于助听器"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="3374057355721486932">"用于助听器"</string> <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"用于 LE_AUDIO"</string> <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"配对"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"配对"</string> @@ -355,6 +355,8 @@ <string name="pointer_location_summary" msgid="957120116989798464">"屏幕叠加层显示当前触摸数据"</string> <string name="show_touches" msgid="8437666942161289025">"显示点按操作反馈"</string> <string name="show_touches_summary" msgid="3692861665994502193">"显示点按操作的视觉反馈"</string> + <string name="show_key_presses" msgid="6360141722735900214">"显示按键操作"</string> + <string name="show_key_presses_summary" msgid="725387457373015024">"显示实体按键操作的视觉反馈"</string> <string name="show_screen_updates" msgid="2078782895825535494">"显示面 (surface) 更新"</string> <string name="show_screen_updates_summary" msgid="2126932969682087406">"窗口中的面 (surface) 更新时全部闪烁"</string> <string name="show_hw_screen_updates" msgid="2021286231267747506">"显示视图更新"</string> diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml index e6da4bc6eca5..9ec57a33a931 100644 --- a/packages/SettingsLib/res/values-zh-rHK/strings.xml +++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml @@ -114,9 +114,9 @@ <string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM 卡存取"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"高清音訊:<xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"高清音訊"</string> - <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"助聽器"</string> + <string name="bluetooth_profile_hearing_aid" msgid="2607867572569689732">"助聽器"</string> <string name="bluetooth_profile_le_audio" msgid="1725521360076451751">"LE Audio"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"已連接助聽器"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="5757754050938807276">"已連接助聽器"</string> <string name="bluetooth_le_audio_profile_summary_connected" msgid="6916226974453480650">"已連接 LE Audio"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"已連接媒體音頻裝置"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"已連接手機耳機"</string> @@ -134,7 +134,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="808970643123744170">"用於手機音效"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"用於傳輸檔案"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"用於輸入"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"用於助聽器"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="3374057355721486932">"用於助聽器"</string> <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"用於 LE_AUDIO"</string> <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"配對"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"配對"</string> @@ -355,6 +355,8 @@ <string name="pointer_location_summary" msgid="957120116989798464">"在螢幕上重疊顯示目前的觸控資料"</string> <string name="show_touches" msgid="8437666942161289025">"顯示輕按回應"</string> <string name="show_touches_summary" msgid="3692861665994502193">"顯示輕按位置的視覺回應"</string> + <string name="show_key_presses" msgid="6360141722735900214">"顯示按鍵操作"</string> + <string name="show_key_presses_summary" msgid="725387457373015024">"顯示實際按鍵操作的視覺回應"</string> <string name="show_screen_updates" msgid="2078782895825535494">"顯示表層更新"</string> <string name="show_screen_updates_summary" msgid="2126932969682087406">"更新表層時閃動整個視窗表層"</string> <string name="show_hw_screen_updates" msgid="2021286231267747506">"顯示畫面更新"</string> diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml index e33934cead42..59e66aaebb5c 100644 --- a/packages/SettingsLib/res/values-zh-rTW/strings.xml +++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml @@ -114,9 +114,9 @@ <string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM 卡存取權"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD 高解析音訊:<xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD 高解析音訊"</string> - <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"助聽器"</string> + <string name="bluetooth_profile_hearing_aid" msgid="2607867572569689732">"助聽器"</string> <string name="bluetooth_profile_le_audio" msgid="1725521360076451751">"LE Audio"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"已連接到助聽器"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="5757754050938807276">"已連上助聽器"</string> <string name="bluetooth_le_audio_profile_summary_connected" msgid="6916226974453480650">"已連上 LE audio"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"連接至媒體音訊"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"連接至電話音訊"</string> @@ -134,7 +134,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="808970643123744170">"用於電話音訊"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"用於傳輸檔案"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"用於輸入"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"用於助聽器"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="3374057355721486932">"用於助聽器"</string> <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"用於 LE_AUDIO"</string> <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"配對"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"配對"</string> @@ -355,6 +355,8 @@ <string name="pointer_location_summary" msgid="957120116989798464">"在螢幕圖層上顯示目前的觸控資料"</string> <string name="show_touches" msgid="8437666942161289025">"顯示觸控回應"</string> <string name="show_touches_summary" msgid="3692861665994502193">"顯示觸控位置的視覺回應"</string> + <string name="show_key_presses" msgid="6360141722735900214">"顯示按鍵操作"</string> + <string name="show_key_presses_summary" msgid="725387457373015024">"顯示實際按鍵操作的視覺回饋"</string> <string name="show_screen_updates" msgid="2078782895825535494">"顯示表層更新"</string> <string name="show_screen_updates_summary" msgid="2126932969682087406">"更新表層時閃爍顯示整個視窗表層"</string> <string name="show_hw_screen_updates" msgid="2021286231267747506">"顯示畫面更新"</string> diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml index 5c7a7b557f64..d292bd567718 100644 --- a/packages/SettingsLib/res/values-zu/strings.xml +++ b/packages/SettingsLib/res/values-zu/strings.xml @@ -114,9 +114,9 @@ <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Ukufinyelela kwe-SIM"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"Umsindo we-HD: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"Umsindo we-HD"</string> - <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Izinsiza zokuzwa"</string> + <string name="bluetooth_profile_hearing_aid" msgid="2607867572569689732">"Imishini yendlebe"</string> <string name="bluetooth_profile_le_audio" msgid="1725521360076451751">"Umsindo we-LE"</string> - <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"Kuxhumeke kwizinsiza zokuzwa"</string> + <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="5757754050938807276">"Kuxhume emishinini yendlebe"</string> <string name="bluetooth_le_audio_profile_summary_connected" msgid="6916226974453480650">"Kuxhunywe kumsindo we-LE"</string> <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"Ixhume emsindweni wemidiya"</string> <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"Ixhunywe kumsindo wefoni"</string> @@ -134,7 +134,7 @@ <string name="bluetooth_headset_profile_summary_use_for" msgid="808970643123744170">"Sebenziselwa umsindo wefoni"</string> <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Sebenziselwa ukudlulisa ifayela"</string> <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Isetshenziselwa okufakwayo"</string> - <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Sebenzisa izinsiza zokuzwa"</string> + <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="3374057355721486932">"Sebenzisa imishini yendlebe"</string> <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"Sebenzisela i-LE_AUDIO"</string> <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Bhangqa"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"BHANGQA"</string> @@ -355,6 +355,8 @@ <string name="pointer_location_summary" msgid="957120116989798464">"Imbondela yesikrini ibonisa idatha yokuthinta yamanje"</string> <string name="show_touches" msgid="8437666942161289025">"Bonisa amathebhu"</string> <string name="show_touches_summary" msgid="3692861665994502193">"Bonisa izmpendulo ebukekayo ngamathebhu"</string> + <string name="show_key_presses" msgid="6360141722735900214">"Bonisa ukucindezela ukhiye"</string> + <string name="show_key_presses_summary" msgid="725387457373015024">"Bonisa impendulo ebonakalayo yokucindezela ukhiye obonakalayo"</string> <string name="show_screen_updates" msgid="2078782895825535494">"Buka izibuyekezo ezibonakalayo"</string> <string name="show_screen_updates_summary" msgid="2126932969682087406">"Khanyisa ukubonakala kwalo lonke iwindi uma libuyekezwa"</string> <string name="show_hw_screen_updates" msgid="2021286231267747506">"Bonisa izibuyekezo zokubuka"</string> diff --git a/packages/SettingsLib/res/values/dimens.xml b/packages/SettingsLib/res/values/dimens.xml index 91549d73dfdb..07854bd3e5d8 100644 --- a/packages/SettingsLib/res/values/dimens.xml +++ b/packages/SettingsLib/res/values/dimens.xml @@ -124,4 +124,5 @@ <dimen name="dialog_bottom_padding">18dp</dimen> <dimen name="dialog_side_padding">24dp</dimen> <dimen name="dialog_button_bar_top_padding">32dp</dimen> + <dimen name="button_corner_radius">28dp</dimen> </resources> diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml index b44c0e0799b2..60bc226b5af5 100644 --- a/packages/SettingsLib/res/values/strings.xml +++ b/packages/SettingsLib/res/values/strings.xml @@ -241,12 +241,12 @@ <!-- Bluetooth settings. Similar to bluetooth_profile_a2dp_high_quality, but used when the device supports high quality audio but we don't know which codec that will be used. --> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec">HD audio</string> - <!-- Bluetooth settings. The user-visible string that is used whenever referring to the Hearing Aid profile. --> - <string name="bluetooth_profile_hearing_aid">Hearing Aids</string> + <!-- Bluetooth settings. The user-visible string that is used whenever referring to the Hearing aid profile. --> + <string name="bluetooth_profile_hearing_aid">Hearing aids</string> <!-- Bluetooth settings. The user-visible string that is used whenever referring to the LE audio profile. --> <string name="bluetooth_profile_le_audio">LE Audio</string> - <!-- Bluetooth settings. Connection options screen. The summary for the Hearing Aid checkbox preference when Hearing Aid is connected. --> - <string name="bluetooth_hearing_aid_profile_summary_connected">Connected to Hearing Aids</string> + <!-- Bluetooth settings. Connection options screen. The summary for the Hearing aid checkbox preference when hearing aid is connected. --> + <string name="bluetooth_hearing_aid_profile_summary_connected">Connected to hearing aids</string> <!-- Bluetooth settings. Connection options screen. The summary for the LE audio checkbox preference when LE audio is connected. --> <string name="bluetooth_le_audio_profile_summary_connected">Connected to LE audio</string> @@ -287,8 +287,8 @@ for the HID checkbox preference that describes how checking it will set the HID profile as preferred. --> <string name="bluetooth_hid_profile_summary_use_for">Use for input</string> - <!-- Bluetooth settings. Connection options screen. The summary for the Hearing Aid checkbox preference that describes how checking it will set the Hearing Aid profile as preferred. --> - <string name="bluetooth_hearing_aid_profile_summary_use_for">Use for Hearing Aids</string> + <!-- Bluetooth settings. Connection options screen. The summary for the Hearing aid checkbox preference that describes how checking it will set the hearing aid related profile as preferred. --> + <string name="bluetooth_hearing_aid_profile_summary_use_for">Use for hearing aids</string> <!-- Bluetooth settings. Connection options screen. The summary for the LE_AUDIO checkbox preference that describes how checking it will set the LE_AUDIO profile as preferred. --> <string name="bluetooth_le_audio_profile_summary_use_for">Use for LE_AUDIO</string> @@ -827,6 +827,11 @@ <!-- UI debug setting: show touches location summary [CHAR LIMIT=50] --> <string name="show_touches_summary">Show visual feedback for taps</string> + <!-- UI debug setting: show key presses? [CHAR LIMIT=50] --> + <string name="show_key_presses">Show key presses</string> + <!-- UI debug setting: show physical key presses summary [CHAR LIMIT=150] --> + <string name="show_key_presses_summary">Show visual feedback for physical key presses</string> + <!-- UI debug setting: show where surface updates happen? [CHAR LIMIT=25] --> <string name="show_screen_updates">Show surface updates</string> <!-- UI debug setting: show surface updates summary [CHAR LIMIT=50] --> diff --git a/packages/SettingsLib/res/values/styles.xml b/packages/SettingsLib/res/values/styles.xml index 4ac4aae8f2ab..1249c6b71b55 100644 --- a/packages/SettingsLib/res/values/styles.xml +++ b/packages/SettingsLib/res/values/styles.xml @@ -93,7 +93,6 @@ </style> <style name="DialogButtonPositive"> - <item name="android:buttonCornerRadius">0dp</item> <item name="android:background">@drawable/dialog_btn_filled</item> <item name="android:textColor">?androidprv:attr/textColorOnAccent</item> <item name="android:textSize">14sp</item> @@ -104,7 +103,6 @@ </style> <style name="DialogButtonNegative"> - <item name="android:buttonCornerRadius">28dp</item> <item name="android:background">@drawable/dialog_btn_outline</item> <item name="android:textColor">?android:attr/textColorPrimary</item> <item name="android:textSize">14sp</item> diff --git a/packages/SettingsLib/search/Android.bp b/packages/SettingsLib/search/Android.bp index cfff519705f2..918d696fa481 100644 --- a/packages/SettingsLib/search/Android.bp +++ b/packages/SettingsLib/search/Android.bp @@ -7,8 +7,18 @@ package { default_applicable_licenses: ["frameworks_base_license"], } +java_library { + name: "SettingsLib-search-interface", + visibility: ["//visibility:private"], + srcs: ["interface-src/**/*.java"], + host_supported: true, +} + android_library { name: "SettingsLib-search", + static_libs: [ + "SettingsLib-search-interface", + ], srcs: ["src/**/*.java"], sdk_version: "system_current", @@ -19,12 +29,10 @@ java_plugin { name: "SettingsLib-annotation-processor", processor_class: "com.android.settingslib.search.IndexableProcessor", static_libs: [ + "SettingsLib-search-interface", "javapoet", ], - srcs: [ - "processor-src/**/*.java", - "src/com/android/settingslib/search/SearchIndexable.java", - ], + srcs: ["processor-src/**/*.java"], java_resource_dirs: ["resources"], } diff --git a/packages/SettingsLib/search/common.mk b/packages/SettingsLib/search/common.mk deleted file mode 100644 index 05226db5cb91..000000000000 --- a/packages/SettingsLib/search/common.mk +++ /dev/null @@ -1,10 +0,0 @@ -# Include this file to generate SearchIndexableResourcesImpl - -LOCAL_ANNOTATION_PROCESSORS += \ - SettingsLib-annotation-processor - -LOCAL_ANNOTATION_PROCESSOR_CLASSES += \ - com.android.settingslib.search.IndexableProcessor - -LOCAL_STATIC_JAVA_LIBRARIES += \ - SettingsLib-search diff --git a/packages/SettingsLib/search/src/com/android/settingslib/search/SearchIndexable.java b/packages/SettingsLib/search/interface-src/com/android/settingslib/search/SearchIndexable.java index 638fa3e98138..174f33709b02 100644 --- a/packages/SettingsLib/search/src/com/android/settingslib/search/SearchIndexable.java +++ b/packages/SettingsLib/search/interface-src/com/android/settingslib/search/SearchIndexable.java @@ -27,7 +27,7 @@ public @interface SearchIndexable { /** * Bitfield for the form factors this class should be considered indexable for. * Default is {@link #ALL}. - * + * <p> * TODO: actually use this value somehow */ int forTarget() default ALL; @@ -35,27 +35,27 @@ public @interface SearchIndexable { /** * Indicates that the class should be considered indexable for Mobile. */ - int MOBILE = 1<<0; + int MOBILE = 1 << 0; /** * Indicates that the class should be considered indexable for TV. */ - int TV = 1<<1; + int TV = 1 << 1; /** * Indicates that the class should be considered indexable for Wear. */ - int WEAR = 1<<2; + int WEAR = 1 << 2; /** * Indicates that the class should be considered indexable for Auto. */ - int AUTO = 1<<3; + int AUTO = 1 << 3; /** * Indicates that the class should be considered indexable for ARC++. */ - int ARC = 1<<4; + int ARC = 1 << 4; /** * Indicates that the class should be considered indexable for all targets. diff --git a/packages/SettingsLib/search/processor-src/com/android/settingslib/search/IndexableProcessor.java b/packages/SettingsLib/search/processor-src/com/android/settingslib/search/IndexableProcessor.java index e92157e7c867..fa43915deb6a 100644 --- a/packages/SettingsLib/search/processor-src/com/android/settingslib/search/IndexableProcessor.java +++ b/packages/SettingsLib/search/processor-src/com/android/settingslib/search/IndexableProcessor.java @@ -34,6 +34,7 @@ import javax.annotation.processing.Messager; import javax.annotation.processing.ProcessingEnvironment; import javax.annotation.processing.RoundEnvironment; import javax.annotation.processing.SupportedAnnotationTypes; +import javax.annotation.processing.SupportedOptions; import javax.annotation.processing.SupportedSourceVersion; import javax.lang.model.SourceVersion; import javax.lang.model.element.Element; @@ -48,10 +49,11 @@ import javax.tools.Diagnostic.Kind; * subclasses. */ @SupportedSourceVersion(SourceVersion.RELEASE_17) +@SupportedOptions(IndexableProcessor.PACKAGE_KEY) @SupportedAnnotationTypes({"com.android.settingslib.search.SearchIndexable"}) public class IndexableProcessor extends AbstractProcessor { - private static final String PACKAGE = "com.android.settingslib.search"; + private static final String SETTINGSLIB_SEARCH_PACKAGE = "com.android.settingslib.search"; private static final String CLASS_BASE = "SearchIndexableResourcesBase"; private static final String CLASS_MOBILE = "SearchIndexableResourcesMobile"; private static final String CLASS_TV = "SearchIndexableResourcesTv"; @@ -59,6 +61,9 @@ public class IndexableProcessor extends AbstractProcessor { private static final String CLASS_AUTO = "SearchIndexableResourcesAuto"; private static final String CLASS_ARC = "SearchIndexableResourcesArc"; + static final String PACKAGE_KEY = "com.android.settingslib.search.processor.package"; + + private String mPackage; private Filer mFiler; private Messager mMessager; private boolean mRanOnce; @@ -72,7 +77,8 @@ public class IndexableProcessor extends AbstractProcessor { } mRanOnce = true; - final ClassName searchIndexableData = ClassName.get(PACKAGE, "SearchIndexableData"); + final ClassName searchIndexableData = + ClassName.get(SETTINGSLIB_SEARCH_PACKAGE, "SearchIndexableData"); final FieldSpec providers = FieldSpec.builder( ParameterizedTypeName.get( @@ -130,7 +136,7 @@ public class IndexableProcessor extends AbstractProcessor { builder = arcConstructorBuilder; } builder.addCode( - "$N(new SearchIndexableData($L.class, $L" + "$N(new com.android.settingslib.search.SearchIndexableData($L.class, $L" + ".SEARCH_INDEX_DATA_PROVIDER));\n", addIndex, className, className); } else { @@ -150,50 +156,51 @@ public class IndexableProcessor extends AbstractProcessor { final TypeSpec baseClass = TypeSpec.classBuilder(CLASS_BASE) .addModifiers(Modifier.PUBLIC) - .addSuperinterface(ClassName.get(PACKAGE, "SearchIndexableResources")) + .addSuperinterface( + ClassName.get(SETTINGSLIB_SEARCH_PACKAGE, "SearchIndexableResources")) .addField(providers) .addMethod(baseConstructorBuilder.build()) .addMethod(addIndex) .addMethod(getProviderValues) .build(); - final JavaFile searchIndexableResourcesBase = JavaFile.builder(PACKAGE, baseClass).build(); + final JavaFile searchIndexableResourcesBase = JavaFile.builder(mPackage, baseClass).build(); - final JavaFile searchIndexableResourcesMobile = JavaFile.builder(PACKAGE, + final JavaFile searchIndexableResourcesMobile = JavaFile.builder(mPackage, TypeSpec.classBuilder(CLASS_MOBILE) .addModifiers(Modifier.PUBLIC) - .superclass(ClassName.get(PACKAGE, baseClass.name)) + .superclass(ClassName.get(mPackage, baseClass.name)) .addMethod(mobileConstructorBuilder.build()) .build()) .build(); - final JavaFile searchIndexableResourcesTv = JavaFile.builder(PACKAGE, + final JavaFile searchIndexableResourcesTv = JavaFile.builder(mPackage, TypeSpec.classBuilder(CLASS_TV) .addModifiers(Modifier.PUBLIC) - .superclass(ClassName.get(PACKAGE, baseClass.name)) + .superclass(ClassName.get(mPackage, baseClass.name)) .addMethod(tvConstructorBuilder.build()) .build()) .build(); - final JavaFile searchIndexableResourcesWear = JavaFile.builder(PACKAGE, + final JavaFile searchIndexableResourcesWear = JavaFile.builder(mPackage, TypeSpec.classBuilder(CLASS_WEAR) .addModifiers(Modifier.PUBLIC) - .superclass(ClassName.get(PACKAGE, baseClass.name)) + .superclass(ClassName.get(mPackage, baseClass.name)) .addMethod(wearConstructorBuilder.build()) .build()) .build(); - final JavaFile searchIndexableResourcesAuto = JavaFile.builder(PACKAGE, + final JavaFile searchIndexableResourcesAuto = JavaFile.builder(mPackage, TypeSpec.classBuilder(CLASS_AUTO) .addModifiers(Modifier.PUBLIC) - .superclass(ClassName.get(PACKAGE, baseClass.name)) + .superclass(ClassName.get(mPackage, baseClass.name)) .addMethod(autoConstructorBuilder.build()) .build()) .build(); - final JavaFile searchIndexableResourcesArc = JavaFile.builder(PACKAGE, + final JavaFile searchIndexableResourcesArc = JavaFile.builder(mPackage, TypeSpec.classBuilder(CLASS_ARC) .addModifiers(Modifier.PUBLIC) - .superclass(ClassName.get(PACKAGE, baseClass.name)) + .superclass(ClassName.get(mPackage, baseClass.name)) .addMethod(arcConstructorBuilder.build()) .build()) .build(); @@ -214,6 +221,8 @@ public class IndexableProcessor extends AbstractProcessor { @Override public synchronized void init(ProcessingEnvironment processingEnvironment) { super.init(processingEnvironment); + mPackage = processingEnvironment.getOptions() + .getOrDefault(PACKAGE_KEY, SETTINGSLIB_SEARCH_PACKAGE); mFiler = processingEnvironment.getFiler(); mMessager = processingEnvironment.getMessager(); } diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java index 79fb56602328..a05a6e9781da 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java @@ -237,7 +237,8 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile { "startBroadcast: language = " + language + " ,programInfo = " + programInfo); } buildContentMetadata(language, programInfo); - mService.startBroadcast(mBluetoothLeAudioContentMetadata, mBroadcastCode); + mService.startBroadcast(mBluetoothLeAudioContentMetadata, + (mBroadcastCode != null && mBroadcastCode.length > 0) ? mBroadcastCode : null); } public String getProgramInfo() { diff --git a/packages/SettingsLib/src/com/android/settingslib/core/AbstractPreferenceController.java b/packages/SettingsLib/src/com/android/settingslib/core/AbstractPreferenceController.java index 9567a3b38896..ea65adecd763 100644 --- a/packages/SettingsLib/src/com/android/settingslib/core/AbstractPreferenceController.java +++ b/packages/SettingsLib/src/com/android/settingslib/core/AbstractPreferenceController.java @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package com.android.settingslib.core; import android.app.admin.DevicePolicyManager; @@ -6,8 +22,11 @@ import android.os.Build; import android.text.TextUtils; import android.util.Log; +import androidx.annotation.EmptySuper; +import androidx.annotation.NonNull; import androidx.annotation.RequiresApi; import androidx.core.os.BuildCompat; +import androidx.lifecycle.LifecycleOwner; import androidx.preference.Preference; import androidx.preference.PreferenceGroup; import androidx.preference.PreferenceScreen; @@ -52,6 +71,13 @@ public abstract class AbstractPreferenceController { } /** + * Called on view created. + */ + @EmptySuper + public void onViewCreated(@NonNull LifecycleOwner viewLifecycleOwner) { + } + + /** * Updates the current status of preference (summary, switch state, etc) */ public void updateState(Preference preference) { diff --git a/packages/SettingsLib/src/com/android/settingslib/users/CreateUserDialogController.java b/packages/SettingsLib/src/com/android/settingslib/users/CreateUserDialogController.java index e61c8f5ab152..997d1f432a82 100644 --- a/packages/SettingsLib/src/com/android/settingslib/users/CreateUserDialogController.java +++ b/packages/SettingsLib/src/com/android/settingslib/users/CreateUserDialogController.java @@ -59,6 +59,7 @@ public class CreateUserDialogController { private static final String KEY_IS_ADMIN = "admin_status"; private static final String KEY_ADD_USER_LONG_MESSAGE_DISPLAYED = "key_add_user_long_message_displayed"; + public static final int MESSAGE_PADDING = 10; @Retention(RetentionPolicy.SOURCE) @IntDef({EXIT_DIALOG, INITIAL_DIALOG, GRANT_ADMIN_DIALOG, @@ -191,6 +192,7 @@ public class CreateUserDialogController { cancelCallback.run(); clear(); }); + mCustomDialogHelper.setMessagePadding(MESSAGE_PADDING); mUserCreationDialog.setCanceledOnTouchOutside(true); return mUserCreationDialog; } @@ -212,7 +214,6 @@ public class CreateUserDialogController { } updateLayout(); }); - return; } private void updateLayout() { @@ -234,7 +235,6 @@ public class CreateUserDialogController { } Drawable icon = mActivity.getDrawable(R.drawable.ic_person_add); mCustomDialogHelper.setVisibility(mCustomDialogHelper.ICON, true) - .setVisibility(mCustomDialogHelper.TITLE, true) .setVisibility(mCustomDialogHelper.MESSAGE, true) .setIcon(icon) .setButtonEnabled(true) @@ -248,7 +248,6 @@ public class CreateUserDialogController { mGrantAdminView.setVisibility(View.VISIBLE); mCustomDialogHelper .setVisibility(mCustomDialogHelper.ICON, true) - .setVisibility(mCustomDialogHelper.TITLE, true) .setVisibility(mCustomDialogHelper.MESSAGE, true) .setIcon(mActivity.getDrawable(R.drawable.ic_admin_panel_settings)) .setTitle(R.string.user_grant_admin_title) @@ -262,8 +261,8 @@ public class CreateUserDialogController { case EDIT_NAME_DIALOG: mCustomDialogHelper .setVisibility(mCustomDialogHelper.ICON, false) - .setVisibility(mCustomDialogHelper.TITLE, false) .setVisibility(mCustomDialogHelper.MESSAGE, false) + .setTitle(R.string.user_info_settings_title) .setNegativeButtonText(R.string.back) .setPositiveButtonText(R.string.done); mEditUserInfoView.setVisibility(View.VISIBLE); diff --git a/packages/SettingsLib/src/com/android/settingslib/users/EditUserInfoController.java b/packages/SettingsLib/src/com/android/settingslib/users/EditUserInfoController.java index e55d7eac34df..cd5f59731e7f 100644 --- a/packages/SettingsLib/src/com/android/settingslib/users/EditUserInfoController.java +++ b/packages/SettingsLib/src/com/android/settingslib/users/EditUserInfoController.java @@ -17,7 +17,6 @@ package com.android.settingslib.users; import android.app.Activity; -import android.app.AlertDialog; import android.app.Dialog; import android.content.Context; import android.content.Intent; @@ -31,7 +30,6 @@ import android.view.View; import android.view.WindowManager; import android.widget.EditText; import android.widget.ImageView; -import android.widget.ScrollView; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; @@ -41,6 +39,7 @@ import com.android.settingslib.R; import com.android.settingslib.RestrictedLockUtils; import com.android.settingslib.RestrictedLockUtilsInternal; import com.android.settingslib.drawable.CircleFramedDrawable; +import com.android.settingslib.utils.CustomDialogHelper; import java.io.File; import java.util.function.BiConsumer; @@ -128,7 +127,7 @@ public class EditUserInfoController { * codes to take photo/choose photo/crop photo. */ public Dialog createDialog(Activity activity, ActivityStarter activityStarter, - @Nullable Drawable oldUserIcon, String defaultUserName, String title, + @Nullable Drawable oldUserIcon, String defaultUserName, BiConsumer<String, Drawable> successCallback, Runnable cancelCallback) { LayoutInflater inflater = LayoutInflater.from(activity); View content = inflater.inflate(R.layout.edit_user_info_dialog_content, null); @@ -160,10 +159,8 @@ public class EditUserInfoController { userPhotoView); } } - ScrollView scrollView = content.findViewById(R.id.user_info_scroll); - scrollView.setClipToOutline(true); mEditUserInfoDialog = buildDialog(activity, content, userNameView, oldUserIcon, - defaultUserName, title, successCallback, cancelCallback); + defaultUserName, successCallback, cancelCallback); // Make sure the IME is up. mEditUserInfoDialog.getWindow() @@ -181,12 +178,13 @@ public class EditUserInfoController { } private Dialog buildDialog(Activity activity, View content, EditText userNameView, - @Nullable Drawable oldUserIcon, String defaultUserName, String title, + @Nullable Drawable oldUserIcon, String defaultUserName, BiConsumer<String, Drawable> successCallback, Runnable cancelCallback) { - return new AlertDialog.Builder(activity) - .setView(content) - .setCancelable(true) - .setPositiveButton(android.R.string.ok, (dialog, which) -> { + CustomDialogHelper dialogHelper = new CustomDialogHelper(activity); + dialogHelper + .setTitle(R.string.user_info_settings_title) + .addCustomView(content) + .setPositiveButton(android.R.string.ok, view -> { Drawable newUserIcon = mEditUserPhotoController != null ? mEditUserPhotoController.getNewUserPhotoDrawable() : null; @@ -201,20 +199,23 @@ public class EditUserInfoController { if (successCallback != null) { successCallback.accept(userName, userIcon); } + dialogHelper.getDialog().dismiss(); }) - .setNegativeButton(android.R.string.cancel, (dialog, which) -> { + .setBackButton(android.R.string.cancel, view -> { clear(); if (cancelCallback != null) { cancelCallback.run(); } - }) - .setOnCancelListener(dialog -> { - clear(); - if (cancelCallback != null) { - cancelCallback.run(); - } - }) - .create(); + dialogHelper.getDialog().dismiss(); + }); + dialogHelper.getDialog().setOnCancelListener(dialog -> { + clear(); + if (cancelCallback != null) { + cancelCallback.run(); + } + dialogHelper.getDialog().dismiss(); + }); + return dialogHelper.getDialog(); } @VisibleForTesting diff --git a/packages/SettingsLib/src/com/android/settingslib/utils/CustomDialogHelper.java b/packages/SettingsLib/src/com/android/settingslib/utils/CustomDialogHelper.java index de488144be6c..5201b3ddc606 100644 --- a/packages/SettingsLib/src/com/android/settingslib/utils/CustomDialogHelper.java +++ b/packages/SettingsLib/src/com/android/settingslib/utils/CustomDialogHelper.java @@ -193,6 +193,14 @@ public class CustomDialogHelper { } /** + * Sets message padding of the dialog. + */ + public CustomDialogHelper setMessagePadding(int dp) { + mDialogMessage.setPadding(dp, dp, dp, dp); + return this; + } + + /** * Sets icon of the dialog. */ public CustomDialogHelper setIcon(Drawable icon) { diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/users/CreateUserDialogControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/users/CreateUserDialogControllerTest.java index e989ed27508b..b53807744516 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/users/CreateUserDialogControllerTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/users/CreateUserDialogControllerTest.java @@ -111,13 +111,13 @@ public class CreateUserDialogControllerTest { mActivityStarter, true, null, cancelCallback); dialog.show(); - assertThat(dialog.findViewById(R.id.user_info_scroll).getVisibility()).isEqualTo(View.GONE); + assertThat(dialog.findViewById(R.id.user_info_editor).getVisibility()).isEqualTo(View.GONE); Button next = dialog.findViewById(R.id.button_ok); next.performClick(); ((RadioButton) dialog.findViewById(R.id.grant_admin_yes)).setChecked(true); - assertThat(dialog.findViewById(R.id.user_info_scroll).getVisibility()).isEqualTo(View.GONE); + assertThat(dialog.findViewById(R.id.user_info_editor).getVisibility()).isEqualTo(View.GONE); next.performClick(); - assertThat(dialog.findViewById(R.id.user_info_scroll).getVisibility()) + assertThat(dialog.findViewById(R.id.user_info_editor).getVisibility()) .isEqualTo(View.VISIBLE); dialog.dismiss(); } diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/users/EditUserInfoControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/users/EditUserInfoControllerTest.java index f760032e4a40..f595cd334105 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/users/EditUserInfoControllerTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/users/EditUserInfoControllerTest.java @@ -27,7 +27,6 @@ import static org.mockito.Mockito.when; import android.app.Activity; import android.app.AlertDialog; -import android.app.Dialog; import android.content.Context; import android.content.Intent; import android.graphics.drawable.Drawable; @@ -108,7 +107,7 @@ public class EditUserInfoControllerTest { @Test public void photoControllerOnActivityResult_whenWaiting_isCalled() { mController.createDialog(mActivity, mActivityStarter, mCurrentIcon, "test user", - "title", null, null); + null, null); mController.startingActivityForResult(); Intent resultData = new Intent(); mController.onActivityResult(0, 0, resultData); @@ -126,9 +125,7 @@ public class EditUserInfoControllerTest { () -> String.valueOf('A')).limit(200).collect(Collectors.joining()); final AlertDialog dialog = (AlertDialog) mController.createDialog(mActivity, - mActivityStarter, mCurrentIcon, - "test user", "title", null, - null); + mActivityStarter, mCurrentIcon, "test user", null, null); dialog.show(); final EditText userNameEditText = dialog.findViewById(R.id.user_name); userNameEditText.setText(longName); @@ -143,7 +140,7 @@ public class EditUserInfoControllerTest { AlertDialog dialog = (AlertDialog) mController.createDialog( mActivity, mActivityStarter, mCurrentIcon, "test", - "title", successCallback, cancelCallback); + successCallback, cancelCallback); dialog.show(); dialog.cancel(); @@ -159,9 +156,9 @@ public class EditUserInfoControllerTest { AlertDialog dialog = (AlertDialog) mController.createDialog( mActivity, mActivityStarter, mCurrentIcon, "test", - "title", successCallback, cancelCallback); + successCallback, cancelCallback); dialog.show(); - dialog.getButton(Dialog.BUTTON_NEGATIVE).performClick(); + dialog.findViewById(R.id.button_back).performClick(); verifyNoInteractions(successCallback); verify(cancelCallback, times(1)) @@ -176,11 +173,11 @@ public class EditUserInfoControllerTest { Drawable oldUserIcon = mCurrentIcon; AlertDialog dialog = (AlertDialog) mController.createDialog( mActivity, mActivityStarter, oldUserIcon, "test", - "title", successCallback, cancelCallback); + successCallback, cancelCallback); // No change to the photo. when(mController.getPhotoController().getNewUserPhotoDrawable()).thenReturn(null); dialog.show(); - dialog.getButton(Dialog.BUTTON_POSITIVE).performClick(); + dialog.findViewById(R.id.button_ok).performClick(); verify(successCallback, times(1)) .accept("test", oldUserIcon); @@ -194,11 +191,11 @@ public class EditUserInfoControllerTest { AlertDialog dialog = (AlertDialog) mController.createDialog( mActivity, mActivityStarter, null, "test", - "title", successCallback, cancelCallback); + successCallback, cancelCallback); // No change to the photo. when(mController.getPhotoController().getNewUserPhotoDrawable()).thenReturn(null); dialog.show(); - dialog.getButton(Dialog.BUTTON_POSITIVE).performClick(); + dialog.findViewById(R.id.button_ok).performClick(); verify(successCallback, times(1)) .accept("test", null); @@ -212,14 +209,14 @@ public class EditUserInfoControllerTest { AlertDialog dialog = (AlertDialog) mController.createDialog( mActivity, mActivityStarter, mCurrentIcon, "test", - "title", successCallback, cancelCallback); + successCallback, cancelCallback); // No change to the photo. when(mController.getPhotoController().getNewUserPhotoDrawable()).thenReturn(null); dialog.show(); String expectedNewName = "new test user"; EditText editText = (EditText) dialog.findViewById(R.id.user_name); editText.setText(expectedNewName); - dialog.getButton(Dialog.BUTTON_POSITIVE).performClick(); + dialog.findViewById(R.id.button_ok).performClick(); verify(successCallback, times(1)) .accept(expectedNewName, mCurrentIcon); @@ -233,12 +230,12 @@ public class EditUserInfoControllerTest { AlertDialog dialog = (AlertDialog) mController.createDialog( mActivity, mActivityStarter, mCurrentIcon, "test", - "title", successCallback, cancelCallback); + successCallback, cancelCallback); // A different drawable. Drawable newPhoto = mock(Drawable.class); when(mController.getPhotoController().getNewUserPhotoDrawable()).thenReturn(newPhoto); dialog.show(); - dialog.getButton(Dialog.BUTTON_POSITIVE).performClick(); + dialog.findViewById(R.id.button_ok).performClick(); verify(successCallback, times(1)) .accept("test", newPhoto); @@ -252,12 +249,12 @@ public class EditUserInfoControllerTest { AlertDialog dialog = (AlertDialog) mController.createDialog( mActivity, mActivityStarter, null, "test", - "title", successCallback, cancelCallback); + successCallback, cancelCallback); // A different drawable. Drawable newPhoto = mock(Drawable.class); when(mController.getPhotoController().getNewUserPhotoDrawable()).thenReturn(newPhoto); dialog.show(); - dialog.getButton(Dialog.BUTTON_POSITIVE).performClick(); + dialog.findViewById(R.id.button_ok).performClick(); verify(successCallback, times(1)) .accept("test", newPhoto); @@ -269,7 +266,7 @@ public class EditUserInfoControllerTest { mPhotoRestrictedByBase = true; mController.createDialog(mActivity, mActivityStarter, mCurrentIcon, - "test", "title", null, null); + "test", null, null); assertThat(mController.mPhotoController).isNull(); } diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java index 873b434aa4fd..dd8eb3b3f3fb 100644 --- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java +++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java @@ -590,6 +590,7 @@ public class SettingsBackupTest { Settings.Global.APPOP_HISTORY_BASE_INTERVAL_MILLIS, Settings.Global.AUTO_REVOKE_PARAMETERS, Settings.Global.ENABLE_RADIO_BUG_DETECTION, + Settings.Global.REPAIR_MODE_ACTIVE, Settings.Global.RADIO_BUG_WAKELOCK_TIMEOUT_COUNT_THRESHOLD, Settings.Global.RADIO_BUG_SYSTEM_ERROR_COUNT_THRESHOLD, Settings.Global.ENABLED_SUBSCRIPTION_FOR_SLOT, @@ -724,6 +725,7 @@ public class SettingsBackupTest { Settings.Secure.CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS, Settings.Secure.CONTENT_CAPTURE_ENABLED, Settings.Secure.DEFAULT_INPUT_METHOD, + Settings.Secure.DEFAULT_NOTE_TASK_PROFILE, Settings.Secure.DEVICE_PAIRED, Settings.Secure.DIALER_DEFAULT_APPLICATION, Settings.Secure.DISABLED_PRINT_SERVICES, diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp index 7df3dfb89d96..a443b5c4aa60 100644 --- a/packages/SystemUI/Android.bp +++ b/packages/SystemUI/Android.bp @@ -250,17 +250,17 @@ filegroup { "tests/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepositoryTest.kt", "tests/src/com/android/systemui/keyguard/data/repository/TrustRepositoryTest.kt", // domain - "tests/src/com/android/systemui/keyguard/domain/interactor/AlternateBouncerInteractorTest.kt", + "tests/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorTest.kt", + "tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerCallbackInteractorTest.kt", + "tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorWithCoroutinesTest.kt", "tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt", "tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractorTest.kt", "tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt", "tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt", "tests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt", - "tests/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerCallbackInteractorTest.kt", - "tests/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractorWithCoroutinesTest.kt", // ui + "tests/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModelTest.kt", "tests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModelTest.kt", - "tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBouncerViewModelTest.kt", "tests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModelTest.kt", "tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt", "tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt", diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index 5c036988eb30..2913c169a0be 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -176,6 +176,7 @@ <uses-permission android:name="android.permission.FOREGROUND_SERVICE_SYSTEM_EXEMPTED" /> <uses-permission android:name="android.permission.RECORD_AUDIO" /> <uses-permission android:name="android.permission.CAPTURE_AUDIO_OUTPUT"/> + <uses-permission android:name="android.permission.USE_EXACT_ALARM"/> <!-- Assist --> <uses-permission android:name="android.permission.ACCESS_VOICE_INTERACTION_SERVICE" /> @@ -369,7 +370,7 @@ android:defaultToDeviceProtectedStorage="true" android:directBootAware="true" tools:replace="android:appComponentFactory" - android:appComponentFactory=".SystemUIAppComponentFactory"> + android:appComponentFactory=".PhoneSystemUIAppComponentFactory"> <!-- Keep theme in sync with SystemUIApplication.onCreate(). Setting the theme on the application does not affect views inflated by services. The application theme is set again from onCreate to take effect for those views. --> @@ -451,12 +452,14 @@ android:noHistory="true" /> <service android:name=".screenshot.appclips.AppClipsScreenshotHelperService" - android:permission="com.android.systemui.permission.SELF" - android:exported="false" /> + android:exported="false" + android:singleUser="true" + android:permission="com.android.systemui.permission.SELF" /> <service android:name=".screenshot.appclips.AppClipsService" - android:permission="android.permission.LAUNCH_CAPTURE_CONTENT_ACTIVITY_FOR_NOTE" - android:exported="true" /> + android:exported="true" + android:singleUser="true" + android:permission="android.permission.LAUNCH_CAPTURE_CONTENT_ACTIVITY_FOR_NOTE" /> <service android:name=".screenrecord.RecordingService" android:foregroundServiceType="systemExempted"/> @@ -814,7 +817,8 @@ <service android:name=".dreams.DreamOverlayService" android:enabled="false" - android:exported="true" /> + android:exported="true" + android:singleUser="true" /> <activity android:name=".keyguard.WorkLockActivity" android:label="@string/accessibility_desc_work_lock" @@ -992,6 +996,11 @@ <service android:name=".notetask.NoteTaskControllerUpdateService" /> + <service android:name=".notetask.NoteTaskBubblesController$NoteTaskBubblesService" + android:exported="false" + android:singleUser="true" + android:permission="com.android.systemui.permission.SELF" /> + <activity android:name=".notetask.shortcut.LaunchNoteTaskActivity" android:exported="true" @@ -1004,20 +1013,10 @@ </intent-filter> </activity> - <!-- LaunchNoteTaskManagedProfileProxyActivity MUST NOT be exported because it allows caller - to specify an Android user when launching the default notes app. --> - <activity - android:name=".notetask.shortcut.LaunchNoteTaskManagedProfileProxyActivity" - android:exported="false" - android:enabled="true" - android:excludeFromRecents="true" - android:theme="@android:style/Theme.NoDisplay" /> - <activity android:name=".notetask.LaunchNotesRoleSettingsTrampolineActivity" android:exported="true" android:excludeFromRecents="true" - android:resizeableActivity="false" android:theme="@android:style/Theme.NoDisplay" > <intent-filter> <action android:name="com.android.systemui.action.MANAGE_NOTES_ROLE_FROM_QUICK_AFFORDANCE" /> @@ -1069,15 +1068,6 @@ android:exported="true"> </provider> - <!-- Provides list and realistic previews of clock faces for the picker app. --> - <provider - android:name="com.android.keyguard.clock.ClockOptionsProvider" - android:authorities="com.android.keyguard.clock" - android:enabled="false" - android:exported="false" - android:grantUriPermissions="true"> - </provider> - <receiver android:name=".statusbar.KeyboardShortcutsReceiver" android:exported="true"> diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt index 48dd08f206c1..dbfa192f5ec4 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt @@ -36,6 +36,7 @@ import android.widget.FrameLayout import com.android.app.animation.Interpolators import com.android.internal.jank.InteractionJankMonitor import com.android.internal.jank.InteractionJankMonitor.CujType +import com.android.systemui.animation.view.LaunchableFrameLayout import com.android.systemui.util.registerAnimationOnBackInvoked import kotlin.math.roundToInt diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/LaunchableFrameLayout.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/view/LaunchableFrameLayout.kt index 2eb503b43cc5..7538f188fb81 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/LaunchableFrameLayout.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/view/LaunchableFrameLayout.kt @@ -14,11 +14,13 @@ * limitations under the License. */ -package com.android.systemui.animation +package com.android.systemui.animation.view import android.content.Context import android.util.AttributeSet import android.widget.FrameLayout +import com.android.systemui.animation.LaunchableView +import com.android.systemui.animation.LaunchableViewDelegate /** A [FrameLayout] that also implements [LaunchableView]. */ open class LaunchableFrameLayout : FrameLayout, LaunchableView { diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/Easings.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/Easings.kt index 8fe1f48dcaee..4fe9f89830f6 100644 --- a/packages/SystemUI/compose/core/src/com/android/compose/animation/Easings.kt +++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/Easings.kt @@ -27,37 +27,52 @@ import com.android.app.animation.InterpolatorsAndroidX object Easings { /** The standard interpolator that should be used on every normal animation */ - val StandardEasing = fromInterpolator(InterpolatorsAndroidX.STANDARD) + val Standard = fromInterpolator(InterpolatorsAndroidX.STANDARD) /** * The standard accelerating interpolator that should be used on every regular movement of * content that is disappearing e.g. when moving off screen. */ - val StandardAccelerateEasing = fromInterpolator(InterpolatorsAndroidX.STANDARD_ACCELERATE) + val StandardAccelerate = fromInterpolator(InterpolatorsAndroidX.STANDARD_ACCELERATE) /** * The standard decelerating interpolator that should be used on every regular movement of * content that is appearing e.g. when coming from off screen. */ - val StandardDecelerateEasing = fromInterpolator(InterpolatorsAndroidX.STANDARD_DECELERATE) + val StandardDecelerate = fromInterpolator(InterpolatorsAndroidX.STANDARD_DECELERATE) /** The default emphasized interpolator. Used for hero / emphasized movement of content. */ - val EmphasizedEasing = fromInterpolator(InterpolatorsAndroidX.EMPHASIZED) + val Emphasized = fromInterpolator(InterpolatorsAndroidX.EMPHASIZED) /** * The accelerated emphasized interpolator. Used for hero / emphasized movement of content that * is disappearing e.g. when moving off screen. */ - val EmphasizedAccelerateEasing = fromInterpolator(InterpolatorsAndroidX.EMPHASIZED_ACCELERATE) + val EmphasizedAccelerate = fromInterpolator(InterpolatorsAndroidX.EMPHASIZED_ACCELERATE) /** * The decelerating emphasized interpolator. Used for hero / emphasized movement of content that * is appearing e.g. when coming from off screen */ - val EmphasizedDecelerateEasing = fromInterpolator(InterpolatorsAndroidX.EMPHASIZED_DECELERATE) + val EmphasizedDecelerate = fromInterpolator(InterpolatorsAndroidX.EMPHASIZED_DECELERATE) /** The linear interpolator. */ - val LinearEasing = fromInterpolator(InterpolatorsAndroidX.LINEAR) + val Linear = fromInterpolator(InterpolatorsAndroidX.LINEAR) + + /** The default legacy interpolator as defined in Material 1. Also known as FAST_OUT_SLOW_IN. */ + val Legacy = fromInterpolator(InterpolatorsAndroidX.LEGACY) + + /** + * The default legacy accelerating interpolator as defined in Material 1. Also known as + * FAST_OUT_LINEAR_IN. + */ + val LegacyAccelerate = fromInterpolator(InterpolatorsAndroidX.LEGACY_ACCELERATE) + + /** + * T The default legacy decelerating interpolator as defined in Material 1. Also known as + * LINEAR_OUT_SLOW_IN. + */ + val LegacyDecelerate = fromInterpolator(InterpolatorsAndroidX.LEGACY_DECELERATE) private fun fromInterpolator(source: Interpolator) = Easing { x -> source.getInterpolation(x) } } diff --git a/packages/SystemUI/compose/facade/disabled/src/com/android/systemui/compose/ComposeFacade.kt b/packages/SystemUI/compose/facade/disabled/src/com/android/systemui/compose/ComposeFacade.kt index cc337459a83c..82fe3f265384 100644 --- a/packages/SystemUI/compose/facade/disabled/src/com/android/systemui/compose/ComposeFacade.kt +++ b/packages/SystemUI/compose/facade/disabled/src/com/android/systemui/compose/ComposeFacade.kt @@ -24,6 +24,9 @@ import androidx.lifecycle.LifecycleOwner import com.android.systemui.multishade.ui.viewmodel.MultiShadeViewModel import com.android.systemui.people.ui.viewmodel.PeopleViewModel import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel +import com.android.systemui.scene.shared.model.Scene +import com.android.systemui.scene.shared.model.SceneKey +import com.android.systemui.scene.ui.viewmodel.SceneContainerViewModel import com.android.systemui.util.time.SystemClock /** The Compose facade, when Compose is *not* available. */ @@ -58,6 +61,14 @@ object ComposeFacade : BaseComposeFacade { throwComposeUnavailableError() } + override fun createSceneContainerView( + context: Context, + viewModel: SceneContainerViewModel, + sceneByKey: Map<SceneKey, Scene>, + ): View { + throwComposeUnavailableError() + } + private fun throwComposeUnavailableError(): Nothing { error( "Compose is not available. Make sure to check isComposeAvailable() before calling any" + diff --git a/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/compose/ComposeFacade.kt b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/compose/ComposeFacade.kt index 0e79b18b1c24..7926f9224347 100644 --- a/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/compose/ComposeFacade.kt +++ b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/compose/ComposeFacade.kt @@ -29,6 +29,11 @@ import com.android.systemui.people.ui.compose.PeopleScreen import com.android.systemui.people.ui.viewmodel.PeopleViewModel import com.android.systemui.qs.footer.ui.compose.FooterActions import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel +import com.android.systemui.scene.shared.model.Scene +import com.android.systemui.scene.shared.model.SceneKey +import com.android.systemui.scene.ui.composable.ComposableScene +import com.android.systemui.scene.ui.composable.SceneContainer +import com.android.systemui.scene.ui.viewmodel.SceneContainerViewModel import com.android.systemui.util.time.SystemClock /** The Compose facade, when Compose is available. */ @@ -71,4 +76,22 @@ object ComposeFacade : BaseComposeFacade { } } } + + override fun createSceneContainerView( + context: Context, + viewModel: SceneContainerViewModel, + sceneByKey: Map<SceneKey, Scene>, + ): View { + return ComposeView(context).apply { + setContent { + PlatformTheme { + SceneContainer( + viewModel = viewModel, + sceneByKey = + sceneByKey.mapValues { (_, scene) -> scene as ComposableScene }, + ) + } + } + } + } } diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PatternBouncer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PatternBouncer.kt index 88441146ad23..b3d2e350ed50 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PatternBouncer.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PatternBouncer.kt @@ -131,7 +131,7 @@ internal fun PatternBouncer( animationSpec = tween( durationMillis = SELECTED_DOT_REACTION_ANIMATION_DURATION_MS, - easing = Easings.StandardAccelerateEasing, + easing = Easings.StandardAccelerate, ), ) } else { @@ -140,7 +140,7 @@ internal fun PatternBouncer( animationSpec = tween( durationMillis = SELECTED_DOT_RETRACT_ANIMATION_DURATION_MS, - easing = Easings.StandardDecelerateEasing, + easing = Easings.StandardDecelerate, ), ) } @@ -333,7 +333,7 @@ private suspend fun showFailureAnimation( FAILURE_ANIMATION_DOT_SHRINK_ANIMATION_DURATION_MS, delayMillis = rowIndex * FAILURE_ANIMATION_DOT_SHRINK_STAGGER_DELAY_MS, - easing = Easings.LinearEasing, + easing = Easings.Linear, ), ) @@ -343,7 +343,7 @@ private suspend fun showFailureAnimation( tween( durationMillis = FAILURE_ANIMATION_DOT_REVERT_ANIMATION_DURATION, - easing = Easings.StandardEasing, + easing = Easings.Standard, ), ) } diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinBouncer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinBouncer.kt index 968e5ab8ad8c..323fed0c11f3 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinBouncer.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinBouncer.kt @@ -19,23 +19,22 @@ package com.android.systemui.bouncer.ui.composable import android.view.HapticFeedbackConstants -import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.ExperimentalAnimationApi import androidx.compose.animation.animateColorAsState -import androidx.compose.animation.animateContentSize +import androidx.compose.animation.core.Animatable import androidx.compose.animation.core.AnimationSpec -import androidx.compose.animation.core.LinearEasing +import androidx.compose.animation.core.AnimationVector1D +import androidx.compose.animation.core.MutableTransitionState +import androidx.compose.animation.core.Transition +import androidx.compose.animation.core.animateDp import androidx.compose.animation.core.animateDpAsState +import androidx.compose.animation.core.animateFloatAsState +import androidx.compose.animation.core.keyframes +import androidx.compose.animation.core.snap import androidx.compose.animation.core.tween -import androidx.compose.animation.fadeIn -import androidx.compose.animation.fadeOut -import androidx.compose.animation.scaleIn -import androidx.compose.animation.scaleOut -import androidx.compose.animation.slideInHorizontally -import androidx.compose.animation.slideOutHorizontally -import androidx.compose.foundation.background +import androidx.compose.animation.core.updateTransition +import androidx.compose.foundation.Canvas import androidx.compose.foundation.gestures.detectTapGestures -import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row @@ -43,38 +42,46 @@ import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.heightIn import androidx.compose.foundation.layout.size -import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.layout.wrapContentSize import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue +import androidx.compose.runtime.key import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue +import androidx.compose.runtime.snapshotFlow +import androidx.compose.runtime.snapshots.SnapshotStateList import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.drawBehind import androidx.compose.ui.geometry.CornerRadius import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.input.pointer.pointerInput +import androidx.compose.ui.layout.Layout import androidx.compose.ui.platform.LocalView +import androidx.compose.ui.unit.Constraints import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import com.android.compose.animation.Easings import com.android.compose.grid.VerticalGrid import com.android.systemui.R +import com.android.systemui.bouncer.ui.viewmodel.ActionButtonAppearance +import com.android.systemui.bouncer.ui.viewmodel.EnteredKey import com.android.systemui.bouncer.ui.viewmodel.PinBouncerViewModel import com.android.systemui.common.shared.model.ContentDescription import com.android.systemui.common.shared.model.Icon import com.android.systemui.common.ui.compose.Icon import com.android.systemui.compose.modifiers.thenIf -import kotlin.math.max import kotlin.time.Duration.Companion.milliseconds import kotlin.time.DurationUnit import kotlinx.coroutines.async +import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.delay import kotlinx.coroutines.launch @@ -86,139 +93,306 @@ internal fun PinBouncer( // Report that the UI is shown to let the view-model run some logic. LaunchedEffect(Unit) { viewModel.onShown() } - // The length of the PIN input received so far, so we know how many dots to render. - val pinLength: Pair<Int, Int> by viewModel.pinLengths.collectAsState() - val isInputEnabled: Boolean by viewModel.isInputEnabled.collectAsState() - val animateFailure: Boolean by viewModel.animateFailure.collectAsState() + Column( + horizontalAlignment = Alignment.CenterHorizontally, + modifier = modifier, + ) { + PinInputDisplay(viewModel) + Spacer(Modifier.height(100.dp)) + PinPad(viewModel) + } +} - // Show the failure animation if the user entered the wrong input. - LaunchedEffect(animateFailure) { - if (animateFailure) { - showFailureAnimation() - viewModel.onFailureAnimationShown() +@Composable +private fun PinInputDisplay(viewModel: PinBouncerViewModel) { + val currentPinEntries: List<EnteredKey> by viewModel.pinEntries.collectAsState() + + // visiblePinEntries keeps pins removed from currentPinEntries in the composition until their + // disappear-animation completed. The list is sorted by the natural ordering of EnteredKey, + // which is guaranteed to produce the original edit order, since the model only modifies entries + // at the end. + val visiblePinEntries = remember { SnapshotStateList<EnteredKey>() } + currentPinEntries.forEach { + val index = visiblePinEntries.binarySearch(it) + if (index < 0) { + val insertionPoint = -(index + 1) + visiblePinEntries.add(insertionPoint, it) } } - Column( - horizontalAlignment = Alignment.CenterHorizontally, - modifier = modifier, + Row( + modifier = + Modifier.heightIn(min = entryShapeSize) + // Pins overflowing horizontally should still be shown as scrolling. + .wrapContentSize(unbounded = true), ) { - Row( - horizontalArrangement = Arrangement.spacedBy(12.dp), - modifier = Modifier.heightIn(min = 16.dp).animateContentSize(), - ) { - // TODO(b/281871687): add support for dot shapes. - val (previousPinLength, currentPinLength) = pinLength - val dotCount = max(previousPinLength, currentPinLength) + 1 - repeat(dotCount) { index -> - AnimatedVisibility( - visible = index < currentPinLength, - enter = fadeIn() + scaleIn() + slideInHorizontally(), - exit = fadeOut() + scaleOut() + slideOutHorizontally(), - ) { - Box( - modifier = - Modifier.size(16.dp) - .background( - MaterialTheme.colorScheme.onSurfaceVariant, - CircleShape, - ) - ) + visiblePinEntries.forEachIndexed { index, entry -> + key(entry) { + val visibility = remember { + MutableTransitionState<EntryVisibility>(EntryVisibility.Hidden) + } + visibility.targetState = + when { + currentPinEntries.isEmpty() && visiblePinEntries.size > 1 -> + EntryVisibility.BulkHidden(index, visiblePinEntries.size) + currentPinEntries.contains(entry) -> EntryVisibility.Shown + else -> EntryVisibility.Hidden + } + + ObscuredInputEntry(updateTransition(visibility, label = "Pin Entry $entry")) + + LaunchedEffect(entry) { + // Remove entry from visiblePinEntries once the hide transition completed. + snapshotFlow { + visibility.currentState == visibility.targetState && + visibility.targetState != EntryVisibility.Shown + } + .collect { isRemoved -> + if (isRemoved) { + visiblePinEntries.remove(entry) + } + } } } } + } +} - Spacer(Modifier.height(100.dp)) +private sealed class EntryVisibility { + object Shown : EntryVisibility() + + object Hidden : EntryVisibility() + + /** + * Same as [Hidden], but applies when multiple entries are hidden simultaneously, without + * collapsing during the hide. + */ + data class BulkHidden(val staggerIndex: Int, val totalEntryCount: Int) : EntryVisibility() +} - VerticalGrid( - columns = 3, - verticalSpacing = 12.dp, - horizontalSpacing = 20.dp, - ) { - repeat(9) { index -> - val digit = index + 1 - PinButton( - onClicked = { viewModel.onPinButtonClicked(digit) }, - isEnabled = isInputEnabled, - ) { contentColor -> - PinDigit(digit, contentColor) +@Composable +private fun ObscuredInputEntry(transition: Transition<EntryVisibility>) { + // spec: http://shortn/_DEhE3Xl2bi + val shapePadding = 6.dp + val shapeOvershootSize = 22.dp + val dismissStaggerDelayMs = 33 + val dismissDurationMs = 450 + val expansionDurationMs = 250 + val shapeExpandDurationMs = 83 + val shapeRetractDurationMs = 167 + val shapeCollapseDurationMs = 200 + + val animatedEntryWidth by + transition.animateDp( + transitionSpec = { + when (val target = targetState) { + is EntryVisibility.BulkHidden -> + // only collapse horizontal space once all entries are removed + snap(dismissDurationMs + dismissStaggerDelayMs * target.totalEntryCount) + else -> tween(expansionDurationMs, easing = Easings.Standard) } - } + }, + label = "entry space" + ) { state -> + if (state == EntryVisibility.Shown) entryShapeSize + (shapePadding * 2) else 0.dp + } - PinButton( - onClicked = { viewModel.onBackspaceButtonClicked() }, - onLongPressed = { viewModel.onBackspaceButtonLongPressed() }, - isEnabled = isInputEnabled, - isIconButton = true, - ) { contentColor -> - PinIcon( - Icon.Resource( - res = R.drawable.ic_backspace_24dp, - contentDescription = - ContentDescription.Resource(R.string.keyboardview_keycode_delete), - ), - contentColor, - ) + val animatedShapeSize by + transition.animateDp( + transitionSpec = { + when { + EntryVisibility.Hidden isTransitioningTo EntryVisibility.Shown -> + keyframes { + durationMillis = shapeExpandDurationMs + shapeRetractDurationMs + 0.dp at 0 with Easings.Linear + shapeOvershootSize at shapeExpandDurationMs with Easings.Legacy + } + targetState is EntryVisibility.BulkHidden -> { + val target = targetState as EntryVisibility.BulkHidden + tween( + dismissDurationMs, + delayMillis = target.staggerIndex * dismissStaggerDelayMs, + easing = Easings.Legacy, + ) + } + else -> tween(shapeCollapseDurationMs, easing = Easings.StandardDecelerate) + } + }, + label = "shape size" + ) { state -> + when (state) { + EntryVisibility.Shown -> entryShapeSize + else -> 0.dp } + } - PinButton( - onClicked = { viewModel.onPinButtonClicked(0) }, - isEnabled = isInputEnabled, - ) { contentColor -> - PinDigit(0, contentColor) - } + val dotColor = MaterialTheme.colorScheme.onSurfaceVariant + Layout( + content = { + // TODO(b/282730134): add support for dot shapes. + Canvas(Modifier) { drawCircle(dotColor) } + } + ) { measurables, _ -> + val shapeSizePx = animatedShapeSize.roundToPx() + val placeable = measurables.single().measure(Constraints.fixed(shapeSizePx, shapeSizePx)) - PinButton( - onClicked = { viewModel.onAuthenticateButtonClicked() }, - isEnabled = isInputEnabled, - isIconButton = true, - ) { contentColor -> - PinIcon( - Icon.Resource( - res = R.drawable.ic_keyboard_tab_36dp, - contentDescription = - ContentDescription.Resource(R.string.keyboardview_keycode_enter), - ), - contentColor, - ) - } + layout(animatedEntryWidth.roundToPx(), entryShapeSize.roundToPx()) { + placeable.place( + ((animatedEntryWidth - animatedShapeSize) / 2f).roundToPx(), + ((entryShapeSize - animatedShapeSize) / 2f).roundToPx() + ) } } } @Composable -private fun PinDigit( +private fun PinPad(viewModel: PinBouncerViewModel) { + val isInputEnabled: Boolean by viewModel.isInputEnabled.collectAsState() + val backspaceButtonAppearance by viewModel.backspaceButtonAppearance.collectAsState() + val confirmButtonAppearance by viewModel.confirmButtonAppearance.collectAsState() + val animateFailure: Boolean by viewModel.animateFailure.collectAsState() + + val buttonScaleAnimatables = remember { List(12) { Animatable(1f) } } + LaunchedEffect(animateFailure) { + // Show the failure animation if the user entered the wrong input. + if (animateFailure) { + showFailureAnimation(buttonScaleAnimatables) + viewModel.onFailureAnimationShown() + } + } + + VerticalGrid( + columns = 3, + verticalSpacing = 12.dp, + horizontalSpacing = 20.dp, + ) { + repeat(9) { index -> + DigitButton( + index + 1, + isInputEnabled, + viewModel::onPinButtonClicked, + buttonScaleAnimatables[index]::value, + ) + } + + ActionButton( + icon = + Icon.Resource( + res = R.drawable.ic_backspace_24dp, + contentDescription = + ContentDescription.Resource(R.string.keyboardview_keycode_delete), + ), + isInputEnabled = isInputEnabled, + onClicked = viewModel::onBackspaceButtonClicked, + onLongPressed = viewModel::onBackspaceButtonLongPressed, + appearance = backspaceButtonAppearance, + scaling = buttonScaleAnimatables[9]::value, + ) + + DigitButton( + 0, + isInputEnabled, + viewModel::onPinButtonClicked, + buttonScaleAnimatables[10]::value, + ) + + ActionButton( + icon = + Icon.Resource( + res = R.drawable.ic_keyboard_tab_36dp, + contentDescription = + ContentDescription.Resource(R.string.keyboardview_keycode_enter), + ), + isInputEnabled = isInputEnabled, + onClicked = viewModel::onAuthenticateButtonClicked, + appearance = confirmButtonAppearance, + scaling = buttonScaleAnimatables[11]::value, + ) + } +} + +@Composable +private fun DigitButton( digit: Int, - contentColor: Color, + isInputEnabled: Boolean, + onClicked: (Int) -> Unit, + scaling: () -> Float, ) { - // TODO(b/281878426): once "color: () -> Color" (added to BasicText in aosp/2568972) makes it - // into Text, use that here, to animate more efficiently. - Text( - text = digit.toString(), - style = MaterialTheme.typography.headlineLarge, - color = contentColor, - ) + PinPadButton( + onClicked = { onClicked(digit) }, + isEnabled = isInputEnabled, + backgroundColor = MaterialTheme.colorScheme.surfaceVariant, + foregroundColor = MaterialTheme.colorScheme.onSurfaceVariant, + modifier = + Modifier.graphicsLayer { + val scale = scaling() + scaleX = scale + scaleY = scale + } + ) { contentColor -> + // TODO(b/281878426): once "color: () -> Color" (added to BasicText in aosp/2568972) makes + // it into Text, use that here, to animate more efficiently. + Text( + text = digit.toString(), + style = MaterialTheme.typography.headlineLarge, + color = contentColor(), + ) + } } @Composable -private fun PinIcon( +private fun ActionButton( icon: Icon, - contentColor: Color, + isInputEnabled: Boolean, + onClicked: () -> Unit, + onLongPressed: (() -> Unit)? = null, + appearance: ActionButtonAppearance, + scaling: () -> Float, ) { - Icon( - icon = icon, - tint = contentColor, - ) + val isHidden = appearance == ActionButtonAppearance.Hidden + val hiddenAlpha by animateFloatAsState(if (isHidden) 0f else 1f, label = "Action button alpha") + + val foregroundColor = + when (appearance) { + ActionButtonAppearance.Shown -> MaterialTheme.colorScheme.onSecondaryContainer + else -> MaterialTheme.colorScheme.onSurface + } + val backgroundColor = + when (appearance) { + ActionButtonAppearance.Shown -> MaterialTheme.colorScheme.secondaryContainer + else -> MaterialTheme.colorScheme.surface + } + + PinPadButton( + onClicked = onClicked, + onLongPressed = onLongPressed, + isEnabled = isInputEnabled && !isHidden, + backgroundColor = backgroundColor, + foregroundColor = foregroundColor, + modifier = + Modifier.graphicsLayer { + alpha = hiddenAlpha + val scale = scaling() + scaleX = scale + scaleY = scale + } + ) { contentColor -> + Icon( + icon = icon, + tint = contentColor(), + ) + } } @Composable -private fun PinButton( +private fun PinPadButton( onClicked: () -> Unit, isEnabled: Boolean, + backgroundColor: Color, + foregroundColor: Color, modifier: Modifier = Modifier, onLongPressed: (() -> Unit)? = null, - isIconButton: Boolean = false, - content: @Composable (contentColor: Color) -> Unit, + content: @Composable (contentColor: () -> Color) -> Unit, ) { var isPressed: Boolean by remember { mutableStateOf(false) } @@ -252,18 +426,16 @@ private fun PinButton( animateColorAsState( when { isPressed -> MaterialTheme.colorScheme.primary - isIconButton -> MaterialTheme.colorScheme.secondaryContainer - else -> MaterialTheme.colorScheme.surfaceVariant + else -> backgroundColor }, label = "Pin button container color", animationSpec = colorAnimationSpec ) - val contentColor: Color by + val contentColor = animateColorAsState( when { isPressed -> MaterialTheme.colorScheme.onPrimary - isIconButton -> MaterialTheme.colorScheme.onSecondaryContainer - else -> MaterialTheme.colorScheme.onSurfaceVariant + else -> foregroundColor }, label = "Pin button container color", animationSpec = colorAnimationSpec @@ -302,19 +474,50 @@ private fun PinButton( } }, ) { - content(contentColor) + content(contentColor::value) } } -private fun showFailureAnimation() { - // TODO(b/282730134): implement. +private suspend fun showFailureAnimation( + buttonScaleAnimatables: List<Animatable<Float, AnimationVector1D>> +) { + coroutineScope { + buttonScaleAnimatables.forEachIndexed { index, animatable -> + launch { + animatable.animateTo( + targetValue = pinButtonErrorShrinkFactor, + animationSpec = + tween( + durationMillis = pinButtonErrorShrinkMs, + delayMillis = index * pinButtonErrorStaggerDelayMs, + easing = Easings.Linear, + ), + ) + + animatable.animateTo( + targetValue = 1f, + animationSpec = + tween( + durationMillis = pinButtonErrorRevertMs, + easing = Easings.Legacy, + ), + ) + } + } + } } +private val entryShapeSize = 16.dp + private val pinButtonSize = 84.dp +private val pinButtonErrorShrinkFactor = 67.dp / pinButtonSize +private const val pinButtonErrorShrinkMs = 50 +private const val pinButtonErrorStaggerDelayMs = 33 +private const val pinButtonErrorRevertMs = 617 // Pin button motion spec: http://shortn/_9TTIG6SoEa private val pinButtonPressedDuration = 100.milliseconds -private val pinButtonPressedEasing = LinearEasing +private val pinButtonPressedEasing = Easings.Linear private val pinButtonHoldTime = 33.milliseconds private val pinButtonReleasedDuration = 420.milliseconds -private val pinButtonReleasedEasing = Easings.StandardEasing +private val pinButtonReleasedEasing = Easings.Standard diff --git a/packages/SystemUI/compose/features/tests/AndroidManifest.xml b/packages/SystemUI/compose/features/tests/AndroidManifest.xml index 2f41ea92b30f..8fe9656c1879 100644 --- a/packages/SystemUI/compose/features/tests/AndroidManifest.xml +++ b/packages/SystemUI/compose/features/tests/AndroidManifest.xml @@ -40,11 +40,6 @@ android:enabled="false" tools:replace="android:authorities" tools:node="remove" /> - <provider android:name="com.android.keyguard.clock.ClockOptionsProvider" - android:authorities="com.android.systemui.test.keyguard.clock.disabled" - android:enabled="false" - tools:replace="android:authorities" - tools:node="remove" /> <provider android:name="com.android.systemui.people.PeopleProvider" android:authorities="com.android.systemui.test.people.disabled" android:enabled="false" diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt index 702cc05f7f5f..8cba2ab0b70b 100644 --- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt +++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt @@ -56,6 +56,7 @@ private val KNOWN_PLUGINS = "com.android.systemui.falcon.four" to listOf(ClockMetadata("DIGITAL_CLOCK_GROWTH")), "com.android.systemui.falcon.five" to listOf(ClockMetadata("DIGITAL_CLOCK_HANDWRITTEN")), "com.android.systemui.falcon.six" to listOf(ClockMetadata("DIGITAL_CLOCK_INFLATE")), + "com.android.systemui.falcon.seven" to listOf(ClockMetadata("DIGITAL_CLOCK_METRO")), "com.android.systemui.falcon.eight" to listOf(ClockMetadata("DIGITAL_CLOCK_NUMBEROVERLAP")), "com.android.systemui.falcon.nine" to listOf(ClockMetadata("DIGITAL_CLOCK_WEATHER")), ) diff --git a/packages/SystemUI/docs/clock-plugins.md b/packages/SystemUI/docs/clock-plugins.md index 2226d7956ded..9cb115a696c9 100644 --- a/packages/SystemUI/docs/clock-plugins.md +++ b/packages/SystemUI/docs/clock-plugins.md @@ -1,7 +1,7 @@ # Clock Plugins -The clock appearing on the lock screen and always on display (AOD) can be -customized via the ClockProviderPlugin plugin interface. +The clock appearing on the lock screen and always on display (AOD) can be customized via the +ClockProviderPlugin plugin interface. The ClockPlugin interface has been removed. ## Lock screen integration The lockscreen code has two main components, a [clock customization library](../customization), and diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java index 2baeaf67df93..9cc87fde122f 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java @@ -133,6 +133,9 @@ public interface ActivityStarter { boolean willAnimateOnKeyguard, @Nullable String customMessage); + /** Whether we should animate an activity launch. */ + boolean shouldAnimateLaunch(boolean isActivityIntent); + interface Callback { void onActivityStarted(int resultCode); } diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockPlugin.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockPlugin.java deleted file mode 100644 index bef61b867f7d..000000000000 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockPlugin.java +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file - * except in compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the - * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ -package com.android.systemui.plugins; - -import android.graphics.Bitmap; -import android.graphics.Paint.Style; -import android.view.View; - -import com.android.systemui.plugins.annotations.ProvidesInterface; - -import java.util.TimeZone; - -/** - * Plugin used to replace main clock in keyguard. - * @deprecated Migrating to ClockProviderPlugin - */ -@Deprecated -@ProvidesInterface(action = ClockPlugin.ACTION, version = ClockPlugin.VERSION) -public interface ClockPlugin extends Plugin { - - String ACTION = "com.android.systemui.action.PLUGIN_CLOCK"; - int VERSION = 5; - - /** - * Get the name of the clock face. - * - * This name should not be translated. - */ - String getName(); - - /** - * Get the title of the clock face to be shown in the picker app. - */ - String getTitle(); - - /** - * Get thumbnail of clock face to be shown in the picker app. - */ - Bitmap getThumbnail(); - - /** - * Get preview images of clock face to be shown in the picker app. - * - * Preview image should be realistic and show what the clock face will look like on AOD and lock - * screen. - * - * @param width width of the preview image, should be the same as device width in pixels. - * @param height height of the preview image, should be the same as device height in pixels. - */ - Bitmap getPreview(int width, int height); - - /** - * Get clock view. - * @return clock view from plugin. - */ - View getView(); - - /** - * Get clock view for a large clock that appears behind NSSL. - */ - default View getBigClockView() { - return null; - } - - /** - * Returns the preferred Y position of the clock. - * - * @param totalHeight Height of the parent container. - * @return preferred Y position. - */ - int getPreferredY(int totalHeight); - - /** - * Allows the plugin to clean up resources when no longer needed. - * - * Called when the view previously created by {@link ClockPlugin#getView()} has been detached - * from the view hierarchy. - */ - void onDestroyView(); - - /** - * Set clock paint style. - * @param style The new style to set in the paint. - */ - void setStyle(Style style); - - /** - * Set clock text color. - * @param color A color value. - */ - void setTextColor(int color); - - /** - * Sets the color palette for the clock face. - * @param supportsDarkText Whether dark text can be displayed. - * @param colors Colors that should be used on the clock face, ordered from darker to lighter. - */ - default void setColorPalette(boolean supportsDarkText, int[] colors) {} - - /** - * Set the amount (ratio) that the device has transitioned to doze. - * @param darkAmount Amount of transition to doze: 1f for doze and 0f for awake. - */ - default void setDarkAmount(float darkAmount) {} - - /** - * Notifies that time tick alarm from doze service fired. - * - * Implement this method instead of registering a broadcast listener for TIME_TICK. - */ - default void onTimeTick() {} - - /** - * Notifies that the time zone has changed. - * - * Implement this method instead of registering a broadcast listener for TIME_ZONE_CHANGED. - */ - default void onTimeZoneChanged(TimeZone timeZone) {} - - /** - * Notifies that the time format has changed. - * - * @param timeFormat "12" for 12-hour format, "24" for 24-hour format - */ - default void onTimeFormatChanged(String timeFormat) {} - - /** - * Indicates whether the keyguard status area (date) should be shown below - * the clock. - */ - default boolean shouldShowStatusArea() { - return true; - } -} diff --git a/packages/SystemUI/proguard.flags b/packages/SystemUI/proguard.flags index a8ed84393cfb..b534fcec2a85 100644 --- a/packages/SystemUI/proguard.flags +++ b/packages/SystemUI/proguard.flags @@ -1,13 +1,7 @@ -include proguard_common.flags --keep class com.android.systemui.statusbar.tv.TvStatusBar -keep class com.android.systemui.SystemUIInitializerImpl { *; } --keep class com.android.systemui.tv.TvSystemUIInitializer { - *; -} - --keep,allowoptimization,allowaccessmodification class com.android.systemui.dagger.DaggerReferenceGlobalRootComponent** { !synthetic *; } --keep,allowoptimization,allowaccessmodification class com.android.systemui.tv.DaggerTvGlobalRootComponent** { !synthetic *; }
\ No newline at end of file +-keep,allowoptimization,allowaccessmodification class com.android.systemui.dagger.DaggerReferenceGlobalRootComponent** { !synthetic *; }
\ No newline at end of file diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_password_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_password_view.xml index 3412a303c2a6..2fc1d2ec5807 100644 --- a/packages/SystemUI/res-keyguard/layout/keyguard_password_view.xml +++ b/packages/SystemUI/res-keyguard/layout/keyguard_password_view.xml @@ -29,7 +29,7 @@ > <include layout="@layout/keyguard_bouncer_message_area"/> - <com.android.systemui.keyguard.bouncer.ui.BouncerMessageView + <com.android.systemui.bouncer.ui.BouncerMessageView android:id="@+id/bouncer_message_view" android:layout_width="match_parent" android:layout_height="wrap_content" diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_pattern_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_pattern_view.xml index 78a7c171ab14..d9011c26dfd7 100644 --- a/packages/SystemUI/res-keyguard/layout/keyguard_pattern_view.xml +++ b/packages/SystemUI/res-keyguard/layout/keyguard_pattern_view.xml @@ -33,7 +33,7 @@ android:clipToPadding="false"> <include layout="@layout/keyguard_bouncer_message_area"/> - <com.android.systemui.keyguard.bouncer.ui.BouncerMessageView + <com.android.systemui.bouncer.ui.BouncerMessageView android:id="@+id/bouncer_message_view" android:layout_width="match_parent" android:layout_height="wrap_content" diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml index 01c5443e9355..c7d2d81ded36 100644 --- a/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml +++ b/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml @@ -29,7 +29,7 @@ androidprv:layout_maxWidth="@dimen/keyguard_security_width"> <include layout="@layout/keyguard_bouncer_message_area"/> -<com.android.systemui.keyguard.bouncer.ui.BouncerMessageView +<com.android.systemui.bouncer.ui.BouncerMessageView android:id="@+id/bouncer_message_view" android:layout_width="match_parent" android:layout_height="wrap_content" diff --git a/packages/SystemUI/res/layout/combined_qs_header.xml b/packages/SystemUI/res/layout/combined_qs_header.xml index 386c9d66a0c1..665c6127e06d 100644 --- a/packages/SystemUI/res/layout/combined_qs_header.xml +++ b/packages/SystemUI/res/layout/combined_qs_header.xml @@ -74,7 +74,7 @@ frame when animating QS <-> QQS transition android:layout_height="wrap_content" android:id="@+id/barrier" app:barrierDirection="start" - app:constraint_referenced_ids="statusIcons,privacy_container" /> + app:constraint_referenced_ids="shade_header_system_icons,privacy_container" /> <com.android.systemui.statusbar.policy.Clock android:id="@+id/clock" @@ -108,46 +108,39 @@ frame when animating QS <-> QQS transition <include android:id="@+id/carrier_group" layout="@layout/shade_carrier_group" - app:layout_constraintHeight_min="@dimen/large_screen_shade_header_min_height" - android:minHeight="@dimen/large_screen_shade_header_min_height" - app:layout_constraintWidth_min="48dp" android:layout_width="0dp" android:layout_height="0dp" - app:layout_constrainedWidth="true" android:layout_gravity="end|center_vertical" android:layout_marginStart="8dp" - app:layout_constraintStart_toEndOf="@id/date" - app:layout_constraintEnd_toStartOf="@id/statusIcons" - app:layout_constraintTop_toTopOf="@id/clock" app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toStartOf="@id/shade_header_system_icons" app:layout_constraintHorizontal_bias="1" - /> + app:layout_constraintStart_toEndOf="@id/date" + app:layout_constraintTop_toTopOf="@id/clock" /> - <com.android.systemui.statusbar.phone.StatusIconContainer - android:id="@+id/statusIcons" - app:layout_constraintHeight_min="@dimen/large_screen_shade_header_min_height" - android:paddingEnd="@dimen/signal_cluster_battery_padding" + <LinearLayout + android:id="@+id/shade_header_system_icons" android:layout_width="wrap_content" + app:layout_constraintHeight_min="@dimen/large_screen_shade_header_min_height" android:layout_height="@dimen/large_screen_shade_header_min_height" - app:layout_constraintStart_toEndOf="@id/carrier_group" - app:layout_constraintEnd_toStartOf="@id/batteryRemainingIcon" - app:layout_constraintTop_toTopOf="@id/clock" + android:clickable="true" + android:orientation="horizontal" app:layout_constraintBottom_toBottomOf="parent" - app:layout_constraintHorizontal_bias="1" - /> + app:layout_constraintEnd_toEndOf="@id/privacy_container" + app:layout_constraintTop_toTopOf="@id/clock"> - <com.android.systemui.battery.BatteryMeterView - android:id="@+id/batteryRemainingIcon" - android:layout_width="wrap_content" - android:layout_height="@dimen/large_screen_shade_header_min_height" - app:layout_constraintHeight_min="@dimen/large_screen_shade_header_min_height" - app:layout_constrainedWidth="true" - app:textAppearance="@style/TextAppearance.QS.Status" - app:layout_constraintStart_toEndOf="@id/statusIcons" - app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintTop_toTopOf="@id/clock" - app:layout_constraintBottom_toBottomOf="parent" - /> + <com.android.systemui.statusbar.phone.StatusIconContainer + android:id="@+id/statusIcons" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:paddingEnd="@dimen/signal_cluster_battery_padding" /> + + <com.android.systemui.battery.BatteryMeterView + android:id="@+id/batteryRemainingIcon" + android:layout_width="wrap_content" + android:layout_height="match_parent" + app:textAppearance="@style/TextAppearance.QS.Status" /> + </LinearLayout> <FrameLayout android:id="@+id/privacy_container" diff --git a/packages/SystemUI/res/layout/keyguard_bottom_area.xml b/packages/SystemUI/res/layout/keyguard_bottom_area.xml index a3a7135dabad..66c57fc2a9ac 100644 --- a/packages/SystemUI/res/layout/keyguard_bottom_area.xml +++ b/packages/SystemUI/res/layout/keyguard_bottom_area.xml @@ -23,7 +23,7 @@ android:outlineProvider="none" > <LinearLayout - android:id="@+id/keyguard_indication_area" + android:id="@id/keyguard_indication_area" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginBottom="@dimen/keyguard_indication_margin_bottom" @@ -31,7 +31,7 @@ android:orientation="vertical"> <com.android.systemui.statusbar.phone.KeyguardIndicationTextView - android:id="@+id/keyguard_indication_text" + android:id="@id/keyguard_indication_text" android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center" @@ -41,13 +41,12 @@ android:accessibilityLiveRegion="polite"/> <com.android.systemui.statusbar.phone.KeyguardIndicationTextView - android:id="@+id/keyguard_indication_text_bottom" + android:id="@id/keyguard_indication_text_bottom" android:layout_width="wrap_content" android:layout_height="wrap_content" android:gravity="center" - android:minHeight="48dp" + android:minHeight="@dimen/keyguard_indication_text_min_height" android:layout_gravity="center_horizontal" - android:layout_centerHorizontal="true" android:paddingStart="@dimen/keyguard_indication_text_padding" android:paddingEnd="@dimen/keyguard_indication_text_padding" android:textAppearance="@style/TextAppearance.Keyguard.BottomArea" diff --git a/packages/SystemUI/res/layout/keyguard_qs_user_switch.xml b/packages/SystemUI/res/layout/keyguard_qs_user_switch.xml index 07c428b5dd7a..61943112f9e9 100644 --- a/packages/SystemUI/res/layout/keyguard_qs_user_switch.xml +++ b/packages/SystemUI/res/layout/keyguard_qs_user_switch.xml @@ -24,7 +24,7 @@ android:layout_gravity="end"> <!-- We add a background behind the UserAvatarView with the same color and with a circular shape so that this view can be expanded into a Dialog or an Activity. --> - <com.android.systemui.animation.LaunchableFrameLayout + <com.android.systemui.animation.view.LaunchableFrameLayout android:id="@+id/kg_multi_user_avatar_with_background" android:layout_width="wrap_content" android:layout_height="wrap_content" @@ -42,5 +42,5 @@ systemui:framePadding="0dp" systemui:frameWidth="0dp"> </com.android.systemui.statusbar.phone.UserAvatarView> - </com.android.systemui.animation.LaunchableFrameLayout> + </com.android.systemui.animation.view.LaunchableFrameLayout> </FrameLayout>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/scene_window_root.xml b/packages/SystemUI/res/layout/scene_window_root.xml new file mode 100644 index 000000000000..0dcd15b429c1 --- /dev/null +++ b/packages/SystemUI/res/layout/scene_window_root.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +** +** Copyright 2023, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<!-- The root view of the scene window. --> +<com.android.systemui.scene.ui.view.SceneWindowRootView + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:sysui="http://schemas.android.com/apk/res-auto" + android:id="@+id/scene_window_root" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:fitsSystemWindows="true"> + + <include layout="@layout/super_notification_shade" + android:layout_width="match_parent" + android:layout_height="match_parent"/> +</com.android.systemui.scene.ui.view.SceneWindowRootView> diff --git a/packages/SystemUI/res/layout/super_notification_shade.xml b/packages/SystemUI/res/layout/super_notification_shade.xml index d9fe949bb327..2fde9479d42a 100644 --- a/packages/SystemUI/res/layout/super_notification_shade.xml +++ b/packages/SystemUI/res/layout/super_notification_shade.xml @@ -21,6 +21,7 @@ <com.android.systemui.shade.NotificationShadeWindowView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:sysui="http://schemas.android.com/apk/res-auto" + android:id="@+id/legacy_window_root" android:layout_width="match_parent" android:layout_height="match_parent" android:fitsSystemWindows="true"> @@ -64,6 +65,12 @@ android:layout_width="match_parent" android:layout_height="match_parent" /> + <!-- Root for all keyguard content. It was previously located within the shade. --> + <com.android.systemui.keyguard.ui.view.KeyguardRootView + android:id="@id/keyguard_root_view" + android:layout_width="match_parent" + android:layout_height="match_parent" /> + <include layout="@layout/status_bar_expanded" android:layout_width="match_parent" android:layout_height="match_parent" diff --git a/packages/SystemUI/res/layout/window_magnification_settings_view.xml b/packages/SystemUI/res/layout/window_magnification_settings_view.xml index a8febe79acae..efdb0a360031 100644 --- a/packages/SystemUI/res/layout/window_magnification_settings_view.xml +++ b/packages/SystemUI/res/layout/window_magnification_settings_view.xml @@ -147,10 +147,12 @@ android:id="@+id/magnifier_zoom_slider" android:layout_width="match_parent" android:layout_height="wrap_content" - app:max="6" app:progress="0" app:iconStartContentDescription="@string/accessibility_control_zoom_out" - app:iconEndContentDescription="@string/accessibility_control_zoom_in"/> + app:iconEndContentDescription="@string/accessibility_control_zoom_in" + app:tickMark="@android:color/transparent" + app:seekBarChangeMagnitude="10" + /> <Button android:id="@+id/magnifier_done_button" diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml index ca58ba0ad7e5..71b02bfc5dbc 100644 --- a/packages/SystemUI/res/values-af/strings.xml +++ b/packages/SystemUI/res/values-af/strings.xml @@ -243,7 +243,7 @@ <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Oudio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Kopstuk"</string> <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Invoer"</string> - <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="3003338571871392293">"Gehoortoestelle"</string> + <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Gehoortoestelle"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Skakel tans aan …"</string> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Outo-draai"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Outodraai skerm"</string> @@ -859,6 +859,7 @@ <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Vergroot die hele skerm"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Vergroot \'n deel van die skerm"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Maak vergrotinginstellings oop"</string> + <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Maak vergrotinginstellings toe"</string> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Sleep hoek om grootte te verander"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Laat diagonale rollees toe"</string> <string name="accessibility_resize" msgid="5733759136600611551">"Verander grootte"</string> @@ -1045,6 +1046,8 @@ <string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Kon nie jou batterymeter lees nie"</string> <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Tik vir meer inligting"</string> <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Geen wekker nie"</string> + <!-- no translation found for accessibility_bouncer (5896923685673320070) --> + <skip /> <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Vingerafdruksensor"</string> <string name="accessibility_authenticate_hint" msgid="798914151813205721">"staaf"</string> <string name="accessibility_enter_hint" msgid="2617864063504824834">"gaan by toestel in"</string> @@ -1107,7 +1110,8 @@ <string name="dream_overlay_status_bar_assistant_attention_indicator" msgid="4712565923771372690">"Assistent luister"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# kennisgewing}other{# kennisgewings}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <string name="note_task_button_label" msgid="8718616095800343136">"Neem notas"</string> + <string name="note_task_button_label" msgid="230135078402003532">"Maak notas"</string> + <string name="note_task_shortcut_long_label" msgid="7729325091147319409">"Maak notas, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Uitsaai"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Hou op om <xliff:g id="APP_NAME">%1$s</xliff:g> uit te saai?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"As jy <xliff:g id="SWITCHAPP">%1$s</xliff:g> uitsaai of die uitvoer verander, sal jou huidige uitsending stop"</string> diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml index ac682710e681..e2038fb0ad8e 100644 --- a/packages/SystemUI/res/values-am/strings.xml +++ b/packages/SystemUI/res/values-am/strings.xml @@ -243,7 +243,7 @@ <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ኦዲዮ"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ማዳመጫ"</string> <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"ግቤት"</string> - <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="3003338571871392293">"አጋዥ መስሚያዎች"</string> + <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"መስሚያ አጋዥ መሣሪያዎች"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"በማብራት ላይ..."</string> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"በራስ ሰር አሽከርክር"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"ማያ ገጽን በራስ-አሽከርክር"</string> @@ -859,6 +859,7 @@ <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"ሙሉ ገፅ እይታን ያጉሉ"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"የማያ ገጹን ክፍል አጉላ"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"የማጉያ ቅንብሮችን ክፈት"</string> + <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"የማጉላት ቅንብሮችን ዝጋ"</string> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"መጠን ለመቀየር ጠርዙን ይዘው ይጎትቱ"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"ሰያፍ ሽብለላን ፍቀድ"</string> <string name="accessibility_resize" msgid="5733759136600611551">"መጠን ቀይር"</string> @@ -1045,6 +1046,8 @@ <string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"የባትሪ መለኪያዎን የማንበብ ችግር"</string> <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"ለበለጠ መረጃ መታ ያድርጉ"</string> <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"ምንም ማንቂያ አልተቀናበረም"</string> + <!-- no translation found for accessibility_bouncer (5896923685673320070) --> + <skip /> <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"የጣት አሻራ ዳሳሽ"</string> <string name="accessibility_authenticate_hint" msgid="798914151813205721">"ያረጋግጡ"</string> <string name="accessibility_enter_hint" msgid="2617864063504824834">"መሣሪያን ያስገቡ"</string> @@ -1107,7 +1110,8 @@ <string name="dream_overlay_status_bar_assistant_attention_indicator" msgid="4712565923771372690">"ረዳት በማዳመጥ ላይ ነው"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# ማሳወቂያ}one{# ማሳወቂያዎች}other{# ማሳወቂያዎች}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>፣ <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <string name="note_task_button_label" msgid="8718616095800343136">"Notetaking"</string> + <string name="note_task_button_label" msgid="230135078402003532">"የማስታወሻ አያያዝ"</string> + <string name="note_task_shortcut_long_label" msgid="7729325091147319409">"የማስታወሻ አያያዝ፣ <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"በማሰራጨት ላይ"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g>ን ማሰራጨት ይቁም?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"<xliff:g id="SWITCHAPP">%1$s</xliff:g>ን ካሰራጩ ወይም ውፅዓትን ከቀየሩ የአሁኑ ስርጭትዎ ይቆማል"</string> diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml index 93f72f451ca7..4d4ee949cc20 100644 --- a/packages/SystemUI/res/values-ar/strings.xml +++ b/packages/SystemUI/res/values-ar/strings.xml @@ -243,7 +243,7 @@ <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"صوت"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"سماعة الرأس"</string> <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"الإدخال"</string> - <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="3003338571871392293">"سماعات الأذن الطبية"</string> + <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"سماعات الأذن الطبية"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"جارٍ التفعيل…"</string> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"التدوير التلقائي"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"التدوير التلقائي للشاشة"</string> @@ -859,6 +859,7 @@ <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"تكبير الشاشة كلها"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"تكبير جزء من الشاشة"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"فتح إعدادات التكبير"</string> + <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"إغلاق إعدادات التكبير"</string> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"اسحب الزاوية لتغيير الحجم."</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"السماح بالتمرير القطري"</string> <string name="accessibility_resize" msgid="5733759136600611551">"تغيير الحجم"</string> @@ -1045,6 +1046,8 @@ <string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"حدثت مشكلة أثناء قراءة مقياس مستوى شحن البطارية."</string> <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"انقر للحصول على مزيد من المعلومات."</string> <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"لم يتم ضبط منبّه."</string> + <!-- no translation found for accessibility_bouncer (5896923685673320070) --> + <skip /> <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"مستشعر بصمات الإصبع"</string> <string name="accessibility_authenticate_hint" msgid="798914151813205721">"المصادقة"</string> <string name="accessibility_enter_hint" msgid="2617864063504824834">"الدخول إلى الجهاز"</string> @@ -1107,7 +1110,8 @@ <string name="dream_overlay_status_bar_assistant_attention_indicator" msgid="4712565923771372690">"يستمع \"مساعد Google\" إليك الآن."</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{إشعار واحد}zero{# إشعار}two{إشعاران}few{# إشعارات}many{# إشعارًا}other{# إشعار}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>، <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <string name="note_task_button_label" msgid="8718616095800343136">"تدوين الملاحظات"</string> + <string name="note_task_button_label" msgid="230135078402003532">"تدوين الملاحظات"</string> + <string name="note_task_shortcut_long_label" msgid="7729325091147319409">"تدوين الملاحظات في \"<xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>\""</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"البث"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"هل تريد إيقاف بث تطبيق <xliff:g id="APP_NAME">%1$s</xliff:g>؟"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"إذا أجريت بث تطبيق <xliff:g id="SWITCHAPP">%1$s</xliff:g> أو غيَّرت جهاز الإخراج، سيتوقَف البث الحالي."</string> diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml index 36a36938ad85..eb07ee39f868 100644 --- a/packages/SystemUI/res/values-as/strings.xml +++ b/packages/SystemUI/res/values-as/strings.xml @@ -243,7 +243,7 @@ <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"অডিঅ’"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"হেডছেট"</string> <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"ইনপুট"</string> - <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="3003338571871392293">"শ্ৰৱণ যন্ত্ৰ"</string> + <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"শ্ৰৱণ যন্ত্ৰ"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"অন কৰি থকা হৈছে…"</string> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"স্বয়ং-ঘূৰ্ণন"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"স্বয়ং-ঘূৰ্ণন স্ক্ৰীন"</string> @@ -859,6 +859,7 @@ <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"পূৰ্ণ স্ক্ৰীন বিবৰ্ধন কৰক"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"স্ক্ৰীনৰ কিছু অংশ বিবৰ্ধন কৰক"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"বিবৰ্ধন কৰাৰ ছেটিং খোলক"</string> + <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"বিবৰ্ধনৰ ছেটিং বন্ধ কৰক"</string> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"আকাৰ সলনি কৰিবলৈ চুককেইটা টানি আনি এৰক"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"কোণীয়াকৈ স্ক্ৰ’ল কৰাৰ অনুমতি দিয়ক"</string> <string name="accessibility_resize" msgid="5733759136600611551">"আকাৰ সলনি কৰক"</string> @@ -1045,6 +1046,8 @@ <string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"আপোনাৰ বেটাৰী মিটাৰ পঢ়োঁতে সমস্যা হৈছে"</string> <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"অধিক তথ্যৰ বাবে টিপক"</string> <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"কোনো এলাৰ্ম ছেট কৰা হোৱা নাই"</string> + <!-- no translation found for accessibility_bouncer (5896923685673320070) --> + <skip /> <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"ফিংগাৰপ্ৰিণ্ট ছেন্সৰ"</string> <string name="accessibility_authenticate_hint" msgid="798914151813205721">"বিশ্বাসযোগ্যতা প্ৰমাণ কৰক"</string> <string name="accessibility_enter_hint" msgid="2617864063504824834">"ডিভাইচ আনলক কৰক"</string> @@ -1107,7 +1110,8 @@ <string name="dream_overlay_status_bar_assistant_attention_indicator" msgid="4712565923771372690">"Assistantএ শুনি আছে"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# টা জাননী}one{# টা জাননী}other{# টা জাননী}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <string name="note_task_button_label" msgid="8718616095800343136">"টোকাগ্ৰহণ"</string> + <string name="note_task_button_label" msgid="230135078402003532">"টোকা গ্ৰহণ কৰা"</string> + <string name="note_task_shortcut_long_label" msgid="7729325091147319409">"টোকা গ্ৰহণ কৰা, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"সম্প্ৰচাৰণ"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g>ৰ সম্প্ৰচাৰ কৰা বন্ধ কৰিবনে?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"যদি আপুনি <xliff:g id="SWITCHAPP">%1$s</xliff:g>ৰ সম্প্ৰচাৰ কৰে অথবা আউটপুট সলনি কৰে, তেন্তে, আপোনাৰ বৰ্তমানৰ সম্প্ৰচাৰ বন্ধ হৈ যাব"</string> diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml index 85ec53a5776d..a99ea62fde07 100644 --- a/packages/SystemUI/res/values-az/strings.xml +++ b/packages/SystemUI/res/values-az/strings.xml @@ -243,7 +243,7 @@ <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Qulaqlıq"</string> <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Giriş"</string> - <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="3003338571871392293">"Eşitmə cihazları"</string> + <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Eşitmə aparatları"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Aktiv edilir..."</string> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Avtodönüş"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Ekranın avtomatik dönməsi"</string> @@ -859,6 +859,7 @@ <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Tam ekranı böyüdün"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Ekran hissəsinin böyüdülməsi"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Böyütmə ayarlarını açın"</string> + <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Böyütmə ayarlarını bağlayın"</string> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Ölçüsünü dəyişmək üçün küncündən sürüşdürün"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Diaqonal sürüşdürməyə icazə verin"</string> <string name="accessibility_resize" msgid="5733759136600611551">"Ölçüsünü dəyişin"</string> @@ -1045,6 +1046,8 @@ <string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Batareya ölçüsünü oxuyarkən problem yarandı"</string> <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Ətraflı məlumat üçün toxunun"</string> <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Siqnal ayarlanmayıb"</string> + <!-- no translation found for accessibility_bouncer (5896923685673320070) --> + <skip /> <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Barmaq izi sensoru"</string> <string name="accessibility_authenticate_hint" msgid="798914151813205721">"doğrulayın"</string> <string name="accessibility_enter_hint" msgid="2617864063504824834">"cihaz daxil edin"</string> @@ -1107,7 +1110,8 @@ <string name="dream_overlay_status_bar_assistant_attention_indicator" msgid="4712565923771372690">"Assistent dinləyir"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# bildiriş}other{# bildiriş}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <string name="note_task_button_label" msgid="8718616095800343136">"Qeyd tutma"</string> + <string name="note_task_button_label" msgid="230135078402003532">"Qeydgötürmə"</string> + <string name="note_task_shortcut_long_label" msgid="7729325091147319409">"Qeydgötürmə, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Yayım"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g> tətbiqinin yayımlanması dayandırılsın?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> tətbiqini yayımlasanız və ya nəticəni dəyişsəniz, cari yayımınız dayandırılacaq"</string> diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml index 71f149fe0735..1dfdde30c264 100644 --- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml +++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml @@ -243,7 +243,7 @@ <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Slušalice"</string> <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Unos"</string> - <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="3003338571871392293">"Slušni aparati"</string> + <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Slušni aparati"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Uključuje se..."</string> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Automatska rotacija"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Automatsko rotiranje ekrana"</string> @@ -859,6 +859,7 @@ <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Uvećajte ceo ekran"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Uvećajte deo ekrana"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Otvori podešavanja uvećanja"</string> + <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Zatvori podešavanja uvećanja"</string> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Prevucite ugao da biste promenili veličinu"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Dozvoli dijagonalno skrolovanje"</string> <string name="accessibility_resize" msgid="5733759136600611551">"Promeni veličinu"</string> @@ -1045,6 +1046,8 @@ <string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Problem sa očitavanjem merača baterije"</string> <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Dodirnite za više informacija"</string> <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Nije podešen"</string> + <!-- no translation found for accessibility_bouncer (5896923685673320070) --> + <skip /> <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Senzor za otisak prsta"</string> <string name="accessibility_authenticate_hint" msgid="798914151813205721">"potvrdite identitet"</string> <string name="accessibility_enter_hint" msgid="2617864063504824834">"unesite uređaj"</string> @@ -1107,7 +1110,8 @@ <string name="dream_overlay_status_bar_assistant_attention_indicator" msgid="4712565923771372690">"Pomoćnik sluša"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# obaveštenje}one{# obaveštenje}few{# obaveštenja}other{# obaveštenja}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <string name="note_task_button_label" msgid="8718616095800343136">"Pravljenje beležaka"</string> + <string name="note_task_button_label" msgid="230135078402003532">"Pravljenje beležaka"</string> + <string name="note_task_shortcut_long_label" msgid="7729325091147319409">"Pravljenje beležaka, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Emitovanje"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Želite da zaustavite emitovanje aplikacije <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Ako emitujete aplikaciju <xliff:g id="SWITCHAPP">%1$s</xliff:g> ili promenite izlaz, aktuelno emitovanje će se zaustaviti"</string> diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml index 6ed2e319c667..05841dceb8ea 100644 --- a/packages/SystemUI/res/values-be/strings.xml +++ b/packages/SystemUI/res/values-be/strings.xml @@ -243,7 +243,7 @@ <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Гук"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Гарнітура"</string> <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Увод"</string> - <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="3003338571871392293">"Слыхавыя апараты"</string> + <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Слыхавыя апараты"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Уключэнне…"</string> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Аўтапаварот"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Аўтаматычны паварот экрана"</string> @@ -859,6 +859,7 @@ <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Павялічыць увесь экран"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Павялічыць частку экрана"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Адкрыць налады павелічэння"</string> + <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Закрыць налады павелічэння"</string> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Каб змяніць памер, перацягніце вугал"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Дазволіць прагортванне па дыяганалі"</string> <string name="accessibility_resize" msgid="5733759136600611551">"Змяніць памер"</string> @@ -1045,6 +1046,8 @@ <string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Праблема з чытаннем індыкатара зараду акумулятара"</string> <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Націсніце, каб убачыць больш"</string> <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Няма будзільнікаў"</string> + <!-- no translation found for accessibility_bouncer (5896923685673320070) --> + <skip /> <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Сканер адбіткаў пальцаў"</string> <string name="accessibility_authenticate_hint" msgid="798914151813205721">"правесці аўтэнтыфікацыю"</string> <string name="accessibility_enter_hint" msgid="2617864063504824834">"адкрыць галоўны экран прылады"</string> @@ -1107,7 +1110,8 @@ <string name="dream_overlay_status_bar_assistant_attention_indicator" msgid="4712565923771372690">"Памочнік слухае"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# апавяшчэнне}one{# апавяшчэнне}few{# апавяшчэнні}many{# апавяшчэнняў}other{# апавяшчэння}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <string name="note_task_button_label" msgid="8718616095800343136">"Стварэнне нататак"</string> + <string name="note_task_button_label" msgid="230135078402003532">"Стварэнне нататак"</string> + <string name="note_task_shortcut_long_label" msgid="7729325091147319409">"Стварэнне нататак, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Перадача даных"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Спыніць трансляцыю праграмы \"<xliff:g id="APP_NAME">%1$s</xliff:g>\"?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Пры пераключэнні на праграму \"<xliff:g id="SWITCHAPP">%1$s</xliff:g>\" ці змяненні вываду бягучая трансляцыя спыняецца"</string> diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml index 016438fc978d..0345471ea49f 100644 --- a/packages/SystemUI/res/values-bg/strings.xml +++ b/packages/SystemUI/res/values-bg/strings.xml @@ -243,7 +243,7 @@ <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Аудио"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Слушалки"</string> <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Вход"</string> - <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="3003338571871392293">"Слухови апарати"</string> + <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Слухови апарати"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Включва се..."</string> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Авт. ориентация"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Автоматично завъртане на екрана"</string> @@ -859,6 +859,7 @@ <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Увеличаване на целия екран"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Увеличаване на част от екрана"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Отваряне на настройките за увеличението"</string> + <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Затваряне на настройките за увеличение"</string> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Плъзнете ъгъла за преоразмеряване"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Разрешаване на диагонално превъртане"</string> <string name="accessibility_resize" msgid="5733759136600611551">"Преоразмеряване"</string> @@ -1045,6 +1046,8 @@ <string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Възникна проблем при четенето на данните за нивото на батерията"</string> <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Докоснете за още информация"</string> <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Няма зададен будилник"</string> + <!-- no translation found for accessibility_bouncer (5896923685673320070) --> + <skip /> <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Сензор за отпечатъци"</string> <string name="accessibility_authenticate_hint" msgid="798914151813205721">"удостоверяване"</string> <string name="accessibility_enter_hint" msgid="2617864063504824834">"вход в устройството"</string> @@ -1107,7 +1110,8 @@ <string name="dream_overlay_status_bar_assistant_attention_indicator" msgid="4712565923771372690">"Асистент слуша"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# известие}other{# известия}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <string name="note_task_button_label" msgid="8718616095800343136">"Водене на бележки"</string> + <string name="note_task_button_label" msgid="230135078402003532">"Водене на бележки"</string> + <string name="note_task_shortcut_long_label" msgid="7729325091147319409">"Водене на бележки, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Излъчване"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Да се спре ли предаването на <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Ако предавате <xliff:g id="SWITCHAPP">%1$s</xliff:g> или промените изхода, текущото ви предаване ще бъде прекратено"</string> diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml index 358174e80e36..2d0388dcc780 100644 --- a/packages/SystemUI/res/values-bn/strings.xml +++ b/packages/SystemUI/res/values-bn/strings.xml @@ -243,7 +243,7 @@ <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"অডিও"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"হেডসেট"</string> <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"ইনপুট"</string> - <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="3003338571871392293">"হিয়ারিং এড"</string> + <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"হিয়ারিং এড"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"চালু করা হচ্ছে…"</string> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"নিজে থেকে ঘুরবে"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"অটো-রোটেট স্ক্রিন"</string> @@ -859,6 +859,7 @@ <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"সম্পূর্ণ স্ক্রিন বড় করে দেখা"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"স্ক্রিনের কিছুটা অংশ বড় করুন"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"বড় করে দেখার সেটিংস খুলুন"</string> + <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"\'বড় করে দেখা\' সেটিংস বন্ধ করুন"</string> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"ছোট বড় করার জন্য কোণ টেনে আনুন"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"কোনাকুনি স্ক্রল করার অনুমতি দেওয়া"</string> <string name="accessibility_resize" msgid="5733759136600611551">"ছোট বড় করা"</string> @@ -1045,6 +1046,8 @@ <string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"ব্যাটারির মিটারের রিডিং নেওয়ার সময় সমস্যা হয়েছে"</string> <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"আরও তথ্যের জন্য ট্যাপ করুন"</string> <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"কোনও অ্যালার্ম সেট করা নেই"</string> + <!-- no translation found for accessibility_bouncer (5896923685673320070) --> + <skip /> <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"ফিঙ্গারপ্রিন্ট সেন্সর"</string> <string name="accessibility_authenticate_hint" msgid="798914151813205721">"যাচাই করিয়ে নিন"</string> <string name="accessibility_enter_hint" msgid="2617864063504824834">"ডিভাইস আনলক করুন"</string> @@ -1107,7 +1110,8 @@ <string name="dream_overlay_status_bar_assistant_attention_indicator" msgid="4712565923771372690">"Assistant শুনছে"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{#টি বিজ্ঞপ্তি}one{#টি বিজ্ঞপ্তি}other{#টি বিজ্ঞপ্তি}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <string name="note_task_button_label" msgid="8718616095800343136">"Notetaking"</string> + <string name="note_task_button_label" msgid="230135078402003532">"নোট নেওয়া"</string> + <string name="note_task_shortcut_long_label" msgid="7729325091147319409">"নোট নেওয়া, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"ব্রডকাস্ট করা হচ্ছে"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g> সম্প্রচার বন্ধ করবেন?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"আপনি <xliff:g id="SWITCHAPP">%1$s</xliff:g> সম্প্রচার করলে বা আউটপুট পরিবর্তন করলে, আপনার বর্তমান সম্প্রচার বন্ধ হয়ে যাবে"</string> diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml index 4fe026c52b76..8e8fb8b84d66 100644 --- a/packages/SystemUI/res/values-bs/strings.xml +++ b/packages/SystemUI/res/values-bs/strings.xml @@ -243,7 +243,7 @@ <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Zvuk"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Slušalice"</string> <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Ulaz"</string> - <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="3003338571871392293">"Slušni aparat"</string> + <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Slušni aparati"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Uključivanje…"</string> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Automatsko rotiranje"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Automatsko rotiranje ekrana"</string> @@ -859,6 +859,7 @@ <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Uvećavanje prikaza preko cijelog ekrana"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Uvećavanje dijela ekrana"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Otvori postavke uvećavanja"</string> + <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Zatvori postavke uvećavanja"</string> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Prevucite ugao da promijenite veličinu"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Dozvoli dijagonalno klizanje"</string> <string name="accessibility_resize" msgid="5733759136600611551">"Promijeni veličinu"</string> @@ -1045,6 +1046,8 @@ <string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Došlo je do problema prilikom očitavanja mjerača stanja baterije"</string> <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Dodirnite za više informacija"</string> <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Nema nijednog alarma"</string> + <!-- no translation found for accessibility_bouncer (5896923685673320070) --> + <skip /> <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Senzor za otisak prsta"</string> <string name="accessibility_authenticate_hint" msgid="798914151813205721">"autentificiranje"</string> <string name="accessibility_enter_hint" msgid="2617864063504824834">"pristup uređaju"</string> @@ -1107,7 +1110,8 @@ <string name="dream_overlay_status_bar_assistant_attention_indicator" msgid="4712565923771372690">"Asistent sluša"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# obavještenje}one{# obavještenje}few{# obavještenja}other{# obavještenja}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <string name="note_task_button_label" msgid="8718616095800343136">"Pisanje bilješki"</string> + <string name="note_task_button_label" msgid="230135078402003532">"Pisanje bilješki"</string> + <string name="note_task_shortcut_long_label" msgid="7729325091147319409">"Pisanje bilješki, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Emitiranje"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Zaustaviti emitiranje aplikacije <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Ako emitirate aplikaciju <xliff:g id="SWITCHAPP">%1$s</xliff:g> ili promijenite izlaz, trenutno emitiranje će se zaustaviti"</string> diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml index cca1904717fd..530482fb0473 100644 --- a/packages/SystemUI/res/values-ca/strings.xml +++ b/packages/SystemUI/res/values-ca/strings.xml @@ -243,7 +243,7 @@ <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Àudio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Auriculars"</string> <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Entrada"</string> - <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="3003338571871392293">"Audiòfons"</string> + <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Audiòfons"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"S\'està activant…"</string> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Gira automàticament"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Gira la pantalla automàticament"</string> @@ -859,6 +859,7 @@ <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Amplia la pantalla completa"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Amplia una part de la pantalla"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Obre la configuració de l\'ampliació"</string> + <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Tanca la configuració de l\'ampliació"</string> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Arrossega el cantó per canviar la mida"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Permet el desplaçament en diagonal"</string> <string name="accessibility_resize" msgid="5733759136600611551">"Canvia la mida"</string> @@ -1045,6 +1046,8 @@ <string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Hi ha hagut un problema en llegir el mesurador de la bateria"</string> <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Toca per obtenir més informació"</string> <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Cap alarma definida"</string> + <!-- no translation found for accessibility_bouncer (5896923685673320070) --> + <skip /> <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Sensor d\'empremtes digitals"</string> <string name="accessibility_authenticate_hint" msgid="798914151813205721">"autenticar"</string> <string name="accessibility_enter_hint" msgid="2617864063504824834">"accedir al dispositiu"</string> @@ -1107,7 +1110,8 @@ <string name="dream_overlay_status_bar_assistant_attention_indicator" msgid="4712565923771372690">"L\'assistent t\'escolta"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# notificació}many{# notificacions}other{# notificacions}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <string name="note_task_button_label" msgid="8718616095800343136">"Presa de notes"</string> + <string name="note_task_button_label" msgid="230135078402003532">"Presa de notes"</string> + <string name="note_task_shortcut_long_label" msgid="7729325091147319409">"Presa de notes, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"S\'està emetent"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Vols deixar d\'emetre <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Si emets <xliff:g id="SWITCHAPP">%1$s</xliff:g> o canvies la sortida, l\'emissió actual s\'aturarà"</string> diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml index 404779f24e3e..fba856d52fee 100644 --- a/packages/SystemUI/res/values-cs/strings.xml +++ b/packages/SystemUI/res/values-cs/strings.xml @@ -243,7 +243,7 @@ <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Zvuk"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Sluchátka"</string> <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Vstup"</string> - <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="3003338571871392293">"Naslouchátka"</string> + <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Naslouchátka"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Zapínání…"</string> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Automatické otáčení"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Automatické otáčení obrazovky"</string> @@ -859,6 +859,7 @@ <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Zvětšit celou obrazovku"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Zvětšit část obrazovky"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Otevřít nastavení zvětšení"</string> + <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Zavřít nastavení zvětšení"</string> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Velikost změníte přetažením rohu"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Povolit diagonální posouvání"</string> <string name="accessibility_resize" msgid="5733759136600611551">"Změnit velikost"</string> @@ -1045,6 +1046,8 @@ <string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Problém s načtením měřiče baterie"</string> <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Klepnutím zobrazíte další informace"</string> <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Budík nenastaven"</string> + <!-- no translation found for accessibility_bouncer (5896923685673320070) --> + <skip /> <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Snímač otisků prstů"</string> <string name="accessibility_authenticate_hint" msgid="798914151813205721">"ověříte"</string> <string name="accessibility_enter_hint" msgid="2617864063504824834">"zadáte zařízení"</string> @@ -1107,7 +1110,8 @@ <string name="dream_overlay_status_bar_assistant_attention_indicator" msgid="4712565923771372690">"Asistent poslouchá"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# oznámení}few{# oznámení}many{# oznámení}other{# oznámení}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g> <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <string name="note_task_button_label" msgid="8718616095800343136">"Poznámky"</string> + <string name="note_task_button_label" msgid="230135078402003532">"Psaní poznámek"</string> + <string name="note_task_shortcut_long_label" msgid="7729325091147319409">"Psaní poznámek, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Vysílání"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Zastavit vysílání v aplikaci <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Pokud budete vysílat v aplikaci <xliff:g id="SWITCHAPP">%1$s</xliff:g> nebo změníte výstup, aktuální vysílání se zastaví"</string> diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml index ededbd39c5d6..386f809f42e5 100644 --- a/packages/SystemUI/res/values-da/strings.xml +++ b/packages/SystemUI/res/values-da/strings.xml @@ -243,7 +243,7 @@ <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Lyd"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string> <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Input"</string> - <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="3003338571871392293">"Høreapparater"</string> + <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Høreapparater"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Aktiverer…"</string> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Roter automatisk"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Roter skærmen automatisk"</string> @@ -859,6 +859,7 @@ <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Forstør hele skærmen"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Forstør en del af skærmen"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Åbn indstillinger for forstørrelse"</string> + <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Luk indstillinger for forstørrelse"</string> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Træk i hjørnet for at justere størrelsen"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Tillad diagonal rulning"</string> <string name="accessibility_resize" msgid="5733759136600611551">"Juster"</string> @@ -1045,6 +1046,8 @@ <string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Der er problemer med at læse dit batteriniveau"</string> <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Tryk for at få flere oplysninger"</string> <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Ingen alarm er indstillet"</string> + <!-- no translation found for accessibility_bouncer (5896923685673320070) --> + <skip /> <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Fingeraftrykssensor"</string> <string name="accessibility_authenticate_hint" msgid="798914151813205721">"godkende"</string> <string name="accessibility_enter_hint" msgid="2617864063504824834">"få adgang til enheden"</string> @@ -1107,7 +1110,8 @@ <string name="dream_overlay_status_bar_assistant_attention_indicator" msgid="4712565923771372690">"Google Assistent lytter med"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# notifikation}one{# notifikation}other{# notifikationer}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <string name="note_task_button_label" msgid="8718616095800343136">"Notetagning"</string> + <string name="note_task_button_label" msgid="230135078402003532">"Notetagning"</string> + <string name="note_task_shortcut_long_label" msgid="7729325091147319409">"Notetagning, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Udsender"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Stop udsendelsen <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Hvis du udsender <xliff:g id="SWITCHAPP">%1$s</xliff:g> eller skifter output, stopper din aktuelle udsendelse"</string> diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml index d987120554c1..b57f023197eb 100644 --- a/packages/SystemUI/res/values-de/strings.xml +++ b/packages/SystemUI/res/values-de/strings.xml @@ -243,7 +243,7 @@ <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string> <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Eingabe"</string> - <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="3003338571871392293">"Hörgeräte"</string> + <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Hörgerät"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Wird aktiviert…"</string> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Autom. drehen"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Bildschirm automatisch drehen"</string> @@ -859,6 +859,7 @@ <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Ganzen Bildschirm vergrößern"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Teil des Bildschirms vergrößern"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Vergrößerungseinstellungen öffnen"</string> + <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Vergrößerungseinstellungen schließen"</string> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Zum Anpassen der Größe Ecke ziehen"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Diagonales Scrollen erlauben"</string> <string name="accessibility_resize" msgid="5733759136600611551">"Größe ändern"</string> @@ -1045,6 +1046,8 @@ <string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Problem beim Lesen des Akkustands"</string> <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Für weitere Informationen tippen"</string> <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Kein Wecker gestellt"</string> + <!-- no translation found for accessibility_bouncer (5896923685673320070) --> + <skip /> <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Fingerabdrucksensor"</string> <string name="accessibility_authenticate_hint" msgid="798914151813205721">"Authentifizieren"</string> <string name="accessibility_enter_hint" msgid="2617864063504824834">"Eingeben des Geräts"</string> @@ -1107,7 +1110,8 @@ <string name="dream_overlay_status_bar_assistant_attention_indicator" msgid="4712565923771372690">"Assistant hört zu"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# Benachrichtigung}other{# Benachrichtigungen}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <string name="note_task_button_label" msgid="8718616095800343136">"Notizen machen"</string> + <string name="note_task_button_label" msgid="230135078402003532">"Notizen"</string> + <string name="note_task_shortcut_long_label" msgid="7729325091147319409">"Notizen, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Übertragung läuft"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g> nicht mehr streamen?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Wenn du <xliff:g id="SWITCHAPP">%1$s</xliff:g> streamst oder die Ausgabe änderst, wird dein aktueller Stream beendet"</string> diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml index a863cc73a3dd..421307ee94bd 100644 --- a/packages/SystemUI/res/values-el/strings.xml +++ b/packages/SystemUI/res/values-el/strings.xml @@ -243,7 +243,7 @@ <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Ήχος"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Ακουστικά"</string> <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Είσοδος"</string> - <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="3003338571871392293">"Βοηθήματα ακρόασης"</string> + <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Βοηθήματα ακοής"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Ενεργοποίηση…"</string> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Αυτόματη περιστροφή"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Αυτόματη περιστροφή οθόνης"</string> @@ -859,6 +859,7 @@ <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Μεγέθυνση πλήρους οθόνης"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Μεγέθυνση μέρους της οθόνης"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Άνοιγμα ρυθμίσεων μεγιστοποίησης"</string> + <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Κλείσιμο ρυθμίσεων μεγιστοποίησης"</string> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Σύρετε τη γωνία για αλλαγή μεγέθους"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Να επιτρέπεται η διαγώνια κύλιση"</string> <string name="accessibility_resize" msgid="5733759136600611551">"Αλλαγή μεγέθους"</string> @@ -1045,6 +1046,8 @@ <string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Υπάρχει κάποιο πρόβλημα με την ανάγνωση του μετρητή μπαταρίας"</string> <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Πατήστε για περισσότερες πληροφορίες."</string> <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Δεν ορίστηκε ξυπνητ."</string> + <!-- no translation found for accessibility_bouncer (5896923685673320070) --> + <skip /> <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Αισθητήρας δακτυλικών αποτυπωμάτων"</string> <string name="accessibility_authenticate_hint" msgid="798914151813205721">"έλεγχος ταυτότητας"</string> <string name="accessibility_enter_hint" msgid="2617864063504824834">"εισαγωγή συσκευής"</string> @@ -1107,7 +1110,8 @@ <string name="dream_overlay_status_bar_assistant_attention_indicator" msgid="4712565923771372690">"Ο Βοηθός ακούει"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# ειδοποίηση}other{# ειδοποιήσεις}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <string name="note_task_button_label" msgid="8718616095800343136">"Δημιουργία σημειώσεων"</string> + <string name="note_task_button_label" msgid="230135078402003532">"Δημιουργία σημειώσεων"</string> + <string name="note_task_shortcut_long_label" msgid="7729325091147319409">"Δημιουργία σημειώσεων, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Μετάδοση"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Διακοπή μετάδοσης με την εφαρμογή <xliff:g id="APP_NAME">%1$s</xliff:g>;"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Εάν κάνετε μετάδοση με την εφαρμογή <xliff:g id="SWITCHAPP">%1$s</xliff:g> ή αλλάξετε την έξοδο, η τρέχουσα μετάδοση θα σταματήσει"</string> diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml index afadffb57928..eb4ccf576a21 100644 --- a/packages/SystemUI/res/values-en-rAU/strings.xml +++ b/packages/SystemUI/res/values-en-rAU/strings.xml @@ -243,7 +243,7 @@ <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string> <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Input"</string> - <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="3003338571871392293">"Hearing aids"</string> + <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Hearing aids"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Turning on…"</string> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Auto-rotate"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Auto-rotate screen"</string> @@ -859,6 +859,7 @@ <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Magnify full screen"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Magnify part of screen"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Open magnification settings"</string> + <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Close magnification settings"</string> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Drag corner to resize"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Allow diagonal scrolling"</string> <string name="accessibility_resize" msgid="5733759136600611551">"Resize"</string> @@ -1045,6 +1046,8 @@ <string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Problem reading your battery meter"</string> <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Tap for more information"</string> <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"No alarm set"</string> + <!-- no translation found for accessibility_bouncer (5896923685673320070) --> + <skip /> <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Fingerprint sensor"</string> <string name="accessibility_authenticate_hint" msgid="798914151813205721">"Authenticate"</string> <string name="accessibility_enter_hint" msgid="2617864063504824834">"enter device"</string> @@ -1107,7 +1110,8 @@ <string name="dream_overlay_status_bar_assistant_attention_indicator" msgid="4712565923771372690">"Assistant is listening"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# notification}other{# notifications}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <string name="note_task_button_label" msgid="8718616095800343136">"Notetaking"</string> + <string name="note_task_button_label" msgid="230135078402003532">"Note-taking"</string> + <string name="note_task_shortcut_long_label" msgid="7729325091147319409">"Note-taking, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Broadcasting"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Stop broadcasting <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"If you broadcast <xliff:g id="SWITCHAPP">%1$s</xliff:g> or change the output, your current broadcast will stop"</string> diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml index 4f46c007ef1b..43d58caa6d4c 100644 --- a/packages/SystemUI/res/values-en-rCA/strings.xml +++ b/packages/SystemUI/res/values-en-rCA/strings.xml @@ -243,7 +243,7 @@ <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string> <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Input"</string> - <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="3003338571871392293">"Hearing Aids"</string> + <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Hearing aids"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Turning on…"</string> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Auto-rotate"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Auto-rotate screen"</string> @@ -859,6 +859,7 @@ <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Magnify full screen"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Magnify part of screen"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Open magnification settings"</string> + <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Close magnification settings"</string> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Drag corner to resize"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Allow diagonal scrolling"</string> <string name="accessibility_resize" msgid="5733759136600611551">"Resize"</string> @@ -1045,6 +1046,7 @@ <string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Problem reading your battery meter"</string> <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Tap for more information"</string> <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"No alarm set"</string> + <string name="accessibility_bouncer" msgid="5896923685673320070">"enter screen lock"</string> <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Fingerprint sensor"</string> <string name="accessibility_authenticate_hint" msgid="798914151813205721">"authenticate"</string> <string name="accessibility_enter_hint" msgid="2617864063504824834">"enter device"</string> @@ -1107,7 +1109,8 @@ <string name="dream_overlay_status_bar_assistant_attention_indicator" msgid="4712565923771372690">"Assistant is listening"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# notification}other{# notifications}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <string name="note_task_button_label" msgid="8718616095800343136">"Notetaking"</string> + <string name="note_task_button_label" msgid="230135078402003532">"Note-taking"</string> + <string name="note_task_shortcut_long_label" msgid="7729325091147319409">"Note-taking, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Broadcasting"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Stop broadcasting <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"If you broadcast <xliff:g id="SWITCHAPP">%1$s</xliff:g> or change the output, your current broadcast will stop"</string> diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml index afadffb57928..eb4ccf576a21 100644 --- a/packages/SystemUI/res/values-en-rGB/strings.xml +++ b/packages/SystemUI/res/values-en-rGB/strings.xml @@ -243,7 +243,7 @@ <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string> <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Input"</string> - <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="3003338571871392293">"Hearing aids"</string> + <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Hearing aids"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Turning on…"</string> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Auto-rotate"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Auto-rotate screen"</string> @@ -859,6 +859,7 @@ <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Magnify full screen"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Magnify part of screen"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Open magnification settings"</string> + <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Close magnification settings"</string> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Drag corner to resize"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Allow diagonal scrolling"</string> <string name="accessibility_resize" msgid="5733759136600611551">"Resize"</string> @@ -1045,6 +1046,8 @@ <string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Problem reading your battery meter"</string> <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Tap for more information"</string> <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"No alarm set"</string> + <!-- no translation found for accessibility_bouncer (5896923685673320070) --> + <skip /> <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Fingerprint sensor"</string> <string name="accessibility_authenticate_hint" msgid="798914151813205721">"Authenticate"</string> <string name="accessibility_enter_hint" msgid="2617864063504824834">"enter device"</string> @@ -1107,7 +1110,8 @@ <string name="dream_overlay_status_bar_assistant_attention_indicator" msgid="4712565923771372690">"Assistant is listening"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# notification}other{# notifications}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <string name="note_task_button_label" msgid="8718616095800343136">"Notetaking"</string> + <string name="note_task_button_label" msgid="230135078402003532">"Note-taking"</string> + <string name="note_task_shortcut_long_label" msgid="7729325091147319409">"Note-taking, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Broadcasting"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Stop broadcasting <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"If you broadcast <xliff:g id="SWITCHAPP">%1$s</xliff:g> or change the output, your current broadcast will stop"</string> diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml index afadffb57928..eb4ccf576a21 100644 --- a/packages/SystemUI/res/values-en-rIN/strings.xml +++ b/packages/SystemUI/res/values-en-rIN/strings.xml @@ -243,7 +243,7 @@ <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string> <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Input"</string> - <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="3003338571871392293">"Hearing aids"</string> + <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Hearing aids"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Turning on…"</string> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Auto-rotate"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Auto-rotate screen"</string> @@ -859,6 +859,7 @@ <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Magnify full screen"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Magnify part of screen"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Open magnification settings"</string> + <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Close magnification settings"</string> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Drag corner to resize"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Allow diagonal scrolling"</string> <string name="accessibility_resize" msgid="5733759136600611551">"Resize"</string> @@ -1045,6 +1046,8 @@ <string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Problem reading your battery meter"</string> <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Tap for more information"</string> <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"No alarm set"</string> + <!-- no translation found for accessibility_bouncer (5896923685673320070) --> + <skip /> <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Fingerprint sensor"</string> <string name="accessibility_authenticate_hint" msgid="798914151813205721">"Authenticate"</string> <string name="accessibility_enter_hint" msgid="2617864063504824834">"enter device"</string> @@ -1107,7 +1110,8 @@ <string name="dream_overlay_status_bar_assistant_attention_indicator" msgid="4712565923771372690">"Assistant is listening"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# notification}other{# notifications}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <string name="note_task_button_label" msgid="8718616095800343136">"Notetaking"</string> + <string name="note_task_button_label" msgid="230135078402003532">"Note-taking"</string> + <string name="note_task_shortcut_long_label" msgid="7729325091147319409">"Note-taking, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Broadcasting"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Stop broadcasting <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"If you broadcast <xliff:g id="SWITCHAPP">%1$s</xliff:g> or change the output, your current broadcast will stop"</string> diff --git a/packages/SystemUI/res/values-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml index c95d6aa8b466..b4c5d10fd271 100644 --- a/packages/SystemUI/res/values-en-rXC/strings.xml +++ b/packages/SystemUI/res/values-en-rXC/strings.xml @@ -243,7 +243,7 @@ <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string> <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Input"</string> - <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="3003338571871392293">"Hearing Aids"</string> + <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Hearing aids"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Turning on…"</string> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Auto-rotate"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Auto-rotate screen"</string> @@ -859,6 +859,7 @@ <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Magnify full screen"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Magnify part of screen"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Open magnification settings"</string> + <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Close magnification settings"</string> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Drag corner to resize"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Allow diagonal scrolling"</string> <string name="accessibility_resize" msgid="5733759136600611551">"Resize"</string> @@ -1045,6 +1046,7 @@ <string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Problem reading your battery meter"</string> <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Tap for more information"</string> <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"No alarm set"</string> + <string name="accessibility_bouncer" msgid="5896923685673320070">"enter screen lock"</string> <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Fingerprint sensor"</string> <string name="accessibility_authenticate_hint" msgid="798914151813205721">"authenticate"</string> <string name="accessibility_enter_hint" msgid="2617864063504824834">"enter device"</string> @@ -1107,7 +1109,8 @@ <string name="dream_overlay_status_bar_assistant_attention_indicator" msgid="4712565923771372690">"Assistant is listening"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# notification}other{# notifications}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <string name="note_task_button_label" msgid="8718616095800343136">"Notetaking"</string> + <string name="note_task_button_label" msgid="230135078402003532">"Note-taking"</string> + <string name="note_task_shortcut_long_label" msgid="7729325091147319409">"Note-taking, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Broadcasting"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Stop broadcasting <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"If you broadcast <xliff:g id="SWITCHAPP">%1$s</xliff:g> or change the output, your current broadcast will stop"</string> diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml index e2ac0ff7c47c..5358a26cfab5 100644 --- a/packages/SystemUI/res/values-es-rUS/strings.xml +++ b/packages/SystemUI/res/values-es-rUS/strings.xml @@ -243,7 +243,7 @@ <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Auriculares"</string> <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Entrada"</string> - <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="3003338571871392293">"Audífonos"</string> + <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Audífonos"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Activando…"</string> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Giro automático"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Girar la pantalla automáticamente"</string> @@ -859,6 +859,7 @@ <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Ampliar pantalla completa"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Ampliar parte de la pantalla"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Abrir la configuración de ampliación"</string> + <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Cerrar configuración de ampliación"</string> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Arrastra la esquina para cambiar el tamaño"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Desplazamiento en diagonal"</string> <string name="accessibility_resize" msgid="5733759136600611551">"Cambiar tamaño"</string> @@ -1045,6 +1046,8 @@ <string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Problema al leer el medidor de batería"</string> <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Presiona para obtener más información"</string> <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"No establecida"</string> + <!-- no translation found for accessibility_bouncer (5896923685673320070) --> + <skip /> <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Sensor de huellas dactilares"</string> <string name="accessibility_authenticate_hint" msgid="798914151813205721">"autenticar"</string> <string name="accessibility_enter_hint" msgid="2617864063504824834">"ingresar al dispositivo"</string> @@ -1107,7 +1110,8 @@ <string name="dream_overlay_status_bar_assistant_attention_indicator" msgid="4712565923771372690">"Asistente está escuchando"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# notificación}many{# notificaciones}other{# notificaciones}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <string name="note_task_button_label" msgid="8718616095800343136">"Tomar notas"</string> + <string name="note_task_button_label" msgid="230135078402003532">"Tomar notas"</string> + <string name="note_task_shortcut_long_label" msgid="7729325091147319409">"Tomar notas, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Transmitiendo"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"¿Quieres dejar de transmitir <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Si transmites <xliff:g id="SWITCHAPP">%1$s</xliff:g> o cambias la salida, tu transmisión actual se detendrá"</string> diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml index f25e0182e6e9..42e390db3473 100644 --- a/packages/SystemUI/res/values-es/strings.xml +++ b/packages/SystemUI/res/values-es/strings.xml @@ -243,7 +243,7 @@ <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Auriculares"</string> <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Entrada"</string> - <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="3003338571871392293">"Audífonos"</string> + <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Audífonos"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Activando…"</string> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Giro automático"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Girar pantalla automáticamente"</string> @@ -859,6 +859,7 @@ <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Ampliar pantalla completa"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Ampliar parte de la pantalla"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Abrir ajustes de ampliación"</string> + <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Cerrar ajustes de ampliación"</string> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Arrastra la esquina para cambiar el tamaño"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Permitir ir en diagonal"</string> <string name="accessibility_resize" msgid="5733759136600611551">"Cambiar tamaño"</string> @@ -1045,6 +1046,8 @@ <string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"No se ha podido leer el indicador de batería"</string> <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Toca la pantalla para consultar más información"</string> <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Ninguna alarma puesta"</string> + <!-- no translation found for accessibility_bouncer (5896923685673320070) --> + <skip /> <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Sensor de huellas digitales"</string> <string name="accessibility_authenticate_hint" msgid="798914151813205721">"autenticarte"</string> <string name="accessibility_enter_hint" msgid="2617864063504824834">"acceder al dispositivo"</string> @@ -1107,7 +1110,8 @@ <string name="dream_overlay_status_bar_assistant_attention_indicator" msgid="4712565923771372690">"El Asistente está escuchando"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# notificación}many{# notificaciones}other{# notificaciones}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <string name="note_task_button_label" msgid="8718616095800343136">"Tomar notas"</string> + <string name="note_task_button_label" msgid="230135078402003532">"Toma de notas"</string> + <string name="note_task_shortcut_long_label" msgid="7729325091147319409">"Toma de notas, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Emitiendo"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"¿Dejar de emitir <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Si emites <xliff:g id="SWITCHAPP">%1$s</xliff:g> o cambias la salida, tu emisión actual se detendrá"</string> diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml index e13fe919d1c1..cd6b47c0bcbe 100644 --- a/packages/SystemUI/res/values-et/strings.xml +++ b/packages/SystemUI/res/values-et/strings.xml @@ -243,7 +243,7 @@ <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Heli"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Peakomplekt"</string> <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Sisend"</string> - <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="3003338571871392293">"Kuuldeaparaadid"</string> + <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Kuuldeaparaadid"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Sisselülitamine …"</string> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Autom. pööramine"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Kuva automaatne pööramine"</string> @@ -859,6 +859,7 @@ <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Täisekraani suurendamine"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Ekraanikuva osa suurendamine"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Ava suurendamisseaded"</string> + <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Sule suurendamisseaded"</string> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Suuruse muutmiseks lohistage nurka"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Luba diagonaalne kerimine"</string> <string name="accessibility_resize" msgid="5733759136600611551">"Muuda suurust"</string> @@ -1045,6 +1046,8 @@ <string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Probleem akumõõdiku lugemisel"</string> <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Puudutage lisateabe saamiseks"</string> <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Äratust pole"</string> + <!-- no translation found for accessibility_bouncer (5896923685673320070) --> + <skip /> <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Sõrmejäljeandur"</string> <string name="accessibility_authenticate_hint" msgid="798914151813205721">"autentimiseks"</string> <string name="accessibility_enter_hint" msgid="2617864063504824834">"seadmesse sisenemiseks"</string> @@ -1107,7 +1110,8 @@ <string name="dream_overlay_status_bar_assistant_attention_indicator" msgid="4712565923771372690">"Assistent kuulab"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# märguanne}other{# märguannet}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <string name="note_task_button_label" msgid="8718616095800343136">"Märkmete tegemine"</string> + <string name="note_task_button_label" msgid="230135078402003532">"Märkmete tegemine"</string> + <string name="note_task_shortcut_long_label" msgid="7729325091147319409">"Märkmete tegemine <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Edastamine"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Kas peatada rakenduse <xliff:g id="APP_NAME">%1$s</xliff:g> ülekandmine?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Kui kannate rakendust <xliff:g id="SWITCHAPP">%1$s</xliff:g> üle või muudate väljundit, peatatakse teie praegune ülekanne"</string> diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml index d80ff83bc0da..a670166bb115 100644 --- a/packages/SystemUI/res/values-eu/strings.xml +++ b/packages/SystemUI/res/values-eu/strings.xml @@ -243,7 +243,7 @@ <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audioa"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Entzungailua"</string> <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Sarrera"</string> - <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="3003338571871392293">"Audifonoak"</string> + <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Audifonoak"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Aktibatzen…"</string> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Biratze automatikoa"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Biratu pantaila automatikoki"</string> @@ -859,6 +859,7 @@ <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Handitu pantaila osoa"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Handitu pantailaren zati bat"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Ireki luparen ezarpenak"</string> + <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Itxi luparen ezarpenak"</string> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Arrastatu izkina bat tamaina aldatzeko"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Eman diagonalki gora eta behera egiteko aukera erabiltzeko baimena"</string> <string name="accessibility_resize" msgid="5733759136600611551">"Aldatu tamaina"</string> @@ -1045,6 +1046,8 @@ <string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Arazo bat izan da bateria-neurgailua irakurtzean"</string> <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Informazio gehiago lortzeko, sakatu hau"</string> <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Ez da ezarri alarmarik"</string> + <!-- no translation found for accessibility_bouncer (5896923685673320070) --> + <skip /> <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Hatz-marken sentsorea"</string> <string name="accessibility_authenticate_hint" msgid="798914151813205721">"autentifikatu"</string> <string name="accessibility_enter_hint" msgid="2617864063504824834">"sartu gailuan"</string> @@ -1107,7 +1110,8 @@ <string name="dream_overlay_status_bar_assistant_attention_indicator" msgid="4712565923771372690">"Laguntzailea entzuten ari da"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# jakinarazpen}other{# jakinarazpen}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <string name="note_task_button_label" msgid="8718616095800343136">"Oharrak idaztea"</string> + <string name="note_task_button_label" msgid="230135078402003532">"Oharrak idaztea"</string> + <string name="note_task_shortcut_long_label" msgid="7729325091147319409">"Oharrak idaztea, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Igortzen"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g> aplikazioaren audioa igortzeari utzi nahi diozu?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> aplikazioaren audioa igortzen baduzu, edo audio-irteera aldatzen baduzu, une hartako igorpena eten egingo da"</string> diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml index 456d91fc2f46..dc24a3dfb810 100644 --- a/packages/SystemUI/res/values-fa/strings.xml +++ b/packages/SystemUI/res/values-fa/strings.xml @@ -243,7 +243,7 @@ <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"صوت"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"هدست"</string> <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"ورودی"</string> - <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="3003338571871392293">"سمعک"</string> + <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"سمعک"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"روشن کردن…"</string> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"چرخش خودکار"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"چرخش خودکار صفحهنمایش"</string> @@ -859,6 +859,7 @@ <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"درشتنمایی تمامصفحه"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"درشتنمایی بخشی از صفحه"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"باز کردن تنظیمات درشتنمایی"</string> + <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"بستن تنظیمات درشتنمایی"</string> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"برای تغییر اندازه، گوشه را بکشید"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"اجازه دادن برای پیمایش قطری"</string> <string name="accessibility_resize" msgid="5733759136600611551">"تغییر اندازه"</string> @@ -1045,6 +1046,8 @@ <string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"مشکلی در خواندن میزان باتری وجود دارد"</string> <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"برای اطلاعات بیشتر ضربه بزنید"</string> <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"هشداری تنظیم نشده"</string> + <!-- no translation found for accessibility_bouncer (5896923685673320070) --> + <skip /> <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"حسگر اثرانگشت"</string> <string name="accessibility_authenticate_hint" msgid="798914151813205721">"اصالتسنجی کردن"</string> <string name="accessibility_enter_hint" msgid="2617864063504824834">"وارد شدن به دستگاه"</string> @@ -1107,7 +1110,8 @@ <string name="dream_overlay_status_bar_assistant_attention_indicator" msgid="4712565923771372690">"«دستیار» درحال گوش کردن است"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# اعلان}one{# اعلان}other{# اعلان}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>، <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <string name="note_task_button_label" msgid="8718616095800343136">"یادداشتبرداری"</string> + <string name="note_task_button_label" msgid="230135078402003532">"یادداشتبرداری"</string> + <string name="note_task_shortcut_long_label" msgid="7729325091147319409">"یادداشتبرداری، <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"همهفرستی"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"همهفرستی <xliff:g id="APP_NAME">%1$s</xliff:g> متوقف شود؟"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"اگر <xliff:g id="SWITCHAPP">%1$s</xliff:g> را همهفرستی کنید یا خروجی را تغییر دهید، همهفرستی کنونی متوقف خواهد شد"</string> diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml index 598bc888f4fc..b6303da6ea52 100644 --- a/packages/SystemUI/res/values-fi/strings.xml +++ b/packages/SystemUI/res/values-fi/strings.xml @@ -243,7 +243,7 @@ <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Ääni"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string> <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Syöttölaite"</string> - <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="3003338571871392293">"Kuulolaitteet"</string> + <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Kuulolaitteet"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Otetaan käyttöön…"</string> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Automaattinen kääntö"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Käännä näyttöä automaattisesti."</string> @@ -859,6 +859,7 @@ <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Koko näytön suurennus"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Suurenna osa näytöstä"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Avaa suurennusasetukset"</string> + <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Sulje suurennusasetukset"</string> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Muuta kokoa vetämällä kulmaa"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Salli diagonaalinen vierittäminen"</string> <string name="accessibility_resize" msgid="5733759136600611551">"Muuta kokoa"</string> @@ -1045,6 +1046,8 @@ <string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Ongelma akkumittarin lukemisessa"</string> <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Saat lisätietoja napauttamalla"</string> <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Ei herätyksiä"</string> + <!-- no translation found for accessibility_bouncer (5896923685673320070) --> + <skip /> <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Sormenjälkitunnistin"</string> <string name="accessibility_authenticate_hint" msgid="798914151813205721">"todentaaksesi"</string> <string name="accessibility_enter_hint" msgid="2617864063504824834">"avataksesi laitteen"</string> @@ -1107,7 +1110,8 @@ <string name="dream_overlay_status_bar_assistant_attention_indicator" msgid="4712565923771372690">"Assistant kuuntelee"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# ilmoitus}other{# ilmoitusta}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <string name="note_task_button_label" msgid="8718616095800343136">"Muistiinpanojen tekeminen"</string> + <string name="note_task_button_label" msgid="230135078402003532">"Muistiinpanojen tekeminen"</string> + <string name="note_task_shortcut_long_label" msgid="7729325091147319409">"Muistiinpanot, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Lähettää"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Lopetetaanko <xliff:g id="APP_NAME">%1$s</xliff:g>-sovelluksen lähettäminen?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Jos lähetät <xliff:g id="SWITCHAPP">%1$s</xliff:g>-sovellusta tai muutat ulostuloa, nykyinen lähetyksesi loppuu"</string> diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml index 848774b4ecfc..d88c9e0807d3 100644 --- a/packages/SystemUI/res/values-fr-rCA/strings.xml +++ b/packages/SystemUI/res/values-fr-rCA/strings.xml @@ -243,7 +243,7 @@ <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Écouteurs"</string> <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Entrée"</string> - <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="3003338571871392293">"Prothèses auditives"</string> + <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Prothèses auditives"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Activation en cours…"</string> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Rotation automatique"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Rotation automatique de l\'écran"</string> @@ -859,6 +859,7 @@ <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Agrandir la totalité de l\'écran"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Agrandir une partie de l\'écran"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Ouvrir les paramètres d\'agrandissement"</string> + <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Fermer les paramètres d\'agrandissement"</string> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Faire glisser le coin pour redimensionner"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Autoriser défilement diagonal"</string> <string name="accessibility_resize" msgid="5733759136600611551">"Redimensionner"</string> @@ -1045,6 +1046,8 @@ <string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Un problème est survenu lors de la lecture du niveau de charge de la pile"</string> <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Touchez pour en savoir plus"</string> <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Aucune alarme définie"</string> + <!-- no translation found for accessibility_bouncer (5896923685673320070) --> + <skip /> <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Capteur d\'empreintes digitales"</string> <string name="accessibility_authenticate_hint" msgid="798914151813205721">"authentifier"</string> <string name="accessibility_enter_hint" msgid="2617864063504824834">"accéder à l\'appareil"</string> @@ -1107,7 +1110,8 @@ <string name="dream_overlay_status_bar_assistant_attention_indicator" msgid="4712565923771372690">"L\'Assistant est à l\'écoute"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# notification}one{# notification}many{# de notifications}other{# notifications}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <string name="note_task_button_label" msgid="8718616095800343136">"Prise de note"</string> + <string name="note_task_button_label" msgid="230135078402003532">"Prise de note"</string> + <string name="note_task_shortcut_long_label" msgid="7729325091147319409">"Prise de note, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Diffusion en cours…"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Arrêter la diffusion de <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Si vous diffusez <xliff:g id="SWITCHAPP">%1$s</xliff:g> ou changez la sortie, votre diffusion actuelle s\'arrêtera"</string> diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml index 90847ad69b2e..b4cfb3418b11 100644 --- a/packages/SystemUI/res/values-fr/strings.xml +++ b/packages/SystemUI/res/values-fr/strings.xml @@ -243,7 +243,7 @@ <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Casque"</string> <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Entrée"</string> - <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="3003338571871392293">"Appareils auditifs"</string> + <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Appareils auditifs"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Activation…"</string> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Rotation automatique"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Rotation automatique de l\'écran"</string> @@ -859,6 +859,7 @@ <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Agrandir tout l\'écran"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Agrandir une partie de l\'écran"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Ouvrir les paramètres d\'agrandissement"</string> + <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Fermer les paramètres d\'agrandissement"</string> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Faire glisser le coin pour redimensionner"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Autoriser le défilement diagonal"</string> <string name="accessibility_resize" msgid="5733759136600611551">"Redimensionner"</string> @@ -1045,6 +1046,8 @@ <string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Un problème est survenu au niveau de la lecture de votre outil de mesure de batterie"</string> <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Appuyer pour en savoir plus"</string> <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Pas d\'alarme définie"</string> + <!-- no translation found for accessibility_bouncer (5896923685673320070) --> + <skip /> <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Lecteur d\'empreinte digitale"</string> <string name="accessibility_authenticate_hint" msgid="798914151813205721">"s\'authentifier"</string> <string name="accessibility_enter_hint" msgid="2617864063504824834">"accéder à l\'appareil"</string> @@ -1107,7 +1110,8 @@ <string name="dream_overlay_status_bar_assistant_attention_indicator" msgid="4712565923771372690">"L\'Assistant écoute"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# notification}one{# notification}many{# notifications}other{# notifications}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <string name="note_task_button_label" msgid="8718616095800343136">"Prise de notes"</string> + <string name="note_task_button_label" msgid="230135078402003532">"Prise de notes"</string> + <string name="note_task_shortcut_long_label" msgid="7729325091147319409">"Prise de notes, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Diffusion…"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Arrêter la diffusion de <xliff:g id="APP_NAME">%1$s</xliff:g> ?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Si vous diffusez <xliff:g id="SWITCHAPP">%1$s</xliff:g> ou que vous modifiez le résultat, votre annonce actuelle s\'arrêtera"</string> diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml index 325d880cce89..014d8baf81c8 100644 --- a/packages/SystemUI/res/values-gl/strings.xml +++ b/packages/SystemUI/res/values-gl/strings.xml @@ -243,7 +243,7 @@ <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Auriculares"</string> <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Entrada"</string> - <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="3003338571871392293">"Audiófonos"</string> + <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Audiófonos"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Activando…"</string> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Xirar automaticamente"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Xirar pantalla automaticamente"</string> @@ -859,6 +859,7 @@ <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Ampliar pantalla completa"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Amplía parte da pantalla"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Abrir configuración da ampliación"</string> + <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Pechar configuración de ampliación"</string> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Arrastrar a esquina para cambiar o tamaño"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Permitir desprazamento diagonal"</string> <string name="accessibility_resize" msgid="5733759136600611551">"Cambiar tamaño"</string> @@ -1045,6 +1046,8 @@ <string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Produciuse un problema ao ler o medidor da batería"</string> <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Toca para obter máis información"</string> <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Sen alarmas postas"</string> + <!-- no translation found for accessibility_bouncer (5896923685673320070) --> + <skip /> <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Sensor de impresión dixital"</string> <string name="accessibility_authenticate_hint" msgid="798914151813205721">"autenticar"</string> <string name="accessibility_enter_hint" msgid="2617864063504824834">"poñer o dispositivo"</string> @@ -1107,7 +1110,8 @@ <string name="dream_overlay_status_bar_assistant_attention_indicator" msgid="4712565923771372690">"O Asistente está escoitando"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# notificación}other{# notificacións}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <string name="note_task_button_label" msgid="8718616095800343136">"Toma de notas"</string> + <string name="note_task_button_label" msgid="230135078402003532">"Toma de notas"</string> + <string name="note_task_shortcut_long_label" msgid="7729325091147319409">"Toma de notas (<xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>)"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Difusión"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Queres deixar de emitir contido a través de <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Se emites contido a través de <xliff:g id="SWITCHAPP">%1$s</xliff:g> ou cambias de saída, a emisión en curso deterase"</string> diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml index 639c6c3c8cea..cd264e2b5338 100644 --- a/packages/SystemUI/res/values-gu/strings.xml +++ b/packages/SystemUI/res/values-gu/strings.xml @@ -243,7 +243,7 @@ <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ઑડિયો"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"હૅડસેટ"</string> <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"ઇનપુટ"</string> - <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="3003338571871392293">"શ્રવણ યંત્રો"</string> + <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"સાંભળવામાં મદદ આપતા યંત્રો"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"ચાલુ કરી રહ્યાં છીએ…"</string> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"ઑટો રોટેટ"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"ઑટો રોટેટ સ્ક્રીન"</string> @@ -859,6 +859,7 @@ <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"પૂર્ણ સ્ક્રીનને મોટી કરો"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"સ્ક્રીનનો કોઈ ભાગ મોટો કરો"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"મોટા કરવાના સેટિંગ ખોલો"</string> + <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"મોટા કરવાના સેટિંગ બંધ કરો"</string> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"કદ બદલવા માટે ખૂણો ખેંચો"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"ડાયગોનલ સ્ક્રોલિંગને મંજૂરી આપો"</string> <string name="accessibility_resize" msgid="5733759136600611551">"કદ બદલો"</string> @@ -1045,6 +1046,8 @@ <string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"તમારું બૅટરી મીટર વાંચવામાં સમસ્યા આવી"</string> <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"વધુ માહિતી માટે ટૅપ કરો"</string> <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"કોઈ અલાર્મ સેટ નથી"</string> + <!-- no translation found for accessibility_bouncer (5896923685673320070) --> + <skip /> <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"ફિંગરપ્રિન્ટ સેન્સર"</string> <string name="accessibility_authenticate_hint" msgid="798914151813205721">"ખાતરી કરો"</string> <string name="accessibility_enter_hint" msgid="2617864063504824834">"ડિવાઇસ અનલૉક કરો"</string> @@ -1107,7 +1110,8 @@ <string name="dream_overlay_status_bar_assistant_attention_indicator" msgid="4712565923771372690">"Assistant સાંભળી રહ્યું છે"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# નોટિફિકેશન}one{# નોટિફિકેશન}other{# નોટિફિકેશન}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <string name="note_task_button_label" msgid="8718616095800343136">"નોંધ લેવી"</string> + <string name="note_task_button_label" msgid="230135078402003532">"નોંધ લેવી"</string> + <string name="note_task_shortcut_long_label" msgid="7729325091147319409">"નોંધ લેવી, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"બ્રૉડકાસ્ટ કરી રહ્યાં છે"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g> બ્રોડકાસ્ટ કરવાનું રોકીએ?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"જો તમે <xliff:g id="SWITCHAPP">%1$s</xliff:g> બ્રોડકાસ્ટ કરો અથવા આઉટપુટ બદલો, તો તમારું હાલનું બ્રોડકાસ્ટ બંધ થઈ જશે"</string> diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml index 6b9d1f53756c..e5c798bdb92e 100644 --- a/packages/SystemUI/res/values-hi/strings.xml +++ b/packages/SystemUI/res/values-hi/strings.xml @@ -244,7 +244,7 @@ <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ऑडियो"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"हेडसेट"</string> <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"इनपुट"</string> - <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="3003338571871392293">"कान की मशीन"</string> + <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"कान की मशीनें"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"ब्लूटूथ चालू हो रहा है…"</string> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"ऑटो-रोटेट"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"स्क्रीन का अपने-आप दिशा बदलना (ऑटो-रोटेट)"</string> @@ -860,6 +860,7 @@ <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"फ़ुल स्क्रीन को ज़ूम करें"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"स्क्रीन के किसी हिस्से को ज़ूम करें"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"ज़ूम करने की सुविधा वाली सेटिंग खोलें"</string> + <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"ज़ूम करने की सुविधा वाली सेटिंग को बंद करें"</string> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"साइज़ बदलने के लिए, कोने को खींचें और छोड़ें"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"तिरछी दिशा में स्क्रोल करने की अनुमति दें"</string> <string name="accessibility_resize" msgid="5733759136600611551">"साइज़ बदलें"</string> @@ -1046,6 +1047,8 @@ <string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"आपके डिवाइस के बैटरी मीटर की रीडिंग लेने में समस्या आ रही है"</string> <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"ज़्यादा जानकारी के लिए टैप करें"</string> <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"कोई अलार्म सेट नहीं है"</string> + <!-- no translation found for accessibility_bouncer (5896923685673320070) --> + <skip /> <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"फ़िंगरप्रिंट सेंसर"</string> <string name="accessibility_authenticate_hint" msgid="798914151813205721">"पुष्टि करें"</string> <string name="accessibility_enter_hint" msgid="2617864063504824834">"डिवाइस की होम स्क्रीन पर जाएं"</string> @@ -1108,7 +1111,8 @@ <string name="dream_overlay_status_bar_assistant_attention_indicator" msgid="4712565923771372690">"Assistant सुन रही है"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# सूचना}one{# सूचना}other{# सूचनाएं}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <string name="note_task_button_label" msgid="8718616095800343136">"नोट बनाएं"</string> + <string name="note_task_button_label" msgid="230135078402003532">"नोट बनाएं"</string> + <string name="note_task_shortcut_long_label" msgid="7729325091147319409">"नोट लेने के लिए, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"ब्रॉडकास्ट ऐप्लिकेशन"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g> पर ब्रॉडकास्ट करना रोकें?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> पर ब्रॉडकास्ट शुरू करने पर या आउटपुट बदलने पर, आपका मौजूदा ब्रॉडकास्ट बंद हो जाएगा"</string> diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml index 7fed2dca0a0a..e921679418d5 100644 --- a/packages/SystemUI/res/values-hr/strings.xml +++ b/packages/SystemUI/res/values-hr/strings.xml @@ -243,7 +243,7 @@ <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Slušalice"</string> <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Unos"</string> - <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="3003338571871392293">"Slušni aparati"</string> + <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Slušna pomagala"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Uključivanje…"</string> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Autom. zakretanje"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Automatsko zakretanje zaslona"</string> @@ -859,6 +859,7 @@ <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Povećajte cijeli zaslon"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Povećaj dio zaslona"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Otvori postavke povećavanja"</string> + <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Zatvori postavke povećavanja"</string> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Povucite kut da biste promijenili veličinu"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Dopusti dijagonalno pomicanje"</string> <string name="accessibility_resize" msgid="5733759136600611551">"Promijeni veličinu"</string> @@ -1045,6 +1046,8 @@ <string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Problem s očitavanjem mjerača baterije"</string> <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Dodirnite za više informacija"</string> <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Nema nijednog alarma"</string> + <!-- no translation found for accessibility_bouncer (5896923685673320070) --> + <skip /> <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Senzor otiska prsta"</string> <string name="accessibility_authenticate_hint" msgid="798914151813205721">"autentificirali"</string> <string name="accessibility_enter_hint" msgid="2617864063504824834">"pristupili uređaju"</string> @@ -1107,7 +1110,8 @@ <string name="dream_overlay_status_bar_assistant_attention_indicator" msgid="4712565923771372690">"Asistent sluša"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# obavijest}one{# obavijest}few{# obavijesti}other{# obavijesti}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <string name="note_task_button_label" msgid="8718616095800343136">"Pisanje bilježaka"</string> + <string name="note_task_button_label" msgid="230135078402003532">"Pisanje bilježaka"</string> + <string name="note_task_shortcut_long_label" msgid="7729325091147319409">"Pisanje bilježaka, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Emitiranje"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Zaustaviti emitiranje aplikacije <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Ako emitirate aplikaciju <xliff:g id="SWITCHAPP">%1$s</xliff:g> ili promijenite izlaz, vaše će se trenutačno emitiranje zaustaviti"</string> diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml index 7b5c9433d25c..b0050cf494e8 100644 --- a/packages/SystemUI/res/values-hu/strings.xml +++ b/packages/SystemUI/res/values-hu/strings.xml @@ -243,7 +243,7 @@ <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Hang"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string> <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Bevitel"</string> - <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="3003338571871392293">"Hallókészülékek"</string> + <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Hallókészülék"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Bekapcsolás…"</string> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Automatikus elforgatás"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Automatikus képernyőforgatás"</string> @@ -859,6 +859,7 @@ <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"A teljes képernyő felnagyítása"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Képernyő bizonyos részének nagyítása"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Nagyítási beállítások megnyitása"</string> + <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Nagyítási beállítások bezárása"</string> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Az átméretezéshez húzza a kívánt sarkot"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Átlós görgetés engedélyezése"</string> <string name="accessibility_resize" msgid="5733759136600611551">"Átméretezés"</string> @@ -1045,6 +1046,8 @@ <string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Probléma merült fel az akkumulátor-töltésmérő olvasásakor"</string> <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Koppintással további információkat érhet el."</string> <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Nincs ébresztés"</string> + <!-- no translation found for accessibility_bouncer (5896923685673320070) --> + <skip /> <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Ujjlenyomat-érzékelő"</string> <string name="accessibility_authenticate_hint" msgid="798914151813205721">"a hitelesítéshez"</string> <string name="accessibility_enter_hint" msgid="2617864063504824834">"eszköz megadásához"</string> @@ -1107,7 +1110,8 @@ <string name="dream_overlay_status_bar_assistant_attention_indicator" msgid="4712565923771372690">"A Segéd figyel"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# értesítés}other{# értesítés}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <string name="note_task_button_label" msgid="8718616095800343136">"Jegyzetelés"</string> + <string name="note_task_button_label" msgid="230135078402003532">"Jegyzetelés"</string> + <string name="note_task_shortcut_long_label" msgid="7729325091147319409">"Jegyzetelés, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Sugárzás"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Leállítja a(z) <xliff:g id="APP_NAME">%1$s</xliff:g> közvetítését?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"A(z) <xliff:g id="SWITCHAPP">%1$s</xliff:g> közvetítése vagy a kimenet módosítása esetén a jelenlegi közvetítés leáll"</string> diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml index f25dc487df5c..59ec076f56de 100644 --- a/packages/SystemUI/res/values-hy/strings.xml +++ b/packages/SystemUI/res/values-hy/strings.xml @@ -243,7 +243,7 @@ <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Աուդիո"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Ականջակալ"</string> <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Մուտքագրում"</string> - <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="3003338571871392293">"Լսողական ապարատ"</string> + <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Լսողական սարք"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Միացում…"</string> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Ինքնապտտում"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Ավտոմատ պտտել էկրանը"</string> @@ -859,6 +859,7 @@ <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Խոշորացնել ամբողջ էկրանը"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Խոշորացնել էկրանի որոշակի հատվածը"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Բացել խոշորացման կարգավորումները"</string> + <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Փակել խոշորացման կարգավորումները"</string> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Քաշեք անկյունը՝ չափը փոխելու համար"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Թույլատրել անկյունագծով ոլորումը"</string> <string name="accessibility_resize" msgid="5733759136600611551">"Փոխել չափը"</string> @@ -1045,6 +1046,8 @@ <string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Մարտկոցի ցուցիչի ցուցմունքը կարդալու հետ կապված խնդիր կա"</string> <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Հպեք՝ ավելին իմանալու համար"</string> <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Զարթուցիչ դրված չէ"</string> + <!-- no translation found for accessibility_bouncer (5896923685673320070) --> + <skip /> <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Մատնահետքի սկաներ"</string> <string name="accessibility_authenticate_hint" msgid="798914151813205721">"նույնականացնել"</string> <string name="accessibility_enter_hint" msgid="2617864063504824834">"նշել սարքը"</string> @@ -1107,7 +1110,8 @@ <string name="dream_overlay_status_bar_assistant_attention_indicator" msgid="4712565923771372690">"Օգնականը լսում է"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# ծանուցում}one{# ծանուցում}other{# ծանուցում}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <string name="note_task_button_label" msgid="8718616095800343136">"Նշումների ստեղծում"</string> + <string name="note_task_button_label" msgid="230135078402003532">"Նշումների ստեղծում"</string> + <string name="note_task_shortcut_long_label" msgid="7729325091147319409">"Նշումների ստեղծում, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Հեռարձակում"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Կանգնեցնել <xliff:g id="APP_NAME">%1$s</xliff:g> հավելվածի հեռարձակումը"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Եթե հեռարձակեք <xliff:g id="SWITCHAPP">%1$s</xliff:g> հավելվածը կամ փոխեք աուդիո ելքը, ձեր ընթացիկ հեռարձակումը կկանգնեցվի։"</string> diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml index 88c06e76fd33..7b3feaabc51f 100644 --- a/packages/SystemUI/res/values-in/strings.xml +++ b/packages/SystemUI/res/values-in/strings.xml @@ -243,7 +243,7 @@ <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string> <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Input"</string> - <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="3003338571871392293">"Alat Bantu Dengar"</string> + <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Alat bantu dengar"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Mengaktifkan…"</string> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Putar Otomatis"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Putar layar otomatis"</string> @@ -859,6 +859,7 @@ <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Memperbesar tampilan layar penuh"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Perbesar sebagian layar"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Buka setelan pembesaran"</string> + <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Tutup setelan pembesaran"</string> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Tarik pojok persegi untuk mengubah ukuran"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Izinkan scrolling diagonal"</string> <string name="accessibility_resize" msgid="5733759136600611551">"Ubah ukuran"</string> @@ -1045,6 +1046,8 @@ <string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Terjadi error saat membaca indikator baterai"</string> <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Ketuk untuk informasi selengkapnya"</string> <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Alarm tidak disetel"</string> + <!-- no translation found for accessibility_bouncer (5896923685673320070) --> + <skip /> <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Sensor sidik jari"</string> <string name="accessibility_authenticate_hint" msgid="798914151813205721">"autentikasi"</string> <string name="accessibility_enter_hint" msgid="2617864063504824834">"masukkan perangkat"</string> @@ -1107,7 +1110,8 @@ <string name="dream_overlay_status_bar_assistant_attention_indicator" msgid="4712565923771372690">"Assistant sedang mendengarkan"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# notifikasi}other{# notifikasi}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <string name="note_task_button_label" msgid="8718616095800343136">"Pembuatan catatan"</string> + <string name="note_task_button_label" msgid="230135078402003532">"Pembuatan catatan"</string> + <string name="note_task_shortcut_long_label" msgid="7729325091147319409">"Pembuatan catatan, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Menyiarkan"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Hentikan siaran <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Jika Anda menyiarkan <xliff:g id="SWITCHAPP">%1$s</xliff:g> atau mengubah output, siaran saat ini akan dihentikan"</string> diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml index 5e3af2b9964a..788320741f2a 100644 --- a/packages/SystemUI/res/values-is/strings.xml +++ b/packages/SystemUI/res/values-is/strings.xml @@ -243,7 +243,7 @@ <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Hljóð"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Höfuðtól"</string> <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Inntak"</string> - <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="3003338571871392293">"Heyrnartæki"</string> + <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Heyrnartæki"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Kveikir…"</string> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Sjálfvirkur snúningur"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Snúa skjá sjálfkrafa"</string> @@ -859,6 +859,7 @@ <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Stækka allan skjáinn"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Stækka hluta skjásins"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Opna stillingar stækkunar"</string> + <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Loka stillingum stækkunar"</string> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Dragðu horn til að breyta stærð"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Leyfa skáflettingu"</string> <string name="accessibility_resize" msgid="5733759136600611551">"Breyta stærð"</string> @@ -1045,6 +1046,8 @@ <string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Vandamál við að lesa stöðu rafhlöðu"</string> <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Ýttu til að fá frekari upplýsingar"</string> <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Enginn vekjari"</string> + <!-- no translation found for accessibility_bouncer (5896923685673320070) --> + <skip /> <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Fingrafaralesari"</string> <string name="accessibility_authenticate_hint" msgid="798914151813205721">"auðkenna"</string> <string name="accessibility_enter_hint" msgid="2617864063504824834">"opna tæki"</string> @@ -1107,7 +1110,8 @@ <string name="dream_overlay_status_bar_assistant_attention_indicator" msgid="4712565923771372690">"Hjálparinn er að hlusta"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# tilkynning}one{# tilkynning}other{# tilkynningar}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <string name="note_task_button_label" msgid="8718616095800343136">"Glósugerð"</string> + <string name="note_task_button_label" msgid="230135078402003532">"Glósugerð"</string> + <string name="note_task_shortcut_long_label" msgid="7729325091147319409">"Glósugerð, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Útsending í gangi"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Hætta að senda út <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Ef þú sendir út <xliff:g id="SWITCHAPP">%1$s</xliff:g> eða skiptir um úttak lýkur yfirstandandi útsendingu"</string> diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml index 9c784b6370a2..fef0fe57f5ee 100644 --- a/packages/SystemUI/res/values-it/strings.xml +++ b/packages/SystemUI/res/values-it/strings.xml @@ -243,7 +243,7 @@ <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Auricolare"</string> <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Ingresso"</string> - <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="3003338571871392293">"Apparecchi acustici"</string> + <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Apparecchi acustici"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Attivazione…"</string> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Rotazione automatica"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Rotazione automatica dello schermo"</string> @@ -859,6 +859,7 @@ <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Ingrandisci l\'intero schermo"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Ingrandisci parte dello schermo"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Apri le impostazioni di ingrandimento"</string> + <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Chiudi impostazioni di ingrandimento"</string> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Trascina l\'angolo per ridimensionare"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Scorrimento diagonale"</string> <string name="accessibility_resize" msgid="5733759136600611551">"Ridimensiona"</string> @@ -1045,6 +1046,8 @@ <string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Problema durante la lettura dell\'indicatore di livello della batteria"</string> <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Tocca per ulteriori informazioni"</string> <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Nessuna"</string> + <!-- no translation found for accessibility_bouncer (5896923685673320070) --> + <skip /> <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Sensore di impronte digitali"</string> <string name="accessibility_authenticate_hint" msgid="798914151813205721">"effettuare l\'autenticazione"</string> <string name="accessibility_enter_hint" msgid="2617864063504824834">"accedere al dispositivo"</string> @@ -1107,7 +1110,8 @@ <string name="dream_overlay_status_bar_assistant_attention_indicator" msgid="4712565923771372690">"L\'assistente è in ascolto"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# notifica}many{# notifiche}other{# notifiche}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <string name="note_task_button_label" msgid="8718616095800343136">"Aggiunta di note"</string> + <string name="note_task_button_label" msgid="230135078402003532">"Aggiunta di note"</string> + <string name="note_task_shortcut_long_label" msgid="7729325091147319409">"Aggiunta di note, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Trasmissione in corso…"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Vuoi interrompere la trasmissione dell\'app <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Se trasmetti l\'app <xliff:g id="SWITCHAPP">%1$s</xliff:g> o cambi l\'uscita, la trasmissione attuale viene interrotta"</string> diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml index 168e3e23b8ac..255c1da2984d 100644 --- a/packages/SystemUI/res/values-iw/strings.xml +++ b/packages/SystemUI/res/values-iw/strings.xml @@ -243,7 +243,7 @@ <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"אודיו"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"אוזניות"</string> <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"קלט"</string> - <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="3003338571871392293">"מכשירי שמיעה"</string> + <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"מכשירי שמיעה"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"ההפעלה מתבצעת…"</string> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"סיבוב אוטומטי"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"סיבוב אוטומטי של המסך"</string> @@ -859,6 +859,7 @@ <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"הגדלה של המסך המלא"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"הגדלת חלק מהמסך"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"פתיחת הגדרות ההגדלה"</string> + <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"סגירת הגדרות ההגדלה"</string> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"צריך לגרור את הפינה כדי לשנות את הגודל"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"הפעלת גלילה באלכסון"</string> <string name="accessibility_resize" msgid="5733759136600611551">"שינוי גודל"</string> @@ -1045,6 +1046,8 @@ <string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"בעיה בקריאת מדדי הסוללה"</string> <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"יש להקיש כדי להציג מידע נוסף"</string> <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"לא הוגדרה"</string> + <!-- no translation found for accessibility_bouncer (5896923685673320070) --> + <skip /> <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"חיישן טביעות אצבע"</string> <string name="accessibility_authenticate_hint" msgid="798914151813205721">"אימות"</string> <string name="accessibility_enter_hint" msgid="2617864063504824834">"הזנת מכשיר"</string> @@ -1107,7 +1110,8 @@ <string name="dream_overlay_status_bar_assistant_attention_indicator" msgid="4712565923771372690">"Assistant מאזינה"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{התראה אחת}one{# התראות}two{# התראות}other{# התראות}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <string name="note_task_button_label" msgid="8718616095800343136">"כתיבת הערות"</string> + <string name="note_task_button_label" msgid="230135078402003532">"כתיבת הערות"</string> + <string name="note_task_shortcut_long_label" msgid="7729325091147319409">"כתיבת הערות, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"שידור"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"האם להפסיק לשדר את התוכן מאפליקציית <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"אם משדרים את התוכן מאפליקציית <xliff:g id="SWITCHAPP">%1$s</xliff:g> או משנים את הפלט, השידור הנוכחי יפסיק לפעול"</string> diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml index f100299ea367..d74d2f1f1698 100644 --- a/packages/SystemUI/res/values-ja/strings.xml +++ b/packages/SystemUI/res/values-ja/strings.xml @@ -243,7 +243,7 @@ <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"オーディオ"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ヘッドセット"</string> <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"入力"</string> - <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="3003338571871392293">"補聴器"</string> + <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"補聴器"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"ON にしています…"</string> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"自動回転"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"画面を自動回転します"</string> @@ -859,6 +859,7 @@ <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"画面全体を拡大します"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"画面の一部を拡大します"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"画面の拡大設定を開く"</string> + <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"拡大の設定を閉じる"</string> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"サイズを変更するには角をドラッグ"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"斜めスクロールを許可"</string> <string name="accessibility_resize" msgid="5733759136600611551">"サイズ変更"</string> @@ -1045,6 +1046,7 @@ <string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"バッテリー残量の読み込み中に問題が発生しました"</string> <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"タップすると詳細が表示されます"</string> <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"アラーム未設定"</string> + <string name="accessibility_bouncer" msgid="5896923685673320070">"画面ロックを設定"</string> <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"指紋認証センサー"</string> <string name="accessibility_authenticate_hint" msgid="798914151813205721">"認証"</string> <string name="accessibility_enter_hint" msgid="2617864063504824834">"デバイスを入力"</string> @@ -1107,7 +1109,8 @@ <string name="dream_overlay_status_bar_assistant_attention_indicator" msgid="4712565923771372690">"アシスタントが音声認識中です"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# 件の通知}other{# 件の通知}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>、<xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <string name="note_task_button_label" msgid="8718616095800343136">"メモ"</string> + <string name="note_task_button_label" msgid="230135078402003532">"メモ"</string> + <string name="note_task_shortcut_long_label" msgid="7729325091147319409">"メモ、<xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"ブロードキャスト"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g> のブロードキャストを停止しますか?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> をブロードキャストしたり、出力を変更したりすると、現在のブロードキャストが停止します。"</string> diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml index 8ed9c431eaf9..053989eda04c 100644 --- a/packages/SystemUI/res/values-ka/strings.xml +++ b/packages/SystemUI/res/values-ka/strings.xml @@ -243,7 +243,7 @@ <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"აუდიო"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ყურსაცვამი"</string> <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"შეყვანა"</string> - <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="3003338571871392293">"სმენის მოწყობილობები"</string> + <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"სმენის მოწყობილობები"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"ირთვება…"</string> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"ავტოროტაცია"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"ეკრანის ავტომატური შეტრიალება"</string> @@ -859,6 +859,7 @@ <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"გაადიდეთ სრულ ეკრანზე"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"ეკრანის ნაწილის გადიდება"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"გახსენით გადიდების პარამეტრები"</string> + <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"გადიდების პარამეტრების დახურვა"</string> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"ჩავლებით გადაიტანეთ კუთხე ზომის შესაცვლელად"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"დიაგონალური გადაადგილების დაშვება"</string> <string name="accessibility_resize" msgid="5733759136600611551">"ზომის შეცვლა"</string> @@ -1045,6 +1046,8 @@ <string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"თქვენი ბატარეის მზომის წაკითხვასთან დაკავშირებით პრობლემა დაფიქსირდა"</string> <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"შეეხეთ მეტი ინფორმაციისთვის"</string> <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"მაღვიძარა არ არის"</string> + <!-- no translation found for accessibility_bouncer (5896923685673320070) --> + <skip /> <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"თითის ანაბეჭდის სენსორი"</string> <string name="accessibility_authenticate_hint" msgid="798914151813205721">"ავტორიზაცია"</string> <string name="accessibility_enter_hint" msgid="2617864063504824834">"მოწყობილობის შეყვანა"</string> @@ -1107,7 +1110,8 @@ <string name="dream_overlay_status_bar_assistant_attention_indicator" msgid="4712565923771372690">"ასისტენტი უსმენს"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# შეტყობინება}other{# შეტყობინება}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <string name="note_task_button_label" msgid="8718616095800343136">"შენიშვნების ჩაწერა"</string> + <string name="note_task_button_label" msgid="230135078402003532">"ჩანიშვნა"</string> + <string name="note_task_shortcut_long_label" msgid="7729325091147319409">"ჩანიშვნა, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"იწყებთ მაუწყებლობას"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"გსურთ <xliff:g id="APP_NAME">%1$s</xliff:g>-ის ტრანსლაციის შეჩერება?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"<xliff:g id="SWITCHAPP">%1$s</xliff:g>-ის ტრანსლაციის შემთხვევაში ან აუდიოს გამოსასვლელის შეცვლისას, მიმდინარე ტრანსლაცია შეჩერდება"</string> diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml index c8441f441062..ef1a4d3756b0 100644 --- a/packages/SystemUI/res/values-kk/strings.xml +++ b/packages/SystemUI/res/values-kk/strings.xml @@ -243,7 +243,7 @@ <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Aудио"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Гарнитура"</string> <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Кіріс"</string> - <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="3003338571871392293">"Есту аппараттары"</string> + <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Есту аппараттары"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Қосылуда…"</string> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Автоматты түрде бұру"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Автоматты айналатын экран"</string> @@ -859,6 +859,7 @@ <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Толық экранды ұлғайту"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Экранның бөлігін ұлғайту"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Ұлғайту параметрлерін ашу"</string> + <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Ұлғайту параметрлерін жабу"</string> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Өлшемін өзгерту үшін бұрышынан сүйреңіз."</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Диагональ бойынша айналдыруға рұқсат беру"</string> <string name="accessibility_resize" msgid="5733759136600611551">"Өлшемін өзгерту"</string> @@ -1045,6 +1046,8 @@ <string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Батарея зарядының дерегі алынбай жатыр"</string> <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Толығырақ ақпарат алу үшін түртіңіз."</string> <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Оятқыш орнатылмаған."</string> + <!-- no translation found for accessibility_bouncer (5896923685673320070) --> + <skip /> <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Саусақ ізін оқу сканері"</string> <string name="accessibility_authenticate_hint" msgid="798914151813205721">"аутентификациялау"</string> <string name="accessibility_enter_hint" msgid="2617864063504824834">"құрылғыны енгізу"</string> @@ -1107,7 +1110,8 @@ <string name="dream_overlay_status_bar_assistant_attention_indicator" msgid="4712565923771372690">"Assistant тыңдап тұр."</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# хабарландыру}other{# хабарландыру}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <string name="note_task_button_label" msgid="8718616095800343136">"Ескертпе жазу"</string> + <string name="note_task_button_label" msgid="230135078402003532">"Ескертпе жазу"</string> + <string name="note_task_shortcut_long_label" msgid="7729325091147319409">"Ескертпе жазу, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Таратуда"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g> қолданбасын таратуды тоқтатасыз ба?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> қолданбасын таратсаңыз немесе аудио шығысын өзгертсеңіз, қазіргі тарату сеансы тоқтайды."</string> diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml index 02a05e93cf57..150546cfd09d 100644 --- a/packages/SystemUI/res/values-km/strings.xml +++ b/packages/SystemUI/res/values-km/strings.xml @@ -243,7 +243,7 @@ <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"សំឡេង"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"កាស"</string> <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"បញ្ចូល"</string> - <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="3003338571871392293">"ឧបករណ៍ជំនួយការស្ដាប់"</string> + <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"ឧបករណ៍ជំនួយការស្ដាប់"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"កំពុងបើក..."</string> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"បង្វិលស្វ័យប្រវត្តិ"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"បង្វិលអេក្រង់ស្វ័យប្រវត្តិ"</string> @@ -859,6 +859,7 @@ <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"ពង្រីកពេញអេក្រង់"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"ពង្រីកផ្នែកនៃអេក្រង់"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"បើកការកំណត់ការពង្រីក"</string> + <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"បិទការកំណត់ការពង្រីក"</string> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"អូសជ្រុងដើម្បីប្ដូរទំហំ"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"អនុញ្ញាតការរំកិលបញ្ឆិត"</string> <string name="accessibility_resize" msgid="5733759136600611551">"ប្ដូរទំហំ"</string> @@ -1045,6 +1046,8 @@ <string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"មានបញ្ហាក្នុងការអានឧបករណ៍រង្វាស់កម្រិតថ្មរបស់អ្នក"</string> <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"ចុចដើម្បីទទួលបានព័ត៌មានបន្ថែម"</string> <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"មិនបានកំណត់ម៉ោងរោទ៍ទេ"</string> + <!-- no translation found for accessibility_bouncer (5896923685673320070) --> + <skip /> <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"ឧបករណ៍ចាប់ស្នាមម្រាមដៃ"</string> <string name="accessibility_authenticate_hint" msgid="798914151813205721">"ផ្ទៀងផ្ទាត់"</string> <string name="accessibility_enter_hint" msgid="2617864063504824834">"បញ្ចូលឧបករណ៍"</string> @@ -1107,7 +1110,8 @@ <string name="dream_overlay_status_bar_assistant_attention_indicator" msgid="4712565923771372690">"Google Assistant កំពុងស្តាប់"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{ការជូនដំណឹង #}other{ការជូនដំណឹង #}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <string name="note_task_button_label" msgid="8718616095800343136">"ការកត់ត្រា"</string> + <string name="note_task_button_label" msgid="230135078402003532">"ការកត់ត្រា"</string> + <string name="note_task_shortcut_long_label" msgid="7729325091147319409">"ការកត់ត្រា, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"ការផ្សាយ"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"បញ្ឈប់ការផ្សាយ <xliff:g id="APP_NAME">%1$s</xliff:g> ឬ?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"ប្រសិនបើអ្នកផ្សាយ <xliff:g id="SWITCHAPP">%1$s</xliff:g> ឬប្ដូរឧបករណ៍បញ្ចេញសំឡេង ការផ្សាយបច្ចុប្បន្នរបស់អ្នកនឹងបញ្ឈប់"</string> diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml index 42bfaf77a007..96e955e38190 100644 --- a/packages/SystemUI/res/values-kn/strings.xml +++ b/packages/SystemUI/res/values-kn/strings.xml @@ -243,7 +243,7 @@ <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ಆಡಿಯೋ"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ಹೆಡ್ಸೆಟ್"</string> <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"ಇನ್ಪುಟ್"</string> - <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="3003338571871392293">"ಶ್ರವಣ ಸಾಧನಗಳು"</string> + <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"ಶ್ರವಣ ಸಾಧನಗಳು"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"ಆನ್ ಮಾಡಲಾಗುತ್ತಿದೆ..."</string> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"ಸ್ವಯಂ-ತಿರುಗುವಿಕೆ"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"ಪರದೆಯನ್ನು ಸ್ವಯಂ-ತಿರುಗಿಸಿ"</string> @@ -859,6 +859,7 @@ <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"ಪೂರ್ಣ ಸ್ಕ್ರೀನ್ ಅನ್ನು ಹಿಗ್ಗಿಸಿ"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"ಸ್ಕ್ರೀನ್ನ ಅರ್ಧಭಾಗವನ್ನು ಝೂಮ್ ಮಾಡಿ"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"ಹಿಗ್ಗಿಸುವಿಕೆ ಸೆಟ್ಟಿಂಗ್ಗಳನ್ನು ತೆರೆಯಿರಿ"</string> + <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"ಮ್ಯಾಗ್ನಿಫಿಕೇಶನ್ ಸೆಟ್ಟಿಂಗ್ಗಳನ್ನು ಮುಚ್ಚಿರಿ"</string> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"ಮರುಗಾತ್ರಗೊಳಿಸಲು ಮೂಲೆಯನ್ನು ಡ್ರ್ಯಾಗ್ ಮಾಡಿ"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"ಡಯಾಗನಲ್ ಸ್ಕ್ರೋಲಿಂಗ್ ಅನ್ನು ಅನುಮತಿಸಿ"</string> <string name="accessibility_resize" msgid="5733759136600611551">"ಮರುಗಾತ್ರಗೊಳಿಸಿ"</string> @@ -1045,6 +1046,8 @@ <string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"ನಿಮ್ಮ ಬ್ಯಾಟರಿ ಮೀಟರ್ ಓದುವಾಗ ಸಮಸ್ಯೆ ಎದುರಾಗಿದೆ"</string> <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"ಇನ್ನಷ್ಟು ಮಾಹಿತಿಗಾಗಿ ಟ್ಯಾಪ್ ಮಾಡಿ"</string> <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"ಅಲಾರಾಂ ಸೆಟ್ ಆಗಿಲ್ಲ"</string> + <!-- no translation found for accessibility_bouncer (5896923685673320070) --> + <skip /> <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"ಫಿಂಗರ್ಪ್ರಿಂಟ್ ಸೆನ್ಸರ್"</string> <string name="accessibility_authenticate_hint" msgid="798914151813205721">"ದೃಢೀಕರಿಸಿ"</string> <string name="accessibility_enter_hint" msgid="2617864063504824834">"ಸಾಧನವನ್ನು ಪ್ರವೇಶಿಸಿ"</string> @@ -1107,7 +1110,8 @@ <string name="dream_overlay_status_bar_assistant_attention_indicator" msgid="4712565923771372690">"ಅಸಿಸ್ಟೆಂಟ್ ಆಲಿಸುತ್ತಿದೆ"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# ನೋಟಿಫಿಕೇಶನ್}one{# ನೋಟಿಫಿಕೇಶನ್ಗಳು}other{# ನೋಟಿಫಿಕೇಶನ್ಗಳು}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <string name="note_task_button_label" msgid="8718616095800343136">"ಟಿಪ್ಪಣಿಗಳನ್ನು ಬರೆದುಕೊಳ್ಳುವುದು"</string> + <string name="note_task_button_label" msgid="230135078402003532">"ಟಿಪ್ಪಣಿ ಮಾಡಿಕೊಳ್ಳುವಿಕೆ"</string> + <string name="note_task_shortcut_long_label" msgid="7729325091147319409">"ಟಿಪ್ಪಣಿ ಮಾಡಿಕೊಳ್ಳುವಿಕೆ, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"ಪ್ರಸಾರ ಮಾಡಲಾಗುತ್ತಿದೆ"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g> ನ ಪ್ರಸಾರವನ್ನು ನಿಲ್ಲಿಸಬೇಕೆ?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"ನೀವು <xliff:g id="SWITCHAPP">%1$s</xliff:g> ಅನ್ನು ಪ್ರಸಾರ ಮಾಡಿದರೆ ಅಥವಾ ಔಟ್ಪುಟ್ ಅನ್ನು ಬದಲಾಯಿಸಿದರೆ, ನಿಮ್ಮ ಪ್ರಸ್ತುತ ಪ್ರಸಾರವು ಸ್ಥಗಿತಗೊಳ್ಳುತ್ತದೆ"</string> diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml index 176f86673bc3..2a82ddcffcb8 100644 --- a/packages/SystemUI/res/values-ko/strings.xml +++ b/packages/SystemUI/res/values-ko/strings.xml @@ -243,7 +243,7 @@ <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"오디오"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"헤드셋"</string> <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"입력"</string> - <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="3003338571871392293">"보청기"</string> + <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"보청기"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"켜는 중..."</string> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"자동 회전"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"화면 자동 회전"</string> @@ -859,6 +859,7 @@ <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"전체 화면 확대"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"화면 일부 확대"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"확대 설정 열기"</string> + <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"확대 설정 닫기"</string> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"모서리를 드래그하여 크기 조절"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"대각선 스크롤 허용"</string> <string name="accessibility_resize" msgid="5733759136600611551">"크기 조절"</string> @@ -1045,6 +1046,8 @@ <string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"배터리 수준을 읽는 중에 문제가 발생함"</string> <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"탭하여 자세한 정보를 확인하세요."</string> <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"설정된 알람 없음"</string> + <!-- no translation found for accessibility_bouncer (5896923685673320070) --> + <skip /> <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"지문 센서"</string> <string name="accessibility_authenticate_hint" msgid="798914151813205721">"인증"</string> <string name="accessibility_enter_hint" msgid="2617864063504824834">"기기 입력"</string> @@ -1107,7 +1110,8 @@ <string name="dream_overlay_status_bar_assistant_attention_indicator" msgid="4712565923771372690">"어시스턴트가 대기 중입니다."</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{알림 #개}other{알림 #개}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <string name="note_task_button_label" msgid="8718616095800343136">"메모"</string> + <string name="note_task_button_label" msgid="230135078402003532">"메모"</string> + <string name="note_task_shortcut_long_label" msgid="7729325091147319409">"메모, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"방송 중"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g> 방송을 중지하시겠습니까?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> 앱을 방송하거나 출력을 변경하면 기존 방송이 중단됩니다"</string> diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml index 96389dfae88c..6ceb1e7686c8 100644 --- a/packages/SystemUI/res/values-ky/strings.xml +++ b/packages/SystemUI/res/values-ky/strings.xml @@ -243,7 +243,7 @@ <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Аудио"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Гарнитура"</string> <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Киргизүү"</string> - <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="3003338571871392293">"Угуу аппараттары"</string> + <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Угуу аппараттары"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Күйгүзүлүүдө…"</string> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Авто буруу"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Экранды авто буруу"</string> @@ -859,6 +859,7 @@ <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Толук экранда ачуу"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Экрандын бир бөлүгүн чоңойтуу"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Чоңойтуу параметрлерин ачуу"</string> + <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Чоңойтуу параметрлерин жабуу"</string> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Өлчөмүн өзгөртүү үчүн бурчун сүйрөңүз"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Диагональ боюнча сыдырууга уруксат берүү"</string> <string name="accessibility_resize" msgid="5733759136600611551">"Өлчөмүн өзгөртүү"</string> @@ -1045,6 +1046,8 @@ <string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Батареяңыздын кубаты аныкталбай жатат"</string> <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Кеңири маалымат алуу үчүн таптап коюңуз"</string> <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Ойготкуч коюлган жок"</string> + <!-- no translation found for accessibility_bouncer (5896923685673320070) --> + <skip /> <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Манжа изинин сенсору"</string> <string name="accessibility_authenticate_hint" msgid="798914151813205721">"аныктыгын текшерүү"</string> <string name="accessibility_enter_hint" msgid="2617864063504824834">"түзмөккө кирүү"</string> @@ -1107,7 +1110,8 @@ <string name="dream_overlay_status_bar_assistant_attention_indicator" msgid="4712565923771372690">"Жардамчы угуп жатат"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# билдирме}other{# билдирме}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <string name="note_task_button_label" msgid="8718616095800343136">"Эскертме жазуу"</string> + <string name="note_task_button_label" msgid="230135078402003532">"Эскертме жазуу"</string> + <string name="note_task_shortcut_long_label" msgid="7729325091147319409">"Эскертме жазуу (<xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>)"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Кеңири таратуу"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g> колдонмосунда кабарлоо токтотулсунбу?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Эгер <xliff:g id="SWITCHAPP">%1$s</xliff:g> колдонмосунда кабарласаңыз же аудионун чыгуусун өзгөртсөңүз, учурдагы кабарлоо токтотулат"</string> diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml index 925dfcebc85f..deec7d03f6de 100644 --- a/packages/SystemUI/res/values-lo/strings.xml +++ b/packages/SystemUI/res/values-lo/strings.xml @@ -243,7 +243,7 @@ <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ສຽງ"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ຊຸດຫູຟັງ"</string> <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"ການປ້ອນຂໍ້ມູນ"</string> - <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="3003338571871392293">"ເຄື່ອງຊ່ວຍຟັງ"</string> + <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"ເຄື່ອງຊ່ວຍຟັງ"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"ກຳລັງເປີດ..."</string> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"ໝຸນອັດຕະໂນມັດ"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"ໝຸນໜ້າຈໍອັດຕະໂນມັດ"</string> @@ -859,6 +859,7 @@ <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"ຂະຫຍາຍເຕັມຈໍ"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"ຂະຫຍາຍບາງສ່ວນຂອງໜ້າຈໍ"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"ເປີດການຕັ້ງຄ່າການຂະຫຍາຍ"</string> + <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"ປິດການຕັ້ງຄ່າການຂະຫຍາຍ"</string> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"ລາກຢູ່ມຸມເພື່ອປັບຂະໜາດ"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"ອະນຸຍາດໃຫ້ເລື່ອນທາງຂວາງ"</string> <string name="accessibility_resize" msgid="5733759136600611551">"ປ່ຽນຂະໜາດ"</string> @@ -1045,6 +1046,8 @@ <string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"ເກີດບັນຫາໃນການອ່ານຕົວວັດແທກແບັດເຕີຣີຂອງທ່ານ"</string> <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"ແຕະເພື່ອເບິ່ງຂໍ້ມູນເພີ່ມເຕີມ"</string> <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"ບໍ່ໄດ້ຕັ້ງໂມງປຸກ"</string> + <!-- no translation found for accessibility_bouncer (5896923685673320070) --> + <skip /> <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"ເຊັນເຊີລາຍນິ້ວມື"</string> <string name="accessibility_authenticate_hint" msgid="798914151813205721">"ພິສູດຢືນຢັນ"</string> <string name="accessibility_enter_hint" msgid="2617864063504824834">"ເຂົ້າອຸປະກອນ"</string> @@ -1107,7 +1110,8 @@ <string name="dream_overlay_status_bar_assistant_attention_indicator" msgid="4712565923771372690">"ຜູ້ຊ່ວຍກຳລັງຟັງ"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# ການແຈ້ງເຕືອນ}other{# ການແຈ້ງເຕືອນ}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <string name="note_task_button_label" msgid="8718616095800343136">"ການຈົດບັນທຶກ"</string> + <string name="note_task_button_label" msgid="230135078402003532">"ການຈົດບັນທຶກ"</string> + <string name="note_task_shortcut_long_label" msgid="7729325091147319409">"ການຈົດບັນທຶກ <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"ກຳລັງອອກອາກາດ"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"ຢຸດການອອກອາກາດ <xliff:g id="APP_NAME">%1$s</xliff:g> ບໍ?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"ຫາກທ່ານອອກອາກາດ <xliff:g id="SWITCHAPP">%1$s</xliff:g> ຫຼື ປ່ຽນເອົ້າພຸດ, ການອອກອາກາດປັດຈຸບັນຂອງທ່ານຈະຢຸດ"</string> diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml index 28da6950febc..44f7b68ef59c 100644 --- a/packages/SystemUI/res/values-lt/strings.xml +++ b/packages/SystemUI/res/values-lt/strings.xml @@ -243,7 +243,7 @@ <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Garsas"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Virtualiosios realybės įrenginys"</string> <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Įvestis"</string> - <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="3003338571871392293">"Klausos aparatai"</string> + <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Klausos aparatai"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Įjungiama…"</string> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Automatinis pasukimas"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Automatiškai sukti ekraną"</string> @@ -859,6 +859,7 @@ <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Viso ekrano didinimas"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Didinti ekrano dalį"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Atidaryti didinimo nustatymus"</string> + <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Uždaryti didinimo nustatymus"</string> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Norėdami keisti dydį, vilkite kampą"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Slinkimo įstrižai leidimas"</string> <string name="accessibility_resize" msgid="5733759136600611551">"Pakeisti dydį"</string> @@ -1045,6 +1046,8 @@ <string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Nuskaitant akumuliatoriaus skaitiklį iškilo problema"</string> <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Palieskite, kad sužinotumėte daugiau informacijos"</string> <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Nenustatyta signalų"</string> + <!-- no translation found for accessibility_bouncer (5896923685673320070) --> + <skip /> <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Kontrolinio kodo jutiklis"</string> <string name="accessibility_authenticate_hint" msgid="798914151813205721">"nustatytumėte tapatybę"</string> <string name="accessibility_enter_hint" msgid="2617864063504824834">"pasiektumėte įrenginį"</string> @@ -1107,7 +1110,8 @@ <string name="dream_overlay_status_bar_assistant_attention_indicator" msgid="4712565923771372690">"Padėjėjas klausosi"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# pranešimas}one{# pranešimas}few{# pranešimai}many{# pranešimo}other{# pranešimų}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <string name="note_task_button_label" msgid="8718616095800343136">"Užrašų kūrimas"</string> + <string name="note_task_button_label" msgid="230135078402003532">"Užrašų kūrimas"</string> + <string name="note_task_shortcut_long_label" msgid="7729325091147319409">"Užrašų kūrimas, „<xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>“"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Transliavimas"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Sustabdyti „<xliff:g id="APP_NAME">%1$s</xliff:g>“ transliaciją?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Jei transliuosite „<xliff:g id="SWITCHAPP">%1$s</xliff:g>“ arba pakeisite išvestį, dabartinė transliacija bus sustabdyta"</string> diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml index 0038e9c3181c..fc227f00a429 100644 --- a/packages/SystemUI/res/values-lv/strings.xml +++ b/packages/SystemUI/res/values-lv/strings.xml @@ -243,7 +243,7 @@ <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Austiņas"</string> <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Ievade"</string> - <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="3003338571871392293">"Dzirdes aparāti"</string> + <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Dzirdes aparāti"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Notiek ieslēgšana…"</string> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Automātiska pagriešana"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Automātiska ekrāna pagriešana"</string> @@ -859,6 +859,7 @@ <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Palielināt visu ekrānu"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Palielināt ekrāna daļu"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Atvērt palielinājuma iestatījumus"</string> + <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Aizvērt palielinājuma iestatījumus"</string> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Velciet stūri, lai mainītu izmērus"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Atļaut ritināšanu pa diagonāli"</string> <string name="accessibility_resize" msgid="5733759136600611551">"Mainīt lielumu"</string> @@ -1045,6 +1046,8 @@ <string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Nevar iegūt informāciju par akumulatora uzlādes līmeni."</string> <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Pieskarieties, lai iegūtu plašāku informāciju."</string> <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Nav iestatīts signāls"</string> + <!-- no translation found for accessibility_bouncer (5896923685673320070) --> + <skip /> <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Pirksta nospieduma sensors"</string> <string name="accessibility_authenticate_hint" msgid="798914151813205721">"veiktu autentificēšanu"</string> <string name="accessibility_enter_hint" msgid="2617864063504824834">"izmantotu ierīci"</string> @@ -1107,7 +1110,8 @@ <string name="dream_overlay_status_bar_assistant_attention_indicator" msgid="4712565923771372690">"Asistents klausās"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# paziņojums}zero{# paziņojumu}one{# paziņojums}other{# paziņojumi}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <string name="note_task_button_label" msgid="8718616095800343136">"Piezīmju pierakstīšana"</string> + <string name="note_task_button_label" msgid="230135078402003532">"Piezīmju pierakstīšana"</string> + <string name="note_task_shortcut_long_label" msgid="7729325091147319409">"Piezīmju pierakstīšana, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Notiek apraidīšana"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Vai apturēt lietotnes <xliff:g id="APP_NAME">%1$s</xliff:g> apraidīšanu?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Ja sāksiet lietotnes <xliff:g id="SWITCHAPP">%1$s</xliff:g> apraidīšanu vai mainīsiet izvadi, pašreizējā apraide tiks apturēta"</string> diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml index 45707680d3d3..5dad015d0986 100644 --- a/packages/SystemUI/res/values-mk/strings.xml +++ b/packages/SystemUI/res/values-mk/strings.xml @@ -243,7 +243,7 @@ <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Аудио"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Слушалки"</string> <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Влез"</string> - <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="3003338571871392293">"Слушни помагала"</string> + <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Слушни помагала"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Се вклучува…"</string> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Автоматско ротирање"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Автоматско ротирање на екранот"</string> @@ -859,6 +859,7 @@ <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Зголемете го целиот екран"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Зголемувајте дел од екранот"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Отвори поставки за зголемување"</string> + <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Затворете ги поставките за зголемување"</string> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Повлечете на аголот за да ја промените големината"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Дозволете дијагонално лизгање"</string> <string name="accessibility_resize" msgid="5733759136600611551">"Промени големина"</string> @@ -1045,6 +1046,8 @@ <string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Проблем при читањето на мерачот на батеријата"</string> <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Допрете за повеќе информации"</string> <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Не е поставен аларм"</string> + <!-- no translation found for accessibility_bouncer (5896923685673320070) --> + <skip /> <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Сензор за отпечатоци"</string> <string name="accessibility_authenticate_hint" msgid="798914151813205721">"автентицирате"</string> <string name="accessibility_enter_hint" msgid="2617864063504824834">"внесете уред"</string> @@ -1107,7 +1110,8 @@ <string name="dream_overlay_status_bar_assistant_attention_indicator" msgid="4712565923771372690">"„Помошникот“ слуша"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# известување}one{# известување}other{# известувања}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <string name="note_task_button_label" msgid="8718616095800343136">"Фаќање белешки"</string> + <string name="note_task_button_label" msgid="230135078402003532">"Фаќање белешки"</string> + <string name="note_task_shortcut_long_label" msgid="7729325091147319409">"Фаќање белешки, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Емитување"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Да се прекине емитувањето на <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Ако емитувате на <xliff:g id="SWITCHAPP">%1$s</xliff:g> или го промените излезот, тековното емитување ќе запре"</string> diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml index ec1d42cfb9d3..83d045e4908d 100644 --- a/packages/SystemUI/res/values-ml/strings.xml +++ b/packages/SystemUI/res/values-ml/strings.xml @@ -243,7 +243,7 @@ <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ഓഡിയോ"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ഹെഡ്സെറ്റ്"</string> <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"ഇൻപുട്ട്"</string> - <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="3003338571871392293">"ശ്രവണ സഹായികൾ"</string> + <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"ശ്രവണ സഹായികൾ"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"ഓണാക്കുന്നു…"</string> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"സ്ക്രീൻ സ്വയമേവ തിരിയൽ"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"സ്ക്രീൻ സ്വയമേവ തിരിക്കുക"</string> @@ -859,6 +859,7 @@ <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"സ്ക്രീൻ പൂർണ്ണമായും മാഗ്നിഫൈ ചെയ്യുക"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"സ്ക്രീനിന്റെ ഭാഗം മാഗ്നിഫൈ ചെയ്യുക"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"മാഗ്നിഫിക്കേഷൻ ക്രമീകരണം തുറക്കുക"</string> + <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"മാഗ്നിഫിക്കേഷൻ ക്രമീകരണം അടയ്ക്കുക"</string> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"വലുപ്പം മാറ്റാൻ മൂല വലിച്ചിടുക"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"ഡയഗണൽ സ്ക്രോളിംഗ് അനുവദിക്കുക"</string> <string name="accessibility_resize" msgid="5733759136600611551">"വലുപ്പം മാറ്റുക"</string> @@ -1045,6 +1046,7 @@ <string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"നിങ്ങളുടെ ബാറ്ററി മീറ്റർ വായിക്കുന്നതിൽ പ്രശ്നമുണ്ട്"</string> <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"കൂടുതൽ വിവരങ്ങൾക്ക് ടാപ്പ് ചെയ്യുക"</string> <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"അലാറം സജ്ജീകരിച്ചിട്ടില്ല"</string> + <string name="accessibility_bouncer" msgid="5896923685673320070">"സ്ക്രീൻ ലോക്ക് നൽകുക"</string> <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"ഫിംഗർപ്രിന്റ് സെൻസർ"</string> <string name="accessibility_authenticate_hint" msgid="798914151813205721">"പരിശോധിച്ചുറപ്പിക്കുക"</string> <string name="accessibility_enter_hint" msgid="2617864063504824834">"ഉപകരണം നൽകുക"</string> @@ -1107,7 +1109,8 @@ <string name="dream_overlay_status_bar_assistant_attention_indicator" msgid="4712565923771372690">"Assistant കേൾക്കുന്നു"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# അറിയിപ്പ്}other{# അറിയിപ്പുകൾ}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <string name="note_task_button_label" msgid="8718616095800343136">"കുറിപ്പ് രേഖപ്പെടുത്തൽ"</string> + <string name="note_task_button_label" msgid="230135078402003532">"കുറിപ്പ് രേഖപ്പെടുത്തൽ"</string> + <string name="note_task_shortcut_long_label" msgid="7729325091147319409">"കുറിപ്പ് രേഖപ്പെടുത്തൽ, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"പ്രക്ഷേപണം ചെയ്യുന്നു"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g> ബ്രോഡ്കാസ്റ്റ് ചെയ്യുന്നത് അവസാനിപ്പിക്കണോ?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"നിങ്ങൾ <xliff:g id="SWITCHAPP">%1$s</xliff:g> ബ്രോഡ്കാസ്റ്റ് ചെയ്യുകയോ ഔട്ട്പുട്ട് മാറ്റുകയോ ചെയ്താൽ നിങ്ങളുടെ നിലവിലുള്ള ബ്രോഡ്കാസ്റ്റ് അവസാനിക്കും"</string> diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml index d978c7099c0a..5c6bef3801cc 100644 --- a/packages/SystemUI/res/values-mn/strings.xml +++ b/packages/SystemUI/res/values-mn/strings.xml @@ -243,7 +243,7 @@ <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Аудио"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Чихэвч"</string> <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Оролт"</string> - <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="3003338571871392293">"Сонсголын төхөөрөмж"</string> + <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Сонсголын төхөөрөмж"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Асааж байна…"</string> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Автоматаар эргэх"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Дэлгэцийг автоматаар эргүүлэх"</string> @@ -859,6 +859,7 @@ <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Бүтэн дэлгэцийг томруулах"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Дэлгэцийн нэг хэсгийг томруулах"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Томруулах тохиргоог нээх"</string> + <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Томруулах тохиргоог хаах"</string> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Хэмжээг өөрчлөхийн тулд булангаас чирнэ үү"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Хөндлөн гүйлгэхийг зөвшөөрөх"</string> <string name="accessibility_resize" msgid="5733759136600611551">"Хэмжээг өөрчлөх"</string> @@ -1045,6 +1046,8 @@ <string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Таны батарей хэмжигчийг уншихад асуудал гарлаа"</string> <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Нэмэлт мэдээлэл авахын тулд товшино уу"</string> <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Сэрүүлэг тавиагүй"</string> + <!-- no translation found for accessibility_bouncer (5896923685673320070) --> + <skip /> <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Хурууны хээ мэдрэгч"</string> <string name="accessibility_authenticate_hint" msgid="798914151813205721">"баталгаажуулах"</string> <string name="accessibility_enter_hint" msgid="2617864063504824834">"төхөөрөмж оруулах"</string> @@ -1107,7 +1110,8 @@ <string name="dream_overlay_status_bar_assistant_attention_indicator" msgid="4712565923771372690">"Туслах сонсож байна"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# мэдэгдэл}other{# мэдэгдэл}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <string name="note_task_button_label" msgid="8718616095800343136">"Тэмдэглэл хөтлөх"</string> + <string name="note_task_button_label" msgid="230135078402003532">"Тэмдэглэл хөтлөх"</string> + <string name="note_task_shortcut_long_label" msgid="7729325091147319409">"Тэмдэглэл хөтлөх, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Нэвтрүүлэлт"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g>-г нэвтрүүлэхээ зогсоох уу?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Хэрэв та <xliff:g id="SWITCHAPP">%1$s</xliff:g>-г нэвтрүүлсэн эсвэл гаралтыг өөрчилсөн бол таны одоогийн нэвтрүүлэлтийг зогсооно"</string> diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml index ace116c08f23..55198be48449 100644 --- a/packages/SystemUI/res/values-mr/strings.xml +++ b/packages/SystemUI/res/values-mr/strings.xml @@ -243,7 +243,7 @@ <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ऑडिओ"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"हेडसेट"</string> <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"इनपुट"</string> - <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="3003338571871392293">"श्रवणयंत्रे"</string> + <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"श्रवणयंत्रे"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"सुरू करत आहे…"</string> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"ऑटो-रोटेट"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"ऑटो-रोटेट स्क्रीन"</string> @@ -859,6 +859,7 @@ <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"फुल स्क्रीन मॅग्निफाय करा"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"स्क्रीनचा काही भाग मॅग्निफाय करा"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"मॅग्निफिकेशन सेटिंग्ज उघडा"</string> + <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"मॅग्निफिकेशन सेटिंग्ज बंद करा"</string> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"आकार बदलण्यासाठी कोपरा ड्रॅग करा"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"तिरपे स्क्रोल करण्याची अनुमती द्या"</string> <string name="accessibility_resize" msgid="5733759136600611551">"आकार बदला"</string> @@ -1045,6 +1046,8 @@ <string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"तुमचे बॅटरी मीटर वाचताना समस्या आली"</string> <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"अधिक माहितीसाठी टॅप करा"</string> <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"अलार्म सेट केला नाही"</string> + <!-- no translation found for accessibility_bouncer (5896923685673320070) --> + <skip /> <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"फिंगरप्रिंट सेन्सर"</string> <string name="accessibility_authenticate_hint" msgid="798914151813205721">"ऑथेंटिकेट करा"</string> <string name="accessibility_enter_hint" msgid="2617864063504824834">"डिव्हाइस एंटर करा"</string> @@ -1107,7 +1110,8 @@ <string name="dream_overlay_status_bar_assistant_attention_indicator" msgid="4712565923771372690">"Assistant ऐकत आहे"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# सूचना}other{# सूचना}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <string name="note_task_button_label" msgid="8718616095800343136">"नोटटेकिंग"</string> + <string name="note_task_button_label" msgid="230135078402003532">"नोंद घेणे"</string> + <string name="note_task_shortcut_long_label" msgid="7729325091147319409">"नोंद घेणे, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"ब्रॉडकास्ट करत आहे"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g> चे प्रसारण थांबवायचे आहे का?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"तुम्ही <xliff:g id="SWITCHAPP">%1$s</xliff:g> चे प्रसारण केल्यास किंवा आउटपुट बदलल्यास, तुमचे सध्याचे प्रसारण बंद होईल"</string> diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml index 9153f1f5a884..100feac95bea 100644 --- a/packages/SystemUI/res/values-ms/strings.xml +++ b/packages/SystemUI/res/values-ms/strings.xml @@ -243,7 +243,7 @@ <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Set Kepala"</string> <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Input"</string> - <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="3003338571871392293">"Alat Bantu Dengar"</string> + <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Alat bantu pendengaran"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Menghidupkan…"</string> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Autoputar"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Autoputar skrin"</string> @@ -859,6 +859,7 @@ <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Besarkan skrin penuh"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Besarkan sebahagian skrin"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Buka tetapan pembesaran"</string> + <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Tutup tetapan pembesaran"</string> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Seret sudut untuk mengubah saiz"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Benarkan penatalan pepenjuru"</string> <string name="accessibility_resize" msgid="5733759136600611551">"Ubah saiz"</string> @@ -1045,6 +1046,8 @@ <string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Masalah membaca meter bateri anda"</string> <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Ketik untuk mendapatkan maklumat lanjut"</string> <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Tiada penggera"</string> + <!-- no translation found for accessibility_bouncer (5896923685673320070) --> + <skip /> <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Penderia cap jari"</string> <string name="accessibility_authenticate_hint" msgid="798914151813205721">"sahkan"</string> <string name="accessibility_enter_hint" msgid="2617864063504824834">"akses peranti"</string> @@ -1107,7 +1110,8 @@ <string name="dream_overlay_status_bar_assistant_attention_indicator" msgid="4712565923771372690">"Pembantu sedang mendengar"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# pemberitahuan}other{# pemberitahuan}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <string name="note_task_button_label" msgid="8718616095800343136">"Pengambilan nota"</string> + <string name="note_task_button_label" msgid="230135078402003532">"Pengambilan nota"</string> + <string name="note_task_shortcut_long_label" msgid="7729325091147319409">"Pengambilan nota, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Menyiarkan"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Hentikan siaran <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Jika anda siarkan <xliff:g id="SWITCHAPP">%1$s</xliff:g> atau tukarkan output, siaran semasa anda akan berhenti"</string> diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml index 729ff60df5e5..34f580aaa849 100644 --- a/packages/SystemUI/res/values-my/strings.xml +++ b/packages/SystemUI/res/values-my/strings.xml @@ -243,7 +243,7 @@ <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"အသံ"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"မိုက်ခွက်ပါနားကြပ်"</string> <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"အဝင်"</string> - <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="3003338571871392293">"နားကြားကိရိယာ"</string> + <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"နားကြားကိရိယာ"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"ဖွင့်နေသည်…"</string> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"အော်တို-လည်"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"မျက်နှာပြင်အား အလိုအလျောက်လှည့်ခြင်း"</string> @@ -859,6 +859,7 @@ <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"ဖန်သားပြင်အပြည့် ချဲ့သည်"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"ဖန်သားပြင် တစ်စိတ်တစ်ပိုင်းကို ချဲ့ပါ"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"ချဲ့ခြင်း ဆက်တင်များ ဖွင့်ရန်"</string> + <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"ချဲ့ခြင်း ဆက်တင်များ ပိတ်ရန်"</string> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"အရွယ်အစားပြန်ပြုပြင်ရန် ထောင့်စွန်းကို ဖိဆွဲပါ"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"ထောင့်ဖြတ် လှိမ့်ခွင့်ပြုရန်"</string> <string name="accessibility_resize" msgid="5733759136600611551">"အရွယ်အစားပြန်ပြုပြင်ရန်"</string> @@ -1045,6 +1046,7 @@ <string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"သင်၏ ဘက်ထရီမီတာကို ဖတ်ရာတွင် ပြဿနာရှိနေသည်"</string> <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"နောက်ထပ်အချက်အလက်များအတွက် တို့ပါ"</string> <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"နှိုးစက်ပေးမထားပါ"</string> + <string name="accessibility_bouncer" msgid="5896923685673320070">"ဖန်သားပြင်လော့ခ် ထည့်ရန်"</string> <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"လက်ဗွေ အာရုံခံကိရိယာ"</string> <string name="accessibility_authenticate_hint" msgid="798914151813205721">"အထောက်အထားစိစစ်ရန်"</string> <string name="accessibility_enter_hint" msgid="2617864063504824834">"စက်ပစ္စည်းသို့ ဝင်ရန်"</string> @@ -1107,7 +1109,8 @@ <string name="dream_overlay_status_bar_assistant_attention_indicator" msgid="4712565923771372690">"Assistant နားထောင်နေသည်"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{အကြောင်းကြားချက် # ခု}other{အကြောင်းကြားချက် # ခု}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>၊ <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <string name="note_task_button_label" msgid="8718616095800343136">"မှတ်စုလိုက်ခြင်း"</string> + <string name="note_task_button_label" msgid="230135078402003532">"မှတ်စုလိုက်ခြင်း"</string> + <string name="note_task_shortcut_long_label" msgid="7729325091147319409">"မှတ်စုလိုက်ခြင်း၊ <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"ထုတ်လွှင့်ခြင်း"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g> ထုတ်လွှင့်ခြင်းကို ရပ်မလား။"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> ကို ထုတ်လွှင့်သောအခါ (သို့) အထွက်ကို ပြောင်းသောအခါ သင့်လက်ရှိထုတ်လွှင့်ခြင်း ရပ်သွားမည်"</string> diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml index e5d1315f343b..d516e507dcbc 100644 --- a/packages/SystemUI/res/values-nb/strings.xml +++ b/packages/SystemUI/res/values-nb/strings.xml @@ -243,7 +243,7 @@ <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Lyd"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Hodetelefoner"</string> <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Innenhet"</string> - <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="3003338571871392293">"Høreapparater"</string> + <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Høreapparater"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Slår på …"</string> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Rotér automatisk"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Rotér skjermen automatisk"</string> @@ -859,6 +859,7 @@ <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Forstørr hele skjermen"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Forstørr en del av skjermen"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Åpne innstillinger for forstørring"</string> + <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Lukk forstørringsinnstillingene"</string> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Dra hjørnet for å endre størrelse"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Tillat diagonal rulling"</string> <string name="accessibility_resize" msgid="5733759136600611551">"Endre størrelse"</string> @@ -1045,6 +1046,8 @@ <string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Kunne ikke lese batterimåleren"</string> <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Trykk for å få mer informasjon"</string> <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Ingen alarm angitt"</string> + <!-- no translation found for accessibility_bouncer (5896923685673320070) --> + <skip /> <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Fingeravtrykkssensor"</string> <string name="accessibility_authenticate_hint" msgid="798914151813205721">"autentiser"</string> <string name="accessibility_enter_hint" msgid="2617864063504824834">"åpne enheten"</string> @@ -1107,7 +1110,8 @@ <string name="dream_overlay_status_bar_assistant_attention_indicator" msgid="4712565923771372690">"Assistenten lytter"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# varsel}other{# varsler}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <string name="note_task_button_label" msgid="8718616095800343136">"Notatskriving"</string> + <string name="note_task_button_label" msgid="230135078402003532">"Notatskriving"</string> + <string name="note_task_shortcut_long_label" msgid="7729325091147319409">"Notatskriving, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Kringkaster"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Vil du stoppe kringkastingen av <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Hvis du kringkaster <xliff:g id="SWITCHAPP">%1$s</xliff:g> eller endrer utgangen, stopper den nåværende kringkastingen din"</string> diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml index b8d807d50df0..70d3d5b98eac 100644 --- a/packages/SystemUI/res/values-ne/strings.xml +++ b/packages/SystemUI/res/values-ne/strings.xml @@ -243,7 +243,7 @@ <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"अडियो"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"हेडसेट"</string> <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"इनपुट"</string> - <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="3003338571871392293">"श्रवण यन्त्रहरू"</string> + <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"श्रवण यन्त्रहरू"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"सक्रिय गर्दै…"</string> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"अटो रोटेट"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"स्क्रिन स्वतःघुम्ने"</string> @@ -859,6 +859,7 @@ <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"पूरै स्क्रिन जुम इन गर्नुहोस्"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"स्क्रिनको केही भाग म्याग्निफाइ गर्नुहोस्"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"जुम इनसम्बन्धी सेटिङ खोल्नुहोस्"</string> + <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"जुम इन गर्ने सुविधाको सेटिङ बन्द गर्नुहोस्"</string> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"आकार बदल्न कुनाबाट ड्र्याग गर्नुहोस्"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"डायगोनल तरिकाले स्क्रोल गर्ने अनुमति दिनुहोस्"</string> <string name="accessibility_resize" msgid="5733759136600611551">"आकार बदल्नुहोस्"</string> @@ -1045,6 +1046,8 @@ <string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"डिभाइसको ब्याट्रीको मिटर रिडिङ क्रममा समस्या भयो"</string> <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"थप जानकारी प्राप्त गर्न ट्याप गर्नुहोस्"</string> <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"अलार्म राखिएको छैन"</string> + <!-- no translation found for accessibility_bouncer (5896923685673320070) --> + <skip /> <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"फिंगरप्रिन्ट सेन्सर"</string> <string name="accessibility_authenticate_hint" msgid="798914151813205721">"प्रमाणित गर्नुहोस्"</string> <string name="accessibility_enter_hint" msgid="2617864063504824834">"डिभाइस हाल्नुहोस्"</string> @@ -1107,7 +1110,8 @@ <string name="dream_overlay_status_bar_assistant_attention_indicator" msgid="4712565923771372690">"सहायकले सुनिरहेको छ"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# वटा सूचना}other{# वटा सूचनाहरू}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <string name="note_task_button_label" msgid="8718616095800343136">"टिपोट गर्ने कार्य"</string> + <string name="note_task_button_label" msgid="230135078402003532">"नोट लेख्ने कार्य"</string> + <string name="note_task_shortcut_long_label" msgid="7729325091147319409">"नोट लेख्ने कार्य, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"प्रसारण गरिँदै छ"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g> ब्रोडकास्ट गर्न छाड्ने हो?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"तपाईंले <xliff:g id="SWITCHAPP">%1$s</xliff:g> ब्रोडकास्ट गर्नुभयो वा आउटपुट परिवर्तन गर्नुभयो भने तपाईंको हालको ब्रोडकास्ट रोकिने छ"</string> diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml index 5e66b1c27b74..5dfdb86b7d7c 100644 --- a/packages/SystemUI/res/values-nl/strings.xml +++ b/packages/SystemUI/res/values-nl/strings.xml @@ -243,7 +243,7 @@ <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string> <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Invoer"</string> - <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="3003338571871392293">"Hoortoestellen"</string> + <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Hoortoestellen"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Aanzetten…"</string> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Automatisch draaien"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Scherm automatisch draaien"</string> @@ -859,6 +859,7 @@ <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Volledig scherm vergroten"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Deel van het scherm vergroten"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Instellingen voor vergroting openen"</string> + <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Instellingen voor vergroting sluiten"</string> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Sleep een hoek om het formaat te wijzigen"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Diagonaal scrollen toestaan"</string> <string name="accessibility_resize" msgid="5733759136600611551">"Formaat aanpassen"</string> @@ -1045,6 +1046,8 @@ <string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Probleem bij het lezen van je batterijmeter"</string> <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Tik hier voor meer informatie"</string> <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Geen wekker gezet"</string> + <!-- no translation found for accessibility_bouncer (5896923685673320070) --> + <skip /> <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Vingerafdruksensor"</string> <string name="accessibility_authenticate_hint" msgid="798914151813205721">"verifiëren"</string> <string name="accessibility_enter_hint" msgid="2617864063504824834">"apparaat opgeven"</string> @@ -1107,7 +1110,8 @@ <string name="dream_overlay_status_bar_assistant_attention_indicator" msgid="4712565923771372690">"De Assistent luistert"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# melding}other{# meldingen}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <string name="note_task_button_label" msgid="8718616095800343136">"Aantekeningen maken"</string> + <string name="note_task_button_label" msgid="230135078402003532">"Aantekeningen maken"</string> + <string name="note_task_shortcut_long_label" msgid="7729325091147319409">"Aantekeningen maken, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Uitzending"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Uitzending van <xliff:g id="APP_NAME">%1$s</xliff:g> stopzetten?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Als je <xliff:g id="SWITCHAPP">%1$s</xliff:g> uitzendt of de uitvoer wijzigt, wordt je huidige uitzending gestopt"</string> diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml index 1cac0ed7ea90..5266083fe2f3 100644 --- a/packages/SystemUI/res/values-or/strings.xml +++ b/packages/SystemUI/res/values-or/strings.xml @@ -243,7 +243,7 @@ <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ଅଡିଓ"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ହେଡସେଟ୍"</string> <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"ଇନପୁଟ୍"</string> - <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="3003338571871392293">"ଶ୍ରବଣ ଯନ୍ତ୍ର"</string> + <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"ଶ୍ରବଣ ଯନ୍ତ୍ର"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"ଅନ୍ ହେଉଛି…"</string> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"ଅଟୋ-ରୋଟେଟ୍"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"ଅଟୋ-ରୋଟେଟ ସ୍କ୍ରିନ"</string> @@ -859,6 +859,7 @@ <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"ସମ୍ପୂର୍ଣ୍ଣ ସ୍କ୍ରିନକୁ ମ୍ୟାଗ୍ନିଫାଏ କରନ୍ତୁ"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"ସ୍କ୍ରିନର ଅଂଶ ମାଗ୍ନିଫାଏ କରନ୍ତୁ"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"ମାଗ୍ନିଫିକେସନ ସେଟିଂସ ଖୋଲନ୍ତୁ"</string> + <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"ମେଗ୍ନିଫିକେସନ ସେଟିଂସକୁ ବନ୍ଦ କରନ୍ତୁ"</string> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"ରିସାଇଜ କରିବା ପାଇଁ କୋଣକୁ ଡ୍ରାଗ କରନ୍ତୁ"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"ଡାଏଗୋନାଲ ସ୍କ୍ରୋଲିଂକୁ ଅନୁମତି ଦିଅନ୍ତୁ"</string> <string name="accessibility_resize" msgid="5733759136600611551">"ରିସାଇଜ କରନ୍ତୁ"</string> @@ -1045,6 +1046,8 @@ <string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"ଆପଣଙ୍କ ବ୍ୟାଟେରୀ ମିଟର୍ ପଢ଼ିବାରେ ସମସ୍ୟା ହେଉଛି"</string> <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"ଅଧିକ ସୂଚନା ପାଇଁ ଟାପ୍ କରନ୍ତୁ"</string> <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"ଆଲାରାମ ସେଟ ହୋଇନାହିଁ"</string> + <!-- no translation found for accessibility_bouncer (5896923685673320070) --> + <skip /> <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"ଟିପଚିହ୍ନ ସେନ୍ସର୍"</string> <string name="accessibility_authenticate_hint" msgid="798914151813205721">"ପ୍ରମାଣୀକରଣ କରନ୍ତୁ"</string> <string name="accessibility_enter_hint" msgid="2617864063504824834">"ଡିଭାଇସ୍ ବିଷୟରେ ସୂଚନା ଲେଖନ୍ତୁ"</string> @@ -1107,7 +1110,8 @@ <string name="dream_overlay_status_bar_assistant_attention_indicator" msgid="4712565923771372690">"Assistant ଶୁଣୁଛି"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{#ଟି ବିଜ୍ଞପ୍ତି}other{#ଟି ବିଜ୍ଞପ୍ତି}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <string name="note_task_button_label" msgid="8718616095800343136">"ନୋଟଟେକିଂ"</string> + <string name="note_task_button_label" msgid="230135078402003532">"ନୋଟ-ଟେକିଂ"</string> + <string name="note_task_shortcut_long_label" msgid="7729325091147319409">"ନୋଟ-ଟେକିଂ, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"ବ୍ରଡକାଷ୍ଟ କରୁଛି"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g> ବ୍ରଡକାଷ୍ଟ କରିବା ବନ୍ଦ କରିବେ?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"ଯଦି ଆପଣ <xliff:g id="SWITCHAPP">%1$s</xliff:g> ବ୍ରଡକାଷ୍ଟ କରନ୍ତି କିମ୍ବା ଆଉଟପୁଟ ବଦଳାନ୍ତି, ତେବେ ଆପଣଙ୍କ ବର୍ତ୍ତମାନର ବ୍ରଡକାଷ୍ଟ ବନ୍ଦ ହୋଇଯିବ"</string> diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml index ad1ddcffd864..bee58780ff28 100644 --- a/packages/SystemUI/res/values-pa/strings.xml +++ b/packages/SystemUI/res/values-pa/strings.xml @@ -243,7 +243,7 @@ <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ਆਡੀਓ"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ਹੈੱਡਸੈੱਟ"</string> <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"ਇਨਪੁੱਟ"</string> - <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="3003338571871392293">"ਸੁਣਨ ਦੇ ਸਾਧਨ"</string> + <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"ਸੁਣਨ ਦੇ ਸਾਧਨ"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"ਚਾਲੂ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ…"</string> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"ਸਵੈ-ਘੁਮਾਓ"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"ਸਕ੍ਰੀਨ ਨੂੰ ਆਪਣੇ ਆਪ ਘੁੰਮਾਓ"</string> @@ -859,6 +859,7 @@ <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"ਪੂਰੀ ਸਕ੍ਰੀਨ ਨੂੰ ਵੱਡਦਰਸ਼ੀ ਕਰੋ"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"ਸਕ੍ਰੀਨ ਦੇ ਹਿੱਸੇ ਨੂੰ ਵੱਡਾ ਕਰੋ"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"ਵੱਡਦਰਸ਼ੀਕਰਨ ਸੈਟਿੰਗਾਂ ਖੋਲ੍ਹੋ"</string> + <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"ਵੱਡਦਰਸ਼ੀਕਰਨ ਸੈਟਿੰਗਾਂ ਬੰਦ ਕਰੋ"</string> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"ਆਕਾਰ ਬਦਲਣ ਲਈ ਕੋਨਾ ਘਸੀਟੋ"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"ਟੇਡੀ ਦਿਸ਼ਾ ਵਿੱਚ ਸਕ੍ਰੋਲ ਕਰਨ ਦਿਓ"</string> <string name="accessibility_resize" msgid="5733759136600611551">"ਆਕਾਰ ਬਦਲੋ"</string> @@ -1045,6 +1046,8 @@ <string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"ਤੁਹਾਡੇ ਬੈਟਰੀ ਮੀਟਰ ਨੂੰ ਪੜ੍ਹਨ ਵਿੱਚ ਸਮੱਸਿਆ ਹੋ ਰਹੀ ਹੈ"</string> <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"ਹੋਰ ਜਾਣਕਾਰੀ ਲਈ ਟੈਪ ਕਰੋ"</string> <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"ਕੋਈ ਅਲਾਰਮ ਸੈੱਟ ਨਹੀਂ"</string> + <!-- no translation found for accessibility_bouncer (5896923685673320070) --> + <skip /> <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"ਫਿੰਗਰਪ੍ਰਿੰਟ ਸੈਂਸਰ"</string> <string name="accessibility_authenticate_hint" msgid="798914151813205721">"ਪ੍ਰਮਾਣਿਤ ਕਰੋ"</string> <string name="accessibility_enter_hint" msgid="2617864063504824834">"ਡੀਵਾਈਸ ਵਿੱਚ ਦਾਖਲ ਹੋਵੋ"</string> @@ -1107,7 +1110,8 @@ <string name="dream_overlay_status_bar_assistant_attention_indicator" msgid="4712565923771372690">"Assistant ਸੁਣ ਰਹੀ ਹੈ"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# ਸੂਚਨਾ}one{# ਸੂਚਨਾ}other{# ਸੂਚਨਾਵਾਂ}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <string name="note_task_button_label" msgid="8718616095800343136">"ਨੋਟ ਬਣਾਉਣਾ"</string> + <string name="note_task_button_label" msgid="230135078402003532">"ਨੋਟ ਬਣਾਉਣਾ"</string> + <string name="note_task_shortcut_long_label" msgid="7729325091147319409">"ਨੋਟ ਬਣਾਉਣਾ, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"ਪ੍ਰਸਾਰਨ"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"ਕੀ <xliff:g id="APP_NAME">%1$s</xliff:g> ਦੇ ਪ੍ਰਸਾਰਨ ਨੂੰ ਰੋਕਣਾ ਹੈ?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"ਜੇ ਤੁਸੀਂ <xliff:g id="SWITCHAPP">%1$s</xliff:g> ਦਾ ਪ੍ਰਸਾਰਨ ਕਰਦੇ ਹੋ ਜਾਂ ਆਊਟਪੁੱਟ ਬਦਲਦੇ ਹੋ, ਤਾਂ ਤੁਹਾਡਾ ਮੌਜੂਦਾ ਪ੍ਰਸਾਰਨ ਰੁਕ ਜਾਵੇਗਾ"</string> diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml index e42fd393cb29..899460e43468 100644 --- a/packages/SystemUI/res/values-pl/strings.xml +++ b/packages/SystemUI/res/values-pl/strings.xml @@ -243,7 +243,7 @@ <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Dźwięk"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Zestaw słuchawkowy"</string> <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Wejście"</string> - <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="3003338571871392293">"Aparaty słuchowe"</string> + <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Aparaty słuchowe"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Włączam…"</string> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Autoobracanie"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Autoobracanie ekranu"</string> @@ -859,6 +859,7 @@ <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Powiększanie pełnego ekranu"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Powiększ część ekranu"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Otwórz ustawienia powiększenia"</string> + <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Zamknij ustawienia powiększenia"</string> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Przeciągnij róg, aby zmienić rozmiar"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Zezwalaj na przewijanie poprzeczne"</string> <string name="accessibility_resize" msgid="5733759136600611551">"Zmień rozmiar"</string> @@ -1045,6 +1046,8 @@ <string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Problem z odczytaniem pomiaru wykorzystania baterii"</string> <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Kliknij, aby uzyskać więcej informacji"</string> <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Nie ustawiono alarmu"</string> + <!-- no translation found for accessibility_bouncer (5896923685673320070) --> + <skip /> <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Czytnik linii papilarnych"</string> <string name="accessibility_authenticate_hint" msgid="798914151813205721">"uwierzytelnij"</string> <string name="accessibility_enter_hint" msgid="2617864063504824834">"otwórz urządzenie"</string> @@ -1107,7 +1110,8 @@ <string name="dream_overlay_status_bar_assistant_attention_indicator" msgid="4712565923771372690">"Asystent słucha"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# powiadomienie}few{# powiadomienia}many{# powiadomień}other{# powiadomienia}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <string name="note_task_button_label" msgid="8718616095800343136">"Notatki"</string> + <string name="note_task_button_label" msgid="230135078402003532">"Notatki"</string> + <string name="note_task_shortcut_long_label" msgid="7729325091147319409">"Notatki, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Transmisja"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Zatrzymaj transmisję aplikacji <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Jeśli transmitujesz aplikację <xliff:g id="SWITCHAPP">%1$s</xliff:g> lub zmieniasz dane wyjściowe, Twoja obecna transmisja zostanie zakończona"</string> diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml index 579a9bdfdf8c..785856ef4723 100644 --- a/packages/SystemUI/res/values-pt-rBR/strings.xml +++ b/packages/SystemUI/res/values-pt-rBR/strings.xml @@ -243,7 +243,7 @@ <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Áudio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Fone de ouvido"</string> <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Entrada"</string> - <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="3003338571871392293">"Aparelhos auditivos"</string> + <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Aparelhos auditivos"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Ativando…"</string> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Giro automático"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Giro automático da tela"</string> @@ -859,6 +859,7 @@ <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Ampliar toda a tela"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Ampliar parte da tela"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Abrir as configurações de ampliação"</string> + <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Fechar configurações de ampliação"</string> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Arraste o canto para redimensionar"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Permitir rolagem diagonal"</string> <string name="accessibility_resize" msgid="5733759136600611551">"Redimensionar"</string> @@ -1045,6 +1046,8 @@ <string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Problema para ler seu medidor de bateria"</string> <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Toque para mais informações"</string> <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Nenhum alarme definido"</string> + <!-- no translation found for accessibility_bouncer (5896923685673320070) --> + <skip /> <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Sensor de impressão digital"</string> <string name="accessibility_authenticate_hint" msgid="798914151813205721">"autenticar"</string> <string name="accessibility_enter_hint" msgid="2617864063504824834">"acessar o dispositivo"</string> @@ -1107,7 +1110,8 @@ <string name="dream_overlay_status_bar_assistant_attention_indicator" msgid="4712565923771372690">"O Google Assistente está ouvindo"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# notificação}one{# notificação}many{# notificações}other{# notificações}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <string name="note_task_button_label" msgid="8718616095800343136">"Anotações"</string> + <string name="note_task_button_label" msgid="230135078402003532">"Anotações"</string> + <string name="note_task_shortcut_long_label" msgid="7729325091147319409">"Anotações, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Transmitindo"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Interromper a transmissão do app <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Se você transmitir o app <xliff:g id="SWITCHAPP">%1$s</xliff:g> ou mudar a saída, a transmissão atual será interrompida"</string> diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml index aca644fa3f85..c6612a7010e1 100644 --- a/packages/SystemUI/res/values-pt-rPT/strings.xml +++ b/packages/SystemUI/res/values-pt-rPT/strings.xml @@ -243,7 +243,7 @@ <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Áudio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Ausc. c/ mic. integ."</string> <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Entrada"</string> - <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="3003338571871392293">"Aparelhos auditivos"</string> + <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Aparelhos auditivos"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"A ativar..."</string> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Rotação auto."</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Rodar o ecrã automaticamente"</string> @@ -859,6 +859,7 @@ <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Ampliar o ecrã inteiro"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Ampliar parte do ecrã"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Abrir definições de ampliação"</string> + <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Fechar definições de ampliação"</string> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Arrastar o canto para redimensionar"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Permitir deslocamento da página na diagonal"</string> <string name="accessibility_resize" msgid="5733759136600611551">"Redimensionar"</string> @@ -1045,6 +1046,8 @@ <string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Ocorreu um problema ao ler o medidor da bateria"</string> <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Toque para obter mais informações"</string> <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Nenhum alarme defin."</string> + <!-- no translation found for accessibility_bouncer (5896923685673320070) --> + <skip /> <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Sensor de impressões digitais"</string> <string name="accessibility_authenticate_hint" msgid="798914151813205721">"autenticar"</string> <string name="accessibility_enter_hint" msgid="2617864063504824834">"entrar no dispositivo"</string> @@ -1107,7 +1110,8 @@ <string name="dream_overlay_status_bar_assistant_attention_indicator" msgid="4712565923771372690">"O Assistente está a ouvir"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# notificação}many{# notificações}other{# notificações}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <string name="note_task_button_label" msgid="8718616095800343136">"Tomar notas"</string> + <string name="note_task_button_label" msgid="230135078402003532">"Tomar notas"</string> + <string name="note_task_shortcut_long_label" msgid="7729325091147319409">"Tomar notas, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"A transmitir"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Interromper a transmissão da app <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Se transmitir a app <xliff:g id="SWITCHAPP">%1$s</xliff:g> ou alterar a saída, a sua transmissão atual é interrompida"</string> diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml index 579a9bdfdf8c..785856ef4723 100644 --- a/packages/SystemUI/res/values-pt/strings.xml +++ b/packages/SystemUI/res/values-pt/strings.xml @@ -243,7 +243,7 @@ <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Áudio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Fone de ouvido"</string> <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Entrada"</string> - <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="3003338571871392293">"Aparelhos auditivos"</string> + <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Aparelhos auditivos"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Ativando…"</string> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Giro automático"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Giro automático da tela"</string> @@ -859,6 +859,7 @@ <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Ampliar toda a tela"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Ampliar parte da tela"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Abrir as configurações de ampliação"</string> + <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Fechar configurações de ampliação"</string> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Arraste o canto para redimensionar"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Permitir rolagem diagonal"</string> <string name="accessibility_resize" msgid="5733759136600611551">"Redimensionar"</string> @@ -1045,6 +1046,8 @@ <string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Problema para ler seu medidor de bateria"</string> <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Toque para mais informações"</string> <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Nenhum alarme definido"</string> + <!-- no translation found for accessibility_bouncer (5896923685673320070) --> + <skip /> <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Sensor de impressão digital"</string> <string name="accessibility_authenticate_hint" msgid="798914151813205721">"autenticar"</string> <string name="accessibility_enter_hint" msgid="2617864063504824834">"acessar o dispositivo"</string> @@ -1107,7 +1110,8 @@ <string name="dream_overlay_status_bar_assistant_attention_indicator" msgid="4712565923771372690">"O Google Assistente está ouvindo"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# notificação}one{# notificação}many{# notificações}other{# notificações}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <string name="note_task_button_label" msgid="8718616095800343136">"Anotações"</string> + <string name="note_task_button_label" msgid="230135078402003532">"Anotações"</string> + <string name="note_task_shortcut_long_label" msgid="7729325091147319409">"Anotações, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Transmitindo"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Interromper a transmissão do app <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Se você transmitir o app <xliff:g id="SWITCHAPP">%1$s</xliff:g> ou mudar a saída, a transmissão atual será interrompida"</string> diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml index 03a0872c042e..a7a2731201f8 100644 --- a/packages/SystemUI/res/values-ro/strings.xml +++ b/packages/SystemUI/res/values-ro/strings.xml @@ -243,7 +243,7 @@ <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Căști"</string> <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Intrare"</string> - <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="3003338571871392293">"Aparate auditive"</string> + <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Aparate auditive"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Se activează..."</string> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Rotire automată"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Rotirea automată a ecranului"</string> @@ -859,6 +859,7 @@ <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Mărește tot ecranul"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Mărește o parte a ecranului"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Deschide setările pentru mărire"</string> + <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Închide setările de mărire"</string> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Trage de colț pentru a redimensiona"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Permite derularea pe diagonală"</string> <string name="accessibility_resize" msgid="5733759136600611551">"Redimensionează"</string> @@ -1045,6 +1046,8 @@ <string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Problemă la citirea măsurării bateriei"</string> <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Atinge pentru mai multe informații"</string> <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Nicio alarmă setată"</string> + <!-- no translation found for accessibility_bouncer (5896923685673320070) --> + <skip /> <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Senzor de amprentă"</string> <string name="accessibility_authenticate_hint" msgid="798914151813205721">"autentifică-te"</string> <string name="accessibility_enter_hint" msgid="2617864063504824834">"Accesează dispozitivul"</string> @@ -1107,7 +1110,8 @@ <string name="dream_overlay_status_bar_assistant_attention_indicator" msgid="4712565923771372690">"Asistentul ascultă"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# notificare}few{# notificări}other{# de notificări}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <string name="note_task_button_label" msgid="8718616095800343136">"Notetaking"</string> + <string name="note_task_button_label" msgid="230135078402003532">"Luare de notițe"</string> + <string name="note_task_shortcut_long_label" msgid="7729325091147319409">"Luare de notițe, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Se difuzează"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Oprești transmisia <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Dacă transmiți <xliff:g id="SWITCHAPP">%1$s</xliff:g> sau schimbi ieșirea, transmisia actuală se va opri"</string> diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml index 58563d50688a..279d9a629409 100644 --- a/packages/SystemUI/res/values-ru/strings.xml +++ b/packages/SystemUI/res/values-ru/strings.xml @@ -243,7 +243,7 @@ <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Аудиоустройство"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Гарнитура"</string> <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Устройство ввода"</string> - <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="3003338571871392293">"Слуховые аппараты"</string> + <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Слуховые аппараты"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Включение…"</string> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Автоповорот"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Автоповорот экрана"</string> @@ -859,6 +859,7 @@ <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Увеличение всего экрана"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Увеличить часть экрана"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Открыть настройки увеличения"</string> + <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Закрыть настройки увеличения"</string> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Потяните за угол, чтобы изменить размер"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Разрешить прокручивать по диагонали"</string> <string name="accessibility_resize" msgid="5733759136600611551">"Изменить размер"</string> @@ -1045,6 +1046,8 @@ <string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Не удалось узнать уровень заряда батареи"</string> <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Нажмите, чтобы узнать больше."</string> <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Нет будильников"</string> + <!-- no translation found for accessibility_bouncer (5896923685673320070) --> + <skip /> <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Сканер отпечатков пальцев"</string> <string name="accessibility_authenticate_hint" msgid="798914151813205721">"выполнить аутентификацию"</string> <string name="accessibility_enter_hint" msgid="2617864063504824834">"указать устройство"</string> @@ -1107,7 +1110,8 @@ <string name="dream_overlay_status_bar_assistant_attention_indicator" msgid="4712565923771372690">"Ассистент вас слушает"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# уведомление}one{# уведомление}few{# уведомления}many{# уведомлений}other{# уведомления}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <string name="note_task_button_label" msgid="8718616095800343136">"Создание заметок"</string> + <string name="note_task_button_label" msgid="230135078402003532">"Создание заметок"</string> + <string name="note_task_shortcut_long_label" msgid="7729325091147319409">"<xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>: создание заметок"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Трансляция"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Остановить трансляцию \"<xliff:g id="APP_NAME">%1$s</xliff:g>\"?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Если вы начнете транслировать \"<xliff:g id="SWITCHAPP">%1$s</xliff:g>\" или смените целевое устройство, текущая трансляция прервется."</string> diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml index 69ebc3fd091b..ddca7f5b5078 100644 --- a/packages/SystemUI/res/values-si/strings.xml +++ b/packages/SystemUI/res/values-si/strings.xml @@ -243,7 +243,7 @@ <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ශ්රව්ය"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"හෙඩ්සෙටය"</string> <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"ආදානය"</string> - <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="3003338571871392293">"ශ්රවණාධාරක"</string> + <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"ශ්රවණාධාරක"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"ක්රියාත්මක කරමින්…"</string> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"ස්වයංක්රීය කරකැවීම"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"ස්වයංක්රීයව-භ්රමණය වන තිරය"</string> @@ -859,6 +859,7 @@ <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"පූර්ණ තිරය විශාලනය කරන්න"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"තිරයේ කොටසක් විශාලනය කරන්න"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"විශාලන සැකසීම් විවෘත කරන්න"</string> + <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"විශාලන සැකසීම් වසන්න"</string> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"ප්රමාණය වෙනස් කිරීමට කොන අදින්න"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"විකර්ණ අනුචලනයට ඉඩ දෙන්න"</string> <string name="accessibility_resize" msgid="5733759136600611551">"ප්රතිප්රමාණය කරන්න"</string> @@ -1045,6 +1046,8 @@ <string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"ඔබගේ බැටරි මනුව කියවීමේ දෝෂයකි"</string> <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"තවත් තොරතුරු සඳහා තට්ටු කරන්න"</string> <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"එලාම සකසා නැත"</string> + <!-- no translation found for accessibility_bouncer (5896923685673320070) --> + <skip /> <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"ඇඟිලි සලකුණු සංවේදකය"</string> <string name="accessibility_authenticate_hint" msgid="798914151813205721">"සත්යාපනය කරන්න"</string> <string name="accessibility_enter_hint" msgid="2617864063504824834">"උපාංගය ඇතුළු කරන්න"</string> @@ -1107,7 +1110,8 @@ <string name="dream_overlay_status_bar_assistant_attention_indicator" msgid="4712565923771372690">"සහයක සවන් දෙයි"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{දැනුම්දීම් #ක්}one{දැනුම්දීම් #ක්}other{දැනුම්දීම් #ක්}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <string name="note_task_button_label" msgid="8718616095800343136">"සටහන් කර ගැනීම"</string> + <string name="note_task_button_label" msgid="230135078402003532">"සටහන් කර ගැනීම"</string> + <string name="note_task_shortcut_long_label" msgid="7729325091147319409">"සටහන් කර ගැනීම, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"විකාශනය කරමින්"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g> විකාශනය කිරීම නවත්වන්නද?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"ඔබ <xliff:g id="SWITCHAPP">%1$s</xliff:g> විකාශනය කළහොත් හෝ ප්රතිදානය වෙනස් කළහොත්, ඔබගේ වත්මන් විකාශනය නවතිනු ඇත."</string> diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml index 7c356ded7502..996f45440381 100644 --- a/packages/SystemUI/res/values-sk/strings.xml +++ b/packages/SystemUI/res/values-sk/strings.xml @@ -243,7 +243,7 @@ <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Zvuk"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Náhlavná súprava"</string> <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Vstup"</string> - <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="3003338571871392293">"Načúvadlá"</string> + <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Načúvadlá"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Zapína sa…"</string> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Automatické otáčanie"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Automatické otáčanie obrazovky"</string> @@ -859,6 +859,7 @@ <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Zväčšenie celej obrazovky"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Zväčšiť časť obrazovky"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Otvoriť nastavenia zväčšenia"</string> + <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Zavrieť nastavenia zväčšenia"</string> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Veľkosť zmeníte presunutím rohu"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Povoliť diagonálne posúvanie"</string> <string name="accessibility_resize" msgid="5733759136600611551">"Zmeniť veľkosť"</string> @@ -1045,6 +1046,8 @@ <string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Pri čítaní meradla batérie sa vyskytol problém"</string> <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Klepnutím si zobrazíte ďalšie informácie"</string> <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Žiadny budík"</string> + <!-- no translation found for accessibility_bouncer (5896923685673320070) --> + <skip /> <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Senzor odtlačkov prstov"</string> <string name="accessibility_authenticate_hint" msgid="798914151813205721">"overte"</string> <string name="accessibility_enter_hint" msgid="2617864063504824834">"vstúpte do zariadenia"</string> @@ -1107,7 +1110,8 @@ <string name="dream_overlay_status_bar_assistant_attention_indicator" msgid="4712565923771372690">"Asistent počúva"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# upozornenie}few{# upozornenia}many{# notifications}other{# upozornení}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <string name="note_task_button_label" msgid="8718616095800343136">"Zapisovanie poznámok"</string> + <string name="note_task_button_label" msgid="230135078402003532">"Zapisovanie poznámok"</string> + <string name="note_task_shortcut_long_label" msgid="7729325091147319409">"Zapisovanie poznámok, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Vysiela"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Chcete zastaviť vysielanie aplikácie <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Ak vysielate aplikáciu <xliff:g id="SWITCHAPP">%1$s</xliff:g> alebo zmeníte výstup, aktuálne vysielanie bude zastavené"</string> diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml index 96ad5baab5ab..6bebdccb42bf 100644 --- a/packages/SystemUI/res/values-sl/strings.xml +++ b/packages/SystemUI/res/values-sl/strings.xml @@ -243,7 +243,7 @@ <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Zvok"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Slušalke z mikrofonom"</string> <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Vhodna naprava"</string> - <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="3003338571871392293">"Slušni pripomočki"</string> + <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Slušni aparati"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Vklapljanje …"</string> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Samodejno sukanje"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Samodejno sukanje zaslona"</string> @@ -859,6 +859,7 @@ <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Povečanje celotnega zaslona"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Povečava dela zaslona"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Odpri nastavitve povečave"</string> + <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Zapri nastavitve povečave"</string> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Povlecite vogal, da spremenite velikost."</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Dovoli diagonalno pomikanje"</string> <string name="accessibility_resize" msgid="5733759136600611551">"Spremeni velikost"</string> @@ -1045,6 +1046,8 @@ <string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Težava z branjem indikatorja stanja napolnjenosti baterije"</string> <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Dotaknite se za več informacij"</string> <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Ni nastavljenih alarmov"</string> + <!-- no translation found for accessibility_bouncer (5896923685673320070) --> + <skip /> <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Tipalo prstnih odtisov"</string> <string name="accessibility_authenticate_hint" msgid="798914151813205721">"preverjanje pristnosti"</string> <string name="accessibility_enter_hint" msgid="2617864063504824834">"vstop v napravo"</string> @@ -1107,7 +1110,8 @@ <string name="dream_overlay_status_bar_assistant_attention_indicator" msgid="4712565923771372690">"Pomočnik posluša."</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# obvestilo}one{# obvestilo}two{# obvestili}few{# obvestila}other{# obvestil}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <string name="note_task_button_label" msgid="8718616095800343136">"Ustvarjanje zapiskov"</string> + <string name="note_task_button_label" msgid="230135078402003532">"Ustvarjanje zapiskov"</string> + <string name="note_task_shortcut_long_label" msgid="7729325091147319409">"Ustvarjanje zapiskov, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Oddajanje"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Želite ustaviti oddajanje aplikacije <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Če oddajate aplikacijo <xliff:g id="SWITCHAPP">%1$s</xliff:g> ali spremenite izhod, bo trenutno oddajanje ustavljeno."</string> diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml index 181756afeccf..24d5fca5d5c9 100644 --- a/packages/SystemUI/res/values-sq/strings.xml +++ b/packages/SystemUI/res/values-sq/strings.xml @@ -243,7 +243,7 @@ <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Kufje me mikrofon"</string> <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Hyrja"</string> - <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="3003338571871392293">"Aparatet e dëgjimit"</string> + <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Aparatet e dëgjimit"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Po aktivizohet…"</string> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Rrotullim automatik"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Rrotullimi automatik i ekranit"</string> @@ -859,6 +859,7 @@ <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Zmadho ekranin e plotë"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Zmadho një pjesë të ekranit"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Hap cilësimet e zmadhimit"</string> + <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Mbyll cilësimet e zmadhimit"</string> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Zvarrit këndin për të ndryshuar përmasat"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Lejo lëvizjen diagonale"</string> <string name="accessibility_resize" msgid="5733759136600611551">"Ndrysho përmasat"</string> @@ -1045,6 +1046,8 @@ <string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Problem me leximin e matësit të baterisë"</string> <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Trokit për më shumë informacione"</string> <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Nuk është caktuar asnjë alarm"</string> + <!-- no translation found for accessibility_bouncer (5896923685673320070) --> + <skip /> <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Sensori i gjurmës së gishtit"</string> <string name="accessibility_authenticate_hint" msgid="798914151813205721">"për ta vërtetuar"</string> <string name="accessibility_enter_hint" msgid="2617864063504824834">"për të hyrë në pajisje"</string> @@ -1107,7 +1110,8 @@ <string name="dream_overlay_status_bar_assistant_attention_indicator" msgid="4712565923771372690">"\"Asistenti\" po dëgjon"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# njoftim}other{# njoftime}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <string name="note_task_button_label" msgid="8718616095800343136">"Mbajtja e shënimeve"</string> + <string name="note_task_button_label" msgid="230135078402003532">"Mbajtja e shënimeve"</string> + <string name="note_task_shortcut_long_label" msgid="7729325091147319409">"Mbajtja e shënimeve, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Po transmeton"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Të ndalohet transmetimi i <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Nëse transmeton <xliff:g id="SWITCHAPP">%1$s</xliff:g> ose ndryshon daljen, transmetimi yt aktual do të ndalojë"</string> diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml index 798b4643164e..9ca97b9ca863 100644 --- a/packages/SystemUI/res/values-sr/strings.xml +++ b/packages/SystemUI/res/values-sr/strings.xml @@ -243,7 +243,7 @@ <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Аудио"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Слушалице"</string> <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Унос"</string> - <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="3003338571871392293">"Слушни апарати"</string> + <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Слушни апарати"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Укључује се..."</string> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Аутоматска ротација"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Аутоматско ротирање екрана"</string> @@ -859,6 +859,7 @@ <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Увећајте цео екран"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Увећајте део екрана"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Отвори подешавања увећања"</string> + <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Затвори подешавања увећања"</string> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Превуците угао да бисте променили величину"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Дозволи дијагонално скроловање"</string> <string name="accessibility_resize" msgid="5733759136600611551">"Промени величину"</string> @@ -1045,6 +1046,8 @@ <string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Проблем са очитавањем мерача батерије"</string> <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Додирните за више информација"</string> <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Није подешен"</string> + <!-- no translation found for accessibility_bouncer (5896923685673320070) --> + <skip /> <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Сензор за отисак прста"</string> <string name="accessibility_authenticate_hint" msgid="798914151813205721">"потврдите идентитет"</string> <string name="accessibility_enter_hint" msgid="2617864063504824834">"унесите уређај"</string> @@ -1107,7 +1110,8 @@ <string name="dream_overlay_status_bar_assistant_attention_indicator" msgid="4712565923771372690">"Помоћник слуша"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# обавештење}one{# обавештење}few{# обавештења}other{# обавештења}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <string name="note_task_button_label" msgid="8718616095800343136">"Прављење бележака"</string> + <string name="note_task_button_label" msgid="230135078402003532">"Прављење бележака"</string> + <string name="note_task_shortcut_long_label" msgid="7729325091147319409">"Прављење бележака, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Емитовање"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Желите да зауставите емитовање апликације <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Ако емитујете апликацију <xliff:g id="SWITCHAPP">%1$s</xliff:g> или промените излаз, актуелно емитовање ће се зауставити"</string> diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml index ba56ab40645a..82468cb6da37 100644 --- a/packages/SystemUI/res/values-sv/strings.xml +++ b/packages/SystemUI/res/values-sv/strings.xml @@ -243,7 +243,7 @@ <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Ljud"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string> <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Ingång"</string> - <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="3003338571871392293">"Hörapparater"</string> + <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Hörapparater"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Aktiverar …"</string> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Rotera automatiskt"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Rotera skärmen automatiskt"</string> @@ -859,6 +859,7 @@ <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Förstora hela skärmen"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Förstora en del av skärmen"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Öppna inställningarna för förstoring"</string> + <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Stäng inställningarna för förstoring"</string> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Dra i hörnet för att ändra storlek"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Tillåt diagonal scrollning"</string> <string name="accessibility_resize" msgid="5733759136600611551">"Ändra storlek"</string> @@ -1045,6 +1046,8 @@ <string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Batteriindikatorn visas inte"</string> <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Tryck för mer information"</string> <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Inget inställt alarm"</string> + <!-- no translation found for accessibility_bouncer (5896923685673320070) --> + <skip /> <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Fingeravtryckssensor"</string> <string name="accessibility_authenticate_hint" msgid="798914151813205721">"autentisera"</string> <string name="accessibility_enter_hint" msgid="2617864063504824834">"ange enhet"</string> @@ -1107,7 +1110,8 @@ <string name="dream_overlay_status_bar_assistant_attention_indicator" msgid="4712565923771372690">"Assistenten lyssnar"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# avisering}other{# aviseringar}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <string name="note_task_button_label" msgid="8718616095800343136">"Anteckningar"</string> + <string name="note_task_button_label" msgid="230135078402003532">"Anteckna"</string> + <string name="note_task_shortcut_long_label" msgid="7729325091147319409">"Anteckna med <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Sänder"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Vill du sluta sända från <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Om en utsändning från <xliff:g id="SWITCHAPP">%1$s</xliff:g> pågår eller om du byter ljudutgång avbryts den nuvarande utsändningen"</string> diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml index e4d3e56ae119..ef92eb6291be 100644 --- a/packages/SystemUI/res/values-sw/strings.xml +++ b/packages/SystemUI/res/values-sw/strings.xml @@ -243,7 +243,7 @@ <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Sauti"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Vifaa vya sauti"</string> <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Vifaa vya kuingiza sauti"</string> - <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="3003338571871392293">"Visaidizi vya Kusikia"</string> + <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Visaidizi vya kusikia"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Inawasha..."</string> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Zungusha kiotomatiki"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Skrini ijizungushe kiotomatiki"</string> @@ -859,6 +859,7 @@ <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Kuza skrini nzima"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Kuza sehemu ya skrini"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Fungua mipangilio ya ukuzaji"</string> + <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Funga mipangilio ya ukuzaji"</string> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Buruta kona ili ubadilishe ukubwa"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Ruhusu usogezaji wa kimshazari"</string> <string name="accessibility_resize" msgid="5733759136600611551">"Badilisha ukubwa"</string> @@ -1045,6 +1046,8 @@ <string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Tatizo la kusoma mita ya betri yako"</string> <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Gusa ili upate maelezo zaidi"</string> <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Hujaweka kengele"</string> + <!-- no translation found for accessibility_bouncer (5896923685673320070) --> + <skip /> <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Kitambua alama ya kidole"</string> <string name="accessibility_authenticate_hint" msgid="798914151813205721">"thibitisha"</string> <string name="accessibility_enter_hint" msgid="2617864063504824834">"weka kifaa"</string> @@ -1107,7 +1110,8 @@ <string name="dream_overlay_status_bar_assistant_attention_indicator" msgid="4712565923771372690">"Programu ya Mratibu inasikiliza"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{Arifa #}other{Arifa #}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <string name="note_task_button_label" msgid="8718616095800343136">"Kuandika vidokezo"</string> + <string name="note_task_button_label" msgid="230135078402003532">"Kuandika madokezo"</string> + <string name="note_task_shortcut_long_label" msgid="7729325091147319409">"Kuandika madokezo, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Inaarifu"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Ungependa kusimamisha utangazaji kwenye <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Ikiwa unatangaza kwenye <xliff:g id="SWITCHAPP">%1$s</xliff:g> au unabadilisha maudhui, tangazo lako la sasa litasimamishwa"</string> diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml index 125adc826932..b277f558e98a 100644 --- a/packages/SystemUI/res/values-ta/strings.xml +++ b/packages/SystemUI/res/values-ta/strings.xml @@ -243,7 +243,7 @@ <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ஆடியோ"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ஹெட்செட்"</string> <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"உள்ளீடு"</string> - <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="3003338571871392293">"செவித்துணை கருவிகள்"</string> + <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"செவித்துணைக் கருவி"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"ஆன் செய்கிறது…"</string> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"தானாகச் சுழற்று"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"திரையைத் தானாகச் சுழற்று"</string> @@ -859,6 +859,7 @@ <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"முழுத்திரையைப் பெரிதாக்கும்"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"திரையின் ஒரு பகுதியைப் பெரிதாக்கும்"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"பெரிதாக்கல் அமைப்புகளைத் திற"</string> + <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"பெரிதாக்கல் அமைப்புகளை மூடுக"</string> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"அளவை மாற்ற மூலையை இழுக்கவும்"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"குறுக்கே ஸ்க்ரோல் செய்வதை அனுமதி"</string> <string name="accessibility_resize" msgid="5733759136600611551">"அளவை மாற்று"</string> @@ -1045,6 +1046,8 @@ <string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"பேட்டரி அளவை அறிவதில் சிக்கல்"</string> <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"மேலும் தகவல்களுக்கு தட்டவும்"</string> <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"அலாரம் எதுவுமில்லை"</string> + <!-- no translation found for accessibility_bouncer (5896923685673320070) --> + <skip /> <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"கைரேகை சென்சார்"</string> <string name="accessibility_authenticate_hint" msgid="798914151813205721">"அங்கீகரி"</string> <string name="accessibility_enter_hint" msgid="2617864063504824834">"சாதனத்தைத் திற"</string> @@ -1107,7 +1110,8 @@ <string name="dream_overlay_status_bar_assistant_attention_indicator" msgid="4712565923771372690">"Assistant கேட்டுக்கொண்டிருக்கிறது"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# அறிவிப்பு}other{# அறிவிப்புகள்}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <string name="note_task_button_label" msgid="8718616095800343136">"குறிப்பெடுத்தல்"</string> + <string name="note_task_button_label" msgid="230135078402003532">"குறிப்பெடுத்தல்"</string> + <string name="note_task_shortcut_long_label" msgid="7729325091147319409">"குறிப்பெடுத்தல், <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"ஒலிபரப்புதல்"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g> ஆப்ஸ் ஒலிபரப்பப்படுவதை நிறுத்தவா?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"நீங்கள் <xliff:g id="SWITCHAPP">%1$s</xliff:g> ஆப்ஸை ஒலிபரப்பினாலோ அவுட்புட்டை மாற்றினாலோ உங்களின் தற்போதைய ஒலிபரப்பு நிறுத்தப்படும்"</string> diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml index 25bf52817ffd..a6cbf3d6e63b 100644 --- a/packages/SystemUI/res/values-te/strings.xml +++ b/packages/SystemUI/res/values-te/strings.xml @@ -243,7 +243,7 @@ <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ఆడియో"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"హెడ్సెట్"</string> <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"ఇన్పుట్"</string> - <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="3003338571871392293">"వినికిడి పరికరాలు"</string> + <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"వినికిడి పరికరాలు"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"ఆన్ చేస్తోంది…"</string> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"ఆటో-రొటేట్"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"స్క్రీన్ ఆటో-రొటేట్"</string> @@ -859,6 +859,7 @@ <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"ఫుల్ స్క్రీన్ను మ్యాగ్నిఫై చేయండి"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"స్క్రీన్లో భాగాన్ని మ్యాగ్నిఫై చేయండి"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"మ్యాగ్నిఫికేషన్ సెట్టింగ్లను తెరవండి"</string> + <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"మాగ్నిఫికేషన్ సెట్టింగ్లను మూసివేయండి"</string> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"సైజ్ మార్చడానికి మూలను లాగండి"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"డయాగనల్ స్క్రోలింగ్ను అనుమతించండి"</string> <string name="accessibility_resize" msgid="5733759136600611551">"సైజ్ మార్చండి"</string> @@ -1045,6 +1046,7 @@ <string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"మీ బ్యాటరీ మీటర్ను చదవడంలో సమస్య"</string> <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"మరింత సమాచారం కోసం ట్యాప్ చేయండి"</string> <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"అలారం సెట్ చేయలేదు"</string> + <string name="accessibility_bouncer" msgid="5896923685673320070">"స్క్రీన్ లాక్ను ఎంటర్ చేయండి"</string> <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"వేలిముద్ర సెన్సార్"</string> <string name="accessibility_authenticate_hint" msgid="798914151813205721">"ప్రామాణీకరించండి"</string> <string name="accessibility_enter_hint" msgid="2617864063504824834">"పరికరాన్ని ఎంటర్ చేయండి"</string> @@ -1107,7 +1109,8 @@ <string name="dream_overlay_status_bar_assistant_attention_indicator" msgid="4712565923771372690">"Assistant వింటోంది"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# నోటిఫికేషన్}other{# నోటిఫికేషన్లు}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <string name="note_task_button_label" msgid="8718616095800343136">"నోట్టేకింగ్"</string> + <string name="note_task_button_label" msgid="230135078402003532">"నోట్-టేకింగ్"</string> + <string name="note_task_shortcut_long_label" msgid="7729325091147319409">"నోట్-టేకింగ్, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"ప్రసారం చేస్తోంది"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g> ప్రసారం చేయడాన్ని ఆపివేయాలా?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"మీరు <xliff:g id="SWITCHAPP">%1$s</xliff:g> ప్రసారం చేస్తే లేదా అవుట్పుట్ను మార్చినట్లయితే, మీ ప్రస్తుత ప్రసారం ఆగిపోతుంది"</string> diff --git a/packages/SystemUI/res/values-television/config.xml b/packages/SystemUI/res/values-television/config.xml index 0ca154e06787..2ace86f919aa 100644 --- a/packages/SystemUI/res/values-television/config.xml +++ b/packages/SystemUI/res/values-television/config.xml @@ -20,11 +20,6 @@ <!-- These resources are around just to allow their values to be customized for different hardware and product builds. --> <resources> - <!-- SystemUIFactory component --> - <string name="config_systemUIFactoryComponent" translatable="false"> - com.android.systemui.tv.TvSystemUIInitializer - </string> - <!-- Svelte specific logic, see RecentsConfiguration.SVELTE_* constants. --> <integer name="recents_svelte_level">3</integer> diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml index 578c843f1258..4b1530904bef 100644 --- a/packages/SystemUI/res/values-th/strings.xml +++ b/packages/SystemUI/res/values-th/strings.xml @@ -243,7 +243,7 @@ <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"เสียง"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ชุดหูฟัง"</string> <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"อินพุต"</string> - <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="3003338571871392293">"เครื่องช่วยฟัง"</string> + <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"เครื่องช่วยฟัง"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"กำลังเปิด..."</string> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"หมุนอัตโนมัติ"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"หมุนหน้าจออัตโนมัติ"</string> @@ -859,6 +859,7 @@ <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"ขยายเป็นเต็มหน้าจอ"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"ขยายบางส่วนของหน้าจอ"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"เปิดการตั้งค่าการขยาย"</string> + <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"ปิดการตั้งค่าการขยาย"</string> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"ลากที่มุมเพื่อปรับขนาด"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"อนุญาตการเลื่อนแบบทแยงมุม"</string> <string name="accessibility_resize" msgid="5733759136600611551">"ปรับขนาด"</string> @@ -1045,6 +1046,8 @@ <string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"พบปัญหาในการอ่านเครื่องวัดแบตเตอรี่"</string> <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"แตะดูข้อมูลเพิ่มเติม"</string> <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"ไม่มีการตั้งปลุก"</string> + <!-- no translation found for accessibility_bouncer (5896923685673320070) --> + <skip /> <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"เซ็นเซอร์ลายนิ้วมือ"</string> <string name="accessibility_authenticate_hint" msgid="798914151813205721">"ตรวจสอบสิทธิ์"</string> <string name="accessibility_enter_hint" msgid="2617864063504824834">"เข้าถึงอุปกรณ์"</string> @@ -1107,7 +1110,8 @@ <string name="dream_overlay_status_bar_assistant_attention_indicator" msgid="4712565923771372690">"Assistant กำลังฟังอยู่"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{การแจ้งเตือน # รายการ}other{การแจ้งเตือน # รายการ}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <string name="note_task_button_label" msgid="8718616095800343136">"การจดบันทึก"</string> + <string name="note_task_button_label" msgid="230135078402003532">"การจดบันทึก"</string> + <string name="note_task_shortcut_long_label" msgid="7729325091147319409">"การจดบันทึก <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"กำลังออกอากาศ"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"หยุดการออกอากาศ <xliff:g id="APP_NAME">%1$s</xliff:g> ไหม"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"หากคุณออกอากาศ <xliff:g id="SWITCHAPP">%1$s</xliff:g> หรือเปลี่ยนแปลงเอาต์พุต การออกอากาศในปัจจุบันจะหยุดลง"</string> diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml index 81a78c9c11c4..39975395085f 100644 --- a/packages/SystemUI/res/values-tl/strings.xml +++ b/packages/SystemUI/res/values-tl/strings.xml @@ -243,7 +243,7 @@ <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string> <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Input"</string> - <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="3003338571871392293">"Mga Hearing Aid"</string> + <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Mga hearing aid"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Ino-on…"</string> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"I-auto rotate"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Awtomatikong i-rotate ang screen"</string> @@ -859,6 +859,7 @@ <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"I-magnify ang buong screen"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"I-magnify ang isang bahagi ng screen"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Buksan ang mga setting ng pag-magnify"</string> + <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Isara ang mga setting ng pag-magnify"</string> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"I-drag ang sulok para i-resize"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Payagan ang diagonal na pag-scroll"</string> <string name="accessibility_resize" msgid="5733759136600611551">"I-resize"</string> @@ -1045,6 +1046,7 @@ <string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Nagkaproblema sa pagbabasa ng iyong battery meter"</string> <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"I-tap para sa higit pang impormasyon"</string> <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Walang alarm"</string> + <string name="accessibility_bouncer" msgid="5896923685673320070">"ilagay ang lock ng screen"</string> <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Sensor para sa fingerprint"</string> <string name="accessibility_authenticate_hint" msgid="798914151813205721">"i-authenticate"</string> <string name="accessibility_enter_hint" msgid="2617864063504824834">"ilagay ang device"</string> @@ -1107,7 +1109,8 @@ <string name="dream_overlay_status_bar_assistant_attention_indicator" msgid="4712565923771372690">"Nakikinig ang Assistant"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# notification}one{# notification}other{# na notification}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <string name="note_task_button_label" msgid="8718616095800343136">"Pagtatala"</string> + <string name="note_task_button_label" msgid="230135078402003532">"Pagtatala"</string> + <string name="note_task_shortcut_long_label" msgid="7729325091147319409">"Pagtatala, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Nagbo-broadcast"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Ihinto ang pag-broadcast ng <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Kung magbo-broadcast ka ng <xliff:g id="SWITCHAPP">%1$s</xliff:g> o babaguhin mo ang output, hihinto ang iyong kasalukuyang broadcast"</string> diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml index 77570df8655a..395c07c7255d 100644 --- a/packages/SystemUI/res/values-tr/strings.xml +++ b/packages/SystemUI/res/values-tr/strings.xml @@ -243,7 +243,7 @@ <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Ses"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Mikrofonlu kulaklık"</string> <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Giriş"</string> - <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="3003338571871392293">"İşitme Cihazları"</string> + <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"İşitme cihazları"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Açılıyor…"</string> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Otomatik döndür"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Ekranı otomatik döndür"</string> @@ -859,6 +859,7 @@ <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Tam ekran büyütme"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Ekranın bir parçasını büyütün"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Büyütme ayarlarını aç"</string> + <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Büyütme ayarlarını kapat"</string> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Yeniden boyutlandırmak için köşeyi sürükleyin"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Çapraz kaydırmaya izin ver"</string> <string name="accessibility_resize" msgid="5733759136600611551">"Yeniden boyutlandır"</string> @@ -1045,6 +1046,8 @@ <string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Pil ölçeriniz okunurken sorun oluştu"</string> <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Daha fazla bilgi için dokunun"</string> <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Alarm yok"</string> + <!-- no translation found for accessibility_bouncer (5896923685673320070) --> + <skip /> <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Parmak izi sensörü"</string> <string name="accessibility_authenticate_hint" msgid="798914151813205721">"kimlik doğrulaması yapın"</string> <string name="accessibility_enter_hint" msgid="2617864063504824834">"cihaz girin"</string> @@ -1107,7 +1110,8 @@ <string name="dream_overlay_status_bar_assistant_attention_indicator" msgid="4712565923771372690">"Asistan dinliyor"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# bildirim}other{# bildirim}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <string name="note_task_button_label" msgid="8718616095800343136">"Not alma"</string> + <string name="note_task_button_label" msgid="230135078402003532">"Not alma"</string> + <string name="note_task_shortcut_long_label" msgid="7729325091147319409">"Not alma, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Yayınlama"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g> uygulamasında anons durdurulsun mu?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> uygulamasında anons yapar veya çıkışı değiştirirseniz mevcut anonsunuz duraklatılır"</string> diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml index 9d101fb99b9b..86b609bea3e3 100644 --- a/packages/SystemUI/res/values-uk/strings.xml +++ b/packages/SystemUI/res/values-uk/strings.xml @@ -243,7 +243,7 @@ <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Аудіопристрій"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Гарнітура"</string> <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Джерело сигналу"</string> - <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="3003338571871392293">"Слухові апарати"</string> + <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Слухові апарати"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Увімкнення…"</string> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Автообертання"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Автоматично обертати екран"</string> @@ -859,6 +859,7 @@ <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Збільшення всього екрана"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Збільшити частину екрана"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Відкрити налаштування збільшення"</string> + <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Закрити налаштування збільшення"</string> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Потягніть кут, щоб змінити розмір"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Дозволити прокручування по діагоналі"</string> <string name="accessibility_resize" msgid="5733759136600611551">"Змінити розмір"</string> @@ -1045,6 +1046,8 @@ <string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Не вдалось отримати дані про рівень заряду акумулятора"</string> <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Натисніть, щоб дізнатися більше"</string> <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Немає будильників"</string> + <!-- no translation found for accessibility_bouncer (5896923685673320070) --> + <skip /> <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Сканер відбитків пальців"</string> <string name="accessibility_authenticate_hint" msgid="798914151813205721">"пройти автентифікацію"</string> <string name="accessibility_enter_hint" msgid="2617864063504824834">"відкрити пристрій"</string> @@ -1107,7 +1110,8 @@ <string name="dream_overlay_status_bar_assistant_attention_indicator" msgid="4712565923771372690">"Асистент слухає"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# сповіщення}one{# сповіщення}few{# сповіщення}many{# сповіщень}other{# сповіщення}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <string name="note_task_button_label" msgid="8718616095800343136">"Створення нотаток"</string> + <string name="note_task_button_label" msgid="230135078402003532">"Створення нотаток"</string> + <string name="note_task_shortcut_long_label" msgid="7729325091147319409">"Створення нотаток, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Трансляція"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Зупинити трансляцію з додатка <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Якщо ви зміните додаток (<xliff:g id="SWITCHAPP">%1$s</xliff:g>) або аудіовихід, поточну трансляцію буде припинено"</string> diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml index fc20a88ba583..2069d26200ff 100644 --- a/packages/SystemUI/res/values-ur/strings.xml +++ b/packages/SystemUI/res/values-ur/strings.xml @@ -243,7 +243,7 @@ <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"آڈیو"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ہیڈ سیٹ"</string> <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"ان پٹ"</string> - <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="3003338571871392293">"سماعتی آلات"</string> + <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"سماعتی آلات"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"آن ہو رہا ہے…"</string> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"خود کار طور پر گھمائیں"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"اسکرین کو خود کار طور پر گھمائیں"</string> @@ -859,6 +859,7 @@ <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"فُل اسکرین کو بڑا کریں"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"اسکرین کا حصہ بڑا کریں"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"میگنیفکیشن کی ترتیبات کھولیں"</string> + <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"میگنیفکیشن کی ترتیبات بند کریں"</string> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"سائز تبدیل کرنے کے لیے کونے کو گھسیٹیں"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"وتری سکرولنگ کی اجازت دیں"</string> <string name="accessibility_resize" msgid="5733759136600611551">"سائز تبدیل کریں"</string> @@ -1045,6 +1046,8 @@ <string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"آپ کے بیٹری میٹر کو پڑھنے میں دشواری"</string> <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"مزید معلومات کے لیے تھپتھپائیں"</string> <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"کوئی الارم سیٹ نہیں ہے"</string> + <!-- no translation found for accessibility_bouncer (5896923685673320070) --> + <skip /> <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"فنگر پرنٹ سینسر"</string> <string name="accessibility_authenticate_hint" msgid="798914151813205721">"تصدیق کریں"</string> <string name="accessibility_enter_hint" msgid="2617864063504824834">"آلہ درج کریں"</string> @@ -1107,7 +1110,8 @@ <string name="dream_overlay_status_bar_assistant_attention_indicator" msgid="4712565923771372690">"اسسٹنٹ سن رہی ہے"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# اطلاع}other{# اطلاعات}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>، <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <string name="note_task_button_label" msgid="8718616095800343136">"نوٹ لینا"</string> + <string name="note_task_button_label" msgid="230135078402003532">"نوٹ لکھنا"</string> + <string name="note_task_shortcut_long_label" msgid="7729325091147319409">"نوٹ لکھنا، <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"نشریات"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g> براڈکاسٹنگ روکیں؟"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"اگر آپ <xliff:g id="SWITCHAPP">%1$s</xliff:g> براڈکاسٹ کرتے ہیں یا آؤٹ پٹ کو تبدیل کرتے ہیں تو آپ کا موجودہ براڈکاسٹ رک جائے گا"</string> diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml index 31ba2ba23c94..e6a941412890 100644 --- a/packages/SystemUI/res/values-uz/strings.xml +++ b/packages/SystemUI/res/values-uz/strings.xml @@ -243,7 +243,7 @@ <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Garnitura"</string> <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Kirish"</string> - <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="3003338571871392293">"Eshitish apparatlari"</string> + <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Eshitish moslamalari"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Yoqilmoqda…"</string> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Avto-burilish"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Ekranning avtomatik burilishi"</string> @@ -859,6 +859,7 @@ <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Ekranni toʻliq kattalashtirish"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Ekran qismini kattalashtirish"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Kattalashtirish sozlamalarini ochish"</string> + <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Kattalashtirish sozlamalarini yopish"</string> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Oʻlchamini oʻzgartirish uchun burchakni torting"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Diagonal aylantirishga ruxsat berish"</string> <string name="accessibility_resize" msgid="5733759136600611551">"Oʻlchamini oʻzgartirish"</string> @@ -1045,6 +1046,8 @@ <string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Batareya quvvati aniqlanmadi"</string> <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Batafsil axborot olish uchun bosing"</string> <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Signal sozlanmagan"</string> + <!-- no translation found for accessibility_bouncer (5896923685673320070) --> + <skip /> <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Barmoq izi skaneri"</string> <string name="accessibility_authenticate_hint" msgid="798914151813205721">"autentifikatsiya"</string> <string name="accessibility_enter_hint" msgid="2617864063504824834">"qurilmani ochish"</string> @@ -1107,7 +1110,8 @@ <string name="dream_overlay_status_bar_assistant_attention_indicator" msgid="4712565923771372690">"Assistent tinglamoqda"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# ta bildirishnoma}other{# ta bildirishnoma}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <string name="note_task_button_label" msgid="8718616095800343136">"Eslatma yozish"</string> + <string name="note_task_button_label" msgid="230135078402003532">"Qayd olish"</string> + <string name="note_task_shortcut_long_label" msgid="7729325091147319409">"<xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>: qayd olish"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Signal uzatish"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g> ilovasiga translatsiya toʻxtatilsinmi?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Agar <xliff:g id="SWITCHAPP">%1$s</xliff:g> ilovasiga translatsiya qilsangiz yoki ovoz chiqishini oʻzgartirsangiz, joriy translatsiya toʻxtab qoladi"</string> diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml index d30a488bcaf0..33ae91ea0689 100644 --- a/packages/SystemUI/res/values-vi/strings.xml +++ b/packages/SystemUI/res/values-vi/strings.xml @@ -243,7 +243,7 @@ <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Âm thanh"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Tai nghe"</string> <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Thiết bị đầu vào"</string> - <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="3003338571871392293">"Thiết bị trợ thính"</string> + <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Thiết bị trợ thính"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Đang bật…"</string> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Tự động xoay"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Tự động xoay màn hình"</string> @@ -859,6 +859,7 @@ <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Phóng to toàn màn hình"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Phóng to một phần màn hình"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Mở chế độ cài đặt phóng to"</string> + <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Đóng bảng cài đặt tính năng phóng to"</string> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Kéo góc để thay đổi kích thước"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Cho phép cuộn chéo"</string> <string name="accessibility_resize" msgid="5733759136600611551">"Đổi kích thước"</string> @@ -1045,6 +1046,8 @@ <string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Đã xảy ra vấn đề khi đọc dung lượng pin của bạn"</string> <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Nhấn để biết thêm thông tin"</string> <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Chưa đặt chuông báo"</string> + <!-- no translation found for accessibility_bouncer (5896923685673320070) --> + <skip /> <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Cảm biến vân tay"</string> <string name="accessibility_authenticate_hint" msgid="798914151813205721">"xác thực"</string> <string name="accessibility_enter_hint" msgid="2617864063504824834">"truy cập thiết bị"</string> @@ -1107,7 +1110,8 @@ <string name="dream_overlay_status_bar_assistant_attention_indicator" msgid="4712565923771372690">"Trợ lý đang nghe bạn nói"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# thông báo}other{# thông báo}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <string name="note_task_button_label" msgid="8718616095800343136">"Ghi chú"</string> + <string name="note_task_button_label" msgid="230135078402003532">"Ghi chú"</string> + <string name="note_task_shortcut_long_label" msgid="7729325091147319409">"Ghi chú, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Phát sóng"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Dừng phát <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Nếu bạn phát <xliff:g id="SWITCHAPP">%1$s</xliff:g> hoặc thay đổi đầu ra, phiên truyền phát hiện tại sẽ dừng"</string> diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml index 18fc39692b82..e167c614c777 100644 --- a/packages/SystemUI/res/values-zh-rCN/strings.xml +++ b/packages/SystemUI/res/values-zh-rCN/strings.xml @@ -243,7 +243,7 @@ <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"音频"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"耳机"</string> <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"输入"</string> - <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="3003338571871392293">"助听器"</string> + <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"助听器"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"正在开启…"</string> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"自动屏幕旋转"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"自动旋转屏幕"</string> @@ -859,6 +859,7 @@ <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"放大整个屏幕"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"放大部分屏幕"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"打开放大功能设置"</string> + <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"关闭放大设置"</string> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"拖动一角即可调整大小"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"允许沿对角线滚动"</string> <string name="accessibility_resize" msgid="5733759136600611551">"调整大小"</string> @@ -1045,6 +1046,7 @@ <string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"读取电池计量器时出现问题"</string> <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"点按即可了解详情"</string> <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"未设置闹钟"</string> + <string name="accessibility_bouncer" msgid="5896923685673320070">"输入屏幕解锁信息"</string> <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"指纹传感器"</string> <string name="accessibility_authenticate_hint" msgid="798914151813205721">"身份验证"</string> <string name="accessibility_enter_hint" msgid="2617864063504824834">"进入设备"</string> @@ -1107,7 +1109,8 @@ <string name="dream_overlay_status_bar_assistant_attention_indicator" msgid="4712565923771372690">"Google 助理正在聆听"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# 条通知}other{# 条通知}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>,<xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <string name="note_task_button_label" msgid="8718616095800343136">"记录"</string> + <string name="note_task_button_label" msgid="230135078402003532">"记事"</string> + <string name="note_task_shortcut_long_label" msgid="7729325091147319409">"记事,<xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"正在广播"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"要停止广播“<xliff:g id="APP_NAME">%1$s</xliff:g>”的内容吗?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"如果广播“<xliff:g id="SWITCHAPP">%1$s</xliff:g>”的内容或更改输出来源,当前的广播就会停止"</string> diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml index 76153b60698a..6c6f5042523d 100644 --- a/packages/SystemUI/res/values-zh-rHK/strings.xml +++ b/packages/SystemUI/res/values-zh-rHK/strings.xml @@ -243,7 +243,7 @@ <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"音訊"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"耳機"</string> <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"輸入"</string> - <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="3003338571871392293">"助聽器"</string> + <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"助聽器"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"正在開啟…"</string> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"自動旋轉"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"自動旋轉螢幕"</string> @@ -859,6 +859,7 @@ <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"放大成個畫面"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"放大部分螢幕畫面"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"開啟放大設定"</string> + <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"關閉放大設定"</string> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"拖曳角落即可調整大小"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"允許斜角捲動"</string> <string name="accessibility_resize" msgid="5733759136600611551">"調整大小"</string> @@ -1045,6 +1046,7 @@ <string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"讀取電池計量器時發生問題"</string> <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"輕按即可瞭解詳情"</string> <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"未設定鬧鐘"</string> + <string name="accessibility_bouncer" msgid="5896923685673320070">"輸入螢幕鎖定憑證"</string> <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"指紋感應器"</string> <string name="accessibility_authenticate_hint" msgid="798914151813205721">"驗證"</string> <string name="accessibility_enter_hint" msgid="2617864063504824834">"進入裝置"</string> @@ -1107,7 +1109,8 @@ <string name="dream_overlay_status_bar_assistant_attention_indicator" msgid="4712565923771372690">"「Google 助理」正在聆聽"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# 則通知}other{# 則通知}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>,<xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <string name="note_task_button_label" msgid="8718616095800343136">"做筆記"</string> + <string name="note_task_button_label" msgid="230135078402003532">"做筆記"</string> + <string name="note_task_shortcut_long_label" msgid="7729325091147319409">"做筆記,<xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"廣播"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"要停止廣播「<xliff:g id="APP_NAME">%1$s</xliff:g>」的內容嗎?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"如要廣播「<xliff:g id="SWITCHAPP">%1$s</xliff:g>」的內容或變更輸出來源,系統就會停止廣播目前的內容"</string> diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml index a5e00c68c80e..888908aaa8ac 100644 --- a/packages/SystemUI/res/values-zh-rTW/strings.xml +++ b/packages/SystemUI/res/values-zh-rTW/strings.xml @@ -243,7 +243,7 @@ <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"音訊"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"耳機"</string> <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"輸入"</string> - <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="3003338571871392293">"助聽器"</string> + <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"助聽器"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"開啟中…"</string> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"自動旋轉"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"自動旋轉螢幕"</string> @@ -859,6 +859,7 @@ <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"放大整個螢幕畫面"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"放大局部螢幕畫面"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"開啟放大功能設定"</string> + <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"關閉放大設定"</string> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"拖曳角落即可調整大小"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"允許沿對角線捲動"</string> <string name="accessibility_resize" msgid="5733759136600611551">"調整大小"</string> @@ -1045,6 +1046,7 @@ <string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"讀取電池計量器時發生問題"</string> <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"輕觸即可瞭解詳情"</string> <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"未設定鬧鐘"</string> + <string name="accessibility_bouncer" msgid="5896923685673320070">"輸入螢幕鎖定憑證"</string> <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"指紋感應器"</string> <string name="accessibility_authenticate_hint" msgid="798914151813205721">"驗證"</string> <string name="accessibility_enter_hint" msgid="2617864063504824834">"進入裝置"</string> @@ -1107,7 +1109,8 @@ <string name="dream_overlay_status_bar_assistant_attention_indicator" msgid="4712565923771372690">"Google 助理正在聆聽"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# 則通知}other{# 則通知}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>,<xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <string name="note_task_button_label" msgid="8718616095800343136">"做筆記"</string> + <string name="note_task_button_label" msgid="230135078402003532">"做筆記"</string> + <string name="note_task_shortcut_long_label" msgid="7729325091147319409">"做筆記,<xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"廣播"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"要停止播送「<xliff:g id="APP_NAME">%1$s</xliff:g>」的內容嗎?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"如果播送「<xliff:g id="SWITCHAPP">%1$s</xliff:g>」的內容或變更輸出來源,系統就會停止播送目前的內容"</string> diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml index 74a688407b5a..e57e60b9a6f4 100644 --- a/packages/SystemUI/res/values-zu/strings.xml +++ b/packages/SystemUI/res/values-zu/strings.xml @@ -243,7 +243,7 @@ <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Umsindo"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Ihedisethi"</string> <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Okokufaka"</string> - <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="3003338571871392293">"Izinsiza zokuzwa"</string> + <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Imishini yendlebe"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Iyavula..."</string> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Ukuphenduka okuzenzakalelayo"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Phendula iskrini ngokuzenzakalela"</string> @@ -859,6 +859,7 @@ <string name="magnification_mode_switch_state_full_screen" msgid="5229653514979530561">"Khulisa isikrini esigcwele"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Khulisa ingxenye eyesikrini"</string> <string name="magnification_open_settings_click_label" msgid="6151849212725923363">"Vula amasethingi okukhuliswa"</string> + <string name="magnification_close_settings_click_label" msgid="4642477260651704517">"Vala amasethingi okukhuliswa"</string> <string name="magnification_drag_corner_to_resize" msgid="1249766311052418130">"Hudula ikhona ukuze usayize kabusha"</string> <string name="accessibility_allow_diagonal_scrolling" msgid="3258050349191496398">"Vumela ukuskrola oku-diagonal"</string> <string name="accessibility_resize" msgid="5733759136600611551">"Shintsha usayizi"</string> @@ -1045,6 +1046,8 @@ <string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Kube khona inkinga ngokufunda imitha yakho yebhethri"</string> <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Thepha ukuze uthole olunye ulwazi"</string> <string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Akukho alamu esethiwe"</string> + <!-- no translation found for accessibility_bouncer (5896923685673320070) --> + <skip /> <string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Inzwa yesigxivizo somunwe"</string> <string name="accessibility_authenticate_hint" msgid="798914151813205721">"gunyaza"</string> <string name="accessibility_enter_hint" msgid="2617864063504824834">"faka idivayisi"</string> @@ -1107,7 +1110,8 @@ <string name="dream_overlay_status_bar_assistant_attention_indicator" msgid="4712565923771372690">"I-Assistant ilalele"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{Isaziso esingu-#}one{Izaziso ezingu-#}other{Izaziso ezingu-#}}"</string> <string name="dream_overlay_weather_complication_desc" msgid="824503662089783824">"<xliff:g id="WEATHER_CONDITION">%1$s</xliff:g>, <xliff:g id="TEMPERATURE">%2$s</xliff:g>"</string> - <string name="note_task_button_label" msgid="8718616095800343136">"Ukuthatha amanothi"</string> + <string name="note_task_button_label" msgid="230135078402003532">"Ukuthatha amanothi"</string> + <string name="note_task_shortcut_long_label" msgid="7729325091147319409">"Ukuthatha amanothi, <xliff:g id="NOTE_TAKING_APP">%1$s</xliff:g>"</string> <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Ukusakaza"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Misa ukusakaza i-<xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Uma usakaza i-<xliff:g id="SWITCHAPP">%1$s</xliff:g> noma ushintsha okuphumayo, ukusakaza kwakho kwamanje kuzoma"</string> diff --git a/packages/SystemUI/res/values/attrs.xml b/packages/SystemUI/res/values/attrs.xml index bd86e51ccf8f..3a1d1a8cbf9d 100644 --- a/packages/SystemUI/res/values/attrs.xml +++ b/packages/SystemUI/res/values/attrs.xml @@ -216,6 +216,8 @@ <attr name="progress" format="integer" /> <attr name="iconStartContentDescription" format="reference" /> <attr name="iconEndContentDescription" format="reference" /> + <attr name="tickMark" format="reference" /> + <attr name="seekBarChangeMagnitude" format="integer" /> </declare-styleable> </resources> diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index 166bd2ac4439..421f41f60d85 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -302,9 +302,6 @@ <!-- Determines whether the shell features all run on another thread. --> <bool name="config_enableShellMainThread">true</bool> - <!-- SystemUIFactory component --> - <string name="config_systemUIFactoryComponent" translatable="false">com.android.systemui.SystemUIInitializerImpl</string> - <!-- QS tile shape store width. negative implies fill configuration instead of stroke--> <dimen name="config_qsTileStrokeWidthActive">-1dp</dimen> <dimen name="config_qsTileStrokeWidthInactive">-1dp</dimen> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 4f768cc39b40..da6417d142d0 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -275,6 +275,9 @@ <!-- The padding at start and end of indication text shown on AOD --> <dimen name="keyguard_indication_text_padding">16dp</dimen> + <!-- The min height on the indication text shown on AOD --> + <dimen name="keyguard_indication_text_min_height">48dp</dimen> + <!-- Shadows under the clock, date and other keyguard text fields --> <dimen name="keyguard_shadow_radius">5</dimen> diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml index e5c946156e46..d651a2159721 100644 --- a/packages/SystemUI/res/values/ids.xml +++ b/packages/SystemUI/res/values/ids.xml @@ -206,5 +206,9 @@ <!-- keyboard backlight indicator--> <item type="id" name="backlight_icon" /> -</resources> + <item type="id" name="keyguard_root_view" /> + <item type="id" name="keyguard_indication_area" /> + <item type="id" name="keyguard_indication_text" /> + <item type="id" name="keyguard_indication_text_bottom" /> +</resources> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 663efea1944b..25f3d224dc4d 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -613,8 +613,8 @@ <string name="quick_settings_bluetooth_secondary_label_headset">Headset</string> <!-- QuickSettings: Bluetooth secondary label for an input/IO device being connected [CHAR LIMIT=20]--> <string name="quick_settings_bluetooth_secondary_label_input">Input</string> - <!-- QuickSettings: Bluetooth secondary label for a Hearing Aids device being connected [CHAR LIMIT=20]--> - <string name="quick_settings_bluetooth_secondary_label_hearing_aids">Hearing Aids</string> + <!-- QuickSettings: Bluetooth secondary label for a Hearing aids device being connected [CHAR LIMIT=20]--> + <string name="quick_settings_bluetooth_secondary_label_hearing_aids">Hearing aids</string> <!-- QuickSettings: Bluetooth secondary label shown when bluetooth is being enabled [CHAR LIMIT=NONE] --> <string name="quick_settings_bluetooth_secondary_label_transient">Turning on…</string> <!-- QuickSettings: Brightness [CHAR LIMIT=NONE] --> @@ -2390,6 +2390,8 @@ <string name="magnification_mode_switch_state_window">Magnify part of screen</string> <!-- Click action label for magnification settings panel. [CHAR LIMIT=NONE] --> <string name="magnification_open_settings_click_label">Open magnification settings</string> + <!-- Click action label for magnification settings panel. [CHAR LIMIT=NONE] --> + <string name="magnification_close_settings_click_label">Close magnification settings</string> <!-- Label of the corner of a rectangle that you can tap and drag to resize the magnification area. [CHAR LIMIT=NONE] --> <string name="magnification_drag_corner_to_resize">Drag corner to resize</string> @@ -2815,6 +2817,8 @@ <!-- Secondary label for alarm tile when there is no next alarm information [CHAR LIMIT=20] --> <string name="qs_alarm_tile_no_alarm">No alarm set</string> + <!-- Accessibility label for a11y action to show the bouncer (pin/pattern/password) screen lock [CHAR LIMIT=NONE] --> + <string name="accessibility_bouncer">enter screen lock</string> <!-- Accessibility label for fingerprint sensor [CHAR LIMIT=NONE] --> <string name="accessibility_fingerprint_label">Fingerprint sensor</string> <!-- Accessibility action for tapping on an affordance that will bring up the user's @@ -2971,9 +2975,11 @@ <xliff:g id="weather_condition" example="Partly cloudy">%1$s</xliff:g>, <xliff:g id="temperature" example="7°C">%2$s</xliff:g> </string> - <!-- TODO(b/259369672): Replace with final resource. --> <!-- [CHAR LIMIT=30] Label used to open Note Task --> - <string name="note_task_button_label">Notetaking</string> + <string name="note_task_button_label">Note-taking</string> + + <!-- [CHAR LIMIT=25] Long label used by Note Task Shortcut --> + <string name="note_task_shortcut_long_label">Note-taking, <xliff:g id="note_taking_app" example="Note-taking App">%1$s</xliff:g></string> <!-- [CHAR LIMIT=NONE] Le audio broadcast dialog, media app is broadcasting --> <string name="broadcasting_description_is_broadcasting">Broadcasting</string> diff --git a/packages/SystemUI/res/xml/combined_qs_header_scene.xml b/packages/SystemUI/res/xml/combined_qs_header_scene.xml index 8512f6fada43..c16725682a82 100644 --- a/packages/SystemUI/res/xml/combined_qs_header_scene.xml +++ b/packages/SystemUI/res/xml/combined_qs_header_scene.xml @@ -53,56 +53,28 @@ android:alpha="0" /> <KeyPosition + app:motionTarget="@id/shade_header_system_icons" app:keyPositionType="deltaRelative" app:percentX="0" app:percentY="@dimen/percent_displacement_at_fade_out" app:framePosition="@integer/fade_out_complete_frame" app:sizePercent="0" - app:curveFit="linear" - app:motionTarget="@id/statusIcons" /> + app:curveFit="linear" /> <KeyPosition + app:motionTarget="@id/shade_header_system_icons" app:keyPositionType="deltaRelative" app:percentX="1" app:percentY="0.5" app:framePosition="50" app:sizePercent="1" - app:curveFit="linear" - app:motionTarget="@id/statusIcons" /> - <KeyAttribute - app:motionTarget="@id/statusIcons" - app:framePosition="@integer/fade_out_complete_frame" - android:alpha="0" - /> - <KeyAttribute - app:motionTarget="@id/statusIcons" - app:framePosition="@integer/fade_in_start_frame" - android:alpha="0" - /> - <KeyPosition - app:keyPositionType="deltaRelative" - app:percentX="0" - app:percentY="@dimen/percent_displacement_at_fade_out" - app:framePosition="@integer/fade_out_complete_frame" - app:percentWidth="1" - app:percentHeight="1" - app:curveFit="linear" - app:motionTarget="@id/batteryRemainingIcon" /> - <KeyPosition - app:keyPositionType="deltaRelative" - app:percentX="1" - app:percentY="0.5" - app:framePosition="50" - app:percentWidth="1" - app:percentHeight="1" - app:curveFit="linear" - app:motionTarget="@id/batteryRemainingIcon" /> + app:curveFit="linear" /> <KeyAttribute - app:motionTarget="@id/batteryRemainingIcon" + app:motionTarget="@id/shade_header_system_icons" app:framePosition="@integer/fade_out_complete_frame" android:alpha="0" /> <KeyAttribute - app:motionTarget="@id/batteryRemainingIcon" + app:motionTarget="@id/shade_header_system_icons" app:framePosition="@integer/fade_in_start_frame" android:alpha="0" /> diff --git a/packages/SystemUI/res/xml/large_screen_shade_header.xml b/packages/SystemUI/res/xml/large_screen_shade_header.xml index bf576dc5790b..39f4c81b6dbe 100644 --- a/packages/SystemUI/res/xml/large_screen_shade_header.xml +++ b/packages/SystemUI/res/xml/large_screen_shade_header.xml @@ -45,7 +45,7 @@ android:layout_height="0dp" android:layout_gravity="end|center_vertical" app:layout_constraintBottom_toBottomOf="parent" - app:layout_constraintEnd_toStartOf="@id/statusIcons" + app:layout_constraintEnd_toStartOf="@id/shade_header_system_icons" app:layout_constraintStart_toEndOf="@id/date" app:layout_constraintTop_toTopOf="parent" app:layout_constraintWidth_default="wrap" @@ -53,28 +53,17 @@ <PropertySet android:alpha="1" /> </Constraint> - <Constraint android:id="@+id/statusIcons"> + <Constraint android:id="@+id/shade_header_system_icons"> <Layout android:layout_width="wrap_content" android:layout_height="@dimen/large_screen_shade_header_min_height" app:layout_constraintBottom_toBottomOf="parent" - app:layout_constraintEnd_toStartOf="@id/batteryRemainingIcon" + app:layout_constraintEnd_toStartOf="@id/privacy_container" app:layout_constraintTop_toTopOf="parent" app:layout_constraintEnd_toEndOf="@id/carrier_group"/> <PropertySet android:alpha="1" /> </Constraint> - <Constraint android:id="@+id/batteryRemainingIcon"> - <Layout - android:layout_width="wrap_content" - android:layout_height="0dp" - app:layout_constraintHeight_min="@dimen/large_screen_shade_header_min_height" - app:layout_constraintBottom_toBottomOf="parent" - app:layout_constraintEnd_toStartOf="@id/privacy_container" - app:layout_constraintTop_toTopOf="parent" /> - <PropertySet android:alpha="1" /> - </Constraint> - <Constraint android:id="@+id/privacy_container"> <Layout android:layout_width="wrap_content" diff --git a/packages/SystemUI/res/xml/qqs_header.xml b/packages/SystemUI/res/xml/qqs_header.xml index 1950965fc298..50a388d0fa64 100644 --- a/packages/SystemUI/res/xml/qqs_header.xml +++ b/packages/SystemUI/res/xml/qqs_header.xml @@ -54,27 +54,12 @@ </Constraint> <Constraint - android:id="@+id/statusIcons"> + android:id="@+id/shade_header_system_icons"> <Layout android:layout_width="wrap_content" android:layout_height="@dimen/new_qs_header_non_clickable_element_height" app:layout_constraintHeight_min="@dimen/new_qs_header_non_clickable_element_height" app:layout_constraintStart_toEndOf="@id/date" - app:layout_constraintEnd_toStartOf="@id/batteryRemainingIcon" - app:layout_constraintTop_toTopOf="parent" - app:layout_constraintBottom_toBottomOf="@id/qqs_header_bottom_guideline" - app:layout_constraintHorizontal_bias="1" - app:layout_constraintHorizontal_chainStyle="packed" - /> - </Constraint> - - <Constraint - android:id="@+id/batteryRemainingIcon"> - <Layout - android:layout_width="wrap_content" - android:layout_height="@dimen/new_qs_header_non_clickable_element_height" - app:layout_constraintHeight_min="@dimen/new_qs_header_non_clickable_element_height" - app:layout_constraintStart_toEndOf="@id/statusIcons" app:layout_constraintEnd_toEndOf="@id/end_guide" app:layout_constraintTop_toTopOf="parent" app:layout_constraintBottom_toBottomOf="@id/qqs_header_bottom_guideline" diff --git a/packages/SystemUI/res/xml/qs_header.xml b/packages/SystemUI/res/xml/qs_header.xml index 8039c68485ca..7b4282f049b8 100644 --- a/packages/SystemUI/res/xml/qs_header.xml +++ b/packages/SystemUI/res/xml/qs_header.xml @@ -59,7 +59,7 @@ /> </Constraint> - <!-- LargeScreenShadeHeaderController helps with managing clock width to layout this view --> + <!-- ShadeHeaderController helps with managing clock width to layout this view --> <Constraint android:id="@+id/carrier_group"> <Layout @@ -78,25 +78,11 @@ </Constraint> <Constraint - android:id="@+id/statusIcons"> + android:id="@+id/shade_header_system_icons"> <Layout android:layout_width="0dp" android:layout_height="@dimen/new_qs_header_non_clickable_element_height" app:layout_constraintWidth_default="wrap" - app:layout_constraintStart_toEndOf="@id/date" - app:layout_constraintEnd_toStartOf="@id/batteryRemainingIcon" - app:layout_constraintTop_toTopOf="@id/date" - app:layout_constraintBottom_toBottomOf="@id/date" - /> - </Constraint> - - <Constraint - android:id="@+id/batteryRemainingIcon"> - <Layout - android:layout_width="0dp" - android:layout_height="@dimen/new_qs_header_non_clickable_element_height" - app:layout_constraintWidth_default="wrap" - app:layout_constraintHeight_min="@dimen/new_qs_header_non_clickable_element_height" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toTopOf="@id/date" app:layout_constraintBottom_toBottomOf="@id/date" diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/condition/Monitor.java b/packages/SystemUI/shared/src/com/android/systemui/shared/condition/Monitor.java index 1f61c64dd057..97d6099227e7 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/condition/Monitor.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/condition/Monitor.java @@ -215,7 +215,7 @@ public class Monitor { mSubscriptions.put(token, state); // Add and associate conditions. - normalizedCondition.getConditions().stream().forEach(condition -> { + normalizedCondition.getConditions().forEach(condition -> { if (!mConditions.containsKey(condition)) { mConditions.put(condition, new ArraySet<>()); condition.addCallback(mConditionCallback); @@ -321,7 +321,6 @@ public class Monitor { private final Callback mCallback; private final Subscription mNestedSubscription; private final ArraySet<Condition> mConditions; - private final ArraySet<Condition> mPreconditions; /** * Default constructor specifying the {@link Callback} for the {@link Subscription}. @@ -337,8 +336,7 @@ public class Monitor { private Builder(Subscription nestedSubscription, Callback callback) { mNestedSubscription = nestedSubscription; mCallback = callback; - mConditions = new ArraySet(); - mPreconditions = new ArraySet(); + mConditions = new ArraySet<>(); } /** @@ -352,29 +350,6 @@ public class Monitor { } /** - * Adds a set of {@link Condition} to be a precondition for {@link Subscription}. - * - * @return The updated {@link Builder}. - */ - public Builder addPreconditions(Set<Condition> condition) { - if (condition == null) { - return this; - } - mPreconditions.addAll(condition); - return this; - } - - /** - * Adds a {@link Condition} to be a precondition for {@link Subscription}. - * - * @return The updated {@link Builder}. - */ - public Builder addPrecondition(Condition condition) { - mPreconditions.add(condition); - return this; - } - - /** * Adds a set of {@link Condition} to be associated with the {@link Subscription}. * * @return The updated {@link Builder}. @@ -394,11 +369,7 @@ public class Monitor { * @return The resulting {@link Subscription}. */ public Subscription build() { - final Subscription subscription = - new Subscription(mConditions, mCallback, mNestedSubscription); - return !mPreconditions.isEmpty() - ? new Subscription(mPreconditions, null, subscription) - : subscription; + return new Subscription(mConditions, mCallback, mNestedSubscription); } } } diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl index 117cf78a4fe3..4b31498e659b 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl @@ -48,7 +48,7 @@ interface ISystemUiProxy { * * Normal gesture: DOWN, MOVE/POINTER_DOWN/POINTER_UP)*, UP or CANCLE */ - oneway void onStatusBarMotionEvent(in MotionEvent event) = 9; + oneway void onStatusBarTouchEvent(in MotionEvent event) = 9; /** * Proxies the assistant gesture's progress started from navigation bar. @@ -125,5 +125,15 @@ interface ISystemUiProxy { */ oneway void takeScreenshot(in ScreenshotRequest request) = 51; - // Next id = 52 + /** + * Dispatches trackpad status bar motion event to the notification shade. Currently these events + * are from the input monitor in {@link TouchInteractionService}. This is different from + * {@link #onStatusBarTouchEvent} above in that, this directly dispatches motion events to the + * notification shade, while {@link #onStatusBarTouchEvent} relies on setting the launcher + * window slippery to allow the frameworks to route those events after passing the initial + * threshold. + */ + oneway void onStatusBarTrackpadEvent(in MotionEvent event) = 52; + + // Next id = 53 } diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java index 751a3f8458bd..2e6c485336f3 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java @@ -104,7 +104,8 @@ public class Utilities { * @return updated set of flags from InputMethodService based off {@param oldHints} * Leaves original hints unmodified */ - public static int calculateBackDispositionHints(int oldHints, int backDisposition, + public static int calculateBackDispositionHints(int oldHints, + @InputMethodService.BackDispositionMode int backDisposition, boolean imeShown, boolean showImeSwitcher) { int hints = oldHints; switch (backDisposition) { diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/regionsampling/RegionSampler.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/regionsampling/RegionSampler.kt index 9a0044761504..0fbeb1a054a7 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/regionsampling/RegionSampler.kt +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/regionsampling/RegionSampler.kt @@ -202,8 +202,6 @@ constructor( fun calculateScreenLocation(sampledView: View): RectF? { - if (!sampledView.isLaidOut) return null - val screenLocation = tmpScreenLocation /** * The method getLocationOnScreen is used to obtain the view coordinates relative to its @@ -219,6 +217,10 @@ constructor( samplingBounds.right = left + sampledView.width samplingBounds.bottom = top + sampledView.height + // ensure never go out of bounds + if (samplingBounds.right > displaySize.x) samplingBounds.right = displaySize.x + if (samplingBounds.bottom > displaySize.y) samplingBounds.bottom = displaySize.y + return RectF(samplingBounds) } @@ -263,6 +265,8 @@ constructor( (colors?.colorHints?.and(WallpaperColors.HINT_SUPPORTS_DARK_TEXT)) != WallpaperColors.HINT_SUPPORTS_DARK_TEXT ) + if (DEBUG) + Log.d(TAG, "onColorsChanged() | region darkness = $regionDarkness for region $area") updateForegroundColor() } diff --git a/packages/SystemUI/src/com/android/keyguard/BouncerKeyguardMessageArea.kt b/packages/SystemUI/src/com/android/keyguard/BouncerKeyguardMessageArea.kt index 084295343bb0..78a5c98f45c8 100644 --- a/packages/SystemUI/src/com/android/keyguard/BouncerKeyguardMessageArea.kt +++ b/packages/SystemUI/src/com/android/keyguard/BouncerKeyguardMessageArea.kt @@ -26,7 +26,7 @@ import android.util.AttributeSet import android.view.View import com.android.app.animation.Interpolators import com.android.settingslib.Utils -import com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants.ColorId.TITLE +import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants.ColorId.TITLE /** Displays security messages for the keyguard bouncer. */ open class BouncerKeyguardMessageArea(context: Context?, attrs: AttributeSet?) : diff --git a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt index 8ea4c31a5ac9..84a2c25999a0 100644 --- a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt +++ b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt @@ -15,18 +15,18 @@ */ package com.android.keyguard -import android.app.WallpaperManager import android.content.BroadcastReceiver import android.content.Context import android.content.Intent import android.content.IntentFilter import android.content.res.Resources import android.text.format.DateFormat -import android.util.TypedValue import android.util.Log +import android.util.TypedValue import android.view.View import android.view.View.OnAttachStateChangeListener import android.view.ViewTreeObserver +import android.widget.FrameLayout import androidx.annotation.VisibleForTesting import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle @@ -99,6 +99,28 @@ constructor( if (!regionSamplingEnabled) { updateColors() + } else { + clock?.let { + smallRegionSampler = createRegionSampler( + it.smallClock.view, + mainExecutor, + bgExecutor, + regionSamplingEnabled, + isLockscreen = true, + ::updateColors + )?.apply { startRegionSampler() } + + largeRegionSampler = createRegionSampler( + it.largeClock.view, + mainExecutor, + bgExecutor, + regionSamplingEnabled, + isLockscreen = true, + ::updateColors + )?.apply { startRegionSampler() } + + updateColors() + } } updateFontSizes() updateTimeListeners() @@ -110,8 +132,25 @@ constructor( } value.smallClock.view.addOnAttachStateChangeListener( object : OnAttachStateChangeListener { - override fun onViewAttachedToWindow(p0: View?) { + var pastVisibility: Int? = null + override fun onViewAttachedToWindow(view: View?) { value.events.onTimeFormatChanged(DateFormat.is24HourFormat(context)) + if (view != null) { + val smallClockFrame = view.parent as FrameLayout + pastVisibility = smallClockFrame.visibility + smallClockFrame.viewTreeObserver.addOnGlobalLayoutListener( + ViewTreeObserver.OnGlobalLayoutListener { + val currentVisibility = smallClockFrame.visibility + if (pastVisibility != currentVisibility) { + pastVisibility = currentVisibility + // when small clock visible, recalculate bounds and sample + if (currentVisibility == View.VISIBLE) { + smallRegionSampler?.stopRegionSampler() + smallRegionSampler?.startRegionSampler() + } + } + }) + } } override fun onViewDetachedFromWindow(p0: View?) { @@ -141,21 +180,19 @@ constructor( private fun updateColors() { - val wallpaperManager = WallpaperManager.getInstance(context) if (regionSamplingEnabled) { - regionSampler?.let { regionSampler -> - clock?.let { clock -> - if (regionSampler.sampledView == clock.smallClock.view) { - smallClockIsDark = regionSampler.currentRegionDarkness().isDark - clock.smallClock.events.onRegionDarknessChanged(smallClockIsDark) - return@updateColors - } else if (regionSampler.sampledView == clock.largeClock.view) { - largeClockIsDark = regionSampler.currentRegionDarkness().isDark - clock.largeClock.events.onRegionDarknessChanged(largeClockIsDark) - return@updateColors - } + clock?.let { clock -> + smallRegionSampler?.let { + smallClockIsDark = it.currentRegionDarkness().isDark + clock.smallClock.events.onRegionDarknessChanged(smallClockIsDark) + } + + largeRegionSampler?.let { + largeClockIsDark = it.currentRegionDarkness().isDark + clock.largeClock.events.onRegionDarknessChanged(largeClockIsDark) } } + return } val isLightTheme = TypedValue() @@ -168,23 +205,6 @@ constructor( largeClock.events.onRegionDarknessChanged(largeClockIsDark) } } - - private fun updateRegionSampler(sampledRegion: View) { - regionSampler?.stopRegionSampler() - regionSampler = - createRegionSampler( - sampledRegion, - mainExecutor, - bgExecutor, - regionSamplingEnabled, - isLockscreen = true, - ::updateColors - ) - ?.apply { startRegionSampler() } - - updateColors() - } - protected open fun createRegionSampler( sampledView: View, mainExecutor: Executor?, @@ -202,7 +222,10 @@ constructor( ) { updateColors() } } - var regionSampler: RegionSampler? = null + var smallRegionSampler: RegionSampler? = null + private set + var largeRegionSampler: RegionSampler? = null + private set var smallTimeListener: TimeListener? = null var largeTimeListener: TimeListener? = null val shouldTimeListenerRun: Boolean @@ -319,7 +342,8 @@ constructor( configurationController.removeCallback(configListener) batteryController.removeCallback(batteryCallback) keyguardUpdateMonitor.removeCallback(keyguardUpdateMonitorCallback) - regionSampler?.stopRegionSampler() + smallRegionSampler?.stopRegionSampler() + largeRegionSampler?.stopRegionSampler() smallTimeListener?.stop() largeTimeListener?.stop() } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java index d7e8616d17e7..bb112175ded7 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java @@ -143,6 +143,7 @@ public abstract class KeyguardAbsKeyInputViewController<T extends KeyguardAbsKey long elapsedRealtime = SystemClock.elapsedRealtime(); long secondsInFuture = (long) Math.ceil( (elapsedRealtimeDeadline - elapsedRealtime) / 1000.0); + getKeyguardSecurityCallback().onAttemptLockoutStart(secondsInFuture); mCountdownTimer = new CountDownTimer(secondsInFuture * 1000, 1000) { @Override diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java index 376e27c6cf44..360f755623f7 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java @@ -303,9 +303,10 @@ public class KeyguardClockSwitch extends RelativeLayout { if (!animate) { out.setAlpha(0f); out.setTranslationY(clockOutYTranslation); + out.setVisibility(INVISIBLE); in.setAlpha(1f); in.setTranslationY(clockInYTranslation); - in.setVisibility(View.VISIBLE); + in.setVisibility(VISIBLE); mStatusArea.setScaleX(statusAreaClockScale); mStatusArea.setScaleY(statusAreaClockScale); mStatusArea.setTranslateXFromClockDesign(statusAreaClockTranslateX); @@ -323,7 +324,10 @@ public class KeyguardClockSwitch extends RelativeLayout { ObjectAnimator.ofFloat(out, TRANSLATION_Y, clockOutYTranslation)); mClockOutAnim.addListener(new AnimatorListenerAdapter() { public void onAnimationEnd(Animator animation) { - mClockOutAnim = null; + if (mClockOutAnim == animation) { + out.setVisibility(INVISIBLE); + mClockOutAnim = null; + } } }); @@ -338,7 +342,9 @@ public class KeyguardClockSwitch extends RelativeLayout { mClockInAnim.setStartDelay(CLOCK_IN_START_DELAY_MILLIS); mClockInAnim.addListener(new AnimatorListenerAdapter() { public void onAnimationEnd(Animator animation) { - mClockInAnim = null; + if (mClockInAnim == animation) { + mClockInAnim = null; + } } }); @@ -359,7 +365,9 @@ public class KeyguardClockSwitch extends RelativeLayout { statusAreaClockTranslateY)); mStatusAreaAnim.addListener(new AnimatorListenerAdapter() { public void onAnimationEnd(Animator animation) { - mStatusAreaAnim = null; + if (mStatusAreaAnim == animation) { + mStatusAreaAnim = null; + } } }); diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java index 41c1eda42e83..62a2f90a0378 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java @@ -437,10 +437,6 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS int clockHeight = clock.getLargeClock().getView().getHeight(); return frameHeight / 2 + clockHeight / 2 + mKeyguardLargeClockTopMargin / -2; } else { - // This is only called if we've never shown the large clock as the frame is inflated - // with 'gone', but then the visibility is never set when it is animated away by - // KeyguardClockSwitch, instead it is removed from the view hierarchy. - // TODO(b/261755021): Cleanup Large Frame Visibility int clockHeight = clock.getSmallClock().getView().getHeight(); return clockHeight + statusBarHeaderHeight + mKeyguardSmallClockTopMargin; } @@ -458,15 +454,11 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS if (mLargeClockFrame.getVisibility() == View.VISIBLE) { return clock.getLargeClock().getView().getHeight(); } else { - // Is not called except in certain edge cases, see comment in getClockBottom - // TODO(b/261755021): Cleanup Large Frame Visibility return clock.getSmallClock().getView().getHeight(); } } boolean isClockTopAligned() { - // Returns false except certain edge cases, see comment in getClockBottom - // TODO(b/261755021): Cleanup Large Frame Visibility return mLargeClockFrame.getVisibility() != View.VISIBLE; } @@ -545,9 +537,13 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS if (clock != null) { clock.dump(pw); } - final RegionSampler regionSampler = mClockEventController.getRegionSampler(); - if (regionSampler != null) { - regionSampler.dump(pw); + final RegionSampler smallRegionSampler = mClockEventController.getSmallRegionSampler(); + if (smallRegionSampler != null) { + smallRegionSampler.dump(pw); + } + final RegionSampler largeRegionSampler = mClockEventController.getLargeRegionSampler(); + if (largeRegionSampler != null) { + largeRegionSampler.dump(pw); } } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java index 3c05299f7a95..20e465693108 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java @@ -17,6 +17,7 @@ package com.android.keyguard; import android.annotation.CallSuper; +import android.annotation.NonNull; import android.annotation.Nullable; import android.content.res.ColorStateList; import android.content.res.Resources; @@ -33,6 +34,10 @@ import com.android.systemui.classifier.FalsingCollector; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.flags.FeatureFlags; import com.android.systemui.flags.Flags; +import com.android.systemui.bouncer.domain.interactor.BouncerMessageInteractor; +import com.android.systemui.bouncer.ui.BouncerMessageView; +import com.android.systemui.bouncer.ui.binder.BouncerMessageViewBinder; +import com.android.systemui.log.BouncerLogger; import com.android.systemui.statusbar.policy.DevicePostureController; import com.android.systemui.util.ViewController; import com.android.systemui.util.concurrency.DelayableExecutor; @@ -168,6 +173,24 @@ public abstract class KeyguardInputViewController<T extends KeyguardInputView> /** Determines the message to show in the bouncer when it first appears. */ protected abstract int getInitialMessageResId(); + /** + * Binds the {@link KeyguardInputView#getBouncerMessageView()} view with the provided context. + */ + public void bindMessageView( + @NonNull BouncerMessageInteractor bouncerMessageInteractor, + KeyguardMessageAreaController.Factory messageAreaControllerFactory, + BouncerLogger bouncerLogger, + FeatureFlags featureFlags) { + BouncerMessageView bouncerMessageView = (BouncerMessageView) mView.getBouncerMessageView(); + if (bouncerMessageView != null) { + BouncerMessageViewBinder.bind(bouncerMessageView, + bouncerMessageInteractor, + messageAreaControllerFactory, + bouncerLogger, + featureFlags); + } + } + /** Factory for a {@link KeyguardInputViewController}. */ public static class Factory { private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java index 3d255a58cf8e..ad9fea6432da 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java @@ -368,6 +368,7 @@ public class KeyguardPatternViewController final long elapsedRealtime = SystemClock.elapsedRealtime(); final long secondsInFuture = (long) Math.ceil( (elapsedRealtimeDeadline - elapsedRealtime) / 1000.0); + getKeyguardSecurityCallback().onAttemptLockoutStart(secondsInFuture); mCountdownTimer = new CountDownTimer(secondsInFuture * 1000, 1000) { @Override diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityCallback.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityCallback.java index bf9c3bbddc30..2878df2fe03f 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityCallback.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityCallback.java @@ -87,6 +87,11 @@ public interface KeyguardSecurityCallback { default void onUserInput() { } + /** + * Invoked when the auth input is disabled for specified number of seconds. + * @param seconds Number of seconds for which the auth input is disabled. + */ + default void onAttemptLockoutStart(long seconds) {} /** * Dismisses keyguard and go to unlocked state. diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java index 74b29a77aad4..7c511a32bb36 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java @@ -74,6 +74,7 @@ import com.android.systemui.classifier.FalsingA11yDelegate; import com.android.systemui.classifier.FalsingCollector; import com.android.systemui.flags.FeatureFlags; import com.android.systemui.flags.Flags; +import com.android.systemui.bouncer.domain.interactor.BouncerMessageInteractor; import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor; import com.android.systemui.log.SessionTracker; import com.android.systemui.plugins.ActivityStarter; @@ -116,6 +117,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard private final Optional<SideFpsController> mSideFpsController; private final FalsingA11yDelegate mFalsingA11yDelegate; private final KeyguardFaceAuthInteractor mKeyguardFaceAuthInteractor; + private final BouncerMessageInteractor mBouncerMessageInteractor; private int mTranslationY; // Whether the volume keys should be handled by keyguard. If true, then // they will be handled here for specific media types such as music, otherwise @@ -178,6 +180,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard @Override public void onUserInput() { + mBouncerMessageInteractor.onPrimaryBouncerUserInput(); mKeyguardFaceAuthInteractor.onPrimaryBouncerUserInput(); mUpdateMonitor.cancelFaceAuth(); } @@ -207,7 +210,15 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard } @Override + public void onAttemptLockoutStart(long seconds) { + mBouncerMessageInteractor.onPrimaryAuthLockedOut(seconds); + } + + @Override public void reportUnlockAttempt(int userId, boolean success, int timeoutMs) { + if (timeoutMs == 0 && !success) { + mBouncerMessageInteractor.onPrimaryAuthIncorrectAttempt(); + } int bouncerSide = SysUiStatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED__SIDE__DEFAULT; if (mView.isSidedSecurityMode()) { bouncerSide = mView.isSecurityLeftAligned() @@ -385,7 +396,8 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard TelephonyManager telephonyManager, ViewMediatorCallback viewMediatorCallback, AudioManager audioManager, - KeyguardFaceAuthInteractor keyguardFaceAuthInteractor + KeyguardFaceAuthInteractor keyguardFaceAuthInteractor, + BouncerMessageInteractor bouncerMessageInteractor ) { super(view); mLockPatternUtils = lockPatternUtils; @@ -411,6 +423,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard mViewMediatorCallback = viewMediatorCallback; mAudioManager = audioManager; mKeyguardFaceAuthInteractor = keyguardFaceAuthInteractor; + mBouncerMessageInteractor = bouncerMessageInteractor; } @Override @@ -431,6 +444,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard // Update ViewMediator with the current input method requirements mViewMediatorCallback.setNeedsInput(needsInput()); mView.setOnKeyListener(mOnKeyListener); + showPrimarySecurityScreen(false); } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimInputView.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardSimInputView.kt index 1461dbef5049..7c8d91fdaba8 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimInputView.kt +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimInputView.kt @@ -22,7 +22,7 @@ import android.widget.ImageView import androidx.core.graphics.drawable.DrawableCompat import com.android.settingslib.Utils import com.android.systemui.R -import com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants.ColorId.EMERGENCY_BUTTON +import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants.ColorId.EMERGENCY_BUTTON abstract class KeyguardSimInputView(context: Context, attrs: AttributeSet) : KeyguardPinBasedInputView(context, attrs) { diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java index ae061c032429..4b3f2814e410 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -155,6 +155,8 @@ import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dump.DumpManager; import com.android.systemui.dump.DumpsysTableLogger; +import com.android.systemui.flags.FeatureFlags; +import com.android.systemui.flags.Flags; import com.android.systemui.keyguard.domain.interactor.FaceAuthenticationListener; import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor; import com.android.systemui.keyguard.shared.constants.TrustAgentUiEvent; @@ -380,6 +382,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab private final ActiveUnlockConfig mActiveUnlockConfig; private final IDreamManager mDreamManager; private final TelephonyManager mTelephonyManager; + private final FeatureFlags mFeatureFlags; @Nullable private final FingerprintManager mFpm; @Nullable @@ -1462,14 +1465,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab ErrorAuthenticationStatus error = (ErrorAuthenticationStatus) status; handleFaceError(error.getMsgId(), error.getMsg()); } else if (status instanceof FailedAuthenticationStatus) { - if (isFaceLockedOut()) { - // TODO b/270090188: remove this hack when biometrics fixes this issue. - // FailedAuthenticationStatus is emitted after ErrorAuthenticationStatus - // for lockout error is received - mLogger.d("onAuthenticationFailed called after" - + " face has been locked out"); - return; - } handleFaceAuthFailed(); } else if (status instanceof HelpAuthenticationStatus) { HelpAuthenticationStatus helpMsg = (HelpAuthenticationStatus) status; @@ -1980,13 +1975,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab @Override public void onAuthenticationFailed() { - if (isFaceLockedOut()) { - // TODO b/270090188: remove this hack when biometrics fixes this issue. - // onAuthenticationFailed is called after onAuthenticationError - // for lockout error is received - mLogger.d("onAuthenticationFailed called after face has been locked out"); - return; - } handleFaceAuthFailed(); } @@ -2333,7 +2321,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab @Nullable BiometricManager biometricManager, FaceWakeUpTriggersConfig faceWakeUpTriggersConfig, DevicePostureController devicePostureController, - Optional<FingerprintInteractiveToAuthProvider> interactiveToAuthProvider) { + Optional<FingerprintInteractiveToAuthProvider> interactiveToAuthProvider, + FeatureFlags featureFlags) { mContext = context; mSubscriptionManager = subscriptionManager; mUserTracker = userTracker; @@ -2365,6 +2354,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab mPackageManager = packageManager; mFpm = fingerprintManager; mFaceManager = faceManager; + mFeatureFlags = featureFlags; mActiveUnlockConfig.setKeyguardUpdateMonitor(this); mFaceAcquiredInfoIgnoreList = Arrays.stream( mContext.getResources().getIntArray( @@ -3056,7 +3046,12 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab || shouldListenForFingerprintAssistant || (mKeyguardOccluded && mIsDreaming) || (mKeyguardOccluded && userDoesNotHaveTrust && mKeyguardShowing - && (mOccludingAppRequestingFp || isUdfps || mAlternateBouncerShowing)); + && (mOccludingAppRequestingFp + || isUdfps + || mAlternateBouncerShowing + || mFeatureFlags.isEnabled(Flags.FP_LISTEN_OCCLUDING_APPS) + ) + ); // Only listen if this KeyguardUpdateMonitor belongs to the system user. There is an // instance of KeyguardUpdateMonitor for each user but KeyguardUpdateMonitor is user-aware. @@ -3213,6 +3208,13 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab || (posture == mConfigFaceAuthSupportedPosture); } + /** + * If the current device posture allows face auth to run. + */ + public boolean doesCurrentPostureAllowFaceAuth() { + return doesPostureAllowFaceAuth(mPostureState); + } + private void logListenerModelData(@NonNull KeyguardListenModel model) { mLogger.logKeyguardListenerModel(model); if (model instanceof KeyguardFingerprintListenModel) { diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardViewController.java index 281067da6757..bc12aeebd84c 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardViewController.java @@ -119,6 +119,14 @@ public interface KeyguardViewController { boolean isUnlockWithWallpaper(); /** + * @return Whether the bouncer over dream is showing. Note that the bouncer over dream is + * handled independently of the rest of the notification panel. As a result, setting this state + * via {@link CentralSurfaces#setBouncerShowing(boolean)} leads to unintended side effects from + * states modified behind the dream. + */ + boolean isBouncerShowingOverDream(); + + /** * @return Whether subtle animation should be used for unlocking the device. */ boolean shouldSubtleWindowAnimationsForUnlock(); diff --git a/packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java b/packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java index 7d76f12880df..a04a48db5d7a 100644 --- a/packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java +++ b/packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java @@ -16,11 +16,11 @@ package com.android.keyguard; import static com.android.settingslib.Utils.getColorAttrDefaultColor; -import static com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants.ColorId.NUM_PAD_BACKGROUND; -import static com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants.ColorId.NUM_PAD_BACKGROUND_PRESSED; -import static com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants.ColorId.NUM_PAD_BUTTON; -import static com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants.ColorId.NUM_PAD_KEY; -import static com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants.ColorId.NUM_PAD_PRESSED; +import static com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants.ColorId.NUM_PAD_BACKGROUND; +import static com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants.ColorId.NUM_PAD_BACKGROUND_PRESSED; +import static com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants.ColorId.NUM_PAD_BUTTON; +import static com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants.ColorId.NUM_PAD_KEY; +import static com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants.ColorId.NUM_PAD_PRESSED; import static com.android.systemui.util.ColorUtilKt.getPrivateAttrColorIfUnset; import android.animation.AnimatorSet; diff --git a/packages/SystemUI/src/com/android/keyguard/NumPadButton.java b/packages/SystemUI/src/com/android/keyguard/NumPadButton.java index ebd234fd0846..3f1741a67a76 100644 --- a/packages/SystemUI/src/com/android/keyguard/NumPadButton.java +++ b/packages/SystemUI/src/com/android/keyguard/NumPadButton.java @@ -15,8 +15,8 @@ */ package com.android.keyguard; -import static com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants.ColorId.NUM_PAD_BUTTON; -import static com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants.ColorId.NUM_PAD_KEY; +import static com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants.ColorId.NUM_PAD_BUTTON; +import static com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants.ColorId.NUM_PAD_KEY; import android.content.Context; import android.content.res.ColorStateList; diff --git a/packages/SystemUI/src/com/android/keyguard/NumPadKey.java b/packages/SystemUI/src/com/android/keyguard/NumPadKey.java index e22fc300ede5..edc298cde032 100644 --- a/packages/SystemUI/src/com/android/keyguard/NumPadKey.java +++ b/packages/SystemUI/src/com/android/keyguard/NumPadKey.java @@ -15,7 +15,7 @@ */ package com.android.keyguard; -import static com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants.ColorId.NUM_PAD_KEY; +import static com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants.ColorId.NUM_PAD_KEY; import android.content.Context; import android.content.res.Configuration; diff --git a/packages/SystemUI/src/com/android/keyguard/PinShapeHintingView.java b/packages/SystemUI/src/com/android/keyguard/PinShapeHintingView.java index 32f06dcdbf20..7c129b4c8905 100644 --- a/packages/SystemUI/src/com/android/keyguard/PinShapeHintingView.java +++ b/packages/SystemUI/src/com/android/keyguard/PinShapeHintingView.java @@ -16,7 +16,7 @@ package com.android.keyguard; -import static com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants.ColorId.PIN_SHAPES; +import static com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants.ColorId.PIN_SHAPES; import android.content.Context; import android.graphics.drawable.AnimatedVectorDrawable; diff --git a/packages/SystemUI/src/com/android/keyguard/PinShapeNonHintingView.java b/packages/SystemUI/src/com/android/keyguard/PinShapeNonHintingView.java index 500910c910c3..56c0953cd822 100644 --- a/packages/SystemUI/src/com/android/keyguard/PinShapeNonHintingView.java +++ b/packages/SystemUI/src/com/android/keyguard/PinShapeNonHintingView.java @@ -16,7 +16,7 @@ package com.android.keyguard; -import static com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants.ColorId.PIN_SHAPES; +import static com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants.ColorId.PIN_SHAPES; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; diff --git a/packages/SystemUI/src/com/android/keyguard/clock/AnalogClockController.java b/packages/SystemUI/src/com/android/keyguard/clock/AnalogClockController.java deleted file mode 100644 index 7517deed7cbb..000000000000 --- a/packages/SystemUI/src/com/android/keyguard/clock/AnalogClockController.java +++ /dev/null @@ -1,210 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.keyguard.clock; - -import android.app.WallpaperManager; -import android.content.res.Resources; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.graphics.Color; -import android.graphics.Paint.Style; -import android.view.LayoutInflater; -import android.view.View; -import android.widget.TextClock; - -import com.android.internal.colorextraction.ColorExtractor; -import com.android.systemui.R; -import com.android.systemui.colorextraction.SysuiColorExtractor; -import com.android.systemui.plugins.ClockPlugin; - -import java.util.TimeZone; - -/** - * Controller for Stretch clock that can appear on lock screen and AOD. - */ -public class AnalogClockController implements ClockPlugin { - - /** - * Resources used to get title and thumbnail. - */ - private final Resources mResources; - - /** - * LayoutInflater used to inflate custom clock views. - */ - private final LayoutInflater mLayoutInflater; - - /** - * Extracts accent color from wallpaper. - */ - private final SysuiColorExtractor mColorExtractor; - - /** - * Computes preferred position of clock. - */ - private final SmallClockPosition mClockPosition; - - /** - * Renders preview from clock view. - */ - private final ViewPreviewer mRenderer = new ViewPreviewer(); - - /** - * Custom clock shown on AOD screen and behind stack scroller on lock. - */ - private ClockLayout mBigClockView; - private ImageClock mAnalogClock; - - /** - * Small clock shown on lock screen above stack scroller. - */ - private View mView; - private TextClock mLockClock; - - /** - * Helper to extract colors from wallpaper palette for clock face. - */ - private final ClockPalette mPalette = new ClockPalette(); - - /** - * Create a BubbleClockController instance. - * - * @param res Resources contains title and thumbnail. - * @param inflater Inflater used to inflate custom clock views. - * @param colorExtractor Extracts accent color from wallpaper. - */ - public AnalogClockController(Resources res, LayoutInflater inflater, - SysuiColorExtractor colorExtractor) { - mResources = res; - mLayoutInflater = inflater; - mColorExtractor = colorExtractor; - mClockPosition = new SmallClockPosition(inflater.getContext()); - } - - private void createViews() { - mBigClockView = (ClockLayout) mLayoutInflater.inflate(R.layout.analog_clock, null); - mAnalogClock = mBigClockView.findViewById(R.id.analog_clock); - - mView = mLayoutInflater.inflate(R.layout.digital_clock, null); - mLockClock = mView.findViewById(R.id.lock_screen_clock); - } - - @Override - public void onDestroyView() { - mBigClockView = null; - mAnalogClock = null; - mView = null; - mLockClock = null; - } - - @Override - public String getName() { - return "analog"; - } - - @Override - public String getTitle() { - return mResources.getString(R.string.clock_title_analog); - } - - @Override - public Bitmap getThumbnail() { - return BitmapFactory.decodeResource(mResources, R.drawable.analog_thumbnail); - } - - @Override - public Bitmap getPreview(int width, int height) { - - // Use the big clock view for the preview - View view = getBigClockView(); - - // Initialize state of plugin before generating preview. - setDarkAmount(1f); - setTextColor(Color.WHITE); - ColorExtractor.GradientColors colors = mColorExtractor.getColors( - WallpaperManager.FLAG_LOCK); - setColorPalette(colors.supportsDarkText(), colors.getColorPalette()); - onTimeTick(); - - return mRenderer.createPreview(view, width, height); - } - - @Override - public View getView() { - if (mView == null) { - createViews(); - } - return mView; - } - - @Override - public View getBigClockView() { - if (mBigClockView == null) { - createViews(); - } - return mBigClockView; - } - - @Override - public int getPreferredY(int totalHeight) { - return mClockPosition.getPreferredY(); - } - - @Override - public void setStyle(Style style) {} - - @Override - public void setTextColor(int color) { - updateColor(); - } - - @Override - public void setColorPalette(boolean supportsDarkText, int[] colorPalette) { - mPalette.setColorPalette(supportsDarkText, colorPalette); - updateColor(); - } - - private void updateColor() { - final int primary = mPalette.getPrimaryColor(); - final int secondary = mPalette.getSecondaryColor(); - mLockClock.setTextColor(secondary); - mAnalogClock.setClockColors(primary, secondary); - } - - @Override - public void onTimeTick() { - mAnalogClock.onTimeChanged(); - mBigClockView.onTimeChanged(); - mLockClock.refreshTime(); - } - - @Override - public void setDarkAmount(float darkAmount) { - mPalette.setDarkAmount(darkAmount); - mClockPosition.setDarkAmount(darkAmount); - mBigClockView.setDarkAmount(darkAmount); - } - - @Override - public void onTimeZoneChanged(TimeZone timeZone) { - mAnalogClock.onTimeZoneChanged(timeZone); - } - - @Override - public boolean shouldShowStatusArea() { - return true; - } -} diff --git a/packages/SystemUI/src/com/android/keyguard/clock/BubbleClockController.java b/packages/SystemUI/src/com/android/keyguard/clock/BubbleClockController.java deleted file mode 100644 index 1add1a3abf5a..000000000000 --- a/packages/SystemUI/src/com/android/keyguard/clock/BubbleClockController.java +++ /dev/null @@ -1,210 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.keyguard.clock; - -import android.app.WallpaperManager; -import android.content.res.Resources; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.graphics.Color; -import android.graphics.Paint.Style; -import android.view.LayoutInflater; -import android.view.View; -import android.widget.TextClock; - -import com.android.internal.colorextraction.ColorExtractor; -import com.android.systemui.R; -import com.android.systemui.colorextraction.SysuiColorExtractor; -import com.android.systemui.plugins.ClockPlugin; - -import java.util.TimeZone; - -/** - * Controller for Bubble clock that can appear on lock screen and AOD. - */ -public class BubbleClockController implements ClockPlugin { - - /** - * Resources used to get title and thumbnail. - */ - private final Resources mResources; - - /** - * LayoutInflater used to inflate custom clock views. - */ - private final LayoutInflater mLayoutInflater; - - /** - * Extracts accent color from wallpaper. - */ - private final SysuiColorExtractor mColorExtractor; - - /** - * Computes preferred position of clock. - */ - private final SmallClockPosition mClockPosition; - - /** - * Renders preview from clock view. - */ - private final ViewPreviewer mRenderer = new ViewPreviewer(); - - /** - * Custom clock shown on AOD screen and behind stack scroller on lock. - */ - private ClockLayout mView; - private ImageClock mAnalogClock; - - /** - * Small clock shown on lock screen above stack scroller. - */ - private View mLockClockContainer; - private TextClock mLockClock; - - /** - * Helper to extract colors from wallpaper palette for clock face. - */ - private final ClockPalette mPalette = new ClockPalette(); - - /** - * Create a BubbleClockController instance. - * - * @param res Resources contains title and thumbnail. - * @param inflater Inflater used to inflate custom clock views. - * @param colorExtractor Extracts accent color from wallpaper. - */ - public BubbleClockController(Resources res, LayoutInflater inflater, - SysuiColorExtractor colorExtractor) { - mResources = res; - mLayoutInflater = inflater; - mColorExtractor = colorExtractor; - mClockPosition = new SmallClockPosition(inflater.getContext()); - } - - private void createViews() { - mView = (ClockLayout) mLayoutInflater.inflate(R.layout.bubble_clock, null); - mAnalogClock = (ImageClock) mView.findViewById(R.id.analog_clock); - - mLockClockContainer = mLayoutInflater.inflate(R.layout.digital_clock, null); - mLockClock = (TextClock) mLockClockContainer.findViewById(R.id.lock_screen_clock); - } - - @Override - public void onDestroyView() { - mView = null; - mAnalogClock = null; - mLockClockContainer = null; - mLockClock = null; - } - - @Override - public String getName() { - return "bubble"; - } - - @Override - public String getTitle() { - return mResources.getString(R.string.clock_title_bubble); - } - - @Override - public Bitmap getThumbnail() { - return BitmapFactory.decodeResource(mResources, R.drawable.bubble_thumbnail); - } - - @Override - public Bitmap getPreview(int width, int height) { - - // Use the big clock view for the preview - View view = getBigClockView(); - - // Initialize state of plugin before generating preview. - setDarkAmount(1f); - setTextColor(Color.WHITE); - ColorExtractor.GradientColors colors = mColorExtractor.getColors( - WallpaperManager.FLAG_LOCK); - setColorPalette(colors.supportsDarkText(), colors.getColorPalette()); - onTimeTick(); - - return mRenderer.createPreview(view, width, height); - } - - @Override - public View getView() { - if (mLockClockContainer == null) { - createViews(); - } - return mLockClockContainer; - } - - @Override - public View getBigClockView() { - if (mView == null) { - createViews(); - } - return mView; - } - - @Override - public int getPreferredY(int totalHeight) { - return mClockPosition.getPreferredY(); - } - - @Override - public void setStyle(Style style) {} - - @Override - public void setTextColor(int color) { - updateColor(); - } - - @Override - public void setColorPalette(boolean supportsDarkText, int[] colorPalette) { - mPalette.setColorPalette(supportsDarkText, colorPalette); - updateColor(); - } - - private void updateColor() { - final int primary = mPalette.getPrimaryColor(); - final int secondary = mPalette.getSecondaryColor(); - mLockClock.setTextColor(secondary); - mAnalogClock.setClockColors(primary, secondary); - } - - @Override - public void setDarkAmount(float darkAmount) { - mPalette.setDarkAmount(darkAmount); - mClockPosition.setDarkAmount(darkAmount); - mView.setDarkAmount(darkAmount); - } - - @Override - public void onTimeTick() { - mAnalogClock.onTimeChanged(); - mView.onTimeChanged(); - mLockClock.refreshTime(); - } - - @Override - public void onTimeZoneChanged(TimeZone timeZone) { - mAnalogClock.onTimeZoneChanged(timeZone); - } - - @Override - public boolean shouldShowStatusArea() { - return true; - } -} diff --git a/packages/SystemUI/src/com/android/keyguard/clock/ClockInfo.java b/packages/SystemUI/src/com/android/keyguard/clock/ClockInfo.java deleted file mode 100644 index 0210e08bb24c..000000000000 --- a/packages/SystemUI/src/com/android/keyguard/clock/ClockInfo.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.keyguard.clock; - -import android.graphics.Bitmap; - -import java.util.function.Supplier; - -/** - * Metadata about an available clock face. - */ -final class ClockInfo { - - private final String mName; - private final Supplier<String> mTitle; - private final String mId; - private final Supplier<Bitmap> mThumbnail; - private final Supplier<Bitmap> mPreview; - - private ClockInfo(String name, Supplier<String> title, String id, - Supplier<Bitmap> thumbnail, Supplier<Bitmap> preview) { - mName = name; - mTitle = title; - mId = id; - mThumbnail = thumbnail; - mPreview = preview; - } - - /** - * Gets the non-internationalized name for the clock face. - */ - String getName() { - return mName; - } - - /** - * Gets the name (title) of the clock face to be shown in the picker app. - */ - String getTitle() { - return mTitle.get(); - } - - /** - * Gets the ID of the clock face, used by the picker to set the current selection. - */ - String getId() { - return mId; - } - - /** - * Gets a thumbnail image of the clock. - */ - Bitmap getThumbnail() { - return mThumbnail.get(); - } - - /** - * Gets a potentially realistic preview image of the clock face. - */ - Bitmap getPreview() { - return mPreview.get(); - } - - static Builder builder() { - return new Builder(); - } - - static class Builder { - private String mName; - private Supplier<String> mTitle; - private String mId; - private Supplier<Bitmap> mThumbnail; - private Supplier<Bitmap> mPreview; - - public ClockInfo build() { - return new ClockInfo(mName, mTitle, mId, mThumbnail, mPreview); - } - - public Builder setName(String name) { - mName = name; - return this; - } - - public Builder setTitle(Supplier<String> title) { - mTitle = title; - return this; - } - - public Builder setId(String id) { - mId = id; - return this; - } - - public Builder setThumbnail(Supplier<Bitmap> thumbnail) { - mThumbnail = thumbnail; - return this; - } - - public Builder setPreview(Supplier<Bitmap> preview) { - mPreview = preview; - return this; - } - } -} diff --git a/packages/SystemUI/src/com/android/keyguard/clock/ClockLayout.java b/packages/SystemUI/src/com/android/keyguard/clock/ClockLayout.java deleted file mode 100644 index d44d89e63e8f..000000000000 --- a/packages/SystemUI/src/com/android/keyguard/clock/ClockLayout.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.keyguard.clock; - -import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInOffset; - -import android.content.Context; -import android.content.res.Resources; -import android.util.AttributeSet; -import android.util.MathUtils; -import android.view.View; -import android.widget.FrameLayout; - -import com.android.systemui.R; - -/** - * Positions clock faces (analog, digital, typographic) and handles pixel shifting - * to prevent screen burn-in. - */ -public class ClockLayout extends FrameLayout { - - private static final int ANALOG_CLOCK_SHIFT_FACTOR = 3; - /** - * Clock face views. - */ - private View mAnalogClock; - - /** - * Pixel shifting amplitudes used to prevent screen burn-in. - */ - private int mBurnInPreventionOffsetX; - private int mBurnInPreventionOffsetY; - - private float mDarkAmount; - - public ClockLayout(Context context) { - this(context, null); - } - - public ClockLayout(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - public ClockLayout(Context context, AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - } - - @Override - protected void onFinishInflate() { - super.onFinishInflate(); - mAnalogClock = findViewById(R.id.analog_clock); - - // Get pixel shifting X, Y amplitudes from resources. - Resources resources = getResources(); - mBurnInPreventionOffsetX = resources.getDimensionPixelSize( - R.dimen.burn_in_prevention_offset_x); - mBurnInPreventionOffsetY = resources.getDimensionPixelSize( - R.dimen.burn_in_prevention_offset_y); - } - - @Override - protected void onLayout(boolean changed, int left, int top, int right, int bottom) { - super.onLayout(changed, left, top, right, bottom); - positionChildren(); - } - - void onTimeChanged() { - positionChildren(); - } - - /** - * See {@link com.android.systemui.plugins.ClockPlugin#setDarkAmount(float)}. - */ - void setDarkAmount(float darkAmount) { - mDarkAmount = darkAmount; - positionChildren(); - } - - private void positionChildren() { - final float offsetX = MathUtils.lerp(0f, - getBurnInOffset(mBurnInPreventionOffsetX * 2, true) - mBurnInPreventionOffsetX, - mDarkAmount); - final float offsetY = MathUtils.lerp(0f, - getBurnInOffset(mBurnInPreventionOffsetY * 2, false) - - 0.5f * mBurnInPreventionOffsetY, - mDarkAmount); - - // Put the analog clock in the middle of the screen. - if (mAnalogClock != null) { - mAnalogClock.setX(Math.max(0f, 0.5f * (getWidth() - mAnalogClock.getWidth())) - + ANALOG_CLOCK_SHIFT_FACTOR * offsetX); - mAnalogClock.setY(Math.max(0f, 0.5f * (getHeight() - mAnalogClock.getHeight())) - + ANALOG_CLOCK_SHIFT_FACTOR * offsetY); - } - } -} diff --git a/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java b/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java deleted file mode 100644 index 122c52138b08..000000000000 --- a/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java +++ /dev/null @@ -1,392 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.keyguard.clock; - -import android.annotation.Nullable; -import android.content.ContentResolver; -import android.content.Context; -import android.content.res.Resources; -import android.database.ContentObserver; -import android.net.Uri; -import android.os.Handler; -import android.os.Looper; -import android.os.UserHandle; -import android.provider.Settings; -import android.util.ArrayMap; -import android.util.DisplayMetrics; -import android.view.LayoutInflater; - -import androidx.annotation.NonNull; -import androidx.annotation.VisibleForTesting; - -import com.android.systemui.colorextraction.SysuiColorExtractor; -import com.android.systemui.dagger.SysUISingleton; -import com.android.systemui.dagger.qualifiers.Main; -import com.android.systemui.dock.DockManager; -import com.android.systemui.dock.DockManager.DockEventListener; -import com.android.systemui.plugins.ClockPlugin; -import com.android.systemui.plugins.PluginListener; -import com.android.systemui.plugins.PluginManager; -import com.android.systemui.settings.UserTracker; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.concurrent.Executor; -import java.util.function.Supplier; - -import javax.inject.Inject; - -/** - * Manages custom clock faces for AOD and lock screen. - * - * @deprecated Migrate to ClockRegistry - */ -@SysUISingleton -@Deprecated -public final class ClockManager { - - private static final String TAG = "ClockOptsProvider"; - - private final AvailableClocks mPreviewClocks; - private final List<Supplier<ClockPlugin>> mBuiltinClocks = new ArrayList<>(); - - private final Context mContext; - private final ContentResolver mContentResolver; - private final SettingsWrapper mSettingsWrapper; - private final Handler mMainHandler = new Handler(Looper.getMainLooper()); - private final UserTracker mUserTracker; - private final Executor mMainExecutor; - - /** - * Observe settings changes to know when to switch the clock face. - */ - private final ContentObserver mContentObserver = - new ContentObserver(mMainHandler) { - @Override - public void onChange(boolean selfChange, Collection<Uri> uris, - int flags, int userId) { - if (Objects.equals(userId, - mUserTracker.getUserId())) { - reload(); - } - } - }; - - /** - * Observe user changes and react by potentially loading the custom clock for the new user. - */ - private final UserTracker.Callback mUserChangedCallback = - new UserTracker.Callback() { - @Override - public void onUserChanged(int newUser, @NonNull Context userContext) { - reload(); - } - }; - - private final PluginManager mPluginManager; - @Nullable private final DockManager mDockManager; - - /** - * Observe changes to dock state to know when to switch the clock face. - */ - private final DockEventListener mDockEventListener = - new DockEventListener() { - @Override - public void onEvent(int event) { - mIsDocked = (event == DockManager.STATE_DOCKED - || event == DockManager.STATE_DOCKED_HIDE); - reload(); - } - }; - - /** - * When docked, the DOCKED_CLOCK_FACE setting will be checked for the custom clock face - * to show. - */ - private boolean mIsDocked; - - /** - * Listeners for onClockChanged event. - * - * Each listener must receive a separate clock plugin instance. Otherwise, there could be - * problems like attempting to attach a view that already has a parent. To deal with this issue, - * each listener is associated with a collection of available clocks. When onClockChanged is - * fired the current clock plugin instance is retrieved from that listeners available clocks. - */ - private final Map<ClockChangedListener, AvailableClocks> mListeners = new ArrayMap<>(); - - private final int mWidth; - private final int mHeight; - - @Inject - public ClockManager(Context context, LayoutInflater layoutInflater, - PluginManager pluginManager, SysuiColorExtractor colorExtractor, - @Nullable DockManager dockManager, UserTracker userTracker, - @Main Executor mainExecutor) { - this(context, layoutInflater, pluginManager, colorExtractor, - context.getContentResolver(), userTracker, mainExecutor, - new SettingsWrapper(context.getContentResolver()), dockManager); - } - - @VisibleForTesting - ClockManager(Context context, LayoutInflater layoutInflater, - PluginManager pluginManager, SysuiColorExtractor colorExtractor, - ContentResolver contentResolver, UserTracker userTracker, Executor mainExecutor, - SettingsWrapper settingsWrapper, DockManager dockManager) { - mContext = context; - mPluginManager = pluginManager; - mContentResolver = contentResolver; - mSettingsWrapper = settingsWrapper; - mUserTracker = userTracker; - mMainExecutor = mainExecutor; - mDockManager = dockManager; - mPreviewClocks = new AvailableClocks(); - - Resources res = context.getResources(); - - addBuiltinClock(() -> new DefaultClockController(res, layoutInflater, colorExtractor)); - - // Store the size of the display for generation of clock preview. - DisplayMetrics dm = res.getDisplayMetrics(); - mWidth = dm.widthPixels; - mHeight = dm.heightPixels; - } - - /** - * Add listener to be notified when clock implementation should change. - */ - public void addOnClockChangedListener(ClockChangedListener listener) { - if (mListeners.isEmpty()) { - register(); - } - AvailableClocks availableClocks = new AvailableClocks(); - for (int i = 0; i < mBuiltinClocks.size(); i++) { - availableClocks.addClockPlugin(mBuiltinClocks.get(i).get()); - } - mListeners.put(listener, availableClocks); - mPluginManager.addPluginListener(availableClocks, ClockPlugin.class, true); - reload(); - } - - /** - * Remove listener added with {@link addOnClockChangedListener}. - */ - public void removeOnClockChangedListener(ClockChangedListener listener) { - AvailableClocks availableClocks = mListeners.remove(listener); - mPluginManager.removePluginListener(availableClocks); - if (mListeners.isEmpty()) { - unregister(); - } - } - - /** - * Get information about available clock faces. - */ - List<ClockInfo> getClockInfos() { - return mPreviewClocks.getInfo(); - } - - /** - * Get the current clock. - * @return current custom clock or null for default. - */ - @Nullable - ClockPlugin getCurrentClock() { - return mPreviewClocks.getCurrentClock(); - } - - @VisibleForTesting - boolean isDocked() { - return mIsDocked; - } - - @VisibleForTesting - ContentObserver getContentObserver() { - return mContentObserver; - } - - @VisibleForTesting - void addBuiltinClock(Supplier<ClockPlugin> pluginSupplier) { - ClockPlugin plugin = pluginSupplier.get(); - mPreviewClocks.addClockPlugin(plugin); - mBuiltinClocks.add(pluginSupplier); - } - - private void register() { - mPluginManager.addPluginListener(mPreviewClocks, ClockPlugin.class, true); - mContentResolver.registerContentObserver( - Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_CUSTOM_CLOCK_FACE), - false, mContentObserver, UserHandle.USER_ALL); - mContentResolver.registerContentObserver( - Settings.Secure.getUriFor(Settings.Secure.DOCKED_CLOCK_FACE), - false, mContentObserver, UserHandle.USER_ALL); - mUserTracker.addCallback(mUserChangedCallback, mMainExecutor); - if (mDockManager != null) { - mDockManager.addListener(mDockEventListener); - } - } - - private void unregister() { - mPluginManager.removePluginListener(mPreviewClocks); - mContentResolver.unregisterContentObserver(mContentObserver); - mUserTracker.removeCallback(mUserChangedCallback); - if (mDockManager != null) { - mDockManager.removeListener(mDockEventListener); - } - } - - private void reload() { - mPreviewClocks.reloadCurrentClock(); - mListeners.forEach((listener, clocks) -> { - clocks.reloadCurrentClock(); - final ClockPlugin clock = clocks.getCurrentClock(); - if (Looper.myLooper() == Looper.getMainLooper()) { - listener.onClockChanged(clock instanceof DefaultClockController ? null : clock); - } else { - mMainHandler.post(() -> listener.onClockChanged( - clock instanceof DefaultClockController ? null : clock)); - } - }); - } - - /** - * Listener for events that should cause the custom clock face to change. - */ - public interface ClockChangedListener { - /** - * Called when custom clock should change. - * - * @param clock Custom clock face to use. A null value indicates the default clock face. - */ - void onClockChanged(ClockPlugin clock); - } - - /** - * Collection of available clocks. - */ - private final class AvailableClocks implements PluginListener<ClockPlugin> { - - /** - * Map from expected value stored in settings to plugin for custom clock face. - */ - private final Map<String, ClockPlugin> mClocks = new ArrayMap<>(); - - /** - * Metadata about available clocks, such as name and preview images. - */ - private final List<ClockInfo> mClockInfo = new ArrayList<>(); - - /** - * Active ClockPlugin. - */ - @Nullable private ClockPlugin mCurrentClock; - - @Override - public void onPluginConnected(ClockPlugin plugin, Context pluginContext) { - addClockPlugin(plugin); - reloadIfNeeded(plugin); - } - - @Override - public void onPluginDisconnected(ClockPlugin plugin) { - removeClockPlugin(plugin); - reloadIfNeeded(plugin); - } - - /** - * Get the current clock. - * @return current custom clock or null for default. - */ - @Nullable - ClockPlugin getCurrentClock() { - return mCurrentClock; - } - - /** - * Get information about available clock faces. - */ - List<ClockInfo> getInfo() { - return mClockInfo; - } - - /** - * Adds a clock plugin to the collection of available clocks. - * - * @param plugin The plugin to add. - */ - void addClockPlugin(ClockPlugin plugin) { - final String id = plugin.getClass().getName(); - mClocks.put(plugin.getClass().getName(), plugin); - mClockInfo.add(ClockInfo.builder() - .setName(plugin.getName()) - .setTitle(plugin::getTitle) - .setId(id) - .setThumbnail(plugin::getThumbnail) - .setPreview(() -> plugin.getPreview(mWidth, mHeight)) - .build()); - } - - private void removeClockPlugin(ClockPlugin plugin) { - final String id = plugin.getClass().getName(); - mClocks.remove(id); - for (int i = 0; i < mClockInfo.size(); i++) { - if (id.equals(mClockInfo.get(i).getId())) { - mClockInfo.remove(i); - break; - } - } - } - - private void reloadIfNeeded(ClockPlugin plugin) { - final boolean wasCurrentClock = plugin == mCurrentClock; - reloadCurrentClock(); - final boolean isCurrentClock = plugin == mCurrentClock; - if (wasCurrentClock || isCurrentClock) { - ClockManager.this.reload(); - } - } - - /** - * Update the current clock. - */ - void reloadCurrentClock() { - mCurrentClock = getClockPlugin(); - } - - private ClockPlugin getClockPlugin() { - ClockPlugin plugin = null; - if (ClockManager.this.isDocked()) { - final String name = mSettingsWrapper.getDockedClockFace( - mUserTracker.getUserId()); - if (name != null) { - plugin = mClocks.get(name); - if (plugin != null) { - return plugin; - } - } - } - final String name = mSettingsWrapper.getLockScreenCustomClockFace( - mUserTracker.getUserId()); - if (name != null) { - plugin = mClocks.get(name); - } - return plugin; - } - } -} diff --git a/packages/SystemUI/src/com/android/keyguard/clock/ClockOptionsProvider.java b/packages/SystemUI/src/com/android/keyguard/clock/ClockOptionsProvider.java deleted file mode 100644 index b6413cb61deb..000000000000 --- a/packages/SystemUI/src/com/android/keyguard/clock/ClockOptionsProvider.java +++ /dev/null @@ -1,183 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.keyguard.clock; - -import android.content.ContentProvider; -import android.content.ContentValues; -import android.database.Cursor; -import android.database.MatrixCursor; -import android.graphics.Bitmap; -import android.net.Uri; -import android.os.Bundle; -import android.os.ParcelFileDescriptor; -import android.os.ParcelFileDescriptor.AutoCloseOutputStream; -import android.text.TextUtils; -import android.util.Log; - -import com.android.internal.annotations.VisibleForTesting; - -import java.io.FileNotFoundException; -import java.util.List; - -import javax.inject.Inject; -import javax.inject.Provider; - -/** - * Exposes custom clock face options and provides realistic preview images. - * - * APIs: - * - * /list_options: List the available clock faces, which has the following columns - * name: name of the clock face - * title: title of the clock face - * id: value used to set the clock face - * thumbnail: uri of the thumbnail image, should be /thumbnail/{name} - * preview: uri of the preview image, should be /preview/{name} - * - * /thumbnail/{id}: Opens a file stream for the thumbnail image for clock face {id}. - * - * /preview/{id}: Opens a file stream for the preview image for clock face {id}. - */ -public final class ClockOptionsProvider extends ContentProvider { - - private static final String TAG = "ClockOptionsProvider"; - private static final String KEY_LIST_OPTIONS = "/list_options"; - private static final String KEY_PREVIEW = "preview"; - private static final String KEY_THUMBNAIL = "thumbnail"; - private static final String COLUMN_NAME = "name"; - private static final String COLUMN_TITLE = "title"; - private static final String COLUMN_ID = "id"; - private static final String COLUMN_THUMBNAIL = "thumbnail"; - private static final String COLUMN_PREVIEW = "preview"; - private static final String MIME_TYPE_PNG = "image/png"; - private static final String CONTENT_SCHEME = "content"; - private static final String AUTHORITY = "com.android.keyguard.clock"; - - @Inject - public Provider<List<ClockInfo>> mClockInfosProvider; - - @VisibleForTesting - ClockOptionsProvider(Provider<List<ClockInfo>> clockInfosProvider) { - mClockInfosProvider = clockInfosProvider; - } - - @Override - public boolean onCreate() { - return true; - } - - @Override - public String getType(Uri uri) { - List<String> segments = uri.getPathSegments(); - if (segments.size() > 0 && (KEY_PREVIEW.equals(segments.get(0)) - || KEY_THUMBNAIL.equals(segments.get(0)))) { - return MIME_TYPE_PNG; - } - return "vnd.android.cursor.dir/clock_faces"; - } - - @Override - public Cursor query(Uri uri, String[] projection, String selection, - String[] selectionArgs, String sortOrder) { - if (!KEY_LIST_OPTIONS.equals(uri.getPath())) { - return null; - } - MatrixCursor cursor = new MatrixCursor(new String[] { - COLUMN_NAME, COLUMN_TITLE, COLUMN_ID, COLUMN_THUMBNAIL, COLUMN_PREVIEW}); - List<ClockInfo> clocks = mClockInfosProvider.get(); - for (int i = 0; i < clocks.size(); i++) { - ClockInfo clock = clocks.get(i); - cursor.newRow() - .add(COLUMN_NAME, clock.getName()) - .add(COLUMN_TITLE, clock.getTitle()) - .add(COLUMN_ID, clock.getId()) - .add(COLUMN_THUMBNAIL, createThumbnailUri(clock)) - .add(COLUMN_PREVIEW, createPreviewUri(clock)); - } - return cursor; - } - - @Override - public Uri insert(Uri uri, ContentValues initialValues) { - return null; - } - - @Override - public int delete(Uri uri, String selection, String[] selectionArgs) { - return 0; - } - - @Override - public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { - return 0; - } - - @Override - public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException { - List<String> segments = uri.getPathSegments(); - if (segments.size() != 2 || !(KEY_PREVIEW.equals(segments.get(0)) - || KEY_THUMBNAIL.equals(segments.get(0)))) { - throw new FileNotFoundException("Invalid preview url"); - } - String id = segments.get(1); - if (TextUtils.isEmpty(id)) { - throw new FileNotFoundException("Invalid preview url, missing id"); - } - ClockInfo clock = null; - List<ClockInfo> clocks = mClockInfosProvider.get(); - for (int i = 0; i < clocks.size(); i++) { - if (id.equals(clocks.get(i).getId())) { - clock = clocks.get(i); - break; - } - } - if (clock == null) { - throw new FileNotFoundException("Invalid preview url, id not found"); - } - return openPipeHelper(uri, MIME_TYPE_PNG, null, KEY_PREVIEW.equals(segments.get(0)) - ? clock.getPreview() : clock.getThumbnail(), new MyWriter()); - } - - private Uri createThumbnailUri(ClockInfo clock) { - return new Uri.Builder() - .scheme(CONTENT_SCHEME) - .authority(AUTHORITY) - .appendPath(KEY_THUMBNAIL) - .appendPath(clock.getId()) - .build(); - } - - private Uri createPreviewUri(ClockInfo clock) { - return new Uri.Builder() - .scheme(CONTENT_SCHEME) - .authority(AUTHORITY) - .appendPath(KEY_PREVIEW) - .appendPath(clock.getId()) - .build(); - } - - private static class MyWriter implements ContentProvider.PipeDataWriter<Bitmap> { - @Override - public void writeDataToPipe(ParcelFileDescriptor output, Uri uri, String mimeType, - Bundle opts, Bitmap bitmap) { - try (AutoCloseOutputStream os = new AutoCloseOutputStream(output)) { - bitmap.compress(Bitmap.CompressFormat.PNG, 100, os); - } catch (Exception e) { - Log.w(TAG, "fail to write to pipe", e); - } - } - } -} diff --git a/packages/SystemUI/src/com/android/keyguard/clock/ClockPalette.kt b/packages/SystemUI/src/com/android/keyguard/clock/ClockPalette.kt deleted file mode 100644 index 5c5493a0c200..000000000000 --- a/packages/SystemUI/src/com/android/keyguard/clock/ClockPalette.kt +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -package com.android.keyguard.clock - -import android.graphics.Color -import android.util.MathUtils - -private const val PRIMARY_INDEX = 5 -private const val SECONDARY_DARK_INDEX = 8 -private const val SECONDARY_LIGHT_INDEX = 2 - -/** - * A helper class to extract colors from a clock face. - */ -class ClockPalette { - - private var darkAmount: Float = 0f - private var accentPrimary: Int = Color.WHITE - private var accentSecondaryLight: Int = Color.WHITE - private var accentSecondaryDark: Int = Color.BLACK - private val lightHSV: FloatArray = FloatArray(3) - private val darkHSV: FloatArray = FloatArray(3) - private val hsv: FloatArray = FloatArray(3) - - /** Returns a color from the palette as an RGB packed int. */ - fun getPrimaryColor(): Int { - return accentPrimary - } - - /** Returns either a light or dark color from the palette as an RGB packed int. */ - fun getSecondaryColor(): Int { - Color.colorToHSV(accentSecondaryLight, lightHSV) - Color.colorToHSV(accentSecondaryDark, darkHSV) - for (i in 0..2) { - hsv[i] = MathUtils.lerp(darkHSV[i], lightHSV[i], darkAmount) - } - return Color.HSVToColor(hsv) - } - - /** See {@link ClockPlugin#setColorPalette}. */ - fun setColorPalette(supportsDarkText: Boolean, colorPalette: IntArray?) { - if (colorPalette == null || colorPalette.isEmpty()) { - accentPrimary = Color.WHITE - accentSecondaryLight = Color.WHITE - accentSecondaryDark = if (supportsDarkText) Color.BLACK else Color.WHITE - return - } - val length = colorPalette.size - accentPrimary = colorPalette[Math.max(0, length - PRIMARY_INDEX)] - accentSecondaryLight = colorPalette[Math.max(0, length - SECONDARY_LIGHT_INDEX)] - accentSecondaryDark = colorPalette[Math.max(0, - length - if (supportsDarkText) SECONDARY_DARK_INDEX else SECONDARY_LIGHT_INDEX)] - } - - /** See {@link ClockPlugin#setDarkAmount}. */ - fun setDarkAmount(darkAmount: Float) { - this.darkAmount = darkAmount - } -} diff --git a/packages/SystemUI/src/com/android/keyguard/clock/CrossFadeDarkController.java b/packages/SystemUI/src/com/android/keyguard/clock/CrossFadeDarkController.java deleted file mode 100644 index 3c3f4759614b..000000000000 --- a/packages/SystemUI/src/com/android/keyguard/clock/CrossFadeDarkController.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.keyguard.clock; - -import android.view.View; - -/** - * Controls transition to dark state by cross fading between views. - */ -final class CrossFadeDarkController { - - private final View mFadeInView; - private final View mFadeOutView; - - /** - * Creates a new controller that fades between views. - * - * @param fadeInView View to fade in when transitioning to AOD. - * @param fadeOutView View to fade out when transitioning to AOD. - */ - CrossFadeDarkController(View fadeInView, View fadeOutView) { - mFadeInView = fadeInView; - mFadeOutView = fadeOutView; - } - - /** - * Sets the amount the system has transitioned to the dark state. - * - * @param darkAmount Amount of transition to dark state: 1f for AOD and 0f for lock screen. - */ - void setDarkAmount(float darkAmount) { - mFadeInView.setAlpha(Math.max(0f, 2f * darkAmount - 1f)); - if (darkAmount == 0f) { - mFadeInView.setVisibility(View.GONE); - } else { - if (mFadeInView.getVisibility() == View.GONE) { - mFadeInView.setVisibility(View.VISIBLE); - } - } - mFadeOutView.setAlpha(Math.max(0f, 1f - 2f * darkAmount)); - if (darkAmount == 1f) { - mFadeOutView.setVisibility(View.GONE); - } else { - if (mFadeOutView.getVisibility() == View.GONE) { - mFadeOutView.setVisibility(View.VISIBLE); - } - } - } -} diff --git a/packages/SystemUI/src/com/android/keyguard/clock/DefaultClockController.java b/packages/SystemUI/src/com/android/keyguard/clock/DefaultClockController.java deleted file mode 100644 index c81935a15a15..000000000000 --- a/packages/SystemUI/src/com/android/keyguard/clock/DefaultClockController.java +++ /dev/null @@ -1,178 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.keyguard.clock; - -import android.app.WallpaperManager; -import android.content.res.Resources; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.graphics.Color; -import android.graphics.Paint.Style; -import android.view.LayoutInflater; -import android.view.View; -import android.widget.TextView; - -import com.android.internal.colorextraction.ColorExtractor; -import com.android.systemui.R; -import com.android.systemui.colorextraction.SysuiColorExtractor; -import com.android.systemui.plugins.ClockPlugin; - -import java.util.TimeZone; - -/** - * Plugin for the default clock face used only to provide a preview. - */ -public class DefaultClockController implements ClockPlugin { - - /** - * Resources used to get title and thumbnail. - */ - private final Resources mResources; - - /** - * LayoutInflater used to inflate custom clock views. - */ - private final LayoutInflater mLayoutInflater; - - /** - * Extracts accent color from wallpaper. - */ - private final SysuiColorExtractor mColorExtractor; - - /** - * Renders preview from clock view. - */ - private final ViewPreviewer mRenderer = new ViewPreviewer(); - - /** - * Root view of preview. - */ - private View mView; - - /** - * Text clock in preview view hierarchy. - */ - private TextView mTextTime; - - /** - * Date showing below time in preview view hierarchy. - */ - private TextView mTextDate; - - /** - * Create a DefaultClockController instance. - * - * @param res Resources contains title and thumbnail. - * @param inflater Inflater used to inflate custom clock views. - * @param colorExtractor Extracts accent color from wallpaper. - */ - public DefaultClockController(Resources res, LayoutInflater inflater, - SysuiColorExtractor colorExtractor) { - mResources = res; - mLayoutInflater = inflater; - mColorExtractor = colorExtractor; - } - - private void createViews() { - mView = mLayoutInflater.inflate(R.layout.default_clock_preview, null); - mTextTime = mView.findViewById(R.id.time); - mTextDate = mView.findViewById(R.id.date); - } - - @Override - public void onDestroyView() { - mView = null; - mTextTime = null; - mTextDate = null; - } - - @Override - public String getName() { - return "default"; - } - - @Override - public String getTitle() { - return mResources.getString(R.string.clock_title_default); - } - - @Override - public Bitmap getThumbnail() { - return BitmapFactory.decodeResource(mResources, R.drawable.default_thumbnail); - } - - @Override - public Bitmap getPreview(int width, int height) { - - // Use the big clock view for the preview - View view = getBigClockView(); - - // Initialize state of plugin before generating preview. - setDarkAmount(1f); - setTextColor(Color.WHITE); - ColorExtractor.GradientColors colors = mColorExtractor.getColors( - WallpaperManager.FLAG_LOCK); - setColorPalette(colors.supportsDarkText(), colors.getColorPalette()); - onTimeTick(); - - return mRenderer.createPreview(view, width, height); - } - - @Override - public View getView() { - return null; - } - - @Override - public View getBigClockView() { - if (mView == null) { - createViews(); - } - return mView; - } - - @Override - public int getPreferredY(int totalHeight) { - return totalHeight / 2; - } - - @Override - public void setStyle(Style style) {} - - @Override - public void setTextColor(int color) { - mTextTime.setTextColor(color); - mTextDate.setTextColor(color); - } - - @Override - public void setColorPalette(boolean supportsDarkText, int[] colorPalette) {} - - @Override - public void onTimeTick() { - } - - @Override - public void setDarkAmount(float darkAmount) {} - - @Override - public void onTimeZoneChanged(TimeZone timeZone) {} - - @Override - public boolean shouldShowStatusArea() { - return true; - } -} diff --git a/packages/SystemUI/src/com/android/keyguard/clock/ImageClock.java b/packages/SystemUI/src/com/android/keyguard/clock/ImageClock.java deleted file mode 100644 index 34c041bbb2dc..000000000000 --- a/packages/SystemUI/src/com/android/keyguard/clock/ImageClock.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.keyguard.clock; - -import android.content.Context; -import android.text.format.DateFormat; -import android.util.AttributeSet; -import android.widget.FrameLayout; -import android.widget.ImageView; - -import com.android.systemui.R; - -import java.text.SimpleDateFormat; -import java.util.Calendar; -import java.util.TimeZone; - -/** - * Clock composed of two images that rotate with the time. - * - * The images are the clock hands. ImageClock expects two child ImageViews - * with ids hour_hand and minute_hand. - */ -public class ImageClock extends FrameLayout { - - private ImageView mHourHand; - private ImageView mMinuteHand; - private final Calendar mTime = Calendar.getInstance(TimeZone.getDefault()); - private String mDescFormat; - private TimeZone mTimeZone; - - public ImageClock(Context context) { - this(context, null); - } - - public ImageClock(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - public ImageClock(Context context, AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - mDescFormat = ((SimpleDateFormat) DateFormat.getTimeFormat(context)).toLocalizedPattern(); - } - - /** - * Call when the time changes to update the rotation of the clock hands. - */ - public void onTimeChanged() { - mTime.setTimeInMillis(System.currentTimeMillis()); - final float hourAngle = mTime.get(Calendar.HOUR) * 30f + mTime.get(Calendar.MINUTE) * 0.5f; - mHourHand.setRotation(hourAngle); - final float minuteAngle = mTime.get(Calendar.MINUTE) * 6f; - mMinuteHand.setRotation(minuteAngle); - setContentDescription(DateFormat.format(mDescFormat, mTime)); - invalidate(); - } - - /** - * Call when the time zone has changed to update clock hands. - * - * @param timeZone The updated time zone that will be used. - */ - public void onTimeZoneChanged(TimeZone timeZone) { - mTimeZone = timeZone; - mTime.setTimeZone(timeZone); - } - - /** - * Sets the colors to use on the clock face. - * @param dark Darker color obtained from color palette. - * @param light Lighter color obtained from color palette. - */ - public void setClockColors(int dark, int light) { - mHourHand.setColorFilter(dark); - mMinuteHand.setColorFilter(light); - } - - @Override - protected void onFinishInflate() { - super.onFinishInflate(); - mHourHand = findViewById(R.id.hour_hand); - mMinuteHand = findViewById(R.id.minute_hand); - } - - @Override - protected void onAttachedToWindow() { - super.onAttachedToWindow(); - mTime.setTimeZone(mTimeZone != null ? mTimeZone : TimeZone.getDefault()); - onTimeChanged(); - } -} diff --git a/packages/SystemUI/src/com/android/keyguard/clock/SettingsWrapper.java b/packages/SystemUI/src/com/android/keyguard/clock/SettingsWrapper.java deleted file mode 100644 index 096e94348429..000000000000 --- a/packages/SystemUI/src/com/android/keyguard/clock/SettingsWrapper.java +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.keyguard.clock; - -import android.annotation.Nullable; -import android.content.ContentResolver; -import android.provider.Settings; -import android.util.Log; - -import com.android.internal.annotations.VisibleForTesting; - -import org.json.JSONException; -import org.json.JSONObject; - -/** - * Wrapper around Settings used for testing. - */ -public class SettingsWrapper { - - private static final String TAG = "ClockFaceSettings"; - private static final String CUSTOM_CLOCK_FACE = Settings.Secure.LOCK_SCREEN_CUSTOM_CLOCK_FACE; - private static final String DOCKED_CLOCK_FACE = Settings.Secure.DOCKED_CLOCK_FACE; - private static final String CLOCK_FIELD = "clock"; - - private final ContentResolver mContentResolver; - private final Migration mMigration; - - SettingsWrapper(ContentResolver contentResolver) { - this(contentResolver, new Migrator(contentResolver)); - } - - @VisibleForTesting - SettingsWrapper(ContentResolver contentResolver, Migration migration) { - mContentResolver = contentResolver; - mMigration = migration; - } - - /** - * Gets the value stored in settings for the custom clock face. - * - * @param userId ID of the user. - */ - String getLockScreenCustomClockFace(int userId) { - return decode( - Settings.Secure.getStringForUser(mContentResolver, CUSTOM_CLOCK_FACE, userId), - userId); - } - - /** - * Gets the value stored in settings for the clock face to use when docked. - * - * @param userId ID of the user. - */ - String getDockedClockFace(int userId) { - return Settings.Secure.getStringForUser(mContentResolver, DOCKED_CLOCK_FACE, userId); - } - - /** - * Decodes the string stored in settings, which should be formatted as JSON. - * @param value String stored in settings. If value is not JSON, then the settings is - * overwritten with JSON containing the prior value. - * @return ID of the clock face to show on AOD and lock screen. If value is not JSON, the value - * is returned. - */ - @VisibleForTesting - String decode(@Nullable String value, int userId) { - if (value == null) { - return value; - } - JSONObject json; - try { - json = new JSONObject(value); - } catch (JSONException ex) { - Log.e(TAG, "Settings value is not valid JSON", ex); - // The settings value isn't JSON since it didn't parse so migrate the value to JSON. - // TODO(b/135674383): Remove this migration path in the following release. - mMigration.migrate(value, userId); - return value; - } - try { - return json.getString(CLOCK_FIELD); - } catch (JSONException ex) { - Log.e(TAG, "JSON object does not contain clock field.", ex); - return null; - } - } - - interface Migration { - void migrate(String value, int userId); - } - - /** - * Implementation of {@link Migration} that writes valid JSON back to Settings. - */ - private static final class Migrator implements Migration { - - private final ContentResolver mContentResolver; - - Migrator(ContentResolver contentResolver) { - mContentResolver = contentResolver; - } - - /** - * Migrate settings values that don't parse by converting to JSON format. - * - * Values in settings must be JSON to be backed up and restored. To help users maintain - * their current settings, convert existing values into the JSON format. - * - * TODO(b/135674383): Remove this migration code in the following release. - */ - @Override - public void migrate(String value, int userId) { - try { - JSONObject json = new JSONObject(); - json.put(CLOCK_FIELD, value); - Settings.Secure.putStringForUser(mContentResolver, CUSTOM_CLOCK_FACE, - json.toString(), - userId); - } catch (JSONException ex) { - Log.e(TAG, "Failed migrating settings value to JSON format", ex); - } - } - } -} diff --git a/packages/SystemUI/src/com/android/keyguard/clock/SmallClockPosition.java b/packages/SystemUI/src/com/android/keyguard/clock/SmallClockPosition.java deleted file mode 100644 index 4e51b98b0a4c..000000000000 --- a/packages/SystemUI/src/com/android/keyguard/clock/SmallClockPosition.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.keyguard.clock; - -import android.content.Context; -import android.util.MathUtils; - -import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.policy.SystemBarUtils; -import com.android.systemui.R; - -/** - * Computes preferred position of clock by considering height of status bar and lock icon. - */ -class SmallClockPosition { - - /** - * Dimensions used to determine preferred clock position. - */ - private final int mStatusBarHeight; - private final int mKeyguardLockPadding; - private final int mKeyguardLockHeight; - private final int mBurnInOffsetY; - - /** - * Amount of transition between AOD and lock screen. - */ - private float mDarkAmount; - - SmallClockPosition(Context context) { - this(SystemBarUtils.getStatusBarHeight(context), - context.getResources().getDimensionPixelSize(R.dimen.keyguard_lock_padding), - context.getResources().getDimensionPixelSize(R.dimen.keyguard_lock_height), - context.getResources().getDimensionPixelSize(R.dimen.burn_in_prevention_offset_y) - ); - } - - @VisibleForTesting - SmallClockPosition(int statusBarHeight, int lockPadding, int lockHeight, int burnInY) { - mStatusBarHeight = statusBarHeight; - mKeyguardLockPadding = lockPadding; - mKeyguardLockHeight = lockHeight; - mBurnInOffsetY = burnInY; - } - - /** - * See {@link ClockPlugin#setDarkAmount}. - */ - void setDarkAmount(float darkAmount) { - mDarkAmount = darkAmount; - } - - /** - * Gets the preferred Y position accounting for status bar and lock icon heights. - */ - int getPreferredY() { - // On AOD, clock needs to appear below the status bar with enough room for pixel shifting - int aodY = mStatusBarHeight + mKeyguardLockHeight + 2 * mKeyguardLockPadding - + mBurnInOffsetY; - // On lock screen, clock needs to appear below the lock icon - int lockY = mStatusBarHeight + mKeyguardLockHeight + 2 * mKeyguardLockPadding; - return (int) MathUtils.lerp(lockY, aodY, mDarkAmount); - } -} diff --git a/packages/SystemUI/src/com/android/keyguard/clock/ViewPreviewer.java b/packages/SystemUI/src/com/android/keyguard/clock/ViewPreviewer.java deleted file mode 100644 index abd0dd28dabc..000000000000 --- a/packages/SystemUI/src/com/android/keyguard/clock/ViewPreviewer.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.keyguard.clock; - -import android.annotation.Nullable; -import android.graphics.Bitmap; -import android.graphics.Canvas; -import android.graphics.Color; -import android.os.Handler; -import android.os.Looper; -import android.util.Log; -import android.view.View; -import android.view.ViewGroup; - -import java.util.concurrent.Callable; -import java.util.concurrent.FutureTask; - -/** - * Creates a preview image ({@link Bitmap}) of a {@link View} for a custom clock face. - */ -final class ViewPreviewer { - - private static final String TAG = "ViewPreviewer"; - - /** - * Handler used to run {@link View#draw(Canvas)} on the main thread. - */ - private final Handler mMainHandler = new Handler(Looper.getMainLooper()); - - /** - * Generate a realistic preview of a clock face. - * - * @param view view is used to generate preview image. - * @param width width of the preview image, should be the same as device width in pixels. - * @param height height of the preview image, should be the same as device height in pixels. - * @return bitmap of view. - */ - @Nullable - Bitmap createPreview(View view, int width, int height) { - if (view == null) { - return null; - } - FutureTask<Bitmap> task = new FutureTask<>(new Callable<Bitmap>() { - @Override - public Bitmap call() { - Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); - - // Draw clock view hierarchy to canvas. - Canvas canvas = new Canvas(bitmap); - canvas.drawColor(Color.BLACK); - dispatchVisibilityAggregated(view, true); - view.measure(View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY), - View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY)); - view.layout(0, 0, width, height); - view.draw(canvas); - - return bitmap; - } - }); - - if (Looper.myLooper() == Looper.getMainLooper()) { - task.run(); - } else { - mMainHandler.post(task); - } - - try { - return task.get(); - } catch (Exception e) { - Log.e(TAG, "Error completing task", e); - return null; - } - } - - private void dispatchVisibilityAggregated(View view, boolean isVisible) { - // Similar to View.dispatchVisibilityAggregated implementation. - final boolean thisVisible = view.getVisibility() == View.VISIBLE; - if (thisVisible || !isVisible) { - view.onVisibilityAggregated(isVisible); - } - - if (view instanceof ViewGroup) { - isVisible = thisVisible && isVisible; - ViewGroup vg = (ViewGroup) view; - int count = vg.getChildCount(); - - for (int i = 0; i < count; i++) { - dispatchVisibilityAggregated(vg.getChildAt(i), isVisible); - } - } - } -} diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerComponent.java b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerComponent.java index 154b0ed2c4d1..b57c2b58f3ef 100644 --- a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerComponent.java +++ b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerComponent.java @@ -20,7 +20,7 @@ import android.view.ViewGroup; import com.android.keyguard.KeyguardSecurityContainerController; import com.android.systemui.dagger.qualifiers.RootView; -import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerInteractor; +import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor; import dagger.BindsInstance; import dagger.Subcomponent; diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerModule.java b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerModule.java index 38f252a221eb..893239ba7ecb 100644 --- a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerModule.java +++ b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerModule.java @@ -28,7 +28,7 @@ import com.android.keyguard.KeyguardSecurityViewFlipper; import com.android.systemui.R; import com.android.systemui.biometrics.SideFpsController; import com.android.systemui.dagger.qualifiers.RootView; -import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerInteractor; +import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor; import java.util.Optional; diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java index be5bb07089dd..1f1b154ef1c8 100644 --- a/packages/SystemUI/src/com/android/systemui/Dependency.java +++ b/packages/SystemUI/src/com/android/systemui/Dependency.java @@ -33,7 +33,6 @@ import com.android.internal.statusbar.IStatusBarService; import com.android.internal.util.Preconditions; import com.android.keyguard.KeyguardSecurityModel; import com.android.keyguard.KeyguardUpdateMonitor; -import com.android.keyguard.clock.ClockManager; import com.android.settingslib.bluetooth.LocalBluetoothManager; import com.android.systemui.accessibility.AccessibilityButtonModeObserver; import com.android.systemui.accessibility.AccessibilityButtonTargetsObserver; @@ -323,7 +322,6 @@ public class Dependency { @Inject @Named(LEAK_REPORT_EMAIL_NAME) Lazy<String> mLeakReportEmail; @Inject @Main Lazy<Executor> mMainExecutor; @Inject @Background Lazy<Executor> mBackgroundExecutor; - @Inject Lazy<ClockManager> mClockManager; @Inject Lazy<ActivityManagerWrapper> mActivityManagerWrapper; @Inject Lazy<DevicePolicyManagerWrapper> mDevicePolicyManagerWrapper; @Inject Lazy<PackageManagerWrapper> mPackageManagerWrapper; @@ -517,7 +515,6 @@ public class Dependency { mProviders.put(SmartReplyController.class, mSmartReplyController::get); mProviders.put(RemoteInputQuickSettingsDisabler.class, mRemoteInputQuickSettingsDisabler::get); - mProviders.put(ClockManager.class, mClockManager::get); mProviders.put(PrivacyItemController.class, mPrivacyItemController::get); mProviders.put(ActivityManagerWrapper.class, mActivityManagerWrapper::get); mProviders.put(DevicePolicyManagerWrapper.class, mDevicePolicyManagerWrapper::get); diff --git a/packages/SystemUI/src/com/android/systemui/PhoneSystemUIAppComponentFactory.kt b/packages/SystemUI/src/com/android/systemui/PhoneSystemUIAppComponentFactory.kt new file mode 100644 index 000000000000..f06cb4104e1c --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/PhoneSystemUIAppComponentFactory.kt @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui + +import android.content.Context + +class PhoneSystemUIAppComponentFactory : SystemUIAppComponentFactoryBase() { + override fun createSystemUIInitializer(context: Context) = SystemUIInitializerImpl(context) +} diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorHwcLayer.kt b/packages/SystemUI/src/com/android/systemui/ScreenDecorHwcLayer.kt index 99dd6b6cd673..670c1fa45e5c 100644 --- a/packages/SystemUI/src/com/android/systemui/ScreenDecorHwcLayer.kt +++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorHwcLayer.kt @@ -49,8 +49,11 @@ import kotlin.math.floor * When the HWC of the device supports Composition.DISPLAY_DECORATION, we use this layer to draw * screen decorations. */ -class ScreenDecorHwcLayer(context: Context, displayDecorationSupport: DisplayDecorationSupport) : - DisplayCutoutBaseView(context) { +class ScreenDecorHwcLayer( + context: Context, + displayDecorationSupport: DisplayDecorationSupport, + private val debug: Boolean, +) : DisplayCutoutBaseView(context) { val colorMode: Int private val useInvertedAlphaColor: Boolean private val color: Int @@ -74,7 +77,7 @@ class ScreenDecorHwcLayer(context: Context, displayDecorationSupport: DisplayDec throw IllegalArgumentException("Attempting to use unsupported mode " + "${PixelFormat.formatToString(displayDecorationSupport.format)}") } - if (DEBUG_COLOR) { + if (debug) { color = Color.GREEN bgColor = Color.TRANSPARENT colorMode = ActivityInfo.COLOR_MODE_DEFAULT @@ -106,7 +109,7 @@ class ScreenDecorHwcLayer(context: Context, displayDecorationSupport: DisplayDec override fun onAttachedToWindow() { super.onAttachedToWindow() parent.requestTransparentRegion(this) - if (!DEBUG_COLOR) { + if (!debug) { viewRootImpl.setDisplayDecoration(true) } @@ -143,12 +146,12 @@ class ScreenDecorHwcLayer(context: Context, displayDecorationSupport: DisplayDec override fun gatherTransparentRegion(region: Region?): Boolean { region?.let { calculateTransparentRect() - if (DEBUG_COLOR) { + if (debug) { // Since we're going to draw a rectangle where the layer would // normally be transparent, treat the transparent region as // empty. We still want this method to be called, though, so // that it calculates the transparent rect at the right time - // to match !DEBUG_COLOR. + // to match ![debug] region.setEmpty() } else { region.op(transparentRect, Region.Op.INTERSECT) @@ -421,8 +424,4 @@ class ScreenDecorHwcLayer(context: Context, displayDecorationSupport: DisplayDec ipw.println("roundedCornerBottomSize=$roundedCornerBottomSize") ipw.decreaseIndent() } - - companion object { - private val DEBUG_COLOR = ScreenDecorations.DEBUG_COLOR - } } diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java index ea0f343e80f4..67d4a2e25051 100644 --- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java +++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java @@ -71,6 +71,7 @@ import com.android.systemui.biometrics.AuthController; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.decor.CutoutDecorProviderFactory; +import com.android.systemui.decor.DebugRoundedCornerDelegate; import com.android.systemui.decor.DecorProvider; import com.android.systemui.decor.DecorProviderFactory; import com.android.systemui.decor.DecorProviderKt; @@ -78,14 +79,12 @@ import com.android.systemui.decor.FaceScanningProviderFactory; import com.android.systemui.decor.OverlayWindow; import com.android.systemui.decor.PrivacyDotDecorProviderFactory; import com.android.systemui.decor.RoundedCornerDecorProviderFactory; -import com.android.systemui.decor.RoundedCornerResDelegate; +import com.android.systemui.decor.RoundedCornerResDelegateImpl; import com.android.systemui.log.ScreenDecorationsLogger; import com.android.systemui.qs.SettingObserver; import com.android.systemui.settings.DisplayTracker; import com.android.systemui.settings.UserTracker; import com.android.systemui.statusbar.events.PrivacyDotViewController; -import com.android.systemui.tuner.TunerService; -import com.android.systemui.tuner.TunerService.Tunable; import com.android.systemui.util.concurrency.DelayableExecutor; import com.android.systemui.util.concurrency.ThreadFactory; import com.android.systemui.util.settings.SecureSettings; @@ -105,19 +104,17 @@ import javax.inject.Inject; * for antialiasing and emulation purposes. */ @SysUISingleton -public class ScreenDecorations implements CoreStartable, Tunable , Dumpable { - private static final boolean DEBUG = false; +public class ScreenDecorations implements CoreStartable, Dumpable { + private static final boolean DEBUG_LOGGING = false; private static final String TAG = "ScreenDecorations"; - public static final String SIZE = "sysui_rounded_size"; - public static final String PADDING = "sysui_rounded_content_padding"; // Provide a way for factory to disable ScreenDecorations to run the Display tests. private static final boolean DEBUG_DISABLE_SCREEN_DECORATIONS = SystemProperties.getBoolean("debug.disable_screen_decorations", false); private static final boolean DEBUG_SCREENSHOT_ROUNDED_CORNERS = SystemProperties.getBoolean("debug.screenshot_rounded_corners", false); - private static final boolean VERBOSE = false; - static final boolean DEBUG_COLOR = DEBUG_SCREENSHOT_ROUNDED_CORNERS; + private boolean mDebug = DEBUG_SCREENSHOT_ROUNDED_CORNERS; + private int mDebugColor = Color.RED; private static final int[] DISPLAY_CUTOUT_IDS = { R.id.display_cutout, @@ -134,7 +131,6 @@ public class ScreenDecorations implements CoreStartable, Tunable , Dumpable { protected boolean mIsRegistered; private final Context mContext; private final Executor mMainExecutor; - private final TunerService mTunerService; private final SecureSettings mSecureSettings; @VisibleForTesting DisplayTracker.Callback mDisplayListener; @@ -147,9 +143,13 @@ public class ScreenDecorations implements CoreStartable, Tunable , Dumpable { public final int mFaceScanningViewId; @VisibleForTesting - protected RoundedCornerResDelegate mRoundedCornerResDelegate; + protected RoundedCornerResDelegateImpl mRoundedCornerResDelegate; @VisibleForTesting protected DecorProviderFactory mRoundedCornerFactory; + @VisibleForTesting + protected DebugRoundedCornerDelegate mDebugRoundedCornerDelegate = + new DebugRoundedCornerDelegate(); + protected DecorProviderFactory mDebugRoundedCornerFactory; private CutoutDecorProviderFactory mCutoutFactory; private int mProviderRefreshToken = 0; @VisibleForTesting @@ -315,7 +315,6 @@ public class ScreenDecorations implements CoreStartable, Tunable , Dumpable { public ScreenDecorations(Context context, @Main Executor mainExecutor, SecureSettings secureSettings, - TunerService tunerService, UserTracker userTracker, DisplayTracker displayTracker, PrivacyDotViewController dotViewController, @@ -327,7 +326,6 @@ public class ScreenDecorations implements CoreStartable, Tunable , Dumpable { mContext = context; mMainExecutor = mainExecutor; mSecureSettings = secureSettings; - mTunerService = tunerService; mUserTracker = userTracker; mDisplayTracker = displayTracker; mDotViewController = dotViewController; @@ -365,16 +363,47 @@ public class ScreenDecorations implements CoreStartable, Tunable , Dumpable { mAuthController.addCallback(mAuthControllerCallback); } + /** + * Change the value of {@link ScreenDecorations#mDebug}. This operation is heavyweight, since + * it requires essentially re-init-ing this screen decorations process with the debug + * information taken into account. + */ + @VisibleForTesting + protected void setDebug(boolean debug) { + if (mDebug == debug) { + return; + } + + mDebug = debug; + if (!mDebug) { + mDebugRoundedCornerDelegate.removeDebugState(); + } + + mExecutor.execute(() -> { + // Re-trigger all of the screen decorations setup here so that the debug values + // can be picked up + removeAllOverlays(); + removeHwcOverlay(); + startOnScreenDecorationsThread(); + updateColorInversionDefault(); + }); + } + private boolean isPrivacyDotEnabled() { return mDotFactory.getHasProviders(); } @NonNull - private List<DecorProvider> getProviders(boolean hasHwLayer) { + @VisibleForTesting + protected List<DecorProvider> getProviders(boolean hasHwLayer) { List<DecorProvider> decorProviders = new ArrayList<>(mDotFactory.getProviders()); decorProviders.addAll(mFaceScanningFactory.getProviders()); if (!hasHwLayer) { - decorProviders.addAll(mRoundedCornerFactory.getProviders()); + if (mDebug && mDebugRoundedCornerFactory.getHasProviders()) { + decorProviders.addAll(mDebugRoundedCornerFactory.getProviders()); + } else { + decorProviders.addAll(mRoundedCornerFactory.getProviders()); + } decorProviders.addAll(mCutoutFactory.getProviders()); } return decorProviders; @@ -416,11 +445,13 @@ public class ScreenDecorations implements CoreStartable, Tunable , Dumpable { mDisplayMode = mDisplayInfo.getMode(); mDisplayUniqueId = mDisplayInfo.uniqueId; mDisplayCutout = mDisplayInfo.displayCutout; - mRoundedCornerResDelegate = new RoundedCornerResDelegate(mContext.getResources(), - mDisplayUniqueId); + mRoundedCornerResDelegate = + new RoundedCornerResDelegateImpl(mContext.getResources(), mDisplayUniqueId); mRoundedCornerResDelegate.setPhysicalPixelDisplaySizeRatio( getPhysicalPixelDisplaySizeRatio()); mRoundedCornerFactory = new RoundedCornerDecorProviderFactory(mRoundedCornerResDelegate); + mDebugRoundedCornerFactory = + new RoundedCornerDecorProviderFactory(mDebugRoundedCornerDelegate); mCutoutFactory = getCutoutFactory(); mHwcScreenDecorationSupport = mContext.getDisplay().getDisplayDecorationSupport(); updateHwLayerRoundedCornerDrawable(); @@ -444,15 +475,12 @@ public class ScreenDecorations implements CoreStartable, Tunable , Dumpable { // - we are trying to redraw. This because WM resized our window and told us to. // - the config change has been dispatched, so WM is no longer deferring layout. mPendingConfigChange = true; - if (DEBUG) { - if (mRotation != newRotation) { - Log.i(TAG, "Rotation changed, deferring " + newRotation - + ", staying at " + mRotation); - } - if (displayModeChanged(mDisplayMode, newDisplayMode)) { - Log.i(TAG, "Resolution changed, deferring " + newDisplayMode - + ", staying at " + mDisplayMode); - } + if (mRotation != newRotation) { + mLogger.logRotationChangeDeferred(mRotation, newRotation); + } + if (displayModeChanged(mDisplayMode, newDisplayMode)) { + mLogger.logDisplayModeChanged( + newDisplayMode.getModeId(), mDisplayMode.getModeId()); } if (mOverlays != null) { @@ -608,12 +636,6 @@ public class ScreenDecorations implements CoreStartable, Tunable , Dumpable { return; } - mMainExecutor.execute(() -> { - Trace.beginSection("ScreenDecorations#addTunable"); - mTunerService.addTunable(this, SIZE); - Trace.endSection(); - }); - // Watch color inversion and invert the overlay as needed. if (mColorInversionSetting == null) { mColorInversionSetting = new SettingObserver(mSecureSettings, mHandler, @@ -632,12 +654,6 @@ public class ScreenDecorations implements CoreStartable, Tunable , Dumpable { mUserTracker.addCallback(mUserChangedCallback, mExecutor); mIsRegistered = true; } else { - mMainExecutor.execute(() -> { - Trace.beginSection("ScreenDecorations#removeTunable"); - mTunerService.removeTunable(this); - Trace.endSection(); - }); - if (mColorInversionSetting != null) { mColorInversionSetting.setListening(false); } @@ -777,7 +793,8 @@ public class ScreenDecorations implements CoreStartable, Tunable , Dumpable { } mScreenDecorHwcWindow = (ViewGroup) LayoutInflater.from(mContext).inflate( R.layout.screen_decor_hwc_layer, null); - mScreenDecorHwcLayer = new ScreenDecorHwcLayer(mContext, mHwcScreenDecorationSupport); + mScreenDecorHwcLayer = + new ScreenDecorHwcLayer(mContext, mHwcScreenDecorationSupport, mDebug); mScreenDecorHwcWindow.addView(mScreenDecorHwcLayer, new FrameLayout.LayoutParams( MATCH_PARENT, MATCH_PARENT, Gravity.TOP | Gravity.START)); mWindowManager.addView(mScreenDecorHwcWindow, getHwcWindowLayoutParams()); @@ -825,7 +842,7 @@ public class ScreenDecorations implements CoreStartable, Tunable , Dumpable { lp.height = MATCH_PARENT; lp.setTitle("ScreenDecorHwcOverlay"); lp.gravity = Gravity.TOP | Gravity.START; - if (!DEBUG_COLOR) { + if (!mDebug) { lp.setColorMode(ActivityInfo.COLOR_MODE_A8); } return lp; @@ -933,19 +950,42 @@ public class ScreenDecorations implements CoreStartable, Tunable , Dumpable { new UserTracker.Callback() { @Override public void onUserChanged(int newUser, @NonNull Context userContext) { - if (DEBUG) { - Log.d(TAG, "UserSwitched newUserId=" + newUser); - } + mLogger.logUserSwitched(newUser); // update color inversion setting to the new user mColorInversionSetting.setUserId(newUser); updateColorInversion(mColorInversionSetting.getValue()); } }; + /** + * Use the current value of {@link ScreenDecorations#mColorInversionSetting} and passes it + * to {@link ScreenDecorations#updateColorInversion} + */ + private void updateColorInversionDefault() { + int inversion = 0; + if (mColorInversionSetting != null) { + inversion = mColorInversionSetting.getValue(); + } + + updateColorInversion(inversion); + } + + /** + * Update the tint color of screen decoration assets. Defaults to Color.BLACK. In the case of + * a color inversion being set, use Color.WHITE (which inverts to black). + * + * When {@link ScreenDecorations#mDebug} is {@code true}, this value is updated to use + * {@link ScreenDecorations#mDebugColor}, and does not handle inversion. + * + * @param colorsInvertedValue if non-zero, assume that colors are inverted, and use Color.WHITE + * for screen decoration tint + */ private void updateColorInversion(int colorsInvertedValue) { mTintColor = colorsInvertedValue != 0 ? Color.WHITE : Color.BLACK; - if (DEBUG_COLOR) { - mTintColor = Color.RED; + if (mDebug) { + mTintColor = mDebugColor; + mDebugRoundedCornerDelegate.setColor(mTintColor); + //TODO(b/285941724): update the hwc layer color here too (or disable it in debug mode) } updateOverlayProviderViews(new Integer[] { @@ -986,7 +1026,9 @@ public class ScreenDecorations implements CoreStartable, Tunable , Dumpable { int oldRotation = mRotation; mPendingConfigChange = false; updateConfiguration(); - if (DEBUG) Log.i(TAG, "onConfigChanged from rot " + oldRotation + " to " + mRotation); + if (oldRotation != mRotation) { + mLogger.logRotationChanged(oldRotation, mRotation); + } setupDecorations(); if (mOverlays != null) { // Updating the layout params ensures that ViewRootImpl will call relayoutWindow(), @@ -1016,6 +1058,7 @@ public class ScreenDecorations implements CoreStartable, Tunable , Dumpable { if (DEBUG_DISABLE_SCREEN_DECORATIONS) { return; } + ipw.println("mDebug:" + mDebug); ipw.println("mIsPrivacyDotEnabled:" + isPrivacyDotEnabled()); ipw.println("shouldOptimizeOverlayVisibility:" + shouldOptimizeVisibility()); @@ -1071,6 +1114,7 @@ public class ScreenDecorations implements CoreStartable, Tunable , Dumpable { } } mRoundedCornerResDelegate.dump(pw, args); + mDebugRoundedCornerDelegate.dump(pw); } @VisibleForTesting @@ -1093,8 +1137,9 @@ public class ScreenDecorations implements CoreStartable, Tunable , Dumpable { mRotation = newRotation; mDisplayMode = newMod; mDisplayCutout = newCutout; - mRoundedCornerResDelegate.setPhysicalPixelDisplaySizeRatio( - getPhysicalPixelDisplaySizeRatio()); + float ratio = getPhysicalPixelDisplaySizeRatio(); + mRoundedCornerResDelegate.setPhysicalPixelDisplaySizeRatio(ratio); + mDebugRoundedCornerDelegate.setPhysicalPixelDisplaySizeRatio(ratio); if (mScreenDecorHwcLayer != null) { mScreenDecorHwcLayer.pendingConfigChange = false; mScreenDecorHwcLayer.updateConfiguration(mDisplayUniqueId); @@ -1117,7 +1162,8 @@ public class ScreenDecorations implements CoreStartable, Tunable , Dumpable { } private boolean hasRoundedCorners() { - return mRoundedCornerFactory.getHasProviders(); + return mRoundedCornerFactory.getHasProviders() + || mDebugRoundedCornerFactory.getHasProviders(); } private boolean shouldOptimizeVisibility() { @@ -1170,41 +1216,17 @@ public class ScreenDecorations implements CoreStartable, Tunable , Dumpable { Trace.endSection(); } - @Override - public void onTuningChanged(String key, String newValue) { - if (DEBUG_DISABLE_SCREEN_DECORATIONS) { - Log.i(TAG, "ScreenDecorations is disabled"); - return; - } - mExecutor.execute(() -> { - if (mOverlays == null || !SIZE.equals(key)) { - return; - } - Trace.beginSection("ScreenDecorations#onTuningChanged"); - try { - final int sizeFactor = Integer.parseInt(newValue); - mRoundedCornerResDelegate.setTuningSizeFactor(sizeFactor); - } catch (NumberFormatException e) { - mRoundedCornerResDelegate.setTuningSizeFactor(null); - } - updateOverlayProviderViews(new Integer[] { - R.id.rounded_corner_top_left, - R.id.rounded_corner_top_right, - R.id.rounded_corner_bottom_left, - R.id.rounded_corner_bottom_right - }); - updateHwLayerRoundedCornerExistAndSize(); - Trace.endSection(); - }); - } - private void updateHwLayerRoundedCornerDrawable() { if (mScreenDecorHwcLayer == null) { return; } - final Drawable topDrawable = mRoundedCornerResDelegate.getTopRoundedDrawable(); - final Drawable bottomDrawable = mRoundedCornerResDelegate.getBottomRoundedDrawable(); + Drawable topDrawable = mRoundedCornerResDelegate.getTopRoundedDrawable(); + Drawable bottomDrawable = mRoundedCornerResDelegate.getBottomRoundedDrawable(); + if (mDebug && (mDebugRoundedCornerFactory.getHasProviders())) { + topDrawable = mDebugRoundedCornerDelegate.getTopRoundedDrawable(); + bottomDrawable = mDebugRoundedCornerDelegate.getBottomRoundedDrawable(); + } if (topDrawable == null || bottomDrawable == null) { return; @@ -1216,11 +1238,19 @@ public class ScreenDecorations implements CoreStartable, Tunable , Dumpable { if (mScreenDecorHwcLayer == null) { return; } - mScreenDecorHwcLayer.updateRoundedCornerExistenceAndSize( - mRoundedCornerResDelegate.getHasTop(), - mRoundedCornerResDelegate.getHasBottom(), - mRoundedCornerResDelegate.getTopRoundedSize().getWidth(), - mRoundedCornerResDelegate.getBottomRoundedSize().getWidth()); + if (mDebug && mDebugRoundedCornerFactory.getHasProviders()) { + mScreenDecorHwcLayer.updateRoundedCornerExistenceAndSize( + mDebugRoundedCornerDelegate.getHasTop(), + mDebugRoundedCornerDelegate.getHasBottom(), + mDebugRoundedCornerDelegate.getTopRoundedSize().getWidth(), + mDebugRoundedCornerDelegate.getBottomRoundedSize().getWidth()); + } else { + mScreenDecorHwcLayer.updateRoundedCornerExistenceAndSize( + mRoundedCornerResDelegate.getHasTop(), + mRoundedCornerResDelegate.getHasBottom(), + mRoundedCornerResDelegate.getTopRoundedSize().getWidth(), + mRoundedCornerResDelegate.getBottomRoundedSize().getWidth()); + } } @VisibleForTesting @@ -1247,7 +1277,7 @@ public class ScreenDecorations implements CoreStartable, Tunable , Dumpable { paint.setColor(mColor); paint.setStyle(Paint.Style.FILL); - if (DEBUG) { + if (DEBUG_LOGGING) { getViewTreeObserver().addOnDrawListener(() -> Log.i(TAG, getWindowTitleByPos(pos) + " drawn in rot " + mRotation)); } @@ -1440,7 +1470,7 @@ public class ScreenDecorations implements CoreStartable, Tunable , Dumpable { mView.getViewTreeObserver().removeOnPreDrawListener(this); if (mTargetRotation == mRotation && !displayModeChanged(mDisplayMode, mTargetDisplayMode)) { - if (DEBUG) { + if (DEBUG_LOGGING) { final String title = mPosition < 0 ? "ScreenDecorHwcLayer" : getWindowTitleByPos(mPosition); Log.i(TAG, title + " already in target rot " @@ -1456,7 +1486,7 @@ public class ScreenDecorations implements CoreStartable, Tunable , Dumpable { // This changes the window attributes - we need to restart the traversal for them to // take effect. updateConfiguration(); - if (DEBUG) { + if (DEBUG_LOGGING) { final String title = mPosition < 0 ? "ScreenDecorHwcLayer" : getWindowTitleByPos(mPosition); Log.i(TAG, title @@ -1491,7 +1521,7 @@ public class ScreenDecorations implements CoreStartable, Tunable , Dumpable { final Display.Mode displayMode = mDisplayInfo.getMode(); if ((displayRotation != mRotation || displayModeChanged(mDisplayMode, displayMode)) && !mPendingConfigChange) { - if (DEBUG) { + if (DEBUG_LOGGING) { if (displayRotation != mRotation) { Log.i(TAG, "Drawing rot " + mRotation + ", but display is at rot " + displayRotation + ". Restarting draw"); diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java index 806d1aff3a30..3ca74acf2d66 100644 --- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java +++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java @@ -81,9 +81,6 @@ public class SwipeHelper implements Gefingerpoken, Dumpable { protected final Handler mHandler; - private float mMinSwipeProgress = 0f; - private float mMaxSwipeProgress = 1f; - private final SpringConfig mSnapBackSpringConfig = new SpringConfig(SpringForce.STIFFNESS_LOW, SpringForce.DAMPING_RATIO_LOW_BOUNCY); @@ -227,18 +224,11 @@ public class SwipeHelper implements Gefingerpoken, Dumpable { return v.getMeasuredWidth(); } - public void setMinSwipeProgress(float minSwipeProgress) { - mMinSwipeProgress = minSwipeProgress; - } - - public void setMaxSwipeProgress(float maxSwipeProgress) { - mMaxSwipeProgress = maxSwipeProgress; - } - private float getSwipeProgressForOffset(View view, float translation) { + if (translation == 0) return 0; float viewSize = getSize(view); float result = Math.abs(translation / viewSize); - return Math.min(Math.max(mMinSwipeProgress, result), mMaxSwipeProgress); + return Math.min(Math.max(0, result), 1); } /** diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIAppComponentFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIAppComponentFactory.java deleted file mode 100644 index 527ce127820e..000000000000 --- a/packages/SystemUI/src/com/android/systemui/SystemUIAppComponentFactory.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui; - -import android.content.Context; - -/** - * Starts up SystemUI using the AOSP {@link SystemUIInitializerImpl}. - * - * This initializer relies on reflection to start everything up and should be considered deprecated. - * Instead, create your own {@link SystemUIAppComponentFactoryBase}, specify it in your - * AndroidManifest.xml and construct your own {@link SystemUIInitializer} directly. - * - * @deprecated Define your own SystemUIAppComponentFactoryBase implementation and use that. This - * implementation may be changed or removed in future releases. - */ -@Deprecated -public class SystemUIAppComponentFactory extends SystemUIAppComponentFactoryBase { - @Override - protected SystemUIInitializer createSystemUIInitializer(Context context) { - return SystemUIInitializerFactory.createWithContext(context); - } -} diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java index 70c39df2a610..453d1d117d0d 100644 --- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java +++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java @@ -54,7 +54,7 @@ import javax.inject.Provider; * Application class for SystemUI. */ public class SystemUIApplication extends Application implements - SystemUIAppComponentFactory.ContextInitializer { + SystemUIAppComponentFactoryBase.ContextInitializer { public static final String TAG = "SystemUIService"; private static final boolean DEBUG = false; @@ -66,7 +66,7 @@ public class SystemUIApplication extends Application implements */ private CoreStartable[] mServices; private boolean mServicesStarted; - private SystemUIAppComponentFactory.ContextAvailableCallback mContextAvailableCallback; + private SystemUIAppComponentFactoryBase.ContextAvailableCallback mContextAvailableCallback; private SysUIComponent mSysUIComponent; private SystemUIInitializer mInitializer; @@ -366,7 +366,7 @@ public class SystemUIApplication extends Application implements @Override public void setContextAvailableCallback( - SystemUIAppComponentFactory.ContextAvailableCallback callback) { + SystemUIAppComponentFactoryBase.ContextAvailableCallback callback) { mContextAvailableCallback = callback; } diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIInitializerFactory.kt b/packages/SystemUI/src/com/android/systemui/SystemUIInitializerFactory.kt deleted file mode 100644 index b9454e8c3be8..000000000000 --- a/packages/SystemUI/src/com/android/systemui/SystemUIInitializerFactory.kt +++ /dev/null @@ -1,72 +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 - -import android.annotation.SuppressLint -import android.content.Context -import android.util.Log -import com.android.internal.annotations.VisibleForTesting -import com.android.systemui.util.Assert - -/** - * Factory to reflectively lookup a [SystemUIInitializer] to start SystemUI with. - */ -@Deprecated("Provide your own {@link SystemUIAppComponentFactoryBase} that doesn't need this.") -object SystemUIInitializerFactory { - private const val TAG = "SysUIInitializerFactory" - @SuppressLint("StaticFieldLeak") - private var initializer: SystemUIInitializer? = null - - /** - * Instantiate a [SystemUIInitializer] reflectively. - */ - @JvmStatic - fun createWithContext(context: Context): SystemUIInitializer { - return createFromConfig(context) - } - - /** - * Instantiate a [SystemUIInitializer] reflectively. - */ - @JvmStatic - private fun createFromConfig(context: Context): SystemUIInitializer { - Assert.isMainThread() - - return createFromConfigNoAssert(context) - } - - @JvmStatic - @VisibleForTesting - fun createFromConfigNoAssert(context: Context): SystemUIInitializer { - - return initializer ?: run { - val className = context.getString(R.string.config_systemUIFactoryComponent) - if (className.isEmpty()) { - throw RuntimeException("No SystemUIFactory component configured") - } - try { - val cls = context.classLoader.loadClass(className) - val constructor = cls.getConstructor(Context::class.java) - (constructor.newInstance(context) as SystemUIInitializer).apply { - initializer = this - } - } catch (t: Throwable) { - Log.w(TAG, "Error creating SystemUIInitializer component: $className", t) - throw t - } - } - } -} diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIService.java b/packages/SystemUI/src/com/android/systemui/SystemUIService.java index 50e03992df49..6cf9eff1da2f 100644 --- a/packages/SystemUI/src/com/android/systemui/SystemUIService.java +++ b/packages/SystemUI/src/com/android/systemui/SystemUIService.java @@ -30,8 +30,10 @@ import com.android.internal.os.BinderInternal; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dump.DumpHandler; +import com.android.systemui.dump.LogBufferEulogizer; import com.android.systemui.dump.LogBufferFreezer; import com.android.systemui.dump.SystemUIAuxiliaryDumpService; +import com.android.systemui.shared.system.UncaughtExceptionPreHandlerManager; import com.android.systemui.statusbar.policy.BatteryStateNotifier; import java.io.FileDescriptor; @@ -44,22 +46,29 @@ public class SystemUIService extends Service { private final Handler mMainHandler; private final DumpHandler mDumpHandler; private final BroadcastDispatcher mBroadcastDispatcher; + private final LogBufferEulogizer mLogBufferEulogizer; private final LogBufferFreezer mLogBufferFreezer; private final BatteryStateNotifier mBatteryStateNotifier; + private final UncaughtExceptionPreHandlerManager mUncaughtExceptionPreHandlerManager; + @Inject public SystemUIService( @Main Handler mainHandler, DumpHandler dumpHandler, BroadcastDispatcher broadcastDispatcher, + LogBufferEulogizer logBufferEulogizer, LogBufferFreezer logBufferFreezer, - BatteryStateNotifier batteryStateNotifier) { + BatteryStateNotifier batteryStateNotifier, + UncaughtExceptionPreHandlerManager uncaughtExceptionPreHandlerManager) { super(); mMainHandler = mainHandler; mDumpHandler = dumpHandler; mBroadcastDispatcher = broadcastDispatcher; + mLogBufferEulogizer = logBufferEulogizer; mLogBufferFreezer = logBufferFreezer; mBatteryStateNotifier = batteryStateNotifier; + mUncaughtExceptionPreHandlerManager = uncaughtExceptionPreHandlerManager; } @Override @@ -71,7 +80,10 @@ public class SystemUIService extends Service { // Finish initializing dump logic mLogBufferFreezer.attach(mBroadcastDispatcher); - mDumpHandler.init(); + + // Attempt to dump all LogBuffers for any uncaught exception + mUncaughtExceptionPreHandlerManager.registerHandler( + (thread, throwable) -> mLogBufferEulogizer.record(throwable)); // If configured, set up a battery notification if (getResources().getBoolean(R.bool.config_showNotificationForUnknownBatteryState)) { diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationSettingsController.java b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationSettingsController.java index c3bb423e5e4e..fd3c15889822 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationSettingsController.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationSettingsController.java @@ -87,14 +87,15 @@ public class MagnificationSettingsController implements ComponentCallbacks { } /** - * Shows magnification settings panel {@link WindowMagnificationSettings}. + * Toggles the visibility of magnification settings panel {@link WindowMagnificationSettings}. + * We show the panel if it is not visible. Otherwise, hide the panel. */ - void showMagnificationSettings() { + void toggleSettingsPanelVisibility() { if (!mWindowMagnificationSettings.isSettingPanelShowing()) { onConfigurationChanged(mContext.getResources().getConfiguration()); mContext.registerComponentCallbacks(this); } - mWindowMagnificationSettings.showSettingPanel(); + mWindowMagnificationSettings.toggleSettingsPanelVisibility(); } void closeMagnificationSettings() { diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java b/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java index 4158390ec953..2f6a68c3ff8d 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java @@ -329,7 +329,9 @@ public class SystemActions implements CoreStartable { // binder calls final Optional<CentralSurfaces> centralSurfacesOptional = mCentralSurfacesOptionalLazy.get(); - if (centralSurfacesOptional.map(CentralSurfaces::isPanelExpanded).orElse(false) + if (centralSurfacesOptional.isPresent() + && centralSurfacesOptional.get().getShadeViewController() != null + && centralSurfacesOptional.get().getShadeViewController().isPanelExpanded() && !centralSurfacesOptional.get().isKeyguardShowing()) { if (!mDismissNotificationShadeActionRegistered) { mA11yManager.registerSystemAction( @@ -485,13 +487,11 @@ public class SystemActions implements CoreStartable { } private void handleNotifications() { - mCentralSurfacesOptionalLazy.get().ifPresent( - CentralSurfaces::animateExpandNotificationsPanel); + mShadeController.animateExpandShade(); } private void handleQuickSettings() { - mCentralSurfacesOptionalLazy.get().ifPresent( - centralSurfaces -> centralSurfaces.animateExpandSettingsPanel(null)); + mShadeController.animateExpandQs(); } private void handlePowerDialog() { diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java index e2b85fa0ac00..2a14dc894d43 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java @@ -171,7 +171,7 @@ public class WindowMagnification implements CoreStartable, CommandQueue.Callback mModeSwitchesController.setClickListenerDelegate( displayId -> mHandler.post(() -> { - showMagnificationSettingsPanel(displayId); + toggleSettingsPanelVisibility(displayId); })); } @@ -254,11 +254,11 @@ public class WindowMagnification implements CoreStartable, CommandQueue.Callback } @MainThread - void showMagnificationSettingsPanel(int displayId) { + void toggleSettingsPanelVisibility(int displayId) { final MagnificationSettingsController magnificationSettingsController = mMagnificationSettingsSupplier.get(displayId); if (magnificationSettingsController != null) { - magnificationSettingsController.showMagnificationSettings(); + magnificationSettingsController.toggleSettingsPanelVisibility(); } } @@ -335,7 +335,7 @@ public class WindowMagnification implements CoreStartable, CommandQueue.Callback @Override public void onClickSettingsButton(int displayId) { mHandler.post(() -> { - showMagnificationSettingsPanel(displayId); + toggleSettingsPanelVisibility(displayId); }); } }; diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java index a67f706777d9..e7eab7e462e1 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java @@ -76,6 +76,7 @@ import android.widget.ImageView; import androidx.core.math.MathUtils; +import com.android.internal.accessibility.common.MagnificationConstants; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.graphics.SfVsyncFrameCallbackProvider; import com.android.systemui.R; @@ -101,7 +102,9 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold // Delay to avoid updating state description too frequently. private static final int UPDATE_STATE_DESCRIPTION_DELAY_MS = 100; // It should be consistent with the value defined in WindowMagnificationGestureHandler. - private static final Range<Float> A11Y_ACTION_SCALE_RANGE = new Range<>(1.0f, 8.0f); + private static final Range<Float> A11Y_ACTION_SCALE_RANGE = new Range<>( + MagnificationConstants.SCALE_MIN_VALUE, + MagnificationConstants.SCALE_MAX_VALUE); private static final float A11Y_CHANGE_SCALE_DIFFERENCE = 1.0f; private static final float ANIMATION_BOUNCE_EFFECT_SCALE = 1.05f; private final SparseArray<Float> mMagnificationSizeScaleOptions = new SparseArray<>(); @@ -221,6 +224,7 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold private boolean mAllowDiagonalScrolling = false; private boolean mEditSizeEnable = false; + private boolean mSettingsPanelVisibility = false; @Nullable private final MirrorWindowControl mMirrorWindowControl; @@ -1399,6 +1403,8 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold return; } + mSettingsPanelVisibility = settingsPanelIsShown; + mDragView.setBackground(mContext.getResources().getDrawable(settingsPanelIsShown ? R.drawable.accessibility_window_magnification_drag_handle_background_change : R.drawable.accessibility_window_magnification_drag_handle_background)); @@ -1439,12 +1445,19 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold private class MirrorWindowA11yDelegate extends View.AccessibilityDelegate { + private CharSequence getClickAccessibilityActionLabel() { + return mSettingsPanelVisibility + ? mContext.getResources().getString( + R.string.magnification_close_settings_click_label) + : mContext.getResources().getString( + R.string.magnification_open_settings_click_label); + } + @Override public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) { super.onInitializeAccessibilityNodeInfo(host, info); final AccessibilityAction clickAction = new AccessibilityAction( - AccessibilityAction.ACTION_CLICK.getId(), mContext.getResources().getString( - R.string.magnification_open_settings_click_label)); + AccessibilityAction.ACTION_CLICK.getId(), getClickAccessibilityActionLabel()); info.addAction(clickAction); info.setClickable(true); info.addAction( diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettings.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettings.java index 3b1d695e3dad..31b0f056df8d 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettings.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettings.java @@ -18,10 +18,12 @@ package com.android.systemui.accessibility; import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL; import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN; -import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_NONE; import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW; import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; +import static com.android.internal.accessibility.common.MagnificationConstants.SCALE_MAX_VALUE; +import static com.android.internal.accessibility.common.MagnificationConstants.SCALE_MIN_VALUE; + import android.annotation.IntDef; import android.content.BroadcastReceiver; import android.content.Context; @@ -79,14 +81,16 @@ class WindowMagnificationSettings implements MagnificationGestureDetector.OnGest private final Runnable mWindowInsetChangeRunnable; private final SfVsyncFrameCallbackProvider mSfVsyncFrameProvider; - private final LayoutParams mParams; + @VisibleForTesting + final LayoutParams mParams; @VisibleForTesting final Rect mDraggableWindowBounds = new Rect(); private boolean mIsVisible = false; private final MagnificationGestureDetector mGestureDetector; private boolean mSingleTapDetected = false; - private SeekBarWithIconButtonsView mZoomSeekbar; + @VisibleForTesting + SeekBarWithIconButtonsView mZoomSeekbar; private LinearLayout mAllowDiagonalScrollingView; private TextView mAllowDiagonalScrollingTitle; private Switch mAllowDiagonalScrollingSwitch; @@ -101,11 +105,15 @@ class WindowMagnificationSettings implements MagnificationGestureDetector.OnGest private ImageButton mFullScreenButton; private int mLastSelectedButtonIndex = MagnificationSize.NONE; private boolean mAllowDiagonalScrolling = false; - private static final float A11Y_CHANGE_SCALE_DIFFERENCE = 1.0f; - private static final float A11Y_SCALE_MIN_VALUE = 1.0f; + /** + * Amount by which magnification scale changes compared to seekbar in settings. + * magnitude = 10 means, for every 1 scale increase, 10 progress increase in seekbar. + */ + private int mSeekBarMagnitude; private WindowMagnificationSettingsCallback mCallback; private ContentObserver mMagnificationCapabilityObserver; + private ContentObserver mMagnificationScaleObserver; @Retention(RetentionPolicy.SOURCE) @IntDef({ @@ -155,18 +163,26 @@ class WindowMagnificationSettings implements MagnificationGestureDetector.OnGest }); } }; + mMagnificationScaleObserver = new ContentObserver( + mContext.getMainThreadHandler()) { + @Override + public void onChange(boolean selfChange) { + setScaleSeekbar(getMagnificationScale()); + } + }; } private class ZoomSeekbarChangeListener implements SeekBar.OnSeekBarChangeListener { @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { - float scale = progress * A11Y_CHANGE_SCALE_DIFFERENCE + A11Y_SCALE_MIN_VALUE; + float scale = (progress / (float) mSeekBarMagnitude) + SCALE_MIN_VALUE; // Update persisted scale only when scale >= PERSISTED_SCALE_MIN_VALUE const. // We assume if the scale is lower than the PERSISTED_SCALE_MIN_VALUE, there will be // no obvious magnification effect. if (scale >= MagnificationConstants.PERSISTED_SCALE_MIN_VALUE) { - Settings.Secure.putFloatForUser(mContext.getContentResolver(), - Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE, scale, + mSecureSettings.putFloatForUser( + Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE, + scale, UserHandle.USER_CURRENT); } mCallback.onMagnifierScale(scale); @@ -300,6 +316,7 @@ class WindowMagnificationSettings implements MagnificationGestureDetector.OnGest // Unregister observer before removing view mSecureSettings.unregisterContentObserver(mMagnificationCapabilityObserver); + mSecureSettings.unregisterContentObserver(mMagnificationScaleObserver); mWindowManager.removeView(mSettingView); mIsVisible = false; if (resetPosition) { @@ -311,6 +328,14 @@ class WindowMagnificationSettings implements MagnificationGestureDetector.OnGest mCallback.onSettingsPanelVisibilityChanged(/* shown= */ false); } + public void toggleSettingsPanelVisibility() { + if (!mIsVisible) { + showSettingPanel(); + } else { + hideSettingPanel(); + } + } + public void showSettingPanel() { showSettingPanel(true); } @@ -320,7 +345,13 @@ class WindowMagnificationSettings implements MagnificationGestureDetector.OnGest } public void setScaleSeekbar(float scale) { - setSeekbarProgress(scale); + int index = (int) ((scale - SCALE_MIN_VALUE) * mSeekBarMagnitude); + if (index < 0) { + index = 0; + } else if (index > mZoomSeekbar.getMax()) { + index = mZoomSeekbar.getMax(); + } + mZoomSeekbar.setProgress(index); } private void transitToMagnificationMode(int mode) { @@ -337,6 +368,7 @@ class WindowMagnificationSettings implements MagnificationGestureDetector.OnGest private void showSettingPanel(boolean resetPosition) { if (!mIsVisible) { updateUIControlsIfNeeded(); + setScaleSeekbar(getMagnificationScale()); if (resetPosition) { mDraggableWindowBounds.set(getDraggableWindowBounds()); mParams.x = mDraggableWindowBounds.right; @@ -349,6 +381,10 @@ class WindowMagnificationSettings implements MagnificationGestureDetector.OnGest Settings.Secure.ACCESSIBILITY_MAGNIFICATION_CAPABILITY, mMagnificationCapabilityObserver, UserHandle.USER_CURRENT); + mSecureSettings.registerContentObserverForUser( + Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE, + mMagnificationScaleObserver, + UserHandle.USER_CURRENT); // Exclude magnification switch button from system gesture area. setSystemGestureExclusion(); @@ -368,22 +404,35 @@ class WindowMagnificationSettings implements MagnificationGestureDetector.OnGest } private int getMagnificationMode() { + // If current capability is window mode, we would like the default value of the mode to + // be WINDOW, otherwise, the default value would be FULLSCREEN. + int defaultValue = + (getMagnificationCapability() == ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW) + ? ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW + : ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN; + return mSecureSettings.getIntForUser( Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE, - ACCESSIBILITY_MAGNIFICATION_MODE_NONE, + defaultValue, UserHandle.USER_CURRENT); } private int getMagnificationCapability() { return mSecureSettings.getIntForUser( Settings.Secure.ACCESSIBILITY_MAGNIFICATION_CAPABILITY, - ACCESSIBILITY_MAGNIFICATION_MODE_NONE, + ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN, + UserHandle.USER_CURRENT); + } + + private float getMagnificationScale() { + return mSecureSettings.getFloatForUser( + Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE, + SCALE_MIN_VALUE, UserHandle.USER_CURRENT); } private void updateUIControlsIfNeeded() { int capability = getMagnificationCapability(); - int selectedButtonIndex = mLastSelectedButtonIndex; switch (capability) { case ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW: @@ -444,14 +493,6 @@ class WindowMagnificationSettings implements MagnificationGestureDetector.OnGest } }; - private void setSeekbarProgress(float scale) { - int index = (int) ((scale - A11Y_SCALE_MIN_VALUE) / A11Y_CHANGE_SCALE_DIFFERENCE); - if (index < 0) { - index = 0; - } - mZoomSeekbar.setProgress(index); - } - void inflateView() { mSettingView = (LinearLayout) View.inflate(mContext, R.layout.window_magnification_settings_view, null); @@ -473,10 +514,13 @@ class WindowMagnificationSettings implements MagnificationGestureDetector.OnGest mSettingView.findViewById(R.id.magnifier_horizontal_lock_title); mZoomSeekbar = mSettingView.findViewById(R.id.magnifier_zoom_slider); + mZoomSeekbar.setMax((int) (mZoomSeekbar.getChangeMagnitude() + * (SCALE_MAX_VALUE - SCALE_MIN_VALUE))); + mSeekBarMagnitude = mZoomSeekbar.getChangeMagnitude(); float scale = mSecureSettings.getFloatForUser( Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE, 0, UserHandle.USER_CURRENT); - setSeekbarProgress(scale); + setScaleSeekbar(scale); mZoomSeekbar.setOnSeekBarChangeListener(new ZoomSeekbarChangeListener()); mAllowDiagonalScrollingView = @@ -521,7 +565,6 @@ class WindowMagnificationSettings implements MagnificationGestureDetector.OnGest // CONFIG_FONT_SCALE: font size change // CONFIG_LOCALE: language change // CONFIG_DENSITY: display size change - mParams.accessibilityTitle = getAccessibilityWindowTitle(mContext); boolean showSettingPanelAfterConfigChange = mIsVisible; @@ -533,16 +576,13 @@ class WindowMagnificationSettings implements MagnificationGestureDetector.OnGest return; } - if ((configDiff & ActivityInfo.CONFIG_ORIENTATION) != 0) { - final Rect previousDraggableBounds = new Rect(mDraggableWindowBounds); + if ((configDiff & ActivityInfo.CONFIG_ORIENTATION) != 0 + || (configDiff & ActivityInfo.CONFIG_SCREEN_SIZE) != 0) { mDraggableWindowBounds.set(getDraggableWindowBounds()); - // Keep the Y position with the same height ratio before the window bounds and - // draggable bounds are changed. - final float windowHeightFraction = (float) (mParams.y - previousDraggableBounds.top) - / previousDraggableBounds.height(); - mParams.y = (int) (windowHeightFraction * mDraggableWindowBounds.height()) - + mDraggableWindowBounds.top; - return; + // reset the panel position to the right-bottom corner + mParams.x = mDraggableWindowBounds.right; + mParams.y = mDraggableWindowBounds.bottom; + updateButtonViewLayoutIfNeeded(); } } @@ -554,7 +594,8 @@ class WindowMagnificationSettings implements MagnificationGestureDetector.OnGest mDraggableWindowBounds.set(newBounds); } - private void updateButtonViewLayoutIfNeeded() { + @VisibleForTesting + void updateButtonViewLayoutIfNeeded() { if (mIsVisible) { mParams.x = MathUtils.constrain(mParams.x, mDraggableWindowBounds.left, mDraggableWindowBounds.right); @@ -595,7 +636,7 @@ class WindowMagnificationSettings implements MagnificationGestureDetector.OnGest @VisibleForTesting void setDiagonalScrolling(boolean enabled) { - Settings.Secure.putIntForUser(mContext.getContentResolver(), + mSecureSettings.putIntForUser( Settings.Secure.ACCESSIBILITY_ALLOW_DIAGONAL_SCROLLING, enabled ? 1 : 0, UserHandle.USER_CURRENT); diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/DismissAnimationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/DismissAnimationController.java index 7f4e78445759..225a2f74e469 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/DismissAnimationController.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/DismissAnimationController.java @@ -25,7 +25,7 @@ import androidx.annotation.NonNull; import androidx.dynamicanimation.animation.DynamicAnimation; import com.android.systemui.R; -import com.android.wm.shell.bubbles.DismissView; +import com.android.wm.shell.common.bubbles.DismissView; import com.android.wm.shell.common.magnetictarget.MagnetizedObject; /** diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java index 3f41a7682357..6ba40d6944e3 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java @@ -61,7 +61,8 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.Preconditions; import com.android.systemui.R; import com.android.systemui.util.settings.SecureSettings; -import com.android.wm.shell.bubbles.DismissView; +import com.android.wm.shell.bubbles.DismissViewUtils; +import com.android.wm.shell.common.bubbles.DismissView; import com.android.wm.shell.common.magnetictarget.MagnetizedObject; import java.lang.annotation.Retention; @@ -184,6 +185,7 @@ class MenuViewLayer extends FrameLayout implements mMenuAnimationController.setDismissCallback(this::hideMenuAndShowMessage); mMenuAnimationController.setSpringAnimationsEndAction(this::onSpringAnimationsEndAction); mDismissView = new DismissView(context); + DismissViewUtils.setup(mDismissView); mDismissAnimationController = new DismissAnimationController(mDismissView, mMenuView); mDismissAnimationController.setMagnetListener(new MagnetizedObject.MagnetListener() { @Override diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/fontscaling/FontScalingDialog.kt b/packages/SystemUI/src/com/android/systemui/accessibility/fontscaling/FontScalingDialog.kt index e79b3f4bf3ad..a910ab58444d 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/fontscaling/FontScalingDialog.kt +++ b/packages/SystemUI/src/com/android/systemui/accessibility/fontscaling/FontScalingDialog.kt @@ -34,6 +34,7 @@ import com.android.systemui.R import com.android.systemui.common.ui.view.SeekBarWithIconButtonsView import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.Main +import com.android.systemui.settings.UserTracker import com.android.systemui.statusbar.phone.SystemUIDialog import com.android.systemui.util.concurrency.DelayableExecutor import com.android.systemui.util.settings.SecureSettings @@ -48,6 +49,7 @@ class FontScalingDialog( private val systemSettings: SystemSettings, private val secureSettings: SecureSettings, private val systemClock: SystemClock, + private val userTracker: UserTracker, @Main mainHandler: Handler, @Background private val backgroundDelayableExecutor: DelayableExecutor ) : SystemUIDialog(context) { @@ -98,7 +100,8 @@ class FontScalingDialog( seekBarWithIconButtonsView.setMax((strEntryValues).size - 1) - val currentScale = systemSettings.getFloat(Settings.System.FONT_SCALE, 1.0f) + val currentScale = + systemSettings.getFloatForUser(Settings.System.FONT_SCALE, 1.0f, userTracker.userId) lastProgress.set(fontSizeValueToIndex(currentScale)) seekBarWithIconButtonsView.setProgress(lastProgress.get()) @@ -195,18 +198,25 @@ class FontScalingDialog( @WorkerThread fun updateFontScale() { - systemSettings.putString(Settings.System.FONT_SCALE, strEntryValues[lastProgress.get()]) + systemSettings.putStringForUser( + Settings.System.FONT_SCALE, + strEntryValues[lastProgress.get()], + userTracker.userId + ) } @WorkerThread fun updateSecureSettingsIfNeeded() { if ( - secureSettings.getString(Settings.Secure.ACCESSIBILITY_FONT_SCALING_HAS_BEEN_CHANGED) != - ON + secureSettings.getStringForUser( + Settings.Secure.ACCESSIBILITY_FONT_SCALING_HAS_BEEN_CHANGED, + userTracker.userId + ) != ON ) { - secureSettings.putString( + secureSettings.putStringForUser( Settings.Secure.ACCESSIBILITY_FONT_SCALING_HAS_BEEN_CHANGED, - ON + ON, + userTracker.userId ) } } diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java index 665a398a3c15..2b83e6b05bbc 100644 --- a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java +++ b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java @@ -105,6 +105,8 @@ public class AssistManager { AssistUtils.INVOCATION_TYPE_HOME_BUTTON_LONG_PRESS; public static final int INVOCATION_TYPE_POWER_BUTTON_LONG_PRESS = AssistUtils.INVOCATION_TYPE_POWER_BUTTON_LONG_PRESS; + public static final int INVOCATION_TYPE_NAV_HANDLE_LONG_PRESS = + AssistUtils.INVOCATION_TYPE_NAV_HANDLE_LONG_PRESS; public static final int DISMISS_REASON_INVOCATION_CANCELLED = 1; public static final int DISMISS_REASON_TAP = 2; diff --git a/packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt b/packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt index c684dc54c6fd..0530aed912bc 100644 --- a/packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt @@ -77,7 +77,9 @@ class AuthenticationRepositoryImpl @Inject constructor() : AuthenticationReposit override val isUnlocked: StateFlow<Boolean> = _isUnlocked.asStateFlow() private val _authenticationMethod = - MutableStateFlow<AuthenticationMethodModel>(AuthenticationMethodModel.PIN(1234)) + MutableStateFlow<AuthenticationMethodModel>( + AuthenticationMethodModel.Pin(listOf(1, 2, 3, 4), autoConfirm = false) + ) override val authenticationMethod: StateFlow<AuthenticationMethodModel> = _authenticationMethod.asStateFlow() diff --git a/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt b/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt index 3984627a181d..20e82f70d8f5 100644 --- a/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt @@ -16,6 +16,7 @@ package com.android.systemui.authentication.domain.interactor +import android.app.admin.DevicePolicyManager import com.android.systemui.authentication.data.repository.AuthenticationRepository import com.android.systemui.authentication.shared.model.AuthenticationMethodModel import com.android.systemui.dagger.SysUISingleton @@ -121,15 +122,37 @@ constructor( /** * Attempts to authenticate the user and unlock the device. * + * If [tryAutoConfirm] is `true`, authentication is attempted if and only if the auth method + * supports auto-confirming, and the input's length is at least the code's length. Otherwise, + * `null` is returned. + * * @param input The input from the user to try to authenticate with. This can be a list of * different things, based on the current authentication method. - * @return `true` if the authentication succeeded and the device is now unlocked; `false` - * otherwise. + * @param tryAutoConfirm `true` if called while the user inputs the code, without an explicit + * request to validate. + * @return `true` if the authentication succeeded and the device is now unlocked; `false` when + * authentication failed, `null` if the check was not performed. */ - fun authenticate(input: List<Any>): Boolean { + fun authenticate(input: List<Any>, tryAutoConfirm: Boolean = false): Boolean? { + val authMethod = this.authenticationMethod.value + if (tryAutoConfirm) { + if ((authMethod as? AuthenticationMethodModel.Pin)?.autoConfirm != true) { + // Do not attempt to authenticate unless the PIN lock is set to auto-confirm. + return null + } + + if (input.size < authMethod.code.size) { + // Do not attempt to authenticate if the PIN has not yet the required amount of + // digits. This intentionally only skip for shorter PINs; if the PIN is longer, the + // layer above might have throttled this check, and the PIN should be rejected via + // the auth code below. + return null + } + } + val isSuccessful = - when (val authMethod = this.authenticationMethod.value) { - is AuthenticationMethodModel.PIN -> input.asCode() == authMethod.code + when (authMethod) { + is AuthenticationMethodModel.Pin -> input.asCode() == authMethod.code is AuthenticationMethodModel.Password -> input.asPassword() == authMethod.password is AuthenticationMethodModel.Pattern -> input.asPattern() == authMethod.coordinates else -> true @@ -177,17 +200,19 @@ constructor( /** * Returns a PIN code from the given list. It's assumed the given list elements are all - * [Int]. + * [Int] in the range [0-9]. */ - private fun List<Any>.asCode(): Int? { - if (isEmpty()) { + private fun List<Any>.asCode(): List<Int>? { + if (isEmpty() || size > DevicePolicyManager.MAX_PASSWORD_LENGTH) { return null } - var code = 0 - map { it as Int }.forEach { integer -> code = code * 10 + integer } - - return code + return map { + require(it is Int && it in 0..9) { + "Pin is required to be Int in range [0..9], but got $it" + } + it + } } /** diff --git a/packages/SystemUI/src/com/android/systemui/authentication/shared/model/AuthenticationMethodModel.kt b/packages/SystemUI/src/com/android/systemui/authentication/shared/model/AuthenticationMethodModel.kt index 6f008c3017b9..1016b6b9a99e 100644 --- a/packages/SystemUI/src/com/android/systemui/authentication/shared/model/AuthenticationMethodModel.kt +++ b/packages/SystemUI/src/com/android/systemui/authentication/shared/model/AuthenticationMethodModel.kt @@ -16,6 +16,8 @@ package com.android.systemui.authentication.shared.model +import androidx.annotation.VisibleForTesting + /** Enumerates all known authentication methods. */ sealed class AuthenticationMethodModel( /** @@ -32,7 +34,22 @@ sealed class AuthenticationMethodModel( /** The most basic authentication method. The lock screen can be swiped away when displayed. */ object Swipe : AuthenticationMethodModel(isSecure = false) - data class PIN(val code: Int) : AuthenticationMethodModel(isSecure = true) + /** + * Authentication method using a PIN. + * + * In practice, a pin is restricted to 16 decimal digits , see + * [android.app.admin.DevicePolicyManager.MAX_PASSWORD_LENGTH] + */ + data class Pin(val code: List<Int>, val autoConfirm: Boolean) : + AuthenticationMethodModel(isSecure = true) { + + /** Convenience constructor for tests only. */ + @VisibleForTesting + constructor( + code: Long, + autoConfirm: Boolean = false + ) : this(code.toString(10).map { it - '0' }, autoConfirm) {} + } data class Password(val password: String) : AuthenticationMethodModel(isSecure = true) diff --git a/packages/SystemUI/src/com/android/systemui/back/domain/interactor/BackActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/back/domain/interactor/BackActionInteractor.kt new file mode 100644 index 000000000000..3c74bf484e98 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/back/domain/interactor/BackActionInteractor.kt @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.back.domain.interactor + +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.plugins.statusbar.StatusBarStateController +import com.android.systemui.shade.QuickSettingsController +import com.android.systemui.shade.ShadeController +import com.android.systemui.shade.ShadeViewController +import com.android.systemui.statusbar.StatusBarState +import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager +import javax.inject.Inject + +/** Handles requests to go back either from a button or gesture. */ +@SysUISingleton +class BackActionInteractor +@Inject +constructor( + private val statusBarStateController: StatusBarStateController, + private val statusBarKeyguardViewManager: StatusBarKeyguardViewManager, + private val shadeController: ShadeController +) { + private lateinit var shadeViewController: ShadeViewController + private lateinit var qsController: QuickSettingsController + + fun setup(qsController: QuickSettingsController, svController: ShadeViewController) { + this.qsController = qsController + this.shadeViewController = svController + } + + fun shouldBackBeHandled(): Boolean { + return statusBarStateController.state != StatusBarState.KEYGUARD && + statusBarStateController.state != StatusBarState.SHADE_LOCKED && + !statusBarKeyguardViewManager.isBouncerShowingOverDream + } + + fun onBackRequested(): Boolean { + if (statusBarKeyguardViewManager.canHandleBackPressed()) { + statusBarKeyguardViewManager.onBackPressed() + return true + } + if (qsController.isCustomizing) { + qsController.closeQsCustomizer() + return true + } + if (qsController.expanded) { + shadeViewController.animateCollapseQs(false) + return true + } + if (shadeViewController.closeUserSwitcherIfOpen()) { + return true + } + if (shouldBackBeHandled()) { + if (shadeViewController.canBeCollapsed()) { + // this is the Shade dismiss animation, so make sure QQS closes when it ends. + shadeViewController.onBackPressed() + shadeController.animateCollapseShade() + } + return true + } + return false + } +} diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java index 5b746f1b424d..76e48e9ef711 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java @@ -201,7 +201,9 @@ public class AuthController implements CoreStartable, CommandQueue.Callbacks, final TaskStackListener mTaskStackListener = new TaskStackListener() { @Override public void onTaskStackChanged() { - mHandler.post(AuthController.this::cancelIfOwnerIsNotInForeground); + if (!isOwnerInForeground()) { + mHandler.post(AuthController.this::cancelIfOwnerIsNotInForeground); + } } }; @@ -239,33 +241,39 @@ public class AuthController implements CoreStartable, CommandQueue.Callbacks, } } + private boolean isOwnerInForeground() { + if (mCurrentDialog != null) { + final String clientPackage = mCurrentDialog.getOpPackageName(); + final List<ActivityManager.RunningTaskInfo> runningTasks = + mActivityTaskManager.getTasks(1); + if (!runningTasks.isEmpty()) { + final String topPackage = runningTasks.get(0).topActivity.getPackageName(); + if (!topPackage.contentEquals(clientPackage) + && !Utils.isSystem(mContext, clientPackage)) { + Log.w(TAG, "Evicting client due to: " + topPackage); + return false; + } + } + } + return true; + } + private void cancelIfOwnerIsNotInForeground() { mExecution.assertIsMainThread(); if (mCurrentDialog != null) { try { - final String clientPackage = mCurrentDialog.getOpPackageName(); - Log.w(TAG, "Task stack changed, current client: " + clientPackage); - final List<ActivityManager.RunningTaskInfo> runningTasks = - mActivityTaskManager.getTasks(1); - if (!runningTasks.isEmpty()) { - final String topPackage = runningTasks.get(0).topActivity.getPackageName(); - if (!topPackage.contentEquals(clientPackage) - && !Utils.isSystem(mContext, clientPackage)) { - Log.e(TAG, "Evicting client due to: " + topPackage); - mCurrentDialog.dismissWithoutCallback(true /* animate */); - mCurrentDialog = null; - - for (Callback cb : mCallbacks) { - cb.onBiometricPromptDismissed(); - } + mCurrentDialog.dismissWithoutCallback(true /* animate */); + mCurrentDialog = null; - if (mReceiver != null) { - mReceiver.onDialogDismissed( - BiometricPrompt.DISMISSED_REASON_USER_CANCEL, - null /* credentialAttestation */); - mReceiver = null; - } - } + for (Callback cb : mCallbacks) { + cb.onBiometricPromptDismissed(); + } + + if (mReceiver != null) { + mReceiver.onDialogDismissed( + BiometricPrompt.DISMISSED_REASON_USER_CANCEL, + null /* credentialAttestation */); + mReceiver = null; } } catch (RemoteException e) { Log.e(TAG, "Remote exception", e); @@ -1268,10 +1276,11 @@ public class AuthController implements CoreStartable, CommandQueue.Callbacks, cb.onBiometricPromptShown(); } mCurrentDialog = newDialog; - mCurrentDialog.show(mWindowManager, savedState); - if (!promptInfo.isAllowBackgroundAuthentication()) { - mHandler.post(this::cancelIfOwnerIsNotInForeground); + if (!promptInfo.isAllowBackgroundAuthentication() && !isOwnerInForeground()) { + cancelIfOwnerIsNotInForeground(); + } else { + mCurrentDialog.show(mWindowManager, savedState); } } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricNotificationService.java b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricNotificationService.java index 6db266f4f1cb..9d8dcc1efdd8 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricNotificationService.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricNotificationService.java @@ -21,6 +21,8 @@ import static android.app.PendingIntent.FLAG_IMMUTABLE; import static com.android.systemui.biometrics.BiometricNotificationBroadcastReceiver.ACTION_SHOW_FACE_REENROLL_DIALOG; import static com.android.systemui.biometrics.BiometricNotificationBroadcastReceiver.ACTION_SHOW_FINGERPRINT_REENROLL_DIALOG; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.app.Notification; import android.app.NotificationChannel; import android.app.NotificationManager; @@ -30,6 +32,9 @@ import android.content.Intent; import android.content.IntentFilter; import android.hardware.biometrics.BiometricFaceConstants; import android.hardware.biometrics.BiometricSourceType; +import android.hardware.biometrics.BiometricStateListener; +import android.hardware.face.FaceManager; +import android.hardware.fingerprint.FingerprintManager; import android.os.Handler; import android.os.UserHandle; import android.provider.Settings; @@ -42,7 +47,6 @@ import com.android.systemui.R; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.statusbar.policy.KeyguardStateController; - import java.util.Optional; import javax.inject.Inject; @@ -69,6 +73,8 @@ public class BiometricNotificationService implements CoreStartable { private final NotificationManager mNotificationManager; private final BiometricNotificationBroadcastReceiver mBroadcastReceiver; private final FingerprintReEnrollNotification mFingerprintReEnrollNotification; + private final FingerprintManager mFingerprintManager; + private final FaceManager mFaceManager; private NotificationChannel mNotificationChannel; private boolean mFaceNotificationQueued; private boolean mFingerprintNotificationQueued; @@ -119,14 +125,29 @@ public class BiometricNotificationService implements CoreStartable { } }; + private final BiometricStateListener mFaceStateListener = new BiometricStateListener() { + @Override + public void onEnrollmentsChanged(int userId, int sensorId, boolean hasEnrollments) { + mNotificationManager.cancelAsUser(TAG, FACE_NOTIFICATION_ID, UserHandle.CURRENT); + } + }; + + private final BiometricStateListener mFingerprintStateListener = new BiometricStateListener() { + @Override + public void onEnrollmentsChanged(int userId, int sensorId, boolean hasEnrollments) { + mNotificationManager.cancelAsUser(TAG, FINGERPRINT_NOTIFICATION_ID, UserHandle.CURRENT); + } + }; @Inject - public BiometricNotificationService(Context context, - KeyguardUpdateMonitor keyguardUpdateMonitor, - KeyguardStateController keyguardStateController, - Handler handler, NotificationManager notificationManager, - BiometricNotificationBroadcastReceiver biometricNotificationBroadcastReceiver, - Optional<FingerprintReEnrollNotification> fingerprintReEnrollNotification) { + public BiometricNotificationService(@NonNull Context context, + @NonNull KeyguardUpdateMonitor keyguardUpdateMonitor, + @NonNull KeyguardStateController keyguardStateController, + @NonNull Handler handler, @NonNull NotificationManager notificationManager, + @NonNull BiometricNotificationBroadcastReceiver biometricNotificationBroadcastReceiver, + @NonNull Optional<FingerprintReEnrollNotification> fingerprintReEnrollNotification, + @Nullable FingerprintManager fingerprintManager, + @Nullable FaceManager faceManager) { mContext = context; mKeyguardUpdateMonitor = keyguardUpdateMonitor; mKeyguardStateController = keyguardStateController; @@ -135,6 +156,8 @@ public class BiometricNotificationService implements CoreStartable { mBroadcastReceiver = biometricNotificationBroadcastReceiver; mFingerprintReEnrollNotification = fingerprintReEnrollNotification.orElse( new FingerprintReEnrollNotificationImpl()); + mFingerprintManager = fingerprintManager; + mFaceManager = faceManager; } @Override @@ -148,9 +171,16 @@ public class BiometricNotificationService implements CoreStartable { intentFilter.addAction(ACTION_SHOW_FACE_REENROLL_DIALOG); mContext.registerReceiver(mBroadcastReceiver, intentFilter, Context.RECEIVER_EXPORTED_UNAUDITED); + if (mFingerprintManager != null) { + mFingerprintManager.registerBiometricStateListener(mFingerprintStateListener); + } + if (mFaceManager != null) { + mFaceManager.registerBiometricStateListener(mFaceStateListener); + } } private void queueFaceReenrollNotification() { + Log.d(TAG, "Face re-enroll notification queued."); mFaceNotificationQueued = true; final String title = mContext.getString(R.string.face_re_enroll_notification_title); final String content = mContext.getString( @@ -163,6 +193,7 @@ public class BiometricNotificationService implements CoreStartable { } private void queueFingerprintReenrollNotification() { + Log.d(TAG, "Fingerprint re-enroll notification queued."); mFingerprintNotificationQueued = true; final String title = mContext.getString(R.string.fingerprint_re_enroll_notification_title); final String content = mContext.getString( diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt index d48b9c339d15..9d0cde18a6ef 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt @@ -54,11 +54,11 @@ import com.android.internal.annotations.VisibleForTesting import com.android.systemui.Dumpable import com.android.systemui.R import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractor +import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.dump.DumpManager -import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor import com.android.systemui.util.boundsOnScreen import com.android.systemui.util.concurrency.DelayableExecutor import com.android.systemui.util.traceSection diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java index 2eb533029cf5..0dc7974475fa 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java @@ -82,9 +82,9 @@ import com.android.systemui.dump.DumpManager; import com.android.systemui.flags.FeatureFlags; import com.android.systemui.flags.Flags; import com.android.systemui.keyguard.ScreenLifecycle; -import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor; +import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor; import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor; -import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerInteractor; +import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor; import com.android.systemui.log.SessionTracker; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; @@ -173,6 +173,7 @@ public class UdfpsController implements DozeReceiver, Dumpable { @NonNull private final SecureSettings mSecureSettings; @NonNull private final UdfpsUtils mUdfpsUtils; @NonNull private final InputManager mInputManager; + @NonNull private final UdfpsKeyguardAccessibilityDelegate mUdfpsKeyguardAccessibilityDelegate; private final boolean mIgnoreRefreshRate; // Currently the UdfpsController supports a single UDFPS sensor. If devices have multiple @@ -272,7 +273,8 @@ public class UdfpsController implements DozeReceiver, Dumpable { mUdfpsDisplayMode, mSecureSettings, requestId, reason, callback, (view, event, fromUdfpsView) -> onTouch(requestId, event, fromUdfpsView), mActivityLaunchAnimator, mFeatureFlags, - mPrimaryBouncerInteractor, mAlternateBouncerInteractor, mUdfpsUtils))); + mPrimaryBouncerInteractor, mAlternateBouncerInteractor, mUdfpsUtils, + mUdfpsKeyguardAccessibilityDelegate))); } @Override @@ -354,7 +356,8 @@ public class UdfpsController implements DozeReceiver, Dumpable { UdfpsController.this.mAlternateTouchProvider.onUiReady(); } else { final long requestId = (mOverlay != null) ? mOverlay.getRequestId() : 0L; - UdfpsController.this.mFingerprintManager.onUiReady(requestId, sensorId); + UdfpsController.this.mFingerprintManager.onUdfpsUiEvent( + FingerprintManager.UDFPS_UI_READY, requestId, sensorId); } } } @@ -825,7 +828,8 @@ public class UdfpsController implements DozeReceiver, Dumpable { @NonNull SecureSettings secureSettings, @NonNull InputManager inputManager, @NonNull UdfpsUtils udfpsUtils, - @NonNull KeyguardFaceAuthInteractor keyguardFaceAuthInteractor) { + @NonNull KeyguardFaceAuthInteractor keyguardFaceAuthInteractor, + @NonNull UdfpsKeyguardAccessibilityDelegate udfpsKeyguardAccessibilityDelegate) { mContext = context; mExecution = execution; mVibrator = vibrator; @@ -871,6 +875,7 @@ public class UdfpsController implements DozeReceiver, Dumpable { mSecureSettings = secureSettings; mUdfpsUtils = udfpsUtils; mInputManager = inputManager; + mUdfpsKeyguardAccessibilityDelegate = udfpsKeyguardAccessibilityDelegate; mTouchProcessor = mFeatureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION) ? singlePointerTouchProcessor : null; @@ -952,6 +957,10 @@ public class UdfpsController implements DozeReceiver, Dumpable { mOnFingerDown = false; mAttemptedToDismissKeyguard = false; mOrientationListener.enable(); + if (mFingerprintManager != null) { + mFingerprintManager.onUdfpsUiEvent(FingerprintManager.UDFPS_UI_OVERLAY_SHOWN, + overlay.getRequestId(), mSensorProps.sensorId); + } } else { Log.v(TAG, "showUdfpsOverlay | the overlay is already showing"); } @@ -1093,7 +1102,8 @@ public class UdfpsController implements DozeReceiver, Dumpable { mLatencyTracker.onActionEnd(LatencyTracker.ACTION_UDFPS_ILLUMINATE); }); } else { - mFingerprintManager.onUiReady(requestId, mSensorProps.sensorId); + mFingerprintManager.onUdfpsUiEvent(FingerprintManager.UDFPS_UI_READY, requestId, + mSensorProps.sensorId); mLatencyTracker.onActionEnd(LatencyTracker.ACTION_UDFPS_ILLUMINATE); } } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt index cabe9008ab47..e5421471931f 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt @@ -53,8 +53,8 @@ import com.android.systemui.animation.ActivityLaunchAnimator import com.android.systemui.dump.DumpManager import com.android.systemui.flags.FeatureFlags import com.android.systemui.flags.Flags -import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor -import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerInteractor +import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor +import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.shade.ShadeExpansionStateManager import com.android.systemui.statusbar.LockscreenShadeTransitionController @@ -103,7 +103,8 @@ class UdfpsControllerOverlay @JvmOverloads constructor( private val primaryBouncerInteractor: PrimaryBouncerInteractor, private val alternateBouncerInteractor: AlternateBouncerInteractor, private val isDebuggable: Boolean = Build.IS_DEBUGGABLE, - private val udfpsUtils: UdfpsUtils + private val udfpsUtils: UdfpsUtils, + private val udfpsKeyguardAccessibilityDelegate: UdfpsKeyguardAccessibilityDelegate, ) { /** The view, when [isShowing], or null. */ var overlayView: UdfpsView? = null @@ -261,6 +262,7 @@ class UdfpsControllerOverlay @JvmOverloads constructor( featureFlags, primaryBouncerInteractor, alternateBouncerInteractor, + udfpsKeyguardAccessibilityDelegate, ) } REASON_AUTH_BP -> { diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardAccessibilityDelegate.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardAccessibilityDelegate.kt new file mode 100644 index 000000000000..fb7b56e12996 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardAccessibilityDelegate.kt @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.biometrics + +import android.content.res.Resources +import android.os.Bundle +import android.view.View +import android.view.accessibility.AccessibilityNodeInfo +import com.android.systemui.R +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Main +import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager +import javax.inject.Inject + +@SysUISingleton +class UdfpsKeyguardAccessibilityDelegate +@Inject +constructor( + @Main private val resources: Resources, + private val keyguardViewManager: StatusBarKeyguardViewManager, +) : View.AccessibilityDelegate() { + override fun onInitializeAccessibilityNodeInfo(host: View?, info: AccessibilityNodeInfo) { + super.onInitializeAccessibilityNodeInfo(host, info) + val clickAction = + AccessibilityNodeInfo.AccessibilityAction( + AccessibilityNodeInfo.AccessibilityAction.ACTION_CLICK.id, + resources.getString(R.string.accessibility_bouncer) + ) + info.addAction(clickAction) + } + + override fun performAccessibilityAction(host: View?, action: Int, args: Bundle?): Boolean { + // when an a11y service is enabled, double tapping on the fingerprint sensor should + // show the primary bouncer + return if (action == AccessibilityNodeInfo.AccessibilityAction.ACTION_CLICK.id) { + keyguardViewManager.showPrimaryBouncer(/* scrimmed */ true) + true + } else super.performAccessibilityAction(host, action, args) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt index 3fc3e82b6b5a..9bafeeca24a8 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt @@ -20,6 +20,7 @@ import android.animation.ValueAnimator import android.content.res.Configuration import android.util.MathUtils import android.view.MotionEvent +import android.view.View import androidx.annotation.VisibleForTesting import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle @@ -28,11 +29,11 @@ import com.android.keyguard.BouncerPanelExpansionCalculator.aboutToShowBouncerPr import com.android.keyguard.KeyguardUpdateMonitor import com.android.systemui.R import com.android.systemui.animation.ActivityLaunchAnimator +import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor +import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor import com.android.systemui.dump.DumpManager import com.android.systemui.flags.FeatureFlags import com.android.systemui.flags.Flags -import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor -import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerInteractor import com.android.systemui.lifecycle.repeatWhenAttached import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.shade.ShadeExpansionListener @@ -71,6 +72,7 @@ constructor( featureFlags: FeatureFlags, private val primaryBouncerInteractor: PrimaryBouncerInteractor, private val alternateBouncerInteractor: AlternateBouncerInteractor, + private val udfpsKeyguardAccessibilityDelegate: UdfpsKeyguardAccessibilityDelegate, ) : UdfpsAnimationViewController<UdfpsKeyguardViewLegacy>( view, @@ -300,7 +302,10 @@ constructor( lockScreenShadeTransitionController.mUdfpsKeyguardViewControllerLegacy = this activityLaunchAnimator.addListener(activityLaunchAnimatorListener) view.mUseExpandedOverlay = useExpandedOverlay - view.startIconAsyncInflate() + view.startIconAsyncInflate { + (view.findViewById(R.id.udfps_animation_view_internal) as View).accessibilityDelegate = + udfpsKeyguardAccessibilityDelegate + } } override fun onViewDetached() { diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacy.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacy.java index 056d692a3641..b916810c57d0 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacy.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacy.java @@ -79,6 +79,7 @@ public class UdfpsKeyguardViewLegacy extends UdfpsAnimationView { private float mInterpolatedDarkAmount; private int mAnimationType = ANIMATION_NONE; private boolean mFullyInflated; + private Runnable mOnFinishInflateRunnable; public UdfpsKeyguardViewLegacy(Context context, @Nullable AttributeSet attrs) { super(context, attrs); @@ -90,7 +91,12 @@ public class UdfpsKeyguardViewLegacy extends UdfpsAnimationView { .getDimensionPixelSize(R.dimen.udfps_burn_in_offset_y); } - public void startIconAsyncInflate() { + /** + * Inflate internal udfps view on a background thread and call the onFinishRunnable + * when inflation is finished. + */ + public void startIconAsyncInflate(Runnable onFinishInflate) { + mOnFinishInflateRunnable = onFinishInflate; // inflate Lottie views on a background thread in case it takes a while to inflate AsyncLayoutInflater inflater = new AsyncLayoutInflater(mContext); inflater.inflate(R.layout.udfps_keyguard_view_internal, this, @@ -330,6 +336,7 @@ public class UdfpsKeyguardViewLegacy extends UdfpsAnimationView { frameInfo -> new PorterDuffColorFilter(mTextColorPrimary, PorterDuff.Mode.SRC_ATOP) ); + mOnFinishInflateRunnable.run(); } }; } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/bouncer/data/factory/BouncerMessageFactory.kt b/packages/SystemUI/src/com/android/systemui/bouncer/data/factory/BouncerMessageFactory.kt index 11bc88a01027..3206c0043d5c 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/bouncer/data/factory/BouncerMessageFactory.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/data/factory/BouncerMessageFactory.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.keyguard.bouncer.data.factory +package com.android.systemui.bouncer.data.factory import android.annotation.IntDef import com.android.keyguard.KeyguardSecurityModel @@ -36,12 +36,46 @@ import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_TIMEOUT import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_TRUSTAGENT_EXPIRED import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_USER_REQUEST import com.android.keyguard.KeyguardUpdateMonitor +import com.android.systemui.R.string.bouncer_face_not_recognized import com.android.systemui.R.string.keyguard_enter_password import com.android.systemui.R.string.keyguard_enter_pattern import com.android.systemui.R.string.keyguard_enter_pin +import com.android.systemui.R.string.kg_bio_too_many_attempts_password +import com.android.systemui.R.string.kg_bio_too_many_attempts_pattern +import com.android.systemui.R.string.kg_bio_too_many_attempts_pin +import com.android.systemui.R.string.kg_bio_try_again_or_password +import com.android.systemui.R.string.kg_bio_try_again_or_pattern +import com.android.systemui.R.string.kg_bio_try_again_or_pin +import com.android.systemui.R.string.kg_face_locked_out +import com.android.systemui.R.string.kg_fp_locked_out +import com.android.systemui.R.string.kg_fp_not_recognized +import com.android.systemui.R.string.kg_primary_auth_locked_out_password +import com.android.systemui.R.string.kg_primary_auth_locked_out_pattern +import com.android.systemui.R.string.kg_primary_auth_locked_out_pin +import com.android.systemui.R.string.kg_prompt_after_dpm_lock +import com.android.systemui.R.string.kg_prompt_after_user_lockdown_password +import com.android.systemui.R.string.kg_prompt_after_user_lockdown_pattern +import com.android.systemui.R.string.kg_prompt_after_user_lockdown_pin +import com.android.systemui.R.string.kg_prompt_auth_timeout +import com.android.systemui.R.string.kg_prompt_password_auth_timeout +import com.android.systemui.R.string.kg_prompt_pattern_auth_timeout +import com.android.systemui.R.string.kg_prompt_pin_auth_timeout +import com.android.systemui.R.string.kg_prompt_reason_restart_password +import com.android.systemui.R.string.kg_prompt_reason_restart_pattern +import com.android.systemui.R.string.kg_prompt_reason_restart_pin +import com.android.systemui.R.string.kg_prompt_unattended_update +import com.android.systemui.R.string.kg_too_many_failed_attempts_countdown +import com.android.systemui.R.string.kg_trust_agent_disabled +import com.android.systemui.R.string.kg_unlock_with_password_or_fp +import com.android.systemui.R.string.kg_unlock_with_pattern_or_fp +import com.android.systemui.R.string.kg_unlock_with_pin_or_fp +import com.android.systemui.R.string.kg_wrong_input_try_fp_suggestion +import com.android.systemui.R.string.kg_wrong_password_try_again +import com.android.systemui.R.string.kg_wrong_pattern_try_again +import com.android.systemui.R.string.kg_wrong_pin_try_again +import com.android.systemui.bouncer.shared.model.BouncerMessageModel +import com.android.systemui.bouncer.shared.model.Message import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.keyguard.bouncer.shared.model.BouncerMessageModel -import com.android.systemui.keyguard.bouncer.shared.model.Message import javax.inject.Inject @SysUISingleton @@ -152,171 +186,175 @@ private fun defaultMessage(securityMode: SecurityMode): Pair<Int, Int> { private fun defaultMessageWithFingerprint(securityMode: SecurityMode): Pair<Int, Int> { return when (securityMode) { - SecurityMode.Pattern -> Pair(keyguard_enter_pattern, 0) - SecurityMode.Password -> Pair(keyguard_enter_password, 0) - SecurityMode.PIN -> Pair(keyguard_enter_pin, 0) + SecurityMode.Pattern -> Pair(kg_unlock_with_pattern_or_fp, 0) + SecurityMode.Password -> Pair(kg_unlock_with_password_or_fp, 0) + SecurityMode.PIN -> Pair(kg_unlock_with_pin_or_fp, 0) else -> Pair(0, 0) } } private fun incorrectSecurityInput(securityMode: SecurityMode): Pair<Int, Int> { return when (securityMode) { - SecurityMode.Pattern -> Pair(keyguard_enter_pattern, 0) - SecurityMode.Password -> Pair(keyguard_enter_password, 0) - SecurityMode.PIN -> Pair(keyguard_enter_pin, 0) + SecurityMode.Pattern -> Pair(kg_wrong_pattern_try_again, 0) + SecurityMode.Password -> Pair(kg_wrong_password_try_again, 0) + SecurityMode.PIN -> Pair(kg_wrong_pin_try_again, 0) else -> Pair(0, 0) } } private fun incorrectSecurityInputWithFingerprint(securityMode: SecurityMode): Pair<Int, Int> { return when (securityMode) { - SecurityMode.Pattern -> Pair(keyguard_enter_pattern, 0) - SecurityMode.Password -> Pair(keyguard_enter_password, 0) - SecurityMode.PIN -> Pair(keyguard_enter_pin, 0) + SecurityMode.Pattern -> Pair(kg_wrong_pattern_try_again, kg_wrong_input_try_fp_suggestion) + SecurityMode.Password -> Pair(kg_wrong_password_try_again, kg_wrong_input_try_fp_suggestion) + SecurityMode.PIN -> Pair(kg_wrong_pin_try_again, kg_wrong_input_try_fp_suggestion) else -> Pair(0, 0) } } private fun incorrectFingerprintInput(securityMode: SecurityMode): Pair<Int, Int> { return when (securityMode) { - SecurityMode.Pattern -> Pair(keyguard_enter_pattern, 0) - SecurityMode.Password -> Pair(keyguard_enter_password, 0) - SecurityMode.PIN -> Pair(keyguard_enter_pin, 0) + SecurityMode.Pattern -> Pair(kg_fp_not_recognized, kg_bio_try_again_or_pattern) + SecurityMode.Password -> Pair(kg_fp_not_recognized, kg_bio_try_again_or_password) + SecurityMode.PIN -> Pair(kg_fp_not_recognized, kg_bio_try_again_or_pin) else -> Pair(0, 0) } } private fun incorrectFaceInput(securityMode: SecurityMode): Pair<Int, Int> { return when (securityMode) { - SecurityMode.Pattern -> Pair(keyguard_enter_pattern, 0) - SecurityMode.Password -> Pair(keyguard_enter_password, 0) - SecurityMode.PIN -> Pair(keyguard_enter_pin, 0) + SecurityMode.Pattern -> Pair(bouncer_face_not_recognized, kg_bio_try_again_or_pattern) + SecurityMode.Password -> Pair(bouncer_face_not_recognized, kg_bio_try_again_or_password) + SecurityMode.PIN -> Pair(bouncer_face_not_recognized, kg_bio_try_again_or_pin) else -> Pair(0, 0) } } private fun incorrectFaceInputWithFingerprintAllowed(securityMode: SecurityMode): Pair<Int, Int> { return when (securityMode) { - SecurityMode.Pattern -> Pair(keyguard_enter_pattern, 0) - SecurityMode.Password -> Pair(keyguard_enter_password, 0) - SecurityMode.PIN -> Pair(keyguard_enter_pin, 0) + SecurityMode.Pattern -> Pair(kg_unlock_with_pattern_or_fp, bouncer_face_not_recognized) + SecurityMode.Password -> Pair(kg_unlock_with_password_or_fp, bouncer_face_not_recognized) + SecurityMode.PIN -> Pair(kg_unlock_with_pin_or_fp, bouncer_face_not_recognized) else -> Pair(0, 0) } } private fun biometricLockout(securityMode: SecurityMode): Pair<Int, Int> { return when (securityMode) { - SecurityMode.Pattern -> Pair(keyguard_enter_pattern, 0) - SecurityMode.Password -> Pair(keyguard_enter_password, 0) - SecurityMode.PIN -> Pair(keyguard_enter_pin, 0) + SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_bio_too_many_attempts_pattern) + SecurityMode.Password -> Pair(keyguard_enter_password, kg_bio_too_many_attempts_password) + SecurityMode.PIN -> Pair(keyguard_enter_pin, kg_bio_too_many_attempts_pin) else -> Pair(0, 0) } } private fun authRequiredAfterReboot(securityMode: SecurityMode): Pair<Int, Int> { return when (securityMode) { - SecurityMode.Pattern -> Pair(keyguard_enter_pattern, 0) - SecurityMode.Password -> Pair(keyguard_enter_password, 0) - SecurityMode.PIN -> Pair(keyguard_enter_pin, 0) + SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_prompt_reason_restart_pattern) + SecurityMode.Password -> Pair(keyguard_enter_password, kg_prompt_reason_restart_password) + SecurityMode.PIN -> Pair(keyguard_enter_pin, kg_prompt_reason_restart_pin) else -> Pair(0, 0) } } private fun authRequiredAfterAdminLockdown(securityMode: SecurityMode): Pair<Int, Int> { return when (securityMode) { - SecurityMode.Pattern -> Pair(keyguard_enter_pattern, 0) - SecurityMode.Password -> Pair(keyguard_enter_password, 0) - SecurityMode.PIN -> Pair(keyguard_enter_pin, 0) + SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_prompt_after_dpm_lock) + SecurityMode.Password -> Pair(keyguard_enter_password, kg_prompt_after_dpm_lock) + SecurityMode.PIN -> Pair(keyguard_enter_pin, kg_prompt_after_dpm_lock) else -> Pair(0, 0) } } private fun authRequiredAfterUserLockdown(securityMode: SecurityMode): Pair<Int, Int> { return when (securityMode) { - SecurityMode.Pattern -> Pair(keyguard_enter_pattern, 0) - SecurityMode.Password -> Pair(keyguard_enter_password, 0) - SecurityMode.PIN -> Pair(keyguard_enter_pin, 0) + SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_prompt_after_user_lockdown_pattern) + SecurityMode.Password -> + Pair(keyguard_enter_password, kg_prompt_after_user_lockdown_password) + SecurityMode.PIN -> Pair(keyguard_enter_pin, kg_prompt_after_user_lockdown_pin) else -> Pair(0, 0) } } private fun authRequiredForUnattendedUpdate(securityMode: SecurityMode): Pair<Int, Int> { return when (securityMode) { - SecurityMode.Pattern -> Pair(keyguard_enter_pattern, 0) - SecurityMode.Password -> Pair(keyguard_enter_password, 0) - SecurityMode.PIN -> Pair(keyguard_enter_pin, 0) + SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_prompt_unattended_update) + SecurityMode.Password -> Pair(keyguard_enter_password, kg_prompt_unattended_update) + SecurityMode.PIN -> Pair(keyguard_enter_pin, kg_prompt_unattended_update) else -> Pair(0, 0) } } private fun authRequiredAfterPrimaryAuthTimeout(securityMode: SecurityMode): Pair<Int, Int> { return when (securityMode) { - SecurityMode.Pattern -> Pair(keyguard_enter_pattern, 0) - SecurityMode.Password -> Pair(keyguard_enter_password, 0) - SecurityMode.PIN -> Pair(keyguard_enter_pin, 0) + SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_prompt_pattern_auth_timeout) + SecurityMode.Password -> Pair(keyguard_enter_password, kg_prompt_password_auth_timeout) + SecurityMode.PIN -> Pair(keyguard_enter_pin, kg_prompt_pin_auth_timeout) else -> Pair(0, 0) } } private fun nonStrongAuthTimeout(securityMode: SecurityMode): Pair<Int, Int> { return when (securityMode) { - SecurityMode.Pattern -> Pair(keyguard_enter_pattern, 0) - SecurityMode.Password -> Pair(keyguard_enter_password, 0) - SecurityMode.PIN -> Pair(keyguard_enter_pin, 0) + SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_prompt_auth_timeout) + SecurityMode.Password -> Pair(keyguard_enter_password, kg_prompt_auth_timeout) + SecurityMode.PIN -> Pair(keyguard_enter_pin, kg_prompt_auth_timeout) else -> Pair(0, 0) } } private fun nonStrongAuthTimeoutWithFingerprintAllowed(securityMode: SecurityMode): Pair<Int, Int> { return when (securityMode) { - SecurityMode.Pattern -> Pair(keyguard_enter_pattern, 0) - SecurityMode.Password -> Pair(keyguard_enter_password, 0) - SecurityMode.PIN -> Pair(keyguard_enter_pin, 0) + SecurityMode.Pattern -> Pair(kg_unlock_with_pattern_or_fp, kg_prompt_auth_timeout) + SecurityMode.Password -> Pair(kg_unlock_with_password_or_fp, kg_prompt_auth_timeout) + SecurityMode.PIN -> Pair(kg_unlock_with_pin_or_fp, kg_prompt_auth_timeout) else -> Pair(0, 0) } } private fun faceUnlockUnavailable(securityMode: SecurityMode): Pair<Int, Int> { return when (securityMode) { - SecurityMode.Pattern -> Pair(keyguard_enter_pattern, 0) - SecurityMode.Password -> Pair(keyguard_enter_password, 0) - SecurityMode.PIN -> Pair(keyguard_enter_pin, 0) + SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_face_locked_out) + SecurityMode.Password -> Pair(keyguard_enter_password, kg_face_locked_out) + SecurityMode.PIN -> Pair(keyguard_enter_pin, kg_face_locked_out) else -> Pair(0, 0) } } private fun fingerprintUnlockUnavailable(securityMode: SecurityMode): Pair<Int, Int> { return when (securityMode) { - SecurityMode.Pattern -> Pair(keyguard_enter_pattern, 0) - SecurityMode.Password -> Pair(keyguard_enter_password, 0) - SecurityMode.PIN -> Pair(keyguard_enter_pin, 0) + SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_fp_locked_out) + SecurityMode.Password -> Pair(keyguard_enter_password, kg_fp_locked_out) + SecurityMode.PIN -> Pair(keyguard_enter_pin, kg_fp_locked_out) else -> Pair(0, 0) } } private fun trustAgentDisabled(securityMode: SecurityMode): Pair<Int, Int> { return when (securityMode) { - SecurityMode.Pattern -> Pair(keyguard_enter_pattern, 0) - SecurityMode.Password -> Pair(keyguard_enter_password, 0) - SecurityMode.PIN -> Pair(keyguard_enter_pin, 0) + SecurityMode.Pattern -> Pair(keyguard_enter_pattern, kg_trust_agent_disabled) + SecurityMode.Password -> Pair(keyguard_enter_password, kg_trust_agent_disabled) + SecurityMode.PIN -> Pair(keyguard_enter_pin, kg_trust_agent_disabled) else -> Pair(0, 0) } } private fun trustAgentDisabledWithFingerprintAllowed(securityMode: SecurityMode): Pair<Int, Int> { return when (securityMode) { - SecurityMode.Pattern -> Pair(keyguard_enter_pattern, 0) - SecurityMode.Password -> Pair(keyguard_enter_password, 0) - SecurityMode.PIN -> Pair(keyguard_enter_pin, 0) + SecurityMode.Pattern -> Pair(kg_unlock_with_pattern_or_fp, kg_trust_agent_disabled) + SecurityMode.Password -> Pair(kg_unlock_with_password_or_fp, kg_trust_agent_disabled) + SecurityMode.PIN -> Pair(kg_unlock_with_pin_or_fp, kg_trust_agent_disabled) else -> Pair(0, 0) } } private fun primaryAuthLockedOut(securityMode: SecurityMode): Pair<Int, Int> { return when (securityMode) { - SecurityMode.Pattern -> Pair(keyguard_enter_pattern, 0) - SecurityMode.Password -> Pair(keyguard_enter_password, 0) - SecurityMode.PIN -> Pair(keyguard_enter_pin, 0) + SecurityMode.Pattern -> + Pair(kg_too_many_failed_attempts_countdown, kg_primary_auth_locked_out_pattern) + SecurityMode.Password -> + Pair(kg_too_many_failed_attempts_countdown, kg_primary_auth_locked_out_password) + SecurityMode.PIN -> + Pair(kg_too_many_failed_attempts_countdown, kg_primary_auth_locked_out_pin) else -> Pair(0, 0) } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/bouncer/data/repository/BouncerMessageRepository.kt b/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/BouncerMessageRepository.kt index c4400bcb6b21..7e420cfcea75 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/bouncer/data/repository/BouncerMessageRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/BouncerMessageRepository.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.keyguard.bouncer.data.repository +package com.android.systemui.bouncer.data.repository import android.hardware.biometrics.BiometricSourceType import android.hardware.biometrics.BiometricSourceType.FACE @@ -33,11 +33,11 @@ import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_TRUSTAGENT_EXPIRE import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_USER_REQUEST import com.android.keyguard.KeyguardUpdateMonitor import com.android.keyguard.KeyguardUpdateMonitorCallback +import com.android.systemui.bouncer.data.factory.BouncerMessageFactory +import com.android.systemui.bouncer.shared.model.BouncerMessageModel import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.keyguard.bouncer.data.factory.BouncerMessageFactory -import com.android.systemui.keyguard.bouncer.shared.model.BouncerMessageModel import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository import com.android.systemui.keyguard.data.repository.DeviceEntryFingerprintAuthRepository import com.android.systemui.keyguard.data.repository.TrustRepository diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/data/repo/BouncerRepository.kt b/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/BouncerRepository.kt index 49a0a3c1e965..ff896fa51350 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/data/repo/BouncerRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/BouncerRepository.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.bouncer.data.repo +package com.android.systemui.bouncer.data.repository import com.android.systemui.bouncer.shared.model.AuthenticationThrottledModel import com.android.systemui.dagger.SysUISingleton diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBouncerRepository.kt b/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/KeyguardBouncerRepository.kt index 5d15e69f0162..918e1680fc66 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBouncerRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/KeyguardBouncerRepository.kt @@ -14,14 +14,14 @@ * limitations under the License */ -package com.android.systemui.keyguard.data.repository +package com.android.systemui.bouncer.data.repository import android.os.Build import android.util.Log +import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants.EXPANSION_HIDDEN +import com.android.systemui.bouncer.shared.model.BouncerShowMessageModel import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application -import com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants.EXPANSION_HIDDEN -import com.android.systemui.keyguard.shared.model.BouncerShowMessageModel import com.android.systemui.log.dagger.BouncerTableLog import com.android.systemui.log.table.TableLogBuffer import com.android.systemui.log.table.logDiffsForTable diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/AlternateBouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractor.kt index 148d4255636c..2abdb849cd9a 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/AlternateBouncerInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractor.kt @@ -14,12 +14,12 @@ * limitations under the License. */ -package com.android.systemui.keyguard.domain.interactor +package com.android.systemui.bouncer.domain.interactor +import com.android.systemui.bouncer.data.repository.KeyguardBouncerRepository import com.android.systemui.dagger.SysUISingleton import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository import com.android.systemui.keyguard.data.repository.DeviceEntryFingerprintAuthRepository -import com.android.systemui.keyguard.data.repository.KeyguardBouncerRepository import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.statusbar.policy.KeyguardStateController import com.android.systemui.util.time.SystemClock diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt index 1d2fce7d8b05..256c63515fc8 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt @@ -21,7 +21,7 @@ import androidx.annotation.VisibleForTesting import com.android.systemui.R import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor import com.android.systemui.authentication.shared.model.AuthenticationMethodModel -import com.android.systemui.bouncer.data.repo.BouncerRepository +import com.android.systemui.bouncer.data.repository.BouncerRepository import com.android.systemui.bouncer.shared.model.AuthenticationThrottledModel import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.scene.domain.interactor.SceneInteractor @@ -149,19 +149,28 @@ constructor( * If the input is correct, the device will be unlocked and the lock screen and bouncer will be * dismissed and hidden. * + * If [tryAutoConfirm] is `true`, authentication is attempted if and only if the auth method + * supports auto-confirming, and the input's length is at least the code's length. Otherwise, + * `null` is returned. + * * @param input The input from the user to try to authenticate with. This can be a list of * different things, based on the current authentication method. - * @return `true` if the authentication succeeded and the device is now unlocked; `false` - * otherwise. + * @param tryAutoConfirm `true` if called while the user inputs the code, without an explicit + * request to validate. + * @return `true` if the authentication succeeded and the device is now unlocked; `false` when + * authentication failed, `null` if the check was not performed. */ fun authenticate( input: List<Any>, - ): Boolean { + tryAutoConfirm: Boolean = false, + ): Boolean? { if (repository.throttling.value != null) { return false } - val isAuthenticated = authenticationInteractor.authenticate(input) + val isAuthenticated = + authenticationInteractor.authenticate(input, tryAutoConfirm) ?: return null + val failedAttempts = authenticationInteractor.failedAuthenticationAttempts.value when { isAuthenticated -> { @@ -197,7 +206,7 @@ constructor( private fun promptMessage(authMethod: AuthenticationMethodModel): String { return when (authMethod) { - is AuthenticationMethodModel.PIN -> + is AuthenticationMethodModel.Pin -> applicationContext.getString(R.string.keyguard_enter_your_pin) is AuthenticationMethodModel.Password -> applicationContext.getString(R.string.keyguard_enter_your_password) @@ -209,7 +218,7 @@ constructor( private fun errorMessage(authMethod: AuthenticationMethodModel): String { return when (authMethod) { - is AuthenticationMethodModel.PIN -> applicationContext.getString(R.string.kg_wrong_pin) + is AuthenticationMethodModel.Pin -> applicationContext.getString(R.string.kg_wrong_pin) is AuthenticationMethodModel.Password -> applicationContext.getString(R.string.kg_wrong_password) is AuthenticationMethodModel.Pattern -> diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/bouncer/domain/interactor/BouncerMessageAuditLogger.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageAuditLogger.kt index 56f81fca0221..497747f59034 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/bouncer/domain/interactor/BouncerMessageAuditLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageAuditLogger.kt @@ -14,15 +14,15 @@ * limitations under the License. */ -package com.android.systemui.keyguard.bouncer.domain.interactor +package com.android.systemui.bouncer.domain.interactor import android.os.Build import android.util.Log import com.android.systemui.CoreStartable +import com.android.systemui.bouncer.data.repository.BouncerMessageRepository +import com.android.systemui.bouncer.shared.model.BouncerMessageModel import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application -import com.android.systemui.keyguard.bouncer.data.repository.BouncerMessageRepository -import com.android.systemui.keyguard.bouncer.shared.model.BouncerMessageModel import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/bouncer/domain/interactor/BouncerMessageInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractor.kt index 1754d934ee3a..d06dd8e1c124 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/bouncer/domain/interactor/BouncerMessageInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractor.kt @@ -14,18 +14,18 @@ * limitations under the License. */ -package com.android.systemui.keyguard.bouncer.domain.interactor +package com.android.systemui.bouncer.domain.interactor import android.os.CountDownTimer import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_DEFAULT import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_INCORRECT_PRIMARY_AUTH_INPUT import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_PRIMARY_AUTH_LOCKED_OUT +import com.android.systemui.bouncer.data.factory.BouncerMessageFactory +import com.android.systemui.bouncer.data.repository.BouncerMessageRepository +import com.android.systemui.bouncer.shared.model.BouncerMessageModel import com.android.systemui.dagger.SysUISingleton import com.android.systemui.flags.FeatureFlags import com.android.systemui.flags.Flags.REVAMPED_BOUNCER_MESSAGES -import com.android.systemui.keyguard.bouncer.data.factory.BouncerMessageFactory -import com.android.systemui.keyguard.bouncer.data.repository.BouncerMessageRepository -import com.android.systemui.keyguard.bouncer.shared.model.BouncerMessageModel import com.android.systemui.user.data.repository.UserRepository import javax.inject.Inject import kotlin.math.roundToInt diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerCallbackInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerCallbackInteractor.kt index 3099a497bf45..5fbe99ad2902 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerCallbackInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerCallbackInteractor.kt @@ -14,12 +14,12 @@ * limitations under the License */ -package com.android.systemui.keyguard.domain.interactor +package com.android.systemui.bouncer.domain.interactor import android.view.View +import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants.EXPANSION_HIDDEN +import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants.EXPANSION_VISIBLE import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants.EXPANSION_HIDDEN -import com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants.EXPANSION_VISIBLE import com.android.systemui.util.ListenerSet import javax.inject.Inject diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractor.kt index 54bc349cc4ac..c48660328bf0 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractor.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.keyguard.domain.interactor +package com.android.systemui.bouncer.domain.interactor import android.content.Context import android.content.res.ColorStateList @@ -29,6 +29,11 @@ import com.android.keyguard.KeyguardUpdateMonitor import com.android.keyguard.KeyguardUpdateMonitorCallback import com.android.systemui.DejankUtils import com.android.systemui.R +import com.android.systemui.bouncer.data.repository.KeyguardBouncerRepository +import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants +import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants.EXPANSION_HIDDEN +import com.android.systemui.bouncer.shared.model.BouncerShowMessageModel +import com.android.systemui.bouncer.ui.BouncerView import com.android.systemui.classifier.FalsingCollector import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application @@ -36,24 +41,19 @@ import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.flags.FeatureFlags import com.android.systemui.flags.Flags import com.android.systemui.keyguard.DismissCallbackRegistry -import com.android.systemui.keyguard.data.BouncerView -import com.android.systemui.keyguard.data.repository.KeyguardBouncerRepository import com.android.systemui.keyguard.data.repository.TrustRepository -import com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants -import com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants.EXPANSION_HIDDEN -import com.android.systemui.keyguard.shared.model.BouncerShowMessageModel import com.android.systemui.plugins.ActivityStarter import com.android.systemui.shared.system.SysUiStatsLog import com.android.systemui.statusbar.policy.KeyguardStateController +import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch -import javax.inject.Inject /** * Encapsulates business logic for interacting with the lock-screen primary (pin/pattern/password) @@ -77,8 +77,8 @@ constructor( private val featureFlags: FeatureFlags, @Application private val applicationScope: CoroutineScope, ) { - private val passiveAuthBouncerDelay = context.resources.getInteger( - R.integer.primary_bouncer_passive_auth_delay).toLong() + private val passiveAuthBouncerDelay = + context.resources.getInteger(R.integer.primary_bouncer_passive_auth_delay).toLong() /** Runnable to show the primary bouncer. */ val showRunnable = Runnable { repository.setPrimaryShow(true) @@ -87,7 +87,7 @@ constructor( } val keyguardAuthenticated: Flow<Boolean> = repository.keyguardAuthenticated.filterNotNull() - val isShowing: Flow<Boolean> = repository.primaryBouncerShow + val isShowing: StateFlow<Boolean> = repository.primaryBouncerShow val startingToHide: Flow<Unit> = repository.primaryBouncerStartingToHide.filter { it }.map {} val isBackButtonEnabled: Flow<Boolean> = repository.isBackButtonEnabled.filterNotNull() val showMessage: Flow<BouncerShowMessageModel> = repository.showMessage.filterNotNull() @@ -384,20 +384,24 @@ constructor( mainHandler.removeCallbacks(showRunnable) } - private fun isBouncerShowing(): Boolean { - return repository.primaryBouncerShow.value + /** Returns whether the primary bouncer is currently showing. */ + fun isBouncerShowing(): Boolean { + return isShowing.value } /** Whether we want to wait to show the bouncer in case passive auth succeeds. */ private fun usePrimaryBouncerPassiveAuthDelay(): Boolean { - val canRunFaceAuth = keyguardStateController.isFaceAuthEnabled && - keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(BiometricSourceType.FACE) - val canRunActiveUnlock = currentUserActiveUnlockRunning && + val canRunFaceAuth = + keyguardStateController.isFaceAuthEnabled && + keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(BiometricSourceType.FACE) && + keyguardUpdateMonitor.doesCurrentPostureAllowFaceAuth() + val canRunActiveUnlock = + currentUserActiveUnlockRunning && keyguardUpdateMonitor.canTriggerActiveUnlockBasedOnDeviceState() return featureFlags.isEnabled(Flags.DELAY_BOUNCER) && - !needsFullscreenBouncer() && - (canRunFaceAuth || canRunActiveUnlock) + !needsFullscreenBouncer() && + (canRunFaceAuth || canRunActiveUnlock) } companion object { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/constants/KeyguardBouncerConstants.kt b/packages/SystemUI/src/com/android/systemui/bouncer/shared/constants/KeyguardBouncerConstants.kt index c45faf0ba961..9f1781177f7a 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/constants/KeyguardBouncerConstants.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/shared/constants/KeyguardBouncerConstants.kt @@ -12,10 +12,9 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ -package com.android.systemui.keyguard.shared.constants +package com.android.systemui.bouncer.shared.constants object KeyguardBouncerConstants { /** diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/BouncerCallbackActionsModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/shared/model/BouncerCallbackActionsModel.kt index 81cf5b41ea71..afddd39aba33 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/BouncerCallbackActionsModel.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/shared/model/BouncerCallbackActionsModel.kt @@ -14,7 +14,7 @@ * limitations under the License */ -package com.android.systemui.keyguard.shared.model +package com.android.systemui.bouncer.shared.model import com.android.systemui.plugins.ActivityStarter diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/bouncer/shared/model/BouncerMessageModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/shared/model/BouncerMessageModel.kt index 46e88733ae6c..0e9e96213340 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/bouncer/shared/model/BouncerMessageModel.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/shared/model/BouncerMessageModel.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.keyguard.bouncer.shared.model +package com.android.systemui.bouncer.shared.model import android.content.res.ColorStateList diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/BouncerShowMessageModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/shared/model/BouncerShowMessageModel.kt index 05cdeaaa106d..187857289069 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/BouncerShowMessageModel.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/shared/model/BouncerShowMessageModel.kt @@ -14,7 +14,7 @@ * limitations under the License */ -package com.android.systemui.keyguard.shared.model +package com.android.systemui.bouncer.shared.model import android.content.res.ColorStateList diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/bouncer/ui/BouncerMessageView.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/BouncerMessageView.kt index 4dc52ff46707..47fac2b4269d 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/bouncer/ui/BouncerMessageView.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/BouncerMessageView.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.keyguard.bouncer.ui +package com.android.systemui.bouncer.ui import android.content.Context import android.util.AttributeSet diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/BouncerView.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/BouncerView.kt index 871a3ff63214..dc0032162d3c 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/BouncerView.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/BouncerView.kt @@ -14,7 +14,7 @@ * limitations under the License */ -package com.android.systemui.keyguard.data +package com.android.systemui.bouncer.ui import android.view.KeyEvent import android.window.OnBackAnimationCallback diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/BouncerViewModule.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/BouncerViewModule.kt index 390c54e0350a..0cbfb68b6e93 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/BouncerViewModule.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/BouncerViewModule.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.keyguard.data +package com.android.systemui.bouncer.ui import dagger.Binds import dagger.Module diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/BouncerMessageViewBinder.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/BouncerMessageViewBinder.kt new file mode 100644 index 000000000000..5a59012a74b8 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/BouncerMessageViewBinder.kt @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.bouncer.ui.binder + +import android.text.TextUtils +import android.util.PluralsMessageFormatter +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.repeatOnLifecycle +import com.android.keyguard.BouncerKeyguardMessageArea +import com.android.keyguard.KeyguardMessageArea +import com.android.keyguard.KeyguardMessageAreaController +import com.android.systemui.bouncer.domain.interactor.BouncerMessageInteractor +import com.android.systemui.bouncer.shared.model.Message +import com.android.systemui.bouncer.ui.BouncerMessageView +import com.android.systemui.flags.FeatureFlags +import com.android.systemui.flags.Flags +import com.android.systemui.lifecycle.repeatWhenAttached +import com.android.systemui.log.BouncerLogger +import kotlinx.coroutines.launch + +object BouncerMessageViewBinder { + @JvmStatic + fun bind( + view: BouncerMessageView, + interactor: BouncerMessageInteractor, + factory: KeyguardMessageAreaController.Factory, + bouncerLogger: BouncerLogger, + featureFlags: FeatureFlags + ) { + view.repeatWhenAttached { + if (!featureFlags.isEnabled(Flags.REVAMPED_BOUNCER_MESSAGES)) { + view.primaryMessageView?.disable() + view.secondaryMessageView?.disable() + return@repeatWhenAttached + } + view.init(factory) + view.primaryMessage?.setIsVisible(true) + view.secondaryMessage?.setIsVisible(true) + repeatOnLifecycle(Lifecycle.State.STARTED) { + bouncerLogger.startBouncerMessageInteractor() + launch { + interactor.bouncerMessage.collect { + bouncerLogger.bouncerMessageUpdated(it) + updateView( + view.primaryMessage, + view.primaryMessageView, + message = it?.message, + allowTruncation = true, + ) + updateView( + view.secondaryMessage, + view.secondaryMessageView, + message = it?.secondaryMessage, + allowTruncation = false, + ) + view.requestLayout() + } + } + } + } + } + + private fun updateView( + controller: KeyguardMessageAreaController<KeyguardMessageArea>?, + view: BouncerKeyguardMessageArea?, + message: Message?, + allowTruncation: Boolean = false, + ) { + if (view == null || controller == null) return + if (message?.message != null || message?.messageResId != null) { + controller.setIsVisible(true) + var newMessage = message.message ?: "" + if (message.messageResId != null && message.messageResId != 0) { + newMessage = view.resources.getString(message.messageResId) + if (message.formatterArgs != null) { + newMessage = + PluralsMessageFormatter.format( + view.resources, + message.formatterArgs, + message.messageResId + ) + } + } + controller.setMessage(newMessage, message.animate) + } else { + controller.setIsVisible(false) + controller.setMessage(0) + } + message?.colorState?.let { controller.setNextMessageColor(it) } + view.ellipsize = + if (allowTruncation) TextUtils.TruncateAt.END else TextUtils.TruncateAt.MARQUEE + } +} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/KeyguardBouncerViewBinder.kt index c1aefc7bcbd7..34e934bec003 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/KeyguardBouncerViewBinder.kt @@ -14,7 +14,7 @@ * limitations under the License */ -package com.android.systemui.keyguard.ui.binder +package com.android.systemui.bouncer.ui.binder import android.view.KeyEvent import android.view.View @@ -22,16 +22,20 @@ import android.view.ViewGroup import android.window.OnBackAnimationCallback import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle +import com.android.keyguard.KeyguardMessageAreaController import com.android.keyguard.KeyguardSecurityContainerController import com.android.keyguard.KeyguardSecurityModel import com.android.keyguard.KeyguardSecurityView import com.android.keyguard.KeyguardUpdateMonitor import com.android.keyguard.dagger.KeyguardBouncerComponent -import com.android.systemui.keyguard.data.BouncerViewDelegate -import com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants.EXPANSION_VISIBLE -import com.android.systemui.keyguard.ui.viewmodel.KeyguardBouncerViewModel +import com.android.systemui.bouncer.domain.interactor.BouncerMessageInteractor +import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants.EXPANSION_VISIBLE +import com.android.systemui.bouncer.ui.BouncerViewDelegate +import com.android.systemui.bouncer.ui.viewmodel.KeyguardBouncerViewModel +import com.android.systemui.flags.FeatureFlags import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel import com.android.systemui.lifecycle.repeatWhenAttached +import com.android.systemui.log.BouncerLogger import com.android.systemui.plugins.ActivityStarter import kotlinx.coroutines.awaitCancellation import kotlinx.coroutines.flow.filter @@ -44,7 +48,11 @@ object KeyguardBouncerViewBinder { view: ViewGroup, viewModel: KeyguardBouncerViewModel, primaryBouncerToGoneTransitionViewModel: PrimaryBouncerToGoneTransitionViewModel, - componentFactory: KeyguardBouncerComponent.Factory + componentFactory: KeyguardBouncerComponent.Factory, + messageAreaControllerFactory: KeyguardMessageAreaController.Factory, + bouncerMessageInteractor: BouncerMessageInteractor, + bouncerLogger: BouncerLogger, + featureFlags: FeatureFlags, ) { // Builds the KeyguardSecurityContainerController from bouncer view group. val securityContainerController: KeyguardSecurityContainerController = @@ -125,8 +133,16 @@ object KeyguardBouncerViewBinder { securityContainerController.onResume( KeyguardSecurityView.SCREEN_ON ) + bouncerLogger.bindingBouncerMessageView() + it.bindMessageView( + bouncerMessageInteractor, + messageAreaControllerFactory, + bouncerLogger, + featureFlags + ) } } else { + bouncerMessageInteractor.onBouncerBeingHidden() securityContainerController.onBouncerVisibilityChanged( /* isVisible= */ false ) diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt index 984d9ab1c1be..527fe6ec847d 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt @@ -126,7 +126,7 @@ constructor( .map { model -> model?.let { when (interactor.authenticationMethod.value) { - is AuthenticationMethodModel.PIN -> + is AuthenticationMethodModel.Pin -> R.string.kg_too_many_failed_pin_attempts_dialog_message is AuthenticationMethodModel.Password -> R.string.kg_too_many_failed_password_attempts_dialog_message @@ -165,7 +165,7 @@ constructor( authMethod: AuthenticationMethodModel, ): AuthMethodBouncerViewModel? { return when (authMethod) { - is AuthenticationMethodModel.PIN -> pin + is AuthenticationMethodModel.Pin -> pin is AuthenticationMethodModel.Password -> password is AuthenticationMethodModel.Pattern -> pattern else -> null diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModel.kt index 9602888ced58..6ba84399585d 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBouncerViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModel.kt @@ -14,13 +14,13 @@ * limitations under the License */ -package com.android.systemui.keyguard.ui.viewmodel +package com.android.systemui.bouncer.ui.viewmodel import android.view.View -import com.android.systemui.keyguard.data.BouncerView -import com.android.systemui.keyguard.data.BouncerViewDelegate -import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerInteractor -import com.android.systemui.keyguard.shared.model.BouncerShowMessageModel +import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor +import com.android.systemui.bouncer.shared.model.BouncerShowMessageModel +import com.android.systemui.bouncer.ui.BouncerView +import com.android.systemui.bouncer.ui.BouncerViewDelegate import javax.inject.Inject import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.filterNotNull diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModel.kt index 55929b566cf1..0146e406703c 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModel.kt @@ -50,7 +50,7 @@ class PasswordBouncerViewModel( /** Notifies that the user has pressed the key for attempting to authenticate the password. */ fun onAuthenticateKeyPressed() { - if (!interactor.authenticate(password.value.toCharArray().toList())) { + if (interactor.authenticate(password.value.toCharArray().toList()) != true) { showFailureAnimation() } diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModel.kt index d9ef75db6103..700703ee3560 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModel.kt @@ -153,9 +153,8 @@ class PatternBouncerViewModel( /** Notifies that the user has ended the drag gesture across the dot grid. */ fun onDragEnd() { - val isSuccessfullyAuthenticated = - interactor.authenticate(_selectedDots.value.map { it.toCoordinate() }) - if (!isSuccessfullyAuthenticated) { + val pattern = _selectedDots.value.map { it.toCoordinate() } + if (interactor.authenticate(pattern) != true) { showFailureAnimation() } diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt index 5c0fd92e7299..1944c74f1d6a 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt @@ -16,22 +16,19 @@ package com.android.systemui.bouncer.ui.viewmodel -import androidx.annotation.VisibleForTesting +import com.android.systemui.authentication.shared.model.AuthenticationMethodModel import com.android.systemui.bouncer.domain.interactor.BouncerInteractor -import com.android.systemui.util.kotlin.pairwise import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Job -import kotlinx.coroutines.delay import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn -import kotlinx.coroutines.launch /** Holds UI state and handles user input for the PIN code bouncer UI. */ class PinBouncerViewModel( - private val applicationScope: CoroutineScope, + applicationScope: CoroutineScope, private val interactor: BouncerInteractor, isInputEnabled: StateFlow<Boolean>, ) : @@ -39,21 +36,44 @@ class PinBouncerViewModel( isInputEnabled = isInputEnabled, ) { - private val entered = MutableStateFlow<List<Int>>(emptyList()) - /** - * The length of the PIN digits that were input so far, two values are supplied the previous and - * the current. - */ - val pinLengths: StateFlow<Pair<Int, Int>> = - entered - .pairwise() - .map { it.previousValue.size to it.newValue.size } + private val mutablePinEntries = MutableStateFlow<List<EnteredKey>>(emptyList()) + val pinEntries: StateFlow<List<EnteredKey>> = mutablePinEntries + + /** The length of the hinted PIN, or null if pin length hint should not be shown. */ + val hintedPinLength: StateFlow<Int?> = + interactor.authenticationMethod + .map { authMethod -> computeHintedPinLength(authMethod) } + .stateIn( + scope = applicationScope, + started = SharingStarted.Eagerly, + initialValue = computeHintedPinLength(interactor.authenticationMethod.value), + ) + + /** Appearance of the backspace button. */ + val backspaceButtonAppearance: StateFlow<ActionButtonAppearance> = + combine(interactor.authenticationMethod, mutablePinEntries) { authMethod, enteredPin -> + computeBackspaceButtonAppearance(authMethod, enteredPin) + } + .stateIn( + scope = applicationScope, + started = SharingStarted.Eagerly, + initialValue = + computeBackspaceButtonAppearance( + interactor.authenticationMethod.value, + mutablePinEntries.value + ), + ) + + /** Appearance of the confirm button. */ + val confirmButtonAppearance: StateFlow<ActionButtonAppearance> = + interactor.authenticationMethod + .map { authMethod -> computeConfirmButtonAppearance(authMethod) } .stateIn( scope = applicationScope, - started = SharingStarted.WhileSubscribed(), - initialValue = 0 to 0, + started = SharingStarted.Eagerly, + initialValue = + computeConfirmButtonAppearance(interactor.authenticationMethod.value), ) - private var resetPinJob: Job? = null /** Notifies that the UI has been shown to the user. */ fun onShown() { @@ -62,47 +82,108 @@ class PinBouncerViewModel( /** Notifies that the user clicked on a PIN button with the given digit value. */ fun onPinButtonClicked(input: Int) { - resetPinJob?.cancel() - resetPinJob = null - - if (entered.value.isEmpty()) { + if (mutablePinEntries.value.isEmpty()) { interactor.clearMessage() } - entered.value += input + mutablePinEntries.value += EnteredKey(input) + + tryAuthenticate(useAutoConfirm = true) } /** Notifies that the user clicked the backspace button. */ fun onBackspaceButtonClicked() { - if (entered.value.isEmpty()) { + if (mutablePinEntries.value.isEmpty()) { return } - - entered.value = entered.value.toMutableList().apply { removeLast() } + mutablePinEntries.value = mutablePinEntries.value.toMutableList().apply { removeLast() } } /** Notifies that the user long-pressed the backspace button. */ fun onBackspaceButtonLongPressed() { - resetPinJob?.cancel() - resetPinJob = - applicationScope.launch { - while (entered.value.isNotEmpty()) { - onBackspaceButtonClicked() - delay(BACKSPACE_LONG_PRESS_DELAY_MS) - } - } + mutablePinEntries.value = emptyList() } /** Notifies that the user clicked the "enter" button. */ fun onAuthenticateButtonClicked() { - if (!interactor.authenticate(entered.value)) { + tryAuthenticate(useAutoConfirm = false) + } + + private fun tryAuthenticate(useAutoConfirm: Boolean) { + val pinCode = mutablePinEntries.value.map { it.input } + val isSuccess = interactor.authenticate(pinCode, useAutoConfirm) ?: return + + if (!isSuccess) { showFailureAnimation() } - entered.value = emptyList() + mutablePinEntries.value = emptyList() + } + + private fun isAutoConfirmEnabled(authMethodModel: AuthenticationMethodModel): Boolean { + return (authMethodModel as? AuthenticationMethodModel.Pin)?.autoConfirm == true } - companion object { - @VisibleForTesting const val BACKSPACE_LONG_PRESS_DELAY_MS = 80L + private fun autoConfirmPinLength(authMethodModel: AuthenticationMethodModel): Int? { + if (!isAutoConfirmEnabled(authMethodModel)) return null + + return (authMethodModel as? AuthenticationMethodModel.Pin)?.code?.size + } + + private fun computeHintedPinLength(authMethodModel: AuthenticationMethodModel): Int? { + // Hinting is enabled for 6-digit codes only + return autoConfirmPinLength(authMethodModel).takeIf { it == HINTING_PASSCODE_LENGTH } + } + + private fun computeBackspaceButtonAppearance( + authMethodModel: AuthenticationMethodModel, + enteredPin: List<EnteredKey> + ): ActionButtonAppearance { + val isAutoConfirmEnabled = isAutoConfirmEnabled(authMethodModel) + val isEmpty = enteredPin.isEmpty() + + return when { + isAutoConfirmEnabled && isEmpty -> ActionButtonAppearance.Hidden + isAutoConfirmEnabled -> ActionButtonAppearance.Subtle + else -> ActionButtonAppearance.Shown + } + } + private fun computeConfirmButtonAppearance( + authMethodModel: AuthenticationMethodModel + ): ActionButtonAppearance { + return if (isAutoConfirmEnabled(authMethodModel)) { + ActionButtonAppearance.Hidden + } else { + ActionButtonAppearance.Shown + } } } + +/** Appearance of pin-pad action buttons. */ +enum class ActionButtonAppearance { + /** Button must not be shown. */ + Hidden, + /** Button is shown, but with no background to make it less prominent. */ + Subtle, + /** Button is shown. */ + Shown, +} + +/** Auto-confirm passcodes of exactly 6 digits show a length hint, see http://shortn/_IXlmSNbDh6 */ +private const val HINTING_PASSCODE_LENGTH = 6 + +private var nextSequenceNumber = 1 + +/** + * The pin bouncer [input] as digits 0-9, together with a [sequenceNumber] to indicate the ordering. + * + * Since the model only allows appending/removing [EnteredKey]s from the end, the [sequenceNumber] + * is strictly increasing in input order of the pin, but not guaranteed to be monotonic or start at + * a specific number. + */ +data class EnteredKey +internal constructor(val input: Int, val sequenceNumber: Int = nextSequenceNumber++) : + Comparable<EnteredKey> { + override fun compareTo(other: EnteredKey): Int = + compareValuesBy(this, other, EnteredKey::sequenceNumber) +} diff --git a/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java b/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java index b2bcb0554b0d..25ccc1658744 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java @@ -396,6 +396,7 @@ public class BrightLineFalsingManager implements FalsingManager { || mDataProvider.isDocked() || mAccessibilityManager.isTouchExplorationEnabled() || mDataProvider.isA11yAction() + || mDataProvider.isFromTrackpad() || (mFeatureFlags.isEnabled(Flags.FALSING_OFF_FOR_UNFOLDED) && mDataProvider.isUnfolded()); } diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingDataProvider.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingDataProvider.java index d6c85fbaf5a9..809d5b289cab 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingDataProvider.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingDataProvider.java @@ -261,6 +261,16 @@ public class FalsingDataProvider { return mLastMotionEvent.getY() < mFirstRecentMotionEvent.getY(); } + public boolean isFromTrackpad() { + if (mRecentMotionEvents.isEmpty()) { + return false; + } + + int classification = mRecentMotionEvents.get(0).getClassification(); + return classification == MotionEvent.CLASSIFICATION_MULTI_FINGER_SWIPE + || classification == MotionEvent.CLASSIFICATION_TWO_FINGER_SWIPE; + } + private void recalculateData() { if (!mDirty) { return; @@ -343,7 +353,9 @@ public class FalsingDataProvider { motionEvent.getDeviceId(), motionEvent.getEdgeFlags(), motionEvent.getSource(), - motionEvent.getFlags() + motionEvent.getDisplayId(), + motionEvent.getFlags(), + motionEvent.getClassification() )); } diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java index 757ebf45e9ad..ffd836b3230f 100644 --- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java +++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java @@ -18,6 +18,7 @@ package com.android.systemui.clipboardoverlay; import static android.content.Intent.ACTION_CLOSE_SYSTEM_DIALOGS; + import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.CLIPBOARD_OVERLAY_SHOW_ACTIONS; import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_ACTION_SHOWN; import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_ACTION_TAPPED; @@ -33,6 +34,7 @@ import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBO import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_TAP_OUTSIDE; import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_TIMED_OUT; import static com.android.systemui.flags.Flags.CLIPBOARD_IMAGE_TIMEOUT; +import static com.android.systemui.flags.Flags.CLIPBOARD_SHARED_TRANSITIONS; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; @@ -47,6 +49,7 @@ import android.hardware.input.InputManager; import android.net.Uri; import android.os.Looper; import android.provider.DeviceConfig; +import android.util.Log; import android.view.InputEvent; import android.view.InputEventReceiver; import android.view.InputMonitor; @@ -54,6 +57,7 @@ import android.view.MotionEvent; import android.view.WindowInsets; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.UiEventLogger; @@ -73,7 +77,8 @@ import javax.inject.Inject; /** * Controls state and UI for the overlay that appears when something is added to the clipboard */ -public class ClipboardOverlayController implements ClipboardListener.ClipboardOverlay { +public class ClipboardOverlayController implements ClipboardListener.ClipboardOverlay, + ClipboardOverlayView.ClipboardOverlayCallbacks { private static final String TAG = "ClipboardOverlayCtrlr"; /** Constants for screenshot/copy deconflicting */ @@ -92,6 +97,7 @@ public class ClipboardOverlayController implements ClipboardListener.ClipboardOv private final FeatureFlags mFeatureFlags; private final Executor mBgExecutor; private final ClipboardImageLoader mClipboardImageLoader; + private final ClipboardTransitionExecutor mTransitionExecutor; private final ClipboardOverlayView mView; @@ -179,10 +185,12 @@ public class ClipboardOverlayController implements ClipboardListener.ClipboardOv ClipboardOverlayUtils clipboardUtils, @Background Executor bgExecutor, ClipboardImageLoader clipboardImageLoader, + ClipboardTransitionExecutor transitionExecutor, UiEventLogger uiEventLogger) { mContext = context; mBroadcastDispatcher = broadcastDispatcher; mClipboardImageLoader = clipboardImageLoader; + mTransitionExecutor = transitionExecutor; mClipboardLogger = new ClipboardLogger(uiEventLogger); @@ -200,7 +208,11 @@ public class ClipboardOverlayController implements ClipboardListener.ClipboardOv mClipboardUtils = clipboardUtils; mBgExecutor = bgExecutor; - mView.setCallbacks(mClipboardCallbacks); + if (mFeatureFlags.isEnabled(CLIPBOARD_SHARED_TRANSITIONS)) { + mView.setCallbacks(this); + } else { + mView.setCallbacks(mClipboardCallbacks); + } mWindow.withWindowAttached(() -> { mWindow.setContentView(mView); @@ -209,16 +221,24 @@ public class ClipboardOverlayController implements ClipboardListener.ClipboardOv }); mTimeoutHandler.setOnTimeoutRunnable(() -> { - mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_TIMED_OUT); - animateOut(); + if (mFeatureFlags.isEnabled(CLIPBOARD_SHARED_TRANSITIONS)) { + finish(CLIPBOARD_OVERLAY_TIMED_OUT); + } else { + mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_TIMED_OUT); + animateOut(); + } }); mCloseDialogsReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if (ACTION_CLOSE_SYSTEM_DIALOGS.equals(intent.getAction())) { - mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_DISMISSED_OTHER); - animateOut(); + if (mFeatureFlags.isEnabled(CLIPBOARD_SHARED_TRANSITIONS)) { + finish(CLIPBOARD_OVERLAY_DISMISSED_OTHER); + } else { + mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_DISMISSED_OTHER); + animateOut(); + } } } }; @@ -229,8 +249,12 @@ public class ClipboardOverlayController implements ClipboardListener.ClipboardOv @Override public void onReceive(Context context, Intent intent) { if (SCREENSHOT_ACTION.equals(intent.getAction())) { - mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_DISMISSED_OTHER); - animateOut(); + if (mFeatureFlags.isEnabled(CLIPBOARD_SHARED_TRANSITIONS)) { + finish(CLIPBOARD_OVERLAY_DISMISSED_OTHER); + } else { + mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_DISMISSED_OTHER); + animateOut(); + } } } }; @@ -457,8 +481,12 @@ public class ClipboardOverlayController implements ClipboardListener.ClipboardOv remoteAction.ifPresent(action -> { mClipboardLogger.logUnguarded(CLIPBOARD_OVERLAY_ACTION_SHOWN); mView.post(() -> mView.setActionChip(action, () -> { - mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_ACTION_TAPPED); - animateOut(); + if (mFeatureFlags.isEnabled(CLIPBOARD_SHARED_TRANSITIONS)) { + finish(CLIPBOARD_OVERLAY_ACTION_TAPPED); + } else { + mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_ACTION_TAPPED); + animateOut(); + } })); }); } @@ -500,8 +528,12 @@ public class ClipboardOverlayController implements ClipboardListener.ClipboardOv if (motionEvent.getActionMasked() == MotionEvent.ACTION_DOWN) { if (!mView.isInTouchRegion( (int) motionEvent.getRawX(), (int) motionEvent.getRawY())) { - mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_TAP_OUTSIDE); - animateOut(); + if (mFeatureFlags.isEnabled(CLIPBOARD_SHARED_TRANSITIONS)) { + finish(CLIPBOARD_OVERLAY_TAP_OUTSIDE); + } else { + mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_TAP_OUTSIDE); + animateOut(); + } } } } @@ -551,12 +583,16 @@ public class ClipboardOverlayController implements ClipboardListener.ClipboardOv mEnterAnimator.start(); } + private void finish(ClipboardOverlayEvent event) { + finish(event, null); + } + private void animateOut() { if (mExitAnimator != null && mExitAnimator.isRunning()) { return; } - Animator anim = mView.getExitAnimation(); - anim.addListener(new AnimatorListenerAdapter() { + mExitAnimator = mView.getExitAnimation(); + mExitAnimator.addListener(new AnimatorListenerAdapter() { private boolean mCancelled; @Override @@ -573,8 +609,47 @@ public class ClipboardOverlayController implements ClipboardListener.ClipboardOv } } }); - mExitAnimator = anim; - anim.start(); + mExitAnimator.start(); + } + + private void finish(ClipboardOverlayEvent event, @Nullable Intent intent) { + if (mExitAnimator != null && mExitAnimator.isRunning()) { + return; + } + mExitAnimator = mView.getExitAnimation(); + mExitAnimator.addListener(new AnimatorListenerAdapter() { + private boolean mCancelled; + + @Override + public void onAnimationCancel(Animator animation) { + super.onAnimationCancel(animation); + mCancelled = true; + } + + @Override + public void onAnimationEnd(Animator animation) { + super.onAnimationEnd(animation); + if (!mCancelled) { + mClipboardLogger.logSessionComplete(event); + if (intent != null) { + mContext.startActivity(intent); + } + hideImmediate(); + } + } + }); + mExitAnimator.start(); + } + + private void finishWithSharedTransition(ClipboardOverlayEvent event, Intent intent) { + if (mExitAnimator != null && mExitAnimator.isRunning()) { + return; + } + mClipboardLogger.logSessionComplete(event); + mExitAnimator = mView.getFadeOutAnimation(); + mExitAnimator.start(); + mTransitionExecutor.startSharedTransition( + mWindow, mView.getPreview(), intent, this::hideImmediate); } void hideImmediate() { @@ -613,6 +688,76 @@ public class ClipboardOverlayController implements ClipboardListener.ClipboardOv mClipboardLogger.reset(); } + @Override + public void onDismissButtonTapped() { + if (mFeatureFlags.isEnabled(CLIPBOARD_SHARED_TRANSITIONS)) { + finish(CLIPBOARD_OVERLAY_DISMISS_TAPPED); + } + } + + @Override + public void onRemoteCopyButtonTapped() { + if (mFeatureFlags.isEnabled(CLIPBOARD_SHARED_TRANSITIONS)) { + finish(CLIPBOARD_OVERLAY_REMOTE_COPY_TAPPED, + IntentCreator.getRemoteCopyIntent(mClipboardModel.getClipData(), mContext)); + } + } + + @Override + public void onShareButtonTapped() { + if (mFeatureFlags.isEnabled(CLIPBOARD_SHARED_TRANSITIONS)) { + if (mClipboardModel.getType() != ClipboardModel.Type.OTHER) { + finishWithSharedTransition(CLIPBOARD_OVERLAY_SHARE_TAPPED, + IntentCreator.getShareIntent(mClipboardModel.getClipData(), mContext)); + } + } + } + + @Override + public void onPreviewTapped() { + if (mFeatureFlags.isEnabled(CLIPBOARD_SHARED_TRANSITIONS)) { + switch (mClipboardModel.getType()) { + case TEXT: + finish(CLIPBOARD_OVERLAY_EDIT_TAPPED, + IntentCreator.getTextEditorIntent(mContext)); + break; + case IMAGE: + finishWithSharedTransition(CLIPBOARD_OVERLAY_EDIT_TAPPED, + IntentCreator.getImageEditIntent(mClipboardModel.getUri(), mContext)); + break; + default: + Log.w(TAG, "Got preview tapped callback for non-editable type " + + mClipboardModel.getType()); + } + } + } + + @Override + public void onMinimizedViewTapped() { + animateFromMinimized(); + } + + @Override + public void onInteraction() { + if (!mClipboardModel.isRemote()) { + mTimeoutHandler.resetTimeout(); + } + } + + @Override + public void onSwipeDismissInitiated(Animator animator) { + if (mExitAnimator != null && mExitAnimator.isRunning()) { + mExitAnimator.cancel(); + } + mExitAnimator = animator; + mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_SWIPE_DISMISSED); + } + + @Override + public void onDismissComplete() { + hideImmediate(); + } + static class ClipboardLogger { private final UiEventLogger mUiEventLogger; private String mClipSource; diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayView.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayView.java index 28c57d31a4f3..a76d2ea816a7 100644 --- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayView.java +++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayView.java @@ -254,6 +254,10 @@ public class ClipboardOverlayView extends DraggableConstraintLayout { }); } + View getPreview() { + return mClipboardPreview; + } + void showImagePreview(@Nullable Bitmap thumbnail) { if (thumbnail == null) { mHiddenPreview.setText(mContext.getString(R.string.clipboard_text_hidden)); @@ -368,6 +372,19 @@ public class ClipboardOverlayView extends DraggableConstraintLayout { return enterAnim; } + Animator getFadeOutAnimation() { + ValueAnimator alphaAnim = ValueAnimator.ofFloat(1, 0); + alphaAnim.addUpdateListener(animation -> { + float alpha = (float) animation.getAnimatedValue(); + mActionContainer.setAlpha(alpha); + mActionContainerBackground.setAlpha(alpha); + mPreviewBorder.setAlpha(alpha); + mDismissButton.setAlpha(alpha); + }); + alphaAnim.setDuration(300); + return alphaAnim; + } + Animator getExitAnimation() { TimeInterpolator linearInterpolator = new LinearInterpolator(); TimeInterpolator scaleInterpolator = new PathInterpolator(.3f, 0, 1f, 1f); diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardTransitionExecutor.kt b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardTransitionExecutor.kt new file mode 100644 index 000000000000..0b8e83edc88a --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardTransitionExecutor.kt @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.clipboardoverlay + +import android.app.ActivityOptions +import android.app.ExitTransitionCoordinator +import android.content.Context +import android.content.Intent +import android.os.RemoteException +import android.util.Log +import android.util.Pair +import android.view.IRemoteAnimationFinishedCallback +import android.view.IRemoteAnimationRunner +import android.view.RemoteAnimationAdapter +import android.view.RemoteAnimationTarget +import android.view.View +import android.view.Window +import android.view.WindowManagerGlobal +import com.android.internal.app.ChooserActivity +import com.android.systemui.settings.DisplayTracker +import javax.inject.Inject + +class ClipboardTransitionExecutor +@Inject +constructor(val context: Context, val displayTracker: DisplayTracker) { + fun startSharedTransition(window: Window, view: View, intent: Intent, onReady: Runnable) { + val transition: Pair<ActivityOptions, ExitTransitionCoordinator> = + ActivityOptions.startSharedElementAnimation( + window, + object : ExitTransitionCoordinator.ExitTransitionCallbacks { + override fun isReturnTransitionAllowed(): Boolean { + return false + } + + override fun hideSharedElements() { + onReady.run() + } + + override fun onFinish() {} + }, + null, + Pair.create(view, ChooserActivity.FIRST_IMAGE_PREVIEW_TRANSITION_NAME) + ) + transition.second.startExit() + context.startActivity(intent, transition.first.toBundle()) + val runner = RemoteAnimationAdapter(NULL_ACTIVITY_TRANSITION, 0, 0) + try { + WindowManagerGlobal.getWindowManagerService() + .overridePendingAppTransitionRemote(runner, displayTracker.defaultDisplayId) + } catch (e: Exception) { + Log.e(TAG, "Error overriding clipboard app transition", e) + } + } + + private val TAG: String = "ClipboardTransitionExec" + + /** + * This is effectively a no-op, but we need something non-null to pass in, in order to + * successfully override the pending activity entrance animation. + */ + private val NULL_ACTIVITY_TRANSITION: IRemoteAnimationRunner.Stub = + object : IRemoteAnimationRunner.Stub() { + override fun onAnimationStart( + transit: Int, + apps: Array<RemoteAnimationTarget>, + wallpapers: Array<RemoteAnimationTarget>, + nonApps: Array<RemoteAnimationTarget>, + finishedCallback: IRemoteAnimationFinishedCallback + ) { + try { + finishedCallback.onAnimationFinished() + } catch (e: RemoteException) { + Log.e(TAG, "Error finishing screenshot remote animation", e) + } + } + + override fun onAnimationCancelled() {} + } +} diff --git a/packages/SystemUI/src/com/android/systemui/common/ui/view/SeekBarWithIconButtonsView.java b/packages/SystemUI/src/com/android/systemui/common/ui/view/SeekBarWithIconButtonsView.java index de3a9901b8c4..4538a6ca7954 100644 --- a/packages/SystemUI/src/com/android/systemui/common/ui/view/SeekBarWithIconButtonsView.java +++ b/packages/SystemUI/src/com/android/systemui/common/ui/view/SeekBarWithIconButtonsView.java @@ -27,6 +27,7 @@ import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.SeekBar; +import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.R; /** @@ -37,12 +38,14 @@ public class SeekBarWithIconButtonsView extends LinearLayout { private static final int DEFAULT_SEEKBAR_MAX = 6; private static final int DEFAULT_SEEKBAR_PROGRESS = 0; + private static final int DEFAULT_SEEKBAR_TICK_MARK = 0; private ViewGroup mIconStartFrame; private ViewGroup mIconEndFrame; private ImageView mIconStart; private ImageView mIconEnd; private SeekBar mSeekbar; + private int mSeekBarChangeMagnitude = 1; private SeekBarChangeListener mSeekBarListener = new SeekBarChangeListener(); private String[] mStateLabels = null; @@ -102,6 +105,15 @@ public class SeekBarWithIconButtonsView extends LinearLayout { context.getString(iconEndFrameContentDescriptionId); mIconEndFrame.setContentDescription(contentDescription); } + int tickMarkId = typedArray.getResourceId( + R.styleable.SeekBarWithIconButtonsView_Layout_tickMark, + DEFAULT_SEEKBAR_TICK_MARK); + if (tickMarkId != DEFAULT_SEEKBAR_TICK_MARK) { + mSeekbar.setTickMark(getResources().getDrawable(tickMarkId)); + } + mSeekBarChangeMagnitude = typedArray.getInt( + R.styleable.SeekBarWithIconButtonsView_Layout_seekBarChangeMagnitude, + /* defValue= */ 1); } else { mSeekbar.setMax(DEFAULT_SEEKBAR_MAX); setProgress(DEFAULT_SEEKBAR_PROGRESS); @@ -112,7 +124,7 @@ public class SeekBarWithIconButtonsView extends LinearLayout { mIconStartFrame.setOnClickListener((view) -> { final int progress = mSeekbar.getProgress(); if (progress > 0) { - mSeekbar.setProgress(progress - 1); + mSeekbar.setProgress(progress - mSeekBarChangeMagnitude); setIconViewAndFrameEnabled(mIconStart, mSeekbar.getProgress() > 0); } }); @@ -120,7 +132,7 @@ public class SeekBarWithIconButtonsView extends LinearLayout { mIconEndFrame.setOnClickListener((view) -> { final int progress = mSeekbar.getProgress(); if (progress < mSeekbar.getMax()) { - mSeekbar.setProgress(progress + 1); + mSeekbar.setProgress(progress + mSeekBarChangeMagnitude); setIconViewAndFrameEnabled(mIconEnd, mSeekbar.getProgress() < mSeekbar.getMax()); } }); @@ -183,6 +195,20 @@ public class SeekBarWithIconButtonsView extends LinearLayout { } /** + * Gets max to the seekbar in the layout. + */ + public int getMax() { + return mSeekbar.getMax(); + } + + /** + * @return the magnitude by which seekbar progress changes when start and end icons are clicked. + */ + public int getChangeMagnitude() { + return mSeekBarChangeMagnitude; + } + + /** * Sets progress to the seekbar in the layout. * If the progress is smaller than or equals to 0, the IconStart will be disabled. If the * progress is larger than or equals to Max, the IconEnd will be disabled. The seekbar progress @@ -193,6 +219,11 @@ public class SeekBarWithIconButtonsView extends LinearLayout { updateIconViewIfNeeded(progress); } + @VisibleForTesting + public int getProgress() { + return mSeekbar.getProgress(); + } + private class SeekBarChangeListener implements SeekBar.OnSeekBarChangeListener { private SeekBar.OnSeekBarChangeListener mOnSeekBarChangeListener = null; diff --git a/packages/SystemUI/src/com/android/systemui/compose/BaseComposeFacade.kt b/packages/SystemUI/src/com/android/systemui/compose/BaseComposeFacade.kt index 4173bdc3c261..b15c60e62ead 100644 --- a/packages/SystemUI/src/com/android/systemui/compose/BaseComposeFacade.kt +++ b/packages/SystemUI/src/com/android/systemui/compose/BaseComposeFacade.kt @@ -24,6 +24,9 @@ import androidx.lifecycle.LifecycleOwner import com.android.systemui.multishade.ui.viewmodel.MultiShadeViewModel import com.android.systemui.people.ui.viewmodel.PeopleViewModel import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel +import com.android.systemui.scene.shared.model.Scene +import com.android.systemui.scene.shared.model.SceneKey +import com.android.systemui.scene.ui.viewmodel.SceneContainerViewModel import com.android.systemui.util.time.SystemClock /** @@ -66,4 +69,11 @@ interface BaseComposeFacade { viewModel: MultiShadeViewModel, clock: SystemClock, ): View + + /** Create a [View] to represent [viewModel] on screen. */ + fun createSceneContainerView( + context: Context, + viewModel: SceneContainerViewModel, + sceneByKey: Map<SceneKey, Scene>, + ): View } diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt index 18bd46756660..ccbde011334d 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt @@ -312,6 +312,7 @@ open class ControlsBindingControllerImpl @Inject constructor( Log.d(TAG, "Canceling loadSubscribtion") it.invoke() } + callback.error("Load cancelled") } override fun onSubscribe(token: IBinder, subs: IControlsSubscription) { diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java index 2262d8ab2000..f68bd49230d9 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java @@ -40,7 +40,6 @@ import com.android.systemui.qs.tileimpl.QSFactoryImpl; import com.android.systemui.recents.Recents; import com.android.systemui.recents.RecentsImplementation; import com.android.systemui.rotationlock.RotationLockModule; -import com.android.systemui.scene.SceneContainerFrameworkModule; import com.android.systemui.screenshot.ReferenceScreenshotModule; import com.android.systemui.settings.dagger.MultiUserUtilsModule; import com.android.systemui.shade.NotificationShadeWindowControllerImpl; @@ -104,7 +103,6 @@ import javax.inject.Named; QSModule.class, ReferenceScreenshotModule.class, RotationLockModule.class, - SceneContainerFrameworkModule.class, StatusBarEventsModule.class, StartCentralSurfacesModule.class, VolumeModule.class, diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java index 52355f34a71d..d82bf587212e 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java @@ -16,7 +16,6 @@ package com.android.systemui.dagger; -import com.android.keyguard.clock.ClockOptionsProvider; import com.android.systemui.BootCompleteCacheImpl; import com.android.systemui.CoreStartable; import com.android.systemui.Dependency; @@ -252,10 +251,5 @@ public interface SysUIComponent { /** * Member injection into the supplied argument. */ - void inject(ClockOptionsProvider clockOptionsProvider); - - /** - * Member injection into the supplied argument. - */ void inject(PeopleProvider peopleProvider); } diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt index 25634f009fc7..76002d3f9693 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt @@ -34,6 +34,7 @@ import com.android.systemui.globalactions.GlobalActionsComponent import com.android.systemui.keyboard.KeyboardUI import com.android.systemui.keyboard.PhysicalKeyboardCoreStartable import com.android.systemui.keyguard.KeyguardViewMediator +import com.android.systemui.keyguard.KeyguardViewConfigurator import com.android.systemui.keyguard.data.quickaffordance.MuteQuickAffordanceCoreStartable import com.android.systemui.log.SessionTracker import com.android.systemui.media.RingtonePlayer @@ -295,4 +296,9 @@ abstract class SystemUICoreStartableModule { @IntoMap @ClassKey(AssistantAttentionMonitor::class) abstract fun bindAssistantAttentionMonitor(sysui: AssistantAttentionMonitor): CoreStartable + + @Binds + @IntoMap + @ClassKey(KeyguardViewConfigurator::class) + abstract fun bindKeyguardViewConfigurator(impl: KeyguardViewConfigurator): CoreStartable } diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java index 5bcf32a9ebdb..b5d3fafeb3df 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java @@ -23,7 +23,6 @@ import android.service.dreams.IDreamManager; import androidx.annotation.Nullable; import com.android.internal.statusbar.IStatusBarService; -import com.android.keyguard.clock.ClockInfoModule; import com.android.keyguard.dagger.ClockRegistryModule; import com.android.keyguard.dagger.KeyguardBouncerComponent; import com.android.systemui.BootCompleteCache; @@ -39,6 +38,7 @@ import com.android.systemui.biometrics.FingerprintReEnrollNotification; import com.android.systemui.biometrics.UdfpsDisplayModeProvider; import com.android.systemui.biometrics.dagger.BiometricsModule; import com.android.systemui.biometrics.dagger.UdfpsModule; +import com.android.systemui.bouncer.ui.BouncerViewModule; import com.android.systemui.classifier.FalsingModule; import com.android.systemui.clipboardoverlay.dagger.ClipboardOverlayModule; import com.android.systemui.common.ui.data.repository.CommonRepositoryModule; @@ -53,7 +53,6 @@ import com.android.systemui.dump.DumpManager; import com.android.systemui.flags.FeatureFlags; import com.android.systemui.flags.FlagsModule; import com.android.systemui.keyboard.KeyboardModule; -import com.android.systemui.keyguard.data.BouncerViewModule; import com.android.systemui.log.dagger.LogModule; import com.android.systemui.log.dagger.MonitorLog; import com.android.systemui.log.table.TableLogBuffer; @@ -74,6 +73,7 @@ import com.android.systemui.qs.QSFragmentStartableModule; import com.android.systemui.qs.footer.dagger.FooterActionsModule; import com.android.systemui.recents.Recents; import com.android.systemui.retail.dagger.RetailModeModule; +import com.android.systemui.scene.SceneContainerFrameworkModule; import com.android.systemui.screenrecord.ScreenRecordModule; import com.android.systemui.screenshot.dagger.ScreenshotModule; import com.android.systemui.security.data.repository.SecurityRepositoryModule; @@ -160,7 +160,6 @@ import javax.inject.Named; BiometricsModule.class, BouncerViewModule.class, ClipboardOverlayModule.class, - ClockInfoModule.class, ClockRegistryModule.class, CommonRepositoryModule.class, ConnectivityModule.class, @@ -186,6 +185,7 @@ import javax.inject.Named; QRCodeScannerModule.class, QSFragmentStartableModule.class, RetailModeModule.class, + SceneContainerFrameworkModule.class, ScreenshotModule.class, SensorModule.class, SecurityRepositoryModule.class, diff --git a/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java index b71871ebdb55..d70c57fda2e1 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java @@ -16,12 +16,11 @@ package com.android.systemui.dagger; -import android.content.Context; import android.os.HandlerThread; import androidx.annotation.Nullable; -import com.android.systemui.SystemUIInitializerFactory; +import com.android.systemui.SystemUIInitializer; import com.android.systemui.tv.TvWMComponent; import com.android.wm.shell.back.BackAnimation; import com.android.wm.shell.bubbles.Bubbles; @@ -49,7 +48,7 @@ import java.util.Optional; /** * Dagger Subcomponent for WindowManager. This class explicitly describes the interfaces exported * from the WM component into the SysUI component (in - * {@link SystemUIInitializerFactory#init(Context, boolean)}), and references the specific dependencies + * {@link SystemUIInitializer#init(boolean)}), and references the specific dependencies * provided by its particular device/form-factor SystemUI implementation. * * ie. {@link WMComponent} includes {@link WMShellModule} diff --git a/packages/SystemUI/src/com/android/systemui/decor/DebugRoundedCornerDelegate.kt b/packages/SystemUI/src/com/android/systemui/decor/DebugRoundedCornerDelegate.kt new file mode 100644 index 000000000000..4069bc7d73d0 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/decor/DebugRoundedCornerDelegate.kt @@ -0,0 +1,198 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.decor + +import android.graphics.Canvas +import android.graphics.Color +import android.graphics.ColorFilter +import android.graphics.Paint +import android.graphics.Path +import android.graphics.PixelFormat +import android.graphics.drawable.Drawable +import android.util.Size +import java.io.PrintWriter + +/** + * Rounded corner delegate that handles incoming debug commands and can convert them to path + * drawables to be shown instead of the system-defined rounded corners. + * + * These debug corners are expected to supersede the system-defined corners + */ +class DebugRoundedCornerDelegate : RoundedCornerResDelegate { + override var hasTop: Boolean = false + private set + override var topRoundedDrawable: Drawable? = null + private set + override var topRoundedSize: Size = Size(0, 0) + private set + + override var hasBottom: Boolean = false + private set + override var bottomRoundedDrawable: Drawable? = null + private set + override var bottomRoundedSize: Size = Size(0, 0) + private set + + override var physicalPixelDisplaySizeRatio: Float = 1f + set(value) { + if (field == value) { + return + } + field = value + reloadMeasures() + } + + var color: Int = Color.RED + set(value) { + if (field == value) { + return + } + + field = value + paint.color = field + } + + var paint = + Paint().apply { + color = Color.RED + style = Paint.Style.FILL + } + + override fun updateDisplayUniqueId(newDisplayUniqueId: String?, newReloadToken: Int?) { + // nop -- debug corners draw the same on every display + } + + fun applyNewDebugCorners( + topCorner: DebugRoundedCornerModel, + bottomCorner: DebugRoundedCornerModel, + ) { + hasTop = true + topRoundedDrawable = topCorner.toPathDrawable(paint) + topRoundedSize = topCorner.size() + + hasBottom = true + bottomRoundedDrawable = bottomCorner.toPathDrawable(paint) + bottomRoundedSize = bottomCorner.size() + } + + /** + * Remove accumulated debug state by clearing out the drawables and setting [hasTop] and + * [hasBottom] to false. + */ + fun removeDebugState() { + hasTop = false + topRoundedDrawable = null + topRoundedSize = Size(0, 0) + + hasBottom = false + bottomRoundedDrawable = null + bottomRoundedSize = Size(0, 0) + } + + /** + * Scaling here happens when the display resolution is changed. This logic is exactly the same + * as in [RoundedCornerResDelegateImpl] + */ + private fun reloadMeasures() { + topRoundedDrawable?.let { topRoundedSize = Size(it.intrinsicWidth, it.intrinsicHeight) } + bottomRoundedDrawable?.let { + bottomRoundedSize = Size(it.intrinsicWidth, it.intrinsicHeight) + } + + if (physicalPixelDisplaySizeRatio != 1f) { + if (topRoundedSize.width != 0) { + topRoundedSize = + Size( + (physicalPixelDisplaySizeRatio * topRoundedSize.width + 0.5f).toInt(), + (physicalPixelDisplaySizeRatio * topRoundedSize.height + 0.5f).toInt() + ) + } + if (bottomRoundedSize.width != 0) { + bottomRoundedSize = + Size( + (physicalPixelDisplaySizeRatio * bottomRoundedSize.width + 0.5f).toInt(), + (physicalPixelDisplaySizeRatio * bottomRoundedSize.height + 0.5f).toInt() + ) + } + } + } + + fun dump(pw: PrintWriter) { + pw.println("DebugRoundedCornerDelegate state:") + pw.println(" hasTop=$hasTop") + pw.println(" hasBottom=$hasBottom") + pw.println(" topRoundedSize(w,h)=(${topRoundedSize.width},${topRoundedSize.height})") + pw.println( + " bottomRoundedSize(w,h)=(${bottomRoundedSize.width},${bottomRoundedSize.height})" + ) + pw.println(" physicalPixelDisplaySizeRatio=$physicalPixelDisplaySizeRatio") + } +} + +/** Encapsulates the data coming in from the command line args and turns into a [PathDrawable] */ +data class DebugRoundedCornerModel( + val path: Path, + val width: Int, + val height: Int, + val scaleX: Float, + val scaleY: Float, +) { + fun size() = Size(width, height) + + fun toPathDrawable(paint: Paint) = + PathDrawable( + path, + width, + height, + scaleX, + scaleY, + paint, + ) +} + +/** + * PathDrawable accepts paths from the command line via [DebugRoundedCornerModel], and renders them + * in the canvas provided by the screen decor rounded corner provider + */ +class PathDrawable( + val path: Path, + val width: Int, + val height: Int, + val scaleX: Float = 1f, + val scaleY: Float = 1f, + val paint: Paint, +) : Drawable() { + private var cf: ColorFilter? = null + + override fun draw(canvas: Canvas) { + if (scaleX != 1f || scaleY != 1f) { + canvas.scale(scaleX, scaleY) + } + canvas.drawPath(path, paint) + } + + override fun getIntrinsicHeight(): Int = height + override fun getIntrinsicWidth(): Int = width + + override fun getOpacity(): Int = PixelFormat.OPAQUE + + override fun setAlpha(alpha: Int) {} + + override fun setColorFilter(colorFilter: ColorFilter?) { + cf = colorFilter + } +} diff --git a/packages/SystemUI/src/com/android/systemui/decor/RoundedCornerResDelegate.kt b/packages/SystemUI/src/com/android/systemui/decor/RoundedCornerResDelegate.kt index 8b4aeefb6ed4..c64766a3eb3c 100644 --- a/packages/SystemUI/src/com/android/systemui/decor/RoundedCornerResDelegate.kt +++ b/packages/SystemUI/src/com/android/systemui/decor/RoundedCornerResDelegate.kt @@ -27,44 +27,50 @@ import com.android.systemui.Dumpable import com.android.systemui.R import java.io.PrintWriter -class RoundedCornerResDelegate( +interface RoundedCornerResDelegate { + val hasTop: Boolean + val topRoundedDrawable: Drawable? + val topRoundedSize: Size + + val hasBottom: Boolean + val bottomRoundedDrawable: Drawable? + val bottomRoundedSize: Size + + var physicalPixelDisplaySizeRatio: Float + + fun updateDisplayUniqueId(newDisplayUniqueId: String?, newReloadToken: Int?) +} + +/** + * Delegate for the device-default rounded corners. These will always be loaded from the config + * values `R.array.config_roundedCornerTopDrawableArray` and `R.drawable.rounded_corner_top` + */ +class RoundedCornerResDelegateImpl( private val res: Resources, private var displayUniqueId: String? -) : Dumpable { - - private val density: Float - get() = res.displayMetrics.density +) : RoundedCornerResDelegate, Dumpable { private var reloadToken: Int = 0 - var hasTop: Boolean = false + override var hasTop: Boolean = false private set - var hasBottom: Boolean = false + override var hasBottom: Boolean = false private set - var topRoundedDrawable: Drawable? = null + override var topRoundedDrawable: Drawable? = null private set - var bottomRoundedDrawable: Drawable? = null + override var bottomRoundedDrawable: Drawable? = null private set - var topRoundedSize = Size(0, 0) + override var topRoundedSize = Size(0, 0) private set - var bottomRoundedSize = Size(0, 0) + override var bottomRoundedSize = Size(0, 0) private set - var tuningSizeFactor: Int? = null - set(value) { - if (field == value) { - return - } - field = value - reloadMeasures() - } - - var physicalPixelDisplaySizeRatio: Float = 1f + override var physicalPixelDisplaySizeRatio: Float = 1f set(value) { if (field == value) { return @@ -78,7 +84,7 @@ class RoundedCornerResDelegate( reloadMeasures() } - fun updateDisplayUniqueId(newDisplayUniqueId: String?, newReloadToken: Int?) { + override fun updateDisplayUniqueId(newDisplayUniqueId: String?, newReloadToken: Int?) { if (displayUniqueId != newDisplayUniqueId) { displayUniqueId = newDisplayUniqueId newReloadToken ?.let { reloadToken = it } @@ -122,19 +128,6 @@ class RoundedCornerResDelegate( bottomRoundedSize = Size(it.intrinsicWidth, it.intrinsicHeight) } - tuningSizeFactor?.let { - if (it <= 0) { - return - } - val length: Int = (it * density).toInt() - if (topRoundedSize.width > 0) { - topRoundedSize = Size(length, length) - } - if (bottomRoundedSize.width > 0) { - bottomRoundedSize = Size(length, length) - } - } - if (physicalPixelDisplaySizeRatio != 1f) { if (topRoundedSize.width != 0) { topRoundedSize = Size( diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java index ff07bb620a44..78ac45325072 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java @@ -39,8 +39,8 @@ import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dreams.dagger.DreamOverlayComponent; import com.android.systemui.dreams.dagger.DreamOverlayModule; import com.android.systemui.dreams.touch.scrim.BouncerlessScrimController; -import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerCallbackInteractor; -import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerCallbackInteractor.PrimaryBouncerExpansionCallback; +import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInteractor; +import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInteractor.PrimaryBouncerExpansionCallback; import com.android.systemui.shade.ShadeExpansionChangeEvent; import com.android.systemui.statusbar.BlurUtils; import com.android.systemui.util.ViewController; diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java index d509015bde1d..553405f2c944 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java @@ -294,6 +294,7 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ mWindow.clearFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); mWindow.addFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE); + mWindow.addPrivateFlags(WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS); mWindow.requestFeature(Window.FEATURE_NO_TITLE); // Hide all insets when the dream is showing mWindow.getDecorView().getWindowInsetsController().hide(WindowInsets.Type.systemBars()); diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStateController.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStateController.java index b141db15ea0f..c2421dcbc6ca 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStateController.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStateController.java @@ -19,7 +19,6 @@ package com.android.systemui.dreams; import static com.android.systemui.dreams.dagger.DreamModule.DREAM_OVERLAY_ENABLED; import android.service.dreams.DreamService; -import android.util.Log; import androidx.annotation.NonNull; @@ -52,7 +51,6 @@ import javax.inject.Named; public class DreamOverlayStateController implements CallbackController<DreamOverlayStateController.Callback> { private static final String TAG = "DreamOverlayStateCtlr"; - private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); public static final int STATE_DREAM_OVERLAY_ACTIVE = 1 << 0; public static final int STATE_LOW_LIGHT_ACTIVE = 1 << 1; @@ -110,13 +108,17 @@ public class DreamOverlayStateController implements private final int mSupportedTypes; + private final DreamLogger mLogger; + @VisibleForTesting @Inject public DreamOverlayStateController(@Main Executor executor, @Named(DREAM_OVERLAY_ENABLED) boolean overlayEnabled, - FeatureFlags featureFlags) { + FeatureFlags featureFlags, + DreamLogger dreamLogger) { mExecutor = executor; mOverlayEnabled = overlayEnabled; + mLogger = dreamLogger; mFeatureFlags = featureFlags; if (mFeatureFlags.isEnabled(Flags.ALWAYS_SHOW_HOME_CONTROLS_ON_DREAMS)) { mSupportedTypes = Complication.COMPLICATION_TYPE_NONE @@ -124,9 +126,7 @@ public class DreamOverlayStateController implements } else { mSupportedTypes = Complication.COMPLICATION_TYPE_NONE; } - if (DEBUG) { - Log.d(TAG, "Dream overlay enabled:" + mOverlayEnabled); - } + mLogger.d(TAG, "Dream overlay enabled: " + mOverlayEnabled); } /** @@ -134,18 +134,14 @@ public class DreamOverlayStateController implements */ public void addComplication(Complication complication) { if (!mOverlayEnabled) { - if (DEBUG) { - Log.d(TAG, - "Ignoring adding complication due to overlay disabled:" + complication); - } + mLogger.d(TAG, + "Ignoring adding complication due to overlay disabled: " + complication); return; } mExecutor.execute(() -> { if (mComplications.add(complication)) { - if (DEBUG) { - Log.d(TAG, "addComplication: added " + complication); - } + mLogger.d(TAG, "Added dream complication: " + complication); mCallbacks.stream().forEach(callback -> callback.onComplicationsChanged()); } }); @@ -156,18 +152,14 @@ public class DreamOverlayStateController implements */ public void removeComplication(Complication complication) { if (!mOverlayEnabled) { - if (DEBUG) { - Log.d(TAG, - "Ignoring removing complication due to overlay disabled:" + complication); - } + mLogger.d(TAG, + "Ignoring removing complication due to overlay disabled: " + complication); return; } mExecutor.execute(() -> { if (mComplications.remove(complication)) { - if (DEBUG) { - Log.d(TAG, "removeComplication: removed " + complication); - } + mLogger.d(TAG, "Removed dream complication: " + complication); mCallbacks.stream().forEach(callback -> callback.onComplicationsChanged()); } }); @@ -313,6 +305,7 @@ public class DreamOverlayStateController implements * @param active {@code true} if overlay is active, {@code false} otherwise. */ public void setOverlayActive(boolean active) { + mLogger.d(TAG, "Dream overlay active: " + active); modifyState(active ? OP_SET_STATE : OP_CLEAR_STATE, STATE_DREAM_OVERLAY_ACTIVE); } @@ -321,6 +314,8 @@ public class DreamOverlayStateController implements * @param active {@code true} if low light mode is active, {@code false} otherwise. */ public void setLowLightActive(boolean active) { + mLogger.d(TAG, "Low light mode active: " + active); + if (isLowLightActive() && !active) { // Notify that we're exiting low light only on the transition from active to not active. mCallbacks.forEach(Callback::onExitLowLight); @@ -351,6 +346,7 @@ public class DreamOverlayStateController implements * @param hasAttention {@code true} if has the user's attention, {@code false} otherwise. */ public void setHasAssistantAttention(boolean hasAttention) { + mLogger.d(TAG, "Dream overlay has Assistant attention: " + hasAttention); modifyState(hasAttention ? OP_SET_STATE : OP_CLEAR_STATE, STATE_HAS_ASSISTANT_ATTENTION); } @@ -359,6 +355,7 @@ public class DreamOverlayStateController implements * @param visible {@code true} if the status bar is visible, {@code false} otherwise. */ public void setDreamOverlayStatusBarVisible(boolean visible) { + mLogger.d(TAG, "Dream overlay status bar visible: " + visible); modifyState( visible ? OP_SET_STATE : OP_CLEAR_STATE, STATE_DREAM_OVERLAY_STATUS_BAR_VISIBLE); } @@ -376,6 +373,7 @@ public class DreamOverlayStateController implements */ public void setAvailableComplicationTypes(@Complication.ComplicationType int types) { mExecutor.execute(() -> { + mLogger.d(TAG, "Available complication types: " + types); mAvailableComplicationTypes = types; mCallbacks.forEach(Callback::onAvailableComplicationTypesChanged); }); @@ -393,6 +391,7 @@ public class DreamOverlayStateController implements */ public void setShouldShowComplications(boolean shouldShowComplications) { mExecutor.execute(() -> { + mLogger.d(TAG, "Should show complications: " + shouldShowComplications); mShouldShowComplications = shouldShowComplications; mCallbacks.forEach(Callback::onAvailableComplicationTypesChanged); }); diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarView.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarView.java index 7c1bfeda30b2..1865c38632e5 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarView.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarView.java @@ -141,6 +141,20 @@ public class DreamOverlayStatusBarView extends ConstraintLayout { mExtraSystemStatusViewGroup = findViewById(R.id.dream_overlay_extra_items); } + protected static String getLoggableStatusIconType(@StatusIconType int type) { + return switch (type) { + case STATUS_ICON_NOTIFICATIONS -> "notifications"; + case STATUS_ICON_WIFI_UNAVAILABLE -> "wifi_unavailable"; + case STATUS_ICON_ALARM_SET -> "alarm_set"; + case STATUS_ICON_CAMERA_DISABLED -> "camera_disabled"; + case STATUS_ICON_MIC_DISABLED -> "mic_disabled"; + case STATUS_ICON_MIC_CAMERA_DISABLED -> "mic_camera_disabled"; + case STATUS_ICON_PRIORITY_MODE_ON -> "priority_mode_on"; + case STATUS_ICON_ASSISTANT_ATTENTION_ACTIVE -> "assistant_attention_active"; + default -> type + "(unknown)"; + }; + } + void showIcon(@StatusIconType int iconType, boolean show, @Nullable String contentDescription) { View icon = mStatusIcons.get(iconType); if (icon == null) { diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java index c954f98ad36e..3a284083e844 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java @@ -61,6 +61,8 @@ import javax.inject.Inject; */ @DreamOverlayComponent.DreamOverlayScope public class DreamOverlayStatusBarViewController extends ViewController<DreamOverlayStatusBarView> { + private static final String TAG = "DreamStatusBarCtrl"; + private final ConnectivityManager mConnectivityManager; private final TouchInsetManager.TouchInsetSession mTouchInsetSession; private final NextAlarmController mNextAlarmController; @@ -78,6 +80,7 @@ public class DreamOverlayStatusBarViewController extends ViewController<DreamOve private final Executor mMainExecutor; private final List<DreamOverlayStatusBarItemsProvider.StatusBarItem> mExtraStatusBarItems = new ArrayList<>(); + private final DreamLogger mLogger; private boolean mIsAttached; @@ -157,7 +160,8 @@ public class DreamOverlayStatusBarViewController extends ViewController<DreamOve StatusBarWindowStateController statusBarWindowStateController, DreamOverlayStatusBarItemsProvider statusBarItemsProvider, DreamOverlayStateController dreamOverlayStateController, - UserTracker userTracker) { + UserTracker userTracker, + DreamLogger dreamLogger) { super(view); mResources = resources; mMainExecutor = mainExecutor; @@ -173,6 +177,7 @@ public class DreamOverlayStatusBarViewController extends ViewController<DreamOve mZenModeController = zenModeController; mDreamOverlayStateController = dreamOverlayStateController; mUserTracker = userTracker; + mLogger = dreamLogger; // Register to receive show/hide updates for the system status bar. Our custom status bar // needs to hide when the system status bar is showing to ovoid overlapping status bars. @@ -341,6 +346,8 @@ public class DreamOverlayStatusBarViewController extends ViewController<DreamOve @Nullable String contentDescription) { mMainExecutor.execute(() -> { if (mIsAttached) { + mLogger.d(TAG, (show ? "Showing" : "Hiding") + " dream status bar item: " + + DreamOverlayStatusBarView.getLoggableStatusIconType(iconType)); mView.showIcon(iconType, show, contentDescription); } }); diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandler.java b/packages/SystemUI/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandler.java index 570132e111eb..1cd37749e2c3 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandler.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandler.java @@ -35,9 +35,11 @@ import androidx.annotation.VisibleForTesting; import com.android.internal.logging.UiEvent; import com.android.internal.logging.UiEventLogger; +import com.android.internal.widget.LockPatternUtils; +import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants; import com.android.systemui.dreams.touch.scrim.ScrimController; import com.android.systemui.dreams.touch.scrim.ScrimManager; -import com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants; +import com.android.systemui.settings.UserTracker; import com.android.systemui.shade.ShadeExpansionChangeEvent; import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.phone.CentralSurfaces; @@ -76,6 +78,8 @@ public class BouncerSwipeTouchHandler implements DreamTouchHandler { private static final String TAG = "BouncerSwipeTouchHandler"; private final NotificationShadeWindowController mNotificationShadeWindowController; + private final LockPatternUtils mLockPatternUtils; + private final UserTracker mUserTracker; private final float mBouncerZoneScreenPercentage; private final ScrimManager mScrimManager; @@ -151,6 +155,11 @@ public class BouncerSwipeTouchHandler implements DreamTouchHandler { return true; } + // Don't set expansion if the user doesn't have a pin/password set. + if (!mLockPatternUtils.isSecure(mUserTracker.getUserId())) { + return true; + } + // For consistency, we adopt the expansion definition found in the // PanelViewController. In this case, expansion refers to the view above the // bouncer. As that view's expansion shrinks, the bouncer appears. The bouncer @@ -204,6 +213,8 @@ public class BouncerSwipeTouchHandler implements DreamTouchHandler { NotificationShadeWindowController notificationShadeWindowController, ValueAnimatorCreator valueAnimatorCreator, VelocityTrackerFactory velocityTrackerFactory, + LockPatternUtils lockPatternUtils, + UserTracker userTracker, @Named(SWIPE_TO_BOUNCER_FLING_ANIMATION_UTILS_OPENING) FlingAnimationUtils flingAnimationUtils, @Named(SWIPE_TO_BOUNCER_FLING_ANIMATION_UTILS_CLOSING) @@ -213,6 +224,8 @@ public class BouncerSwipeTouchHandler implements DreamTouchHandler { mCentralSurfaces = centralSurfaces; mScrimManager = scrimManager; mNotificationShadeWindowController = notificationShadeWindowController; + mLockPatternUtils = lockPatternUtils; + mUserTracker = userTracker; mBouncerZoneScreenPercentage = swipeRegionPercentage; mFlingAnimationUtils = flingAnimationUtils; mFlingAnimationUtilsClosing = flingAnimationUtilsClosing; @@ -347,6 +360,11 @@ public class BouncerSwipeTouchHandler implements DreamTouchHandler { return; } + // Don't set expansion if the user doesn't have a pin/password set. + if (!mLockPatternUtils.isSecure(mUserTracker.getUserId())) { + return; + } + // The animation utils deal in pixel units, rather than expansion height. final float viewHeight = mTouchSession.getBounds().height(); final float currentHeight = viewHeight * mCurrentExpansion; diff --git a/packages/SystemUI/src/com/android/systemui/dump/DumpHandler.kt b/packages/SystemUI/src/com/android/systemui/dump/DumpHandler.kt index 4b03fd334cb5..75284fc18149 100644 --- a/packages/SystemUI/src/com/android/systemui/dump/DumpHandler.kt +++ b/packages/SystemUI/src/com/android/systemui/dump/DumpHandler.kt @@ -20,12 +20,16 @@ import android.content.Context import android.os.SystemClock import android.os.Trace import com.android.systemui.CoreStartable +import com.android.systemui.ProtoDumpable import com.android.systemui.R import com.android.systemui.dump.DumpHandler.Companion.PRIORITY_ARG_CRITICAL import com.android.systemui.dump.DumpHandler.Companion.PRIORITY_ARG_NORMAL +import com.android.systemui.dump.DumpsysEntry.DumpableEntry +import com.android.systemui.dump.DumpsysEntry.LogBufferEntry +import com.android.systemui.dump.DumpsysEntry.TableLogBufferEntry import com.android.systemui.dump.nano.SystemUIProtoDump import com.android.systemui.log.LogBuffer -import com.android.systemui.shared.system.UncaughtExceptionPreHandlerManager +import com.android.systemui.log.table.TableLogBuffer import com.google.protobuf.nano.MessageNano import java.io.BufferedOutputStream import java.io.FileDescriptor @@ -39,11 +43,11 @@ import javax.inject.Provider * * Dump output is split into two sections, CRITICAL and NORMAL. In general, the CRITICAL section * contains all dumpables that were registered to the [DumpManager], while the NORMAL sections - * contains all [LogBuffer]s (due to their length). + * contains all [LogBuffer]s and [TableLogBuffer]s (due to their length). * - * The CRITICAL and NORMAL sections can be found within a bug report by searching for - * "SERVICE com.android.systemui/.SystemUIService" and - * "SERVICE com.android.systemui/.dump.SystemUIAuxiliaryDumpService", respectively. + * The CRITICAL and NORMAL sections can be found within a bug report by searching for "SERVICE + * com.android.systemui/.SystemUIService" and "SERVICE + * com.android.systemui/.dump.SystemUIAuxiliaryDumpService", respectively. * * Finally, some or all of the dump can be triggered on-demand via adb (see below). * @@ -72,6 +76,7 @@ import javax.inject.Provider * # To dump all dumpables or all buffers: * $ <invocation> dumpables * $ <invocation> buffers + * $ <invocation> tables * * # Finally, the following will simulate what we dump during the CRITICAL and NORMAL sections of a * # bug report: @@ -83,37 +88,26 @@ import javax.inject.Provider * $ <invocation> --help * ``` */ -class DumpHandler @Inject constructor( +class DumpHandler +@Inject +constructor( private val context: Context, private val dumpManager: DumpManager, private val logBufferEulogizer: LogBufferEulogizer, private val startables: MutableMap<Class<*>, Provider<CoreStartable>>, - private val uncaughtExceptionPreHandlerManager: UncaughtExceptionPreHandlerManager ) { - /** - * Registers an uncaught exception handler - */ - fun init() { - uncaughtExceptionPreHandlerManager.registerHandler { _, e -> - if (e is Exception) { - logBufferEulogizer.record(e) - } - } - } - - /** - * Dump the diagnostics! Behavior can be controlled via [args]. - */ + /** Dump the diagnostics! Behavior can be controlled via [args]. */ fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<String>) { Trace.beginSection("DumpManager#dump()") val start = SystemClock.uptimeMillis() - val parsedArgs = try { - parseArgs(args) - } catch (e: ArgParseException) { - pw.println(e.message) - return - } + val parsedArgs = + try { + parseArgs(args) + } catch (e: ArgParseException) { + pw.println(e.message) + return + } when { parsedArgs.dumpPriority == PRIORITY_ARG_CRITICAL -> dumpCritical(pw, parsedArgs) @@ -134,6 +128,7 @@ class DumpHandler @Inject constructor( "bugreport-normal" -> dumpNormal(pw, args) "dumpables" -> dumpDumpables(pw, args) "buffers" -> dumpBuffers(pw, args) + "tables" -> dumpTables(pw, args) "config" -> dumpConfig(pw) "help" -> dumpHelp(pw) else -> { @@ -147,44 +142,65 @@ class DumpHandler @Inject constructor( } private fun dumpCritical(pw: PrintWriter, args: ParsedArgs) { - dumpManager.dumpCritical(pw, args.rawArgs) + val targets = dumpManager.getDumpables() + for (target in targets) { + if (target.priority == DumpPriority.CRITICAL) { + dumpDumpable(target, pw, args.rawArgs) + } + } dumpConfig(pw) } private fun dumpNormal(pw: PrintWriter, args: ParsedArgs) { - dumpManager.dumpNormal(pw, args.rawArgs, args.tailLength) - logBufferEulogizer.readEulogyIfPresent(pw) - } + val targets = dumpManager.getDumpables() + for (target in targets) { + if (target.priority == DumpPriority.NORMAL) { + dumpDumpable(target, pw, args.rawArgs) + } + } - private fun dumpDumpables(pw: PrintWriter, args: ParsedArgs) { - if (args.listOnly) { - dumpManager.listDumpables(pw) - } else { - dumpManager.dumpDumpables(pw, args.rawArgs) + val buffers = dumpManager.getLogBuffers() + for (buffer in buffers) { + dumpBuffer(buffer, pw, args.tailLength) } + + val tableBuffers = dumpManager.getTableLogBuffers() + for (table in tableBuffers) { + dumpTableBuffer(table, pw, args.rawArgs) + } + + logBufferEulogizer.readEulogyIfPresent(pw) } - private fun dumpBuffers(pw: PrintWriter, args: ParsedArgs) { - if (args.listOnly) { - dumpManager.listBuffers(pw) - } else { - dumpManager.dumpBuffers(pw, args.tailLength) + private fun dumpDumpables(pw: PrintWriter, args: ParsedArgs) = + dumpManager.getDumpables().listOrDumpEntries(pw, args) + + private fun dumpBuffers(pw: PrintWriter, args: ParsedArgs) = + dumpManager.getLogBuffers().listOrDumpEntries(pw, args) + + private fun dumpTables(pw: PrintWriter, args: ParsedArgs) = + dumpManager.getTableLogBuffers().listOrDumpEntries(pw, args) + + private fun listTargetNames(targets: Collection<DumpsysEntry>, pw: PrintWriter) { + for (target in targets) { + pw.println(target.name) } } - private fun dumpProtoTargets( - targets: List<String>, - fd: FileDescriptor, - args: ParsedArgs - ) { + private fun dumpProtoTargets(targets: List<String>, fd: FileDescriptor, args: ParsedArgs) { val systemUIProto = SystemUIProtoDump() + val dumpables = dumpManager.getDumpables() if (targets.isNotEmpty()) { for (target in targets) { - dumpManager.dumpProtoTarget(target, systemUIProto, args.rawArgs) + findBestProtoTargetMatch(dumpables, target)?.dumpProto(systemUIProto, args.rawArgs) } } else { - dumpManager.dumpProtoDumpables(systemUIProto, args.rawArgs) + // Dump all protos + for (dumpable in dumpables) { + (dumpable.dumpable as? ProtoDumpable)?.dumpProto(systemUIProto, args.rawArgs) + } } + val buffer = BufferedOutputStream(FileOutputStream(fd)) buffer.use { it.write(MessageNano.toByteArray(systemUIProto)) @@ -192,36 +208,70 @@ class DumpHandler @Inject constructor( } } - private fun dumpTargets( - targets: List<String>, - pw: PrintWriter, - args: ParsedArgs - ) { + // Attempts to dump the target list to the given PrintWriter. Since the arguments come in as + // a list of strings, we use the [findBestTargetMatch] method to determine the most-correct + // target with the given search string. + private fun dumpTargets(targets: List<String>, pw: PrintWriter, args: ParsedArgs) { if (targets.isNotEmpty()) { - for (target in targets) { - dumpManager.dumpTarget(target, pw, args.rawArgs, args.tailLength) + val dumpables = dumpManager.getDumpables() + val buffers = dumpManager.getLogBuffers() + val tableBuffers = dumpManager.getTableLogBuffers() + + targets.forEach { target -> + findTargetInCollection(target, dumpables, buffers, tableBuffers)?.dump(pw, args) } } else { if (args.listOnly) { + val dumpables = dumpManager.getDumpables() + val buffers = dumpManager.getLogBuffers() + pw.println("Dumpables:") - dumpManager.listDumpables(pw) + listTargetNames(dumpables, pw) pw.println() pw.println("Buffers:") - dumpManager.listBuffers(pw) + listTargetNames(buffers, pw) } else { pw.println("Nothing to dump :(") } } } + private fun findTargetInCollection( + target: String, + dumpables: Collection<DumpableEntry>, + logBuffers: Collection<LogBufferEntry>, + tableBuffers: Collection<TableLogBufferEntry>, + ) = + sequence { + findBestTargetMatch(dumpables, target)?.let { yield(it) } + findBestTargetMatch(logBuffers, target)?.let { yield(it) } + findBestTargetMatch(tableBuffers, target)?.let { yield(it) } + } + .sortedBy { it.name } + .minByOrNull { it.name.length } + + private fun dumpDumpable(entry: DumpableEntry, pw: PrintWriter, args: Array<String>) { + pw.preamble(entry) + entry.dumpable.dump(pw, args) + } + + private fun dumpBuffer(entry: LogBufferEntry, pw: PrintWriter, tailLength: Int) { + pw.preamble(entry) + entry.buffer.dump(pw, tailLength) + } + + private fun dumpTableBuffer(buffer: TableLogBufferEntry, pw: PrintWriter, args: Array<String>) { + pw.preamble(buffer) + buffer.table.dump(pw, args) + } + private fun dumpConfig(pw: PrintWriter) { pw.println("SystemUiServiceComponents configuration:") pw.print("vendor component: ") pw.println(context.resources.getString(R.string.config_systemUIVendorServiceComponent)) - val services: MutableList<String> = startables.keys - .map({ cls: Class<*> -> cls.simpleName }) - .toMutableList() + val services: MutableList<String> = + startables.keys.map({ cls: Class<*> -> cls.simpleName }).toMutableList() services.add(context.resources.getString(R.string.config_systemUIVendorServiceComponent)) dumpServiceList(pw, "global", services.toTypedArray()) @@ -265,6 +315,7 @@ class DumpHandler @Inject constructor( pw.println("Special commands:") pw.println("$ <invocation> dumpables") pw.println("$ <invocation> buffers") + pw.println("$ <invocation> tables") pw.println("$ <invocation> bugreport-critical") pw.println("$ <invocation> bugreport-normal") pw.println("$ <invocation> config") @@ -274,6 +325,7 @@ class DumpHandler @Inject constructor( pw.println("$ <invocation> --list") pw.println("$ <invocation> dumpables --list") pw.println("$ <invocation> buffers --list") + pw.println("$ <invocation> tables --list") pw.println() pw.println("Show only the most recent N lines of buffers") @@ -291,24 +343,26 @@ class DumpHandler @Inject constructor( iterator.remove() when (arg) { PRIORITY_ARG -> { - pArgs.dumpPriority = readArgument(iterator, PRIORITY_ARG) { - if (PRIORITY_OPTIONS.contains(it)) { - it - } else { - throw IllegalArgumentException() + pArgs.dumpPriority = + readArgument(iterator, PRIORITY_ARG) { + if (PRIORITY_OPTIONS.contains(it)) { + it + } else { + throw IllegalArgumentException() + } } - } } PROTO -> pArgs.proto = true - "-t", "--tail" -> { - pArgs.tailLength = readArgument(iterator, arg) { - it.toInt() - } + "-t", + "--tail" -> { + pArgs.tailLength = readArgument(iterator, arg) { it.toInt() } } - "-l", "--list" -> { + "-l", + "--list" -> { pArgs.listOnly = true } - "-h", "--help" -> { + "-h", + "--help" -> { pArgs.command = "help" } // This flag is passed as part of the proto dump in Bug reports, we can ignore @@ -345,29 +399,131 @@ class DumpHandler @Inject constructor( } } + private fun DumpsysEntry.dump(pw: PrintWriter, args: ParsedArgs) = + when (this) { + is DumpableEntry -> dumpDumpable(this, pw, args.rawArgs) + is LogBufferEntry -> dumpBuffer(this, pw, args.tailLength) + is TableLogBufferEntry -> dumpTableBuffer(this, pw, args.rawArgs) + } + + private fun Collection<DumpsysEntry>.listOrDumpEntries(pw: PrintWriter, args: ParsedArgs) = + if (args.listOnly) { + listTargetNames(this, pw) + } else { + forEach { it.dump(pw, args) } + } + companion object { const val PRIORITY_ARG = "--dump-priority" const val PRIORITY_ARG_CRITICAL = "CRITICAL" const val PRIORITY_ARG_NORMAL = "NORMAL" const val PROTO = "--proto" + + /** + * Important: do not change this divider without updating any bug report processing tools + * (e.g. ABT), since this divider is used to determine boundaries for bug report views + */ + const val DUMPSYS_DUMPABLE_DIVIDER = + "----------------------------------------------------------------------------" + + /** + * Important: do not change this divider without updating any bug report processing tools + * (e.g. ABT), since this divider is used to determine boundaries for bug report views + */ + const val DUMPSYS_BUFFER_DIVIDER = + "============================================================================" + + private fun findBestTargetMatch(c: Collection<DumpsysEntry>, target: String) = + c.asSequence().filter { it.name.endsWith(target) }.minByOrNull { it.name.length } + + private fun findBestProtoTargetMatch( + c: Collection<DumpableEntry>, + target: String + ): ProtoDumpable? = + c.asSequence() + .filter { it.name.endsWith(target) } + .filter { it.dumpable is ProtoDumpable } + .minByOrNull { it.name.length } + ?.dumpable as? ProtoDumpable + + private fun PrintWriter.preamble(entry: DumpsysEntry) = + when (entry) { + // Historically TableLogBuffer was not separate from dumpables, so they have the + // same header + is DumpableEntry, + is TableLogBufferEntry -> { + println() + println(entry.name) + println(DUMPSYS_DUMPABLE_DIVIDER) + } + is LogBufferEntry -> { + println() + println() + println("BUFFER ${entry.name}:") + println(DUMPSYS_BUFFER_DIVIDER) + } + } + + /** + * Zero-arg utility to write a [DumpableEntry] to the given [PrintWriter] in a + * dumpsys-appropriate format. + */ + private fun dumpDumpable(entry: DumpableEntry, pw: PrintWriter) { + pw.preamble(entry) + entry.dumpable.dump(pw, arrayOf()) + } + + /** + * Zero-arg utility to write a [LogBufferEntry] to the given [PrintWriter] in a + * dumpsys-appropriate format. + */ + private fun dumpBuffer(entry: LogBufferEntry, pw: PrintWriter) { + pw.preamble(entry) + entry.buffer.dump(pw, 0) + } + + /** + * Zero-arg utility to write a [TableLogBufferEntry] to the given [PrintWriter] in a + * dumpsys-appropriate format. + */ + private fun dumpTableBuffer(entry: TableLogBufferEntry, pw: PrintWriter) { + pw.preamble(entry) + entry.table.dump(pw, arrayOf()) + } + + /** + * Zero-arg utility to write a [DumpsysEntry] to the given [PrintWriter] in a + * dumpsys-appropriate format. + */ + fun DumpsysEntry.dump(pw: PrintWriter) { + when (this) { + is DumpableEntry -> dumpDumpable(this, pw) + is LogBufferEntry -> dumpBuffer(this, pw) + is TableLogBufferEntry -> dumpTableBuffer(this, pw) + } + } + + /** Format [entries] in a dumpsys-appropriate way, using [pw] */ + fun dumpEntries(entries: Collection<DumpsysEntry>, pw: PrintWriter) { + entries.forEach { it.dump(pw) } + } } } private val PRIORITY_OPTIONS = arrayOf(PRIORITY_ARG_CRITICAL, PRIORITY_ARG_NORMAL) -private val COMMANDS = arrayOf( +private val COMMANDS = + arrayOf( "bugreport-critical", "bugreport-normal", "buffers", "dumpables", + "tables", "config", "help" -) + ) -private class ParsedArgs( - val rawArgs: Array<String>, - val nonFlagArgs: List<String> -) { +private class ParsedArgs(val rawArgs: Array<String>, val nonFlagArgs: List<String>) { var dumpPriority: String? = null var tailLength: Int = 0 var command: String? = null diff --git a/packages/SystemUI/src/com/android/systemui/dump/DumpManager.kt b/packages/SystemUI/src/com/android/systemui/dump/DumpManager.kt index 2d57633e47a8..c924df6da263 100644 --- a/packages/SystemUI/src/com/android/systemui/dump/DumpManager.kt +++ b/packages/SystemUI/src/com/android/systemui/dump/DumpManager.kt @@ -18,9 +18,11 @@ package com.android.systemui.dump import com.android.systemui.Dumpable import com.android.systemui.ProtoDumpable -import com.android.systemui.dump.nano.SystemUIProtoDump +import com.android.systemui.dump.DumpsysEntry.DumpableEntry +import com.android.systemui.dump.DumpsysEntry.LogBufferEntry +import com.android.systemui.dump.DumpsysEntry.TableLogBufferEntry import com.android.systemui.log.LogBuffer -import java.io.PrintWriter +import com.android.systemui.log.table.TableLogBuffer import java.util.TreeMap import javax.inject.Inject import javax.inject.Singleton @@ -37,8 +39,9 @@ import javax.inject.Singleton @Singleton open class DumpManager @Inject constructor() { // NOTE: Using TreeMap ensures that iteration is in a predictable & alphabetical order. - private val dumpables: MutableMap<String, RegisteredDumpable<Dumpable>> = TreeMap() - private val buffers: MutableMap<String, RegisteredDumpable<LogBuffer>> = TreeMap() + private val dumpables: MutableMap<String, DumpableEntry> = TreeMap() + private val buffers: MutableMap<String, LogBufferEntry> = TreeMap() + private val tableLogBuffers: MutableMap<String, TableLogBufferEntry> = TreeMap() /** See [registerCriticalDumpable]. */ fun registerCriticalDumpable(module: Dumpable) { @@ -77,14 +80,14 @@ open class DumpManager @Inject constructor() { * Register a dumpable to be called during a bug report. * * @param name The name to register the dumpable under. This is typically the qualified class - * name of the thing being dumped (getClass().getName()), but can be anything as long as it - * doesn't clash with an existing registration. + * name of the thing being dumped (getClass().getName()), but can be anything as long as it + * doesn't clash with an existing registration. * @param priority the priority level of this dumpable, which affects at what point in the bug - * report this gets dump. By default, the dumpable will be called during the CRITICAL section of - * the bug report, so don't dump an excessive amount of stuff here. + * report this gets dump. By default, the dumpable will be called during the CRITICAL section + * of the bug report, so don't dump an excessive amount of stuff here. * * TODO(b/259973758): Replace all calls to this method with calls to [registerCriticalDumpable] - * or [registerNormalDumpable] instead. + * or [registerNormalDumpable] instead. */ @Synchronized @JvmOverloads @@ -98,7 +101,7 @@ open class DumpManager @Inject constructor() { throw IllegalArgumentException("'$name' is already registered") } - dumpables[name] = RegisteredDumpable(name, module, priority) + dumpables[name] = DumpableEntry(module, name, priority) } /** @@ -110,217 +113,62 @@ open class DumpManager @Inject constructor() { registerDumpable(module::class.java.simpleName, module) } - /** - * Unregisters a previously-registered dumpable. - */ + /** Unregisters a previously-registered dumpable. */ @Synchronized fun unregisterDumpable(name: String) { dumpables.remove(name) } - /** - * Register a [LogBuffer] to be dumped during a bug report. - */ + /** Register a [LogBuffer] to be dumped during a bug report. */ @Synchronized fun registerBuffer(name: String, buffer: LogBuffer) { if (!canAssignToNameLocked(name, buffer)) { throw IllegalArgumentException("'$name' is already registered") } - // All buffers must be priority NORMAL, not CRITICAL, because they often contain a lot of - // data. - buffers[name] = RegisteredDumpable(name, buffer, DumpPriority.NORMAL) - } - - /** - * Dumps the alphabetically first, shortest-named dumpable or buffer whose registered name ends - * with [target]. - */ - @Synchronized - fun dumpTarget( - target: String, - pw: PrintWriter, - args: Array<String>, - tailLength: Int, - ) { - sequence { - findBestTargetMatch(dumpables, target)?.let { - yield(it.name to { dumpDumpable(it, pw, args) }) - } - findBestTargetMatch(buffers, target)?.let { - yield(it.name to { dumpBuffer(it, pw, tailLength) }) - } - }.sortedBy { it.first }.minByOrNull { it.first.length }?.second?.invoke() - } - - @Synchronized - fun dumpProtoTarget( - target: String, - protoDump: SystemUIProtoDump, - args: Array<String> - ) { - findBestProtoTargetMatch(dumpables, target)?.let { - dumpProtoDumpable(it, protoDump, args) - } + buffers[name] = LogBufferEntry(buffer, name) } + /** Register a [TableLogBuffer] to be dumped during a bugreport */ @Synchronized - fun dumpProtoDumpables( - systemUIProtoDump: SystemUIProtoDump, - args: Array<String> - ) { - for (dumpable in dumpables.values) { - if (dumpable.dumpable is ProtoDumpable) { - dumpProtoDumpable( - dumpable.dumpable, - systemUIProtoDump, - args - ) - } - } - } - - /** - * Dumps all registered dumpables with critical priority to [pw] - */ - @Synchronized - fun dumpCritical(pw: PrintWriter, args: Array<String>) { - for (dumpable in dumpables.values) { - if (dumpable.priority == DumpPriority.CRITICAL) { - dumpDumpable(dumpable, pw, args) - } - } - } - - /** - * To [pw], dumps (1) all registered dumpables with normal priority; and (2) all [LogBuffer]s. - */ - @Synchronized - fun dumpNormal(pw: PrintWriter, args: Array<String>, tailLength: Int = 0) { - for (dumpable in dumpables.values) { - if (dumpable.priority == DumpPriority.NORMAL) { - dumpDumpable(dumpable, pw, args) - } - } - - for (buffer in buffers.values) { - dumpBuffer(buffer, pw, tailLength) + fun registerTableLogBuffer(name: String, buffer: TableLogBuffer) { + if (!canAssignToNameLocked(name, buffer)) { + throw IllegalArgumentException("'$name' is already registered") } - } - /** - * Dump all the instances of [Dumpable]. - */ - @Synchronized - fun dumpDumpables(pw: PrintWriter, args: Array<String>) { - for (module in dumpables.values) { - dumpDumpable(module, pw, args) - } + // All buffers must be priority NORMAL, not CRITICAL, because they often contain a lot of + // data. + tableLogBuffers[name] = TableLogBufferEntry(buffer, name) } - /** - * Dumps the names of all registered dumpables (one per line) - */ - @Synchronized - fun listDumpables(pw: PrintWriter) { - for (module in dumpables.values) { - pw.println(module.name) - } - } + @Synchronized fun getDumpables(): Collection<DumpableEntry> = dumpables.values.toList() - /** - * Dumps all registered [LogBuffer]s to [pw] - */ - @Synchronized - fun dumpBuffers(pw: PrintWriter, tailLength: Int) { - for (buffer in buffers.values) { - dumpBuffer(buffer, pw, tailLength) - } - } + @Synchronized fun getLogBuffers(): Collection<LogBufferEntry> = buffers.values.toList() - /** - * Dumps the names of all registered buffers (one per line) - */ @Synchronized - fun listBuffers(pw: PrintWriter) { - for (buffer in buffers.values) { - pw.println(buffer.name) - } - } + fun getTableLogBuffers(): Collection<TableLogBufferEntry> = tableLogBuffers.values.toList() @Synchronized fun freezeBuffers() { for (buffer in buffers.values) { - buffer.dumpable.freeze() + buffer.buffer.freeze() } } @Synchronized fun unfreezeBuffers() { for (buffer in buffers.values) { - buffer.dumpable.unfreeze() + buffer.buffer.unfreeze() } } - private fun dumpDumpable( - dumpable: RegisteredDumpable<Dumpable>, - pw: PrintWriter, - args: Array<String> - ) { - pw.println() - pw.println("${dumpable.name}:") - pw.println("----------------------------------------------------------------------------") - dumpable.dumpable.dump(pw, args) - } - - private fun dumpBuffer( - buffer: RegisteredDumpable<LogBuffer>, - pw: PrintWriter, - tailLength: Int - ) { - pw.println() - pw.println() - pw.println("BUFFER ${buffer.name}:") - pw.println("============================================================================") - buffer.dumpable.dump(pw, tailLength) - } - - private fun dumpProtoDumpable( - protoDumpable: ProtoDumpable, - systemUIProtoDump: SystemUIProtoDump, - args: Array<String> - ) { - protoDumpable.dumpProto(systemUIProtoDump, args) - } - private fun canAssignToNameLocked(name: String, newDumpable: Any): Boolean { - val existingDumpable = dumpables[name]?.dumpable ?: buffers[name]?.dumpable + val existingDumpable = + dumpables[name]?.dumpable ?: buffers[name]?.buffer ?: tableLogBuffers[name]?.table return existingDumpable == null || newDumpable == existingDumpable } - - private fun <V : Any> findBestTargetMatch(map: Map<String, V>, target: String): V? = map - .asSequence() - .filter { it.key.endsWith(target) } - .minByOrNull { it.key.length } - ?.value - - private fun findBestProtoTargetMatch( - map: Map<String, RegisteredDumpable<Dumpable>>, - target: String - ): ProtoDumpable? = map - .asSequence() - .filter { it.key.endsWith(target) } - .filter { it.value.dumpable is ProtoDumpable } - .minByOrNull { it.key.length } - ?.value?.dumpable as? ProtoDumpable } -private data class RegisteredDumpable<T>( - val name: String, - val dumpable: T, - val priority: DumpPriority, -) - /** * The priority level for a given dumpable, which affects at what point in the bug report this gets * dumped. diff --git a/packages/SystemUI/src/com/android/systemui/dump/DumpsysEntry.kt b/packages/SystemUI/src/com/android/systemui/dump/DumpsysEntry.kt new file mode 100644 index 000000000000..cd3e1bb7acac --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/dump/DumpsysEntry.kt @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.dump + +import com.android.systemui.Dumpable +import com.android.systemui.log.LogBuffer +import com.android.systemui.log.table.TableLogBuffer + +/** + * A DumpsysEntry is a named, registered entry tracked by [DumpManager] which can be addressed and + * used both in a bugreport / dumpsys invocation or in an individual CLI implementation. + * + * The idea here is that we define every type that [DumpManager] knows about and defines the minimum + * shared interface between each type. So far, just [name] and [priority]. This way, [DumpManager] + * can just store them in separate maps and do the minimal amount of work to discriminate between + * them. + * + * Individual consumers can request these participants in a list via the relevant get* methods on + * [DumpManager] + */ +sealed interface DumpsysEntry { + val name: String + val priority: DumpPriority + + data class DumpableEntry( + val dumpable: Dumpable, + override val name: String, + override val priority: DumpPriority, + ) : DumpsysEntry + + data class LogBufferEntry( + val buffer: LogBuffer, + override val name: String, + ) : DumpsysEntry { + // All buffers must be priority NORMAL, not CRITICAL, because they often contain a lot of + // data. + override val priority: DumpPriority = DumpPriority.NORMAL + } + + data class TableLogBufferEntry( + val table: TableLogBuffer, + override val name: String, + ) : DumpsysEntry { + // All buffers must be priority NORMAL, not CRITICAL, because they often contain a lot of + // data. + override val priority: DumpPriority = DumpPriority.NORMAL + } +} diff --git a/packages/SystemUI/src/com/android/systemui/dump/LogBufferEulogizer.kt b/packages/SystemUI/src/com/android/systemui/dump/LogBufferEulogizer.kt index 2d5c9ae2e641..bd43302a8bb5 100644 --- a/packages/SystemUI/src/com/android/systemui/dump/LogBufferEulogizer.kt +++ b/packages/SystemUI/src/com/android/systemui/dump/LogBufferEulogizer.kt @@ -20,6 +20,7 @@ import android.content.Context import android.icu.text.SimpleDateFormat import android.util.Log import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dump.DumpHandler.Companion.dumpEntries import com.android.systemui.log.LogBuffer import com.android.systemui.util.io.Files import com.android.systemui.util.time.SystemClock @@ -48,20 +49,21 @@ class LogBufferEulogizer( private val files: Files, private val logPath: Path, private val minWriteGap: Long, - private val maxLogAgeToDump: Long + private val maxLogAgeToDump: Long, ) { - @Inject constructor( + @Inject + constructor( context: Context, dumpManager: DumpManager, systemClock: SystemClock, - files: Files + files: Files, ) : this( dumpManager, systemClock, files, Paths.get(context.filesDir.toPath().toString(), "log_buffers.txt"), MIN_WRITE_GAP, - MAX_AGE_TO_DUMP + MAX_AGE_TO_DUMP, ) /** @@ -70,7 +72,7 @@ class LogBufferEulogizer( * The file will be prefaced by the [reason], which will then be returned (presumably so it can * be thrown). */ - fun <T : Exception> record(reason: T): T { + fun <T : Throwable> record(reason: T): T { val start = systemClock.uptimeMillis() var duration = 0L @@ -91,7 +93,8 @@ class LogBufferEulogizer( pw.println() pw.println("Dump triggered by exception:") reason.printStackTrace(pw) - dumpManager.dumpBuffers(pw, 0) + val buffers = dumpManager.getLogBuffers() + dumpEntries(buffers, pw) duration = systemClock.uptimeMillis() - start pw.println() pw.println("Buffer eulogy took ${duration}ms") @@ -105,16 +108,17 @@ class LogBufferEulogizer( return reason } - /** - * If a eulogy file is present, writes its contents to [pw]. - */ + /** If a eulogy file is present, writes its contents to [pw]. */ fun readEulogyIfPresent(pw: PrintWriter) { try { val millisSinceLastWrite = getMillisSinceLastWrite(logPath) if (millisSinceLastWrite > maxLogAgeToDump) { - Log.i(TAG, "Not eulogizing buffers; they are " + + Log.i( + TAG, + "Not eulogizing buffers; they are " + TimeUnit.HOURS.convert(millisSinceLastWrite, TimeUnit.MILLISECONDS) + - " hours old") + " hours old" + ) return } @@ -122,9 +126,7 @@ class LogBufferEulogizer( pw.println() pw.println() pw.println("=============== BUFFERS FROM MOST RECENT CRASH ===============") - s.forEach { line -> - pw.println(line) - } + s.forEach { line -> pw.println(line) } } } catch (e: IOException) { // File doesn't exist, okay @@ -134,12 +136,13 @@ class LogBufferEulogizer( } private fun getMillisSinceLastWrite(path: Path): Long { - val stats = try { - files.readAttributes(path, BasicFileAttributes::class.java) - } catch (e: IOException) { - // File doesn't exist - null - } + val stats = + try { + files.readAttributes(path, BasicFileAttributes::class.java) + } catch (e: IOException) { + // File doesn't exist + null + } return systemClock.currentTimeMillis() - (stats?.lastModifiedTime()?.toMillis() ?: 0) } } @@ -147,4 +150,4 @@ class LogBufferEulogizer( private const val TAG = "BufferEulogizer" private val MIN_WRITE_GAP = TimeUnit.MINUTES.toMillis(5) private val MAX_AGE_TO_DUMP = TimeUnit.HOURS.toMillis(48) -private val DATE_FORMAT = SimpleDateFormat("MM-dd HH:mm:ss.SSS", Locale.US)
\ No newline at end of file +private val DATE_FORMAT = SimpleDateFormat("MM-dd HH:mm:ss.SSS", Locale.US) diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt index 31f7e88daa04..250678e25d98 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt +++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt @@ -72,10 +72,6 @@ object Flags { val NOTIFICATION_MEMORY_LOGGING_ENABLED = unreleasedFlag(119, "notification_memory_logging_enabled") - @JvmField - val SIMPLIFIED_APPEAR_FRACTION = - releasedFlag(259395680, "simplified_appear_fraction") - // TODO(b/257315550): Tracking Bug val NO_HUN_FOR_OLD_WHEN = releasedFlag(118, "no_hun_for_old_when") @@ -251,12 +247,12 @@ object Flags { /** Whether to delay showing bouncer UI when face auth or active unlock are enrolled. */ // TODO(b/279794160): Tracking bug. @JvmField - val DELAY_BOUNCER = unreleasedFlag(235, "delay_bouncer") + val DELAY_BOUNCER = unreleasedFlag(235, "delay_bouncer", teamfood = true) /** Migrate the indication area to the new keyguard root view. */ // TODO(b/280067944): Tracking bug. @JvmField - val MIGRATE_INDICATION_AREA = unreleasedFlag(236, "migrate_indication_area") + val MIGRATE_INDICATION_AREA = unreleasedFlag(236, "migrate_indication_area", teamfood = true) /** Whether to listen for fingerprint authentication over keyguard occluding activities. */ // TODO(b/283260512): Tracking bug. @@ -364,7 +360,7 @@ object Flags { // TODO(b/280426085): Tracking Bug @JvmField - val NEW_BLUETOOTH_REPOSITORY = unreleasedFlag(612, "new_bluetooth_repository") + val NEW_BLUETOOTH_REPOSITORY = releasedFlag(612, "new_bluetooth_repository") // 700 - dialer/calls // TODO(b/254512734): Tracking Bug @@ -446,9 +442,6 @@ object Flags { // TODO(b/254512758): Tracking Bug @JvmField val ROUNDED_BOX_RIPPLE = releasedFlag(1002, "rounded_box_ripple") - // TODO(b/265045965): Tracking Bug - val SHOW_LOWLIGHT_ON_DIRECT_BOOT = releasedFlag(1003, "show_lowlight_on_direct_boot") - // TODO(b/273509374): Tracking Bug @JvmField val ALWAYS_SHOW_HOME_CONTROLS_ON_DREAMS = releasedFlag(1006, @@ -485,7 +478,7 @@ object Flags { @Keep @JvmField val WM_CAPTION_ON_SHELL = - sysPropBooleanFlag(1105, "persist.wm.debug.caption_on_shell", default = false) + sysPropBooleanFlag(1105, "persist.wm.debug.caption_on_shell", default = true) @Keep @JvmField @@ -543,7 +536,7 @@ object Flags { // TODO(b/273443374): Tracking Bug @Keep @JvmField val LOCKSCREEN_LIVE_WALLPAPER = - sysPropBooleanFlag(1117, "persist.wm.debug.lockscreen_live_wallpaper", default = false) + sysPropBooleanFlag(1117, "persist.wm.debug.lockscreen_live_wallpaper", default = true) // TODO(b/281648899): Tracking bug @Keep @@ -578,7 +571,7 @@ object Flags { // TODO(b/270987164): Tracking Bug @JvmField - val TRACKPAD_GESTURE_FEATURES = unreleasedFlag(1205, "trackpad_gesture_features", teamfood = true) + val TRACKPAD_GESTURE_FEATURES = releasedFlag(1205, "trackpad_gesture_features") // TODO(b/263826204): Tracking Bug @JvmField @@ -637,6 +630,9 @@ object Flags { // TODO(b/265944639): Tracking Bug @JvmField val DUAL_SHADE = unreleasedFlag(1801, "dual_shade") + // TODO(b/283300105): Tracking Bug + @JvmField val SCENE_CONTAINER = unreleasedFlag(1802, "scene_container") + // 1900 @JvmField val NOTE_TASKS = releasedFlag(1900, "keycode_flag") @@ -711,23 +707,17 @@ object Flags { // TODO(b/259428678): Tracking Bug @JvmField - val KEYBOARD_BACKLIGHT_INDICATOR = - unreleasedFlag(2601, "keyboard_backlight_indicator", teamfood = true) + val KEYBOARD_BACKLIGHT_INDICATOR = releasedFlag(2601, "keyboard_backlight_indicator") // TODO(b/277192623): Tracking Bug @JvmField val KEYBOARD_EDUCATION = unreleasedFlag(2603, "keyboard_education", teamfood = false) - // TODO(b/272036292): Tracking Bug - @JvmField - val LARGE_SHADE_GRANULAR_ALPHA_INTERPOLATION = - releasedFlag(2602, "large_shade_granular_alpha_interpolation") - // TODO(b/277201412): Tracking Bug @JvmField val SPLIT_SHADE_SUBPIXEL_OPTIMIZATION = - unreleasedFlag(2805, "split_shade_subpixel_optimization", teamfood = true) + releasedFlag(2805, "split_shade_subpixel_optimization") // TODO(b/278761837): Tracking Bug @JvmField diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java index 76322adee7ce..040ee7938f1d 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java @@ -125,6 +125,7 @@ import com.android.systemui.plugins.GlobalActions.GlobalActionsManager; import com.android.systemui.plugins.GlobalActionsPanelPlugin; import com.android.systemui.scrim.ScrimDrawable; import com.android.systemui.settings.UserTracker; +import com.android.systemui.shade.ShadeController; import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.VibratorHelper; import com.android.systemui.statusbar.phone.CentralSurfaces; @@ -251,6 +252,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene private int mSmallestScreenWidthDp; private int mOrientation; private final Optional<CentralSurfaces> mCentralSurfacesOptional; + private final ShadeController mShadeController; private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; private final DialogLaunchAnimator mDialogLaunchAnimator; @@ -361,6 +363,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene @Main Handler handler, PackageManager packageManager, Optional<CentralSurfaces> centralSurfacesOptional, + ShadeController shadeController, KeyguardUpdateMonitor keyguardUpdateMonitor, DialogLaunchAnimator dialogLaunchAnimator) { mContext = context; @@ -394,6 +397,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene mSmallestScreenWidthDp = resources.getConfiguration().smallestScreenWidthDp; mOrientation = resources.getConfiguration().orientation; mCentralSurfacesOptional = centralSurfacesOptional; + mShadeController = shadeController; mKeyguardUpdateMonitor = keyguardUpdateMonitor; mDialogLaunchAnimator = dialogLaunchAnimator; @@ -702,7 +706,9 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene mAdapter, mOverflowAdapter, mSysuiColorExtractor, mStatusBarService, mLightBarController, mNotificationShadeWindowController, this::onRefresh, mKeyguardShowing, - mPowerAdapter, mUiEventLogger, mCentralSurfacesOptional, mKeyguardUpdateMonitor, + mPowerAdapter, mUiEventLogger, mCentralSurfacesOptional, + mShadeController, + mKeyguardUpdateMonitor, mLockPatternUtils); dialog.setOnDismissListener(this); @@ -912,7 +918,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene mUiEventLogger.log(GlobalActionsEvent.GA_EMERGENCY_DIALER_PRESS); if (mTelecomManager != null) { // Close shade so user sees the activity - mCentralSurfacesOptional.ifPresent(CentralSurfaces::collapseShade); + mShadeController.cancelExpansionAndCollapseShade(); Intent intent = mTelecomManager.createLaunchEmergencyDialerIntent( null /* number */); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK @@ -2209,6 +2215,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene private UiEventLogger mUiEventLogger; private GestureDetector mGestureDetector; private Optional<CentralSurfaces> mCentralSurfacesOptional; + private final ShadeController mShadeController; private KeyguardUpdateMonitor mKeyguardUpdateMonitor; private LockPatternUtils mLockPatternUtils; private float mWindowDimAmount; @@ -2282,6 +2289,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene Runnable onRefreshCallback, boolean keyguardShowing, MyPowerOptionsAdapter powerAdapter, UiEventLogger uiEventLogger, Optional<CentralSurfaces> centralSurfacesOptional, + ShadeController shadeController, KeyguardUpdateMonitor keyguardUpdateMonitor, LockPatternUtils lockPatternUtils) { // We set dismissOnDeviceLock to false because we have a custom broadcast receiver to @@ -2299,6 +2307,7 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene mKeyguardShowing = keyguardShowing; mUiEventLogger = uiEventLogger; mCentralSurfacesOptional = centralSurfacesOptional; + mShadeController = shadeController; mKeyguardUpdateMonitor = keyguardUpdateMonitor; mLockPatternUtils = lockPatternUtils; mGestureDetector = new GestureDetector(mContext, mGestureListener); @@ -2348,12 +2357,10 @@ public class GlobalActionsDialogLite implements DialogInterface.OnDismissListene mUiEventLogger.log(GlobalActionsEvent.GA_CLOSE_TAP_OUTSIDE); if (mCentralSurfacesOptional.map(CentralSurfaces::isKeyguardShowing).orElse(false)) { // match existing lockscreen behavior to open QS when swiping from status bar - mCentralSurfacesOptional.ifPresent( - centralSurfaces -> centralSurfaces.animateExpandSettingsPanel(null)); + mShadeController.animateExpandQs(); } else { // otherwise, swiping down should expand notification shade - mCentralSurfacesOptional.ifPresent( - centralSurfaces -> centralSurfaces.animateExpandNotificationsPanel()); + mShadeController.animateExpandShade(); } dismiss(); } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardLifecyclesDispatcher.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardLifecyclesDispatcher.java index 757afb616fd1..67c85bd94bd7 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardLifecyclesDispatcher.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardLifecyclesDispatcher.java @@ -16,11 +16,19 @@ package com.android.systemui.keyguard; +import android.annotation.IntDef; import android.os.Handler; +import android.os.Looper; import android.os.Message; -import android.os.Trace; +import android.os.TraceNameSupplier; + +import androidx.annotation.NonNull; import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.dagger.qualifiers.Main; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import javax.inject.Inject; @@ -29,7 +37,6 @@ import javax.inject.Inject; */ @SysUISingleton public class KeyguardLifecyclesDispatcher { - static final int SCREEN_TURNING_ON = 0; static final int SCREEN_TURNED_ON = 1; static final int SCREEN_TURNING_OFF = 2; @@ -39,19 +46,46 @@ public class KeyguardLifecyclesDispatcher { static final int FINISHED_WAKING_UP = 5; static final int STARTED_GOING_TO_SLEEP = 6; static final int FINISHED_GOING_TO_SLEEP = 7; - private static final String TAG = "KeyguardLifecyclesDispatcher"; - private final ScreenLifecycle mScreenLifecycle; - private final WakefulnessLifecycle mWakefulnessLifecycle; + @IntDef({ + SCREEN_TURNING_ON, + SCREEN_TURNED_ON, + SCREEN_TURNING_OFF, + SCREEN_TURNED_OFF, + STARTED_WAKING_UP, + FINISHED_WAKING_UP, + STARTED_GOING_TO_SLEEP, + FINISHED_GOING_TO_SLEEP, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface KeyguardLifecycleMessageType { + } + + private static String getNameOfMessage(@KeyguardLifecycleMessageType int what) { + return switch (what) { + case SCREEN_TURNING_ON -> "SCREEN_TURNING_ON"; + case SCREEN_TURNED_ON -> "SCREEN_TURNED_ON"; + case SCREEN_TURNING_OFF -> "SCREEN_TURNING_OFF"; + case SCREEN_TURNED_OFF -> "SCREEN_TURNED_OFF"; + case STARTED_WAKING_UP -> "STARTED_WAKING_UP"; + case FINISHED_WAKING_UP -> "FINISHED_WAKING_UP"; + case STARTED_GOING_TO_SLEEP -> "STARTED_GOING_TO_SLEEP"; + case FINISHED_GOING_TO_SLEEP -> "FINISHED_GOING_TO_SLEEP"; + default -> "UNKNOWN"; + }; + } + + private final Handler mHandler; @Inject - public KeyguardLifecyclesDispatcher(ScreenLifecycle screenLifecycle, + public KeyguardLifecyclesDispatcher( + @Main Looper mainLooper, + ScreenLifecycle screenLifecycle, WakefulnessLifecycle wakefulnessLifecycle) { - mScreenLifecycle = screenLifecycle; - mWakefulnessLifecycle = wakefulnessLifecycle; + mHandler = new KeyguardLifecycleHandler(mainLooper, screenLifecycle, wakefulnessLifecycle); } - void dispatch(int what) { + void dispatch(@KeyguardLifecycleMessageType int what) { mHandler.obtainMessage(what).sendToTarget(); } @@ -60,7 +94,7 @@ public class KeyguardLifecyclesDispatcher { * @param pmReason Reason this message was triggered - this should be a value from either * {@link PowerManager.WakeReason} or {@link PowerManager.GoToSleepReason}. */ - void dispatch(int what, int pmReason) { + void dispatch(@KeyguardLifecycleMessageType int what, int pmReason) { final Message message = mHandler.obtainMessage(what); message.arg1 = pmReason; message.sendToTarget(); @@ -70,44 +104,48 @@ public class KeyguardLifecyclesDispatcher { * @param what Message to send. * @param object Object to send with the message */ - void dispatch(int what, Object object) { + void dispatch(@KeyguardLifecycleMessageType int what, Object object) { mHandler.obtainMessage(what, object).sendToTarget(); } - private Handler mHandler = new Handler() { + private static class KeyguardLifecycleHandler extends Handler { + private static final String TAG = "KeyguardLifecycleHandler"; + private final ScreenLifecycle mScreenLifecycle; + private final WakefulnessLifecycle mWakefulnessLifecycle; + + public KeyguardLifecycleHandler(Looper looper, + ScreenLifecycle screenLifecycle, + WakefulnessLifecycle wakefulnessLifecycle) { + super(looper); + mScreenLifecycle = screenLifecycle; + mWakefulnessLifecycle = wakefulnessLifecycle; + } + + @NonNull @Override - public void handleMessage(Message msg) { - switch (msg.what) { - case SCREEN_TURNING_ON: - Trace.beginSection("KeyguardLifecyclesDispatcher#SCREEN_TURNING_ON"); - mScreenLifecycle.dispatchScreenTurningOn(); - Trace.endSection(); - break; - case SCREEN_TURNED_ON: - mScreenLifecycle.dispatchScreenTurnedOn(); - break; - case SCREEN_TURNING_OFF: - mScreenLifecycle.dispatchScreenTurningOff(); - break; - case SCREEN_TURNED_OFF: - mScreenLifecycle.dispatchScreenTurnedOff(); - break; - case STARTED_WAKING_UP: - mWakefulnessLifecycle.dispatchStartedWakingUp(msg.arg1 /* pmReason */); - break; - case FINISHED_WAKING_UP: - mWakefulnessLifecycle.dispatchFinishedWakingUp(); - break; - case STARTED_GOING_TO_SLEEP: - mWakefulnessLifecycle.dispatchStartedGoingToSleep(msg.arg1 /* pmReason */); - break; - case FINISHED_GOING_TO_SLEEP: - mWakefulnessLifecycle.dispatchFinishedGoingToSleep(); - break; - default: - throw new IllegalArgumentException("Unknown message: " + msg); + public String getTraceName(@NonNull Message msg) { + if (msg.getCallback() instanceof TraceNameSupplier || msg.getCallback() != null) { + return super.getTraceName(msg); } + return TAG + "#" + getNameOfMessage(msg.what); } - }; + @Override + public void handleMessage(@NonNull Message msg) { + switch (msg.what) { + case SCREEN_TURNING_ON -> mScreenLifecycle.dispatchScreenTurningOn(); + case SCREEN_TURNED_ON -> mScreenLifecycle.dispatchScreenTurnedOn(); + case SCREEN_TURNING_OFF -> mScreenLifecycle.dispatchScreenTurningOff(); + case SCREEN_TURNED_OFF -> mScreenLifecycle.dispatchScreenTurnedOff(); + case STARTED_WAKING_UP -> + mWakefulnessLifecycle.dispatchStartedWakingUp(msg.arg1 /* pmReason */); + case FINISHED_WAKING_UP -> mWakefulnessLifecycle.dispatchFinishedWakingUp(); + case STARTED_GOING_TO_SLEEP -> + mWakefulnessLifecycle.dispatchStartedGoingToSleep(msg.arg1 /* pmReason */); + case FINISHED_GOING_TO_SLEEP -> + mWakefulnessLifecycle.dispatchFinishedGoingToSleep(); + default -> throw new IllegalArgumentException("Unknown message: " + msg); + } + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java index 573de9737eb8..051131433143 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java @@ -49,7 +49,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardUpdateMonitorCallback; import com.android.systemui.R; -import com.android.systemui.SystemUIAppComponentFactory; +import com.android.systemui.SystemUIAppComponentFactoryBase; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.settings.UserTracker; import com.android.systemui.statusbar.NotificationMediaManager; @@ -72,13 +72,13 @@ import javax.inject.Inject; /** * Simple Slice provider that shows the current date. * - * Injection is handled by {@link SystemUIAppComponentFactory} + + * Injection is handled by {@link SystemUIAppComponentFactoryBase} + * {@link com.android.systemui.dagger.GlobalRootComponent#inject(KeyguardSliceProvider)}. */ public class KeyguardSliceProvider extends SliceProvider implements NextAlarmController.NextAlarmChangeCallback, ZenModeController.Callback, NotificationMediaManager.MediaListener, StatusBarStateController.StateListener, - SystemUIAppComponentFactory.ContextInitializer { + SystemUIAppComponentFactoryBase.ContextInitializer { private static final String TAG = "KgdSliceProvider"; @@ -148,7 +148,7 @@ public class KeyguardSliceProvider extends SliceProvider implements protected boolean mDozing; private int mStatusBarState; private boolean mMediaIsVisible; - private SystemUIAppComponentFactory.ContextAvailableCallback mContextAvailableCallback; + private SystemUIAppComponentFactoryBase.ContextAvailableCallback mContextAvailableCallback; @Inject WakeLockLogger mWakeLockLogger; @@ -533,7 +533,7 @@ public class KeyguardSliceProvider extends SliceProvider implements @Override public void setContextAvailableCallback( - SystemUIAppComponentFactory.ContextAvailableCallback callback) { + SystemUIAppComponentFactoryBase.ContextAvailableCallback callback) { mContextAvailableCallback = callback; } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt index 68e72c58972b..364614421567 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt @@ -906,10 +906,7 @@ class KeyguardUnlockAnimationController @Inject constructor( } fun setWallpaperAppearAmount(amount: Float) { - val animationAlpha = when { - !powerManager.isInteractive -> 0f - else -> amount - } + val animationAlpha = amount wallpaperTargets?.forEach { wallpaper -> // SyncRtSurfaceTransactionApplier cannot apply transaction when the target view is diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt new file mode 100644 index 000000000000..05c23aefe974 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.android.systemui.keyguard + +import android.view.View +import android.view.ViewGroup +import com.android.systemui.CoreStartable +import com.android.systemui.R +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.flags.FeatureFlags +import com.android.systemui.flags.Flags +import com.android.systemui.keyguard.ui.binder.KeyguardIndicationAreaBinder +import com.android.systemui.keyguard.ui.view.KeyguardRootView +import com.android.systemui.keyguard.ui.viewmodel.KeyguardIndicationAreaViewModel +import com.android.systemui.shade.NotificationShadeWindowView +import com.android.systemui.statusbar.KeyguardIndicationController +import javax.inject.Inject +import kotlinx.coroutines.DisposableHandle + +/** Binds keyguard views on startup, and also exposes methods to allow rebinding if views change */ +@SysUISingleton +class KeyguardViewConfigurator +@Inject +constructor( + private val keyguardRootView: KeyguardRootView, + private val keyguardIndicationAreaViewModel: KeyguardIndicationAreaViewModel, + private val notificationShadeWindowView: NotificationShadeWindowView, + private val featureFlags: FeatureFlags, + private val indicationController: KeyguardIndicationController, +) : CoreStartable { + + private var indicationAreaHandle: DisposableHandle? = null + + override fun start() { + bindIndicationArea( + notificationShadeWindowView.requireViewById(R.id.notification_panel) as ViewGroup + ) + } + + fun bindIndicationArea(legacyParent: ViewGroup) { + indicationAreaHandle?.dispose() + + // At startup, 2 views with the ID `R.id.keyguard_indication_area` will be available. + // Disable one of them + if (featureFlags.isEnabled(Flags.MIGRATE_INDICATION_AREA)) { + legacyParent.requireViewById<View>(R.id.keyguard_indication_area).let { + legacyParent.removeView(it) + } + } else { + keyguardRootView.findViewById<View?>(R.id.keyguard_indication_area)?.let { + keyguardRootView.removeView(it) + } + } + + indicationAreaHandle = + KeyguardIndicationAreaBinder.bind( + notificationShadeWindowView, + keyguardIndicationAreaViewModel, + indicationController + ) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index bc41ab3156df..a5d209680a79 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -17,6 +17,8 @@ package com.android.systemui.keyguard; import static android.app.StatusBarManager.SESSION_KEYGUARD; +import static android.provider.Settings.Secure.LOCK_SCREEN_LOCK_AFTER_TIMEOUT; +import static android.provider.Settings.System.LOCKSCREEN_SOUNDS_ENABLED; import static android.provider.Settings.System.SCREEN_OFF_TIMEOUT; import static android.view.RemoteAnimationTarget.MODE_OPENING; import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_NO_WINDOW_ANIMATIONS; @@ -73,7 +75,6 @@ import android.os.Message; import android.os.PowerManager; import android.os.RemoteException; import android.os.ServiceManager; -import android.os.SystemClock; import android.os.SystemProperties; import android.os.Trace; import android.os.UserHandle; @@ -161,6 +162,9 @@ import com.android.systemui.statusbar.phone.ScrimController; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.UserSwitcherController; import com.android.systemui.util.DeviceConfigProxy; +import com.android.systemui.util.settings.SecureSettings; +import com.android.systemui.util.settings.SystemSettings; +import com.android.systemui.util.time.SystemClock; import com.android.wm.shell.keyguard.KeyguardTransitions; import dagger.Lazy; @@ -225,7 +229,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, private final static String TAG = "KeyguardViewMediator"; - private static final String DELAYED_KEYGUARD_ACTION = + public static final String DELAYED_KEYGUARD_ACTION = "com.android.internal.policy.impl.PhoneWindowManager.DELAYED_KEYGUARD"; private static final String DELAYED_LOCK_PROFILE_ACTION = "com.android.internal.policy.impl.PhoneWindowManager.DELAYED_LOCK"; @@ -260,7 +264,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, * turning on the keyguard (i.e, the user has this much time to turn * the screen back on without having to face the keyguard). */ - private static final int KEYGUARD_LOCK_AFTER_DELAY_DEFAULT = 5000; + public static final int KEYGUARD_LOCK_AFTER_DELAY_DEFAULT = 5000; /** * How long we'll wait for the {@link ViewMediatorCallback#keyguardDoneDrawing()} @@ -319,6 +323,9 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, /** UserSwitcherController for creating guest user on boot complete */ private final UserSwitcherController mUserSwitcherController; + private final SecureSettings mSecureSettings; + private final SystemSettings mSystemSettings; + private final SystemClock mSystemClock; private SystemPropertiesHelper mSystemPropertiesHelper; /** @@ -1314,6 +1321,9 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, Lazy<ActivityLaunchAnimator> activityLaunchAnimator, Lazy<ScrimController> scrimControllerLazy, FeatureFlags featureFlags, + SecureSettings secureSettings, + SystemSettings systemSettings, + SystemClock systemClock, @Main CoroutineDispatcher mainDispatcher, Lazy<DreamingToLockscreenTransitionViewModel> dreamingToLockscreenTransitionViewModel, SystemPropertiesHelper systemPropertiesHelper) { @@ -1330,6 +1340,9 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, mPM = powerManager; mTrustManager = trustManager; mUserSwitcherController = userSwitcherController; + mSecureSettings = secureSettings; + mSystemSettings = systemSettings; + mSystemClock = systemClock; mSystemPropertiesHelper = systemPropertiesHelper; mStatusBarService = IStatusBarService.Stub.asInterface( ServiceManager.getService(Context.STATUS_BAR_SERVICE)); @@ -1382,7 +1395,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, } public void userActivity() { - mPM.userActivity(SystemClock.uptimeMillis(), false); + mPM.userActivity(mSystemClock.uptimeMillis(), false); } private void setupLocked() { @@ -1584,7 +1597,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, if (cameraGestureTriggered) { // Just to make sure, make sure the device is awake. - mContext.getSystemService(PowerManager.class).wakeUp(SystemClock.uptimeMillis(), + mContext.getSystemService(PowerManager.class).wakeUp(mSystemClock.uptimeMillis(), PowerManager.WAKE_REASON_CAMERA_LAUNCH, "com.android.systemui:CAMERA_GESTURE_PREVENT_LOCK"); setPendingLock(false); @@ -1681,12 +1694,11 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, // to enable it a bit later (i.e, give the user a chance // to turn the screen back on within a certain window without // having to unlock the screen) - final ContentResolver cr = mContext.getContentResolver(); // From SecuritySettings - final long lockAfterTimeout = Settings.Secure.getIntForUser(cr, - Settings.Secure.LOCK_SCREEN_LOCK_AFTER_TIMEOUT, - KEYGUARD_LOCK_AFTER_DELAY_DEFAULT, userId); + final long lockAfterTimeout = mSecureSettings.getIntForUser(LOCK_SCREEN_LOCK_AFTER_TIMEOUT, + KEYGUARD_LOCK_AFTER_DELAY_DEFAULT, + userId); // From DevicePolicyAdmin final long policyTimeout = mLockPatternUtils.getDevicePolicyManager() @@ -1698,8 +1710,9 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, timeout = lockAfterTimeout; } else { // From DisplaySettings - long displayTimeout = Settings.System.getIntForUser(cr, SCREEN_OFF_TIMEOUT, - KEYGUARD_DISPLAY_TIMEOUT_DELAY_DEFAULT, userId); + long displayTimeout = mSystemSettings.getIntForUser(SCREEN_OFF_TIMEOUT, + KEYGUARD_DISPLAY_TIMEOUT_DELAY_DEFAULT, + userId); // policy in effect. Make sure we don't go beyond policy limit. displayTimeout = Math.max(displayTimeout, 0); // ignore negative values @@ -1720,7 +1733,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, private void doKeyguardLaterLocked(long timeout) { // Lock in the future - long when = SystemClock.elapsedRealtime() + timeout; + long when = mSystemClock.elapsedRealtime() + timeout; Intent intent = new Intent(DELAYED_KEYGUARD_ACTION); intent.setPackage(mContext.getPackageName()); intent.putExtra("seq", mDelayedShowingSequence); @@ -1742,7 +1755,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, if (userTimeout == 0) { doKeyguardForChildProfilesLocked(); } else { - long userWhen = SystemClock.elapsedRealtime() + userTimeout; + long userWhen = mSystemClock.elapsedRealtime() + userTimeout; Intent lockIntent = new Intent(DELAYED_LOCK_PROFILE_ACTION); lockIntent.setPackage(mContext.getPackageName()); lockIntent.putExtra("seq", mDelayedProfileShowingSequence); @@ -2551,9 +2564,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, private void playSound(int soundId) { if (soundId == 0) return; - final ContentResolver cr = mContext.getContentResolver(); - int lockscreenSoundsEnabled = Settings.System.getIntForUser(cr, - Settings.System.LOCKSCREEN_SOUNDS_ENABLED, 1, + int lockscreenSoundsEnabled = mSystemSettings.getIntForUser(LOCKSCREEN_SOUNDS_ENABLED, 1, KeyguardUpdateMonitor.getCurrentUser()); if (lockscreenSoundsEnabled == 1) { @@ -2730,7 +2741,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, // It's possible that the device was unlocked (via BOUNCER) while dozing. It's time to // wake up. if (mAodShowing) { - mPM.wakeUp(SystemClock.uptimeMillis(), PowerManager.WAKE_REASON_GESTURE, + mPM.wakeUp(mSystemClock.uptimeMillis(), PowerManager.WAKE_REASON_GESTURE, "com.android.systemui:BOUNCER_DOZING"); } @@ -2745,7 +2756,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, // TODO(bc-unlock): Fill parameters mNotificationShadeWindowControllerLazy.get().batchApplyWindowLayoutParams(() -> { handleStartKeyguardExitAnimation( - SystemClock.uptimeMillis() + mHideAnimation.getStartOffset(), + mSystemClock.uptimeMillis() + mHideAnimation.getStartOffset(), mHideAnimation.getDuration(), null /* apps */, null /* wallpapers */, null /* nonApps */, null /* finishedCallback */); }); @@ -2754,7 +2765,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, // It's possible that the device was unlocked (via BOUNCER or Fingerprint) while // dreaming. It's time to wake up. if (mDreamOverlayShowing) { - mPM.wakeUp(SystemClock.uptimeMillis(), PowerManager.WAKE_REASON_GESTURE, + mPM.wakeUp(mSystemClock.uptimeMillis(), PowerManager.WAKE_REASON_GESTURE, "com.android.systemui:UNLOCK_DREAMING"); } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java index 1f121e92d7d0..61bacbda98a5 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java @@ -67,6 +67,9 @@ import com.android.systemui.statusbar.phone.ScrimController; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.UserSwitcherController; import com.android.systemui.util.DeviceConfigProxy; +import com.android.systemui.util.settings.SecureSettings; +import com.android.systemui.util.settings.SystemSettings; +import com.android.systemui.util.time.SystemClock; import com.android.wm.shell.keyguard.KeyguardTransitions; import dagger.Lazy; @@ -134,6 +137,9 @@ public class KeyguardModule { Lazy<ActivityLaunchAnimator> activityLaunchAnimator, Lazy<ScrimController> scrimControllerLazy, FeatureFlags featureFlags, + SecureSettings secureSettings, + SystemSettings systemSettings, + SystemClock systemClock, @Main CoroutineDispatcher mainDispatcher, Lazy<DreamingToLockscreenTransitionViewModel> dreamingToLockscreenTransitionViewModel, SystemPropertiesHelper systemPropertiesHelper) { @@ -171,6 +177,9 @@ public class KeyguardModule { activityLaunchAnimator, scrimControllerLazy, featureFlags, + secureSettings, + systemSettings, + systemClock, mainDispatcher, dreamingToLockscreenTransitionViewModel, systemPropertiesHelper); diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt index 9621f03f63a0..f9e9a9305df3 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt @@ -26,6 +26,7 @@ import com.android.internal.logging.UiEventLogger import com.android.keyguard.FaceAuthUiEvent import com.android.systemui.Dumpable import com.android.systemui.R +import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow import com.android.systemui.dagger.SysUISingleton @@ -34,7 +35,6 @@ import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.dump.DumpManager import com.android.systemui.flags.FeatureFlags import com.android.systemui.flags.Flags -import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor import com.android.systemui.keyguard.shared.model.AcquiredAuthenticationStatus diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt index 742e53515e82..81f62b687e6c 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt @@ -25,6 +25,7 @@ import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLoggin import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow import com.android.systemui.common.shared.model.Position import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.doze.DozeMachine import com.android.systemui.doze.DozeTransitionCallback @@ -44,13 +45,16 @@ import com.android.systemui.statusbar.phone.DozeParameters import com.android.systemui.statusbar.policy.KeyguardStateController import javax.inject.Inject import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.channels.awaitClose 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.distinctUntilChanged import kotlinx.coroutines.flow.flowOn +import kotlinx.coroutines.flow.stateIn /** Defines interface for classes that encapsulate application state for the keyguard. */ interface KeyguardRepository { @@ -138,7 +142,7 @@ interface KeyguardRepository { val statusBarState: Flow<StatusBarState> /** Observable for device wake/sleep state */ - val wakefulness: Flow<WakefulnessModel> + val wakefulness: StateFlow<WakefulnessModel> /** Observable for biometric unlock modes */ val biometricUnlockState: Flow<BiometricUnlockModel> @@ -202,7 +206,8 @@ constructor( private val dozeParameters: DozeParameters, private val authController: AuthController, private val dreamOverlayCallbackController: DreamOverlayCallbackController, - @Main private val mainDispatcher: CoroutineDispatcher + @Main private val mainDispatcher: CoroutineDispatcher, + @Application private val scope: CoroutineScope, ) : KeyguardRepository { private val _animateBottomAreaDozingTransitions = MutableStateFlow(false) override val animateBottomAreaDozingTransitions = @@ -486,47 +491,48 @@ constructor( awaitClose { biometricUnlockController.removeListener(callback) } } - override val wakefulness: Flow<WakefulnessModel> = conflatedCallbackFlow { - val observer = - object : WakefulnessLifecycle.Observer { - override fun onStartedWakingUp() { - dispatchNewState() - } - - override fun onFinishedWakingUp() { - dispatchNewState() - } + override val wakefulness: StateFlow<WakefulnessModel> = + conflatedCallbackFlow { + val observer = + object : WakefulnessLifecycle.Observer { + override fun onStartedWakingUp() { + dispatchNewState() + } - override fun onPostFinishedWakingUp() { - dispatchNewState() - } + override fun onFinishedWakingUp() { + dispatchNewState() + } - override fun onStartedGoingToSleep() { - dispatchNewState() - } + override fun onPostFinishedWakingUp() { + dispatchNewState() + } - override fun onFinishedGoingToSleep() { - dispatchNewState() - } + override fun onStartedGoingToSleep() { + dispatchNewState() + } - private fun dispatchNewState() { - trySendWithFailureLogging( - WakefulnessModel.fromWakefulnessLifecycle(wakefulnessLifecycle), - TAG, - "updated wakefulness state" - ) - } - } + override fun onFinishedGoingToSleep() { + dispatchNewState() + } - wakefulnessLifecycle.addObserver(observer) - trySendWithFailureLogging( - WakefulnessModel.fromWakefulnessLifecycle(wakefulnessLifecycle), - TAG, - "initial wakefulness state" - ) + private fun dispatchNewState() { + trySendWithFailureLogging( + WakefulnessModel.fromWakefulnessLifecycle(wakefulnessLifecycle), + TAG, + "updated wakefulness state", + ) + } + } - awaitClose { wakefulnessLifecycle.removeObserver(observer) } - } + wakefulnessLifecycle.addObserver(observer) + awaitClose { wakefulnessLifecycle.removeObserver(observer) } + } + .stateIn( + scope, + // Use Eagerly so that we're always listening and never miss an event. + SharingStarted.Eagerly, + initialValue = WakefulnessModel.fromWakefulnessLifecycle(wakefulnessLifecycle), + ) override val fingerprintSensorLocation: Flow<Point?> = conflatedCallbackFlow { fun sendFpLocation() { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryModule.kt index 4055fd0bbc49..246ee33fa2e8 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryModule.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryModule.kt @@ -17,9 +17,11 @@ package com.android.systemui.keyguard.data.repository import com.android.systemui.CoreStartable -import com.android.systemui.keyguard.bouncer.data.repository.BouncerMessageRepository -import com.android.systemui.keyguard.bouncer.data.repository.BouncerMessageRepositoryImpl -import com.android.systemui.keyguard.bouncer.domain.interactor.BouncerMessageAuditLogger +import com.android.systemui.bouncer.data.repository.BouncerMessageRepository +import com.android.systemui.bouncer.data.repository.BouncerMessageRepositoryImpl +import com.android.systemui.bouncer.data.repository.KeyguardBouncerRepository +import com.android.systemui.bouncer.data.repository.KeyguardBouncerRepositoryImpl +import com.android.systemui.bouncer.domain.interactor.BouncerMessageAuditLogger import dagger.Binds import dagger.Module import dagger.multibindings.ClassKey diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt index 358ab0147049..84cd3ef622ba 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt @@ -47,6 +47,11 @@ import kotlinx.coroutines.flow.filter * To create or modify logic that controls when and how transitions get created, look at * [TransitionInteractor]. These interactors will call [startTransition] and [updateTransition] on * this repository. + * + * To print all transitions to logcat to help with debugging, run this command: + * adb shell settings put global systemui/buffer/KeyguardLog VERBOSE + * + * This will print all keyguard transitions to logcat with the KeyguardTransitionAuditLogger tag. */ interface KeyguardTransitionRepository { /** diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt index c44435ee2be0..228290a3203f 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt @@ -19,12 +19,13 @@ package com.android.systemui.keyguard.domain.interactor import android.app.StatusBarManager import android.graphics.Point +import com.android.systemui.bouncer.data.repository.KeyguardBouncerRepository import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow +import com.android.systemui.common.ui.data.repository.ConfigurationRepository import com.android.systemui.dagger.SysUISingleton import com.android.systemui.flags.FeatureFlags import com.android.systemui.flags.Flags -import com.android.systemui.keyguard.data.repository.KeyguardBouncerRepository import com.android.systemui.keyguard.data.repository.KeyguardRepository import com.android.systemui.keyguard.shared.model.BiometricUnlockModel import com.android.systemui.keyguard.shared.model.CameraLaunchSourceModel @@ -39,6 +40,7 @@ import javax.inject.Inject import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.delay import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.filter @@ -59,6 +61,7 @@ constructor( private val commandQueue: CommandQueue, featureFlags: FeatureFlags, bouncerRepository: KeyguardBouncerRepository, + configurationRepository: ConfigurationRepository, ) { /** * The amount of doze the system is in, where `1.0` is fully dozing and `0.0` is not dozing at @@ -97,7 +100,7 @@ constructor( } /** The device wake/sleep state */ - val wakefulnessModel: Flow<WakefulnessModel> = repository.wakefulness + val wakefulnessModel: StateFlow<WakefulnessModel> = repository.wakefulness /** * Dozing and dreaming have overlapping events. If the doze state remains in FINISH, it means @@ -172,6 +175,9 @@ constructor( /** The approximate location on the screen of the face unlock sensor, if one is available. */ val faceSensorLocation: Flow<Point?> = repository.faceSensorLocation + /** Notifies when a new configuration is set */ + val configurationChange: Flow<Unit> = configurationRepository.onAnyConfigurationChange + fun dozeTransitionTo(vararg states: DozeStateModel): Flow<DozeTransitionModel> { return dozeTransitionModel.filter { states.contains(it.to) } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SystemUIKeyguardFaceAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SystemUIKeyguardFaceAuthInteractor.kt index 6b515dab79f6..8b749f0f4bc4 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SystemUIKeyguardFaceAuthInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/SystemUIKeyguardFaceAuthInteractor.kt @@ -19,6 +19,8 @@ package com.android.systemui.keyguard.domain.interactor import com.android.keyguard.FaceAuthUiEvent import com.android.keyguard.KeyguardUpdateMonitor import com.android.systemui.CoreStartable +import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor +import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Main diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/WakeSleepReason.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/WakeSleepReason.kt index 51ce7ff45182..fb685dab1797 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/WakeSleepReason.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/WakeSleepReason.kt @@ -23,9 +23,12 @@ enum class WakeSleepReason { /** The physical power button was pressed to wake up or sleep the device. */ POWER_BUTTON, - /** The user has taped or double tapped to wake the screen */ + /** The user has tapped or double tapped to wake the screen. */ TAP, + /** The user performed some sort of gesture to wake the screen. */ + GESTURE, + /** Something else happened to wake up or sleep the device. */ OTHER; @@ -34,6 +37,7 @@ enum class WakeSleepReason { return when (reason) { PowerManager.WAKE_REASON_POWER_BUTTON -> POWER_BUTTON PowerManager.WAKE_REASON_TAP -> TAP + PowerManager.WAKE_REASON_GESTURE -> GESTURE else -> OTHER } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/WakefulnessModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/WakefulnessModel.kt index 52e15bece2df..cfd9e0866c06 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/WakefulnessModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/WakefulnessModel.kt @@ -27,7 +27,11 @@ data class WakefulnessModel( fun isStartingToSleep() = state == WakefulnessState.STARTING_TO_SLEEP - fun isStartingToSleepOrAsleep() = isStartingToSleep() || state == WakefulnessState.ASLEEP + private fun isAsleep() = state == WakefulnessState.ASLEEP + + fun isStartingToSleepOrAsleep() = isStartingToSleep() || isAsleep() + + fun isDeviceInteractive() = !isAsleep() fun isStartingToWakeOrAwake() = isStartingToWake() || state == WakefulnessState.AWAKE @@ -43,6 +47,11 @@ data class WakefulnessModel( fun isAwakeFromTap() = state == WakefulnessState.STARTING_TO_WAKE && lastWakeReason == WakeSleepReason.TAP + fun isDeviceInteractiveFromTapOrGesture(): Boolean { + return isDeviceInteractive() && + (lastWakeReason == WakeSleepReason.TAP || lastWakeReason == WakeSleepReason.GESTURE) + } + companion object { fun fromWakefulnessLifecycle(wakefulnessLifecycle: WakefulnessLifecycle): WakefulnessModel { return WakefulnessModel( diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt index a8d662c96284..7d14198bdb17 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt @@ -21,12 +21,10 @@ import android.content.Intent import android.graphics.Rect import android.graphics.drawable.Animatable2 import android.util.Size -import android.util.TypedValue import android.view.View import android.view.ViewGroup import android.view.ViewPropertyAnimator import android.widget.ImageView -import android.widget.TextView import androidx.core.view.isInvisible import androidx.core.view.isVisible import androidx.core.view.updateLayoutParams @@ -108,14 +106,10 @@ object KeyguardBottomAreaViewBinder { activityStarter: ActivityStarter?, messageDisplayer: (Int) -> Unit, ): Binding { - val indicationArea: View = view.requireViewById(R.id.keyguard_indication_area) val ambientIndicationArea: View? = view.findViewById(R.id.ambient_indication_container) val startButton: ImageView = view.requireViewById(R.id.start_button) val endButton: ImageView = view.requireViewById(R.id.end_button) val overlayContainer: View = view.requireViewById(R.id.overlay_container) - val indicationText: TextView = view.requireViewById(R.id.keyguard_indication_text) - val indicationTextBottom: TextView = - view.requireViewById(R.id.keyguard_indication_text_bottom) val settingsMenu: LaunchableLinearLayout = view.requireViewById(R.id.keyguard_settings_button) @@ -183,7 +177,6 @@ object KeyguardBottomAreaViewBinder { } ambientIndicationArea?.alpha = alpha - indicationArea.alpha = alpha } } @@ -205,50 +198,23 @@ object KeyguardBottomAreaViewBinder { launch { viewModel.indicationAreaTranslationX.collect { translationX -> - indicationArea.translationX = translationX ambientIndicationArea?.translationX = translationX } } launch { - combine( - viewModel.isIndicationAreaPadded, - configurationBasedDimensions.map { it.indicationAreaPaddingPx }, - ) { isPadded, paddingIfPaddedPx -> - if (isPadded) { - paddingIfPaddedPx - } else { - 0 - } - } - .collect { paddingPx -> - indicationArea.setPadding(paddingPx, 0, paddingPx, 0) - } - } - - launch { configurationBasedDimensions .map { it.defaultBurnInPreventionYOffsetPx } .flatMapLatest { defaultBurnInOffsetY -> viewModel.indicationAreaTranslationY(defaultBurnInOffsetY) } .collect { translationY -> - indicationArea.translationY = translationY ambientIndicationArea?.translationY = translationY } } launch { configurationBasedDimensions.collect { dimensions -> - indicationText.setTextSize( - TypedValue.COMPLEX_UNIT_PX, - dimensions.indicationTextSizePx.toFloat(), - ) - indicationTextBottom.setTextSize( - TypedValue.COMPLEX_UNIT_PX, - dimensions.indicationTextSizePx.toFloat(), - ) - startButton.updateLayoutParams<ViewGroup.LayoutParams> { width = dimensions.buttonSizePx.width height = dimensions.buttonSizePx.height @@ -305,7 +271,7 @@ object KeyguardBottomAreaViewBinder { return object : Binding { override fun getIndicationAreaAnimators(): List<ViewPropertyAnimator> { - return listOf(indicationArea, ambientIndicationArea).mapNotNull { it?.animate() } + return listOf(ambientIndicationArea).mapNotNull { it?.animate() } } override fun onConfigurationChanged() { @@ -517,12 +483,6 @@ object KeyguardBottomAreaViewBinder { return ConfigurationBasedDimensions( defaultBurnInPreventionYOffsetPx = view.resources.getDimensionPixelOffset(R.dimen.default_burn_in_prevention_offset), - indicationAreaPaddingPx = - view.resources.getDimensionPixelOffset(R.dimen.keyguard_indication_area_padding), - indicationTextSizePx = - view.resources.getDimensionPixelSize( - com.android.internal.R.dimen.text_size_small_material, - ), buttonSizePx = Size( view.resources.getDimensionPixelSize(R.dimen.keyguard_affordance_fixed_width), @@ -552,8 +512,6 @@ object KeyguardBottomAreaViewBinder { private data class ConfigurationBasedDimensions( val defaultBurnInPreventionYOffsetPx: Int, - val indicationAreaPaddingPx: Int, - val indicationTextSizePx: Int, val buttonSizePx: Size, ) } 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 new file mode 100644 index 000000000000..02e6765fa1d3 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardIndicationAreaBinder.kt @@ -0,0 +1,153 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.keyguard.ui.binder + +import android.util.TypedValue +import android.view.View +import android.view.ViewGroup +import android.widget.TextView +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.repeatOnLifecycle +import com.android.systemui.R +import com.android.systemui.keyguard.ui.viewmodel.KeyguardIndicationAreaViewModel +import com.android.systemui.lifecycle.repeatWhenAttached +import com.android.systemui.statusbar.KeyguardIndicationController +import kotlinx.coroutines.DisposableHandle +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.launch + +/** + * Binds a keyguard indication area view to its view-model. + * + * To use this properly, users should maintain a one-to-one relationship between the [View] and the + * view-binding, binding each view only once. It is okay and expected for the same instance of the + * view-model to be reused for multiple view/view-binder bindings. + */ +@OptIn(ExperimentalCoroutinesApi::class) +object KeyguardIndicationAreaBinder { + + /** Binds the view to the view-model, continuing to update the former based on the latter. */ + @JvmStatic + fun bind( + view: ViewGroup, + viewModel: KeyguardIndicationAreaViewModel, + indicationController: KeyguardIndicationController, + ): DisposableHandle { + val indicationArea: ViewGroup = view.requireViewById(R.id.keyguard_indication_area) + indicationController.setIndicationArea(indicationArea) + + val indicationText: TextView = indicationArea.requireViewById(R.id.keyguard_indication_text) + val indicationTextBottom: TextView = + indicationArea.requireViewById(R.id.keyguard_indication_text_bottom) + + view.clipChildren = false + view.clipToPadding = false + + val configurationBasedDimensions = MutableStateFlow(loadFromResources(view)) + val disposableHandle = + view.repeatWhenAttached { + repeatOnLifecycle(Lifecycle.State.STARTED) { + launch { + viewModel.alpha.collect { alpha -> + view.importantForAccessibility = + if (alpha == 0f) { + View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS + } else { + View.IMPORTANT_FOR_ACCESSIBILITY_AUTO + } + + indicationArea.alpha = alpha + } + } + + launch { + viewModel.indicationAreaTranslationX.collect { translationX -> + indicationArea.translationX = translationX + } + } + + launch { + combine( + viewModel.isIndicationAreaPadded, + configurationBasedDimensions.map { it.indicationAreaPaddingPx }, + ) { isPadded, paddingIfPaddedPx -> + if (isPadded) { + paddingIfPaddedPx + } else { + 0 + } + } + .collect { paddingPx -> + indicationArea.setPadding(paddingPx, 0, paddingPx, 0) + } + } + + launch { + configurationBasedDimensions + .map { it.defaultBurnInPreventionYOffsetPx } + .flatMapLatest { defaultBurnInOffsetY -> + viewModel.indicationAreaTranslationY(defaultBurnInOffsetY) + } + .collect { translationY -> indicationArea.translationY = translationY } + } + + launch { + configurationBasedDimensions.collect { dimensions -> + indicationText.setTextSize( + TypedValue.COMPLEX_UNIT_PX, + dimensions.indicationTextSizePx.toFloat(), + ) + indicationTextBottom.setTextSize( + TypedValue.COMPLEX_UNIT_PX, + dimensions.indicationTextSizePx.toFloat(), + ) + } + } + + launch { + viewModel.configurationChange.collect { + configurationBasedDimensions.value = loadFromResources(view) + } + } + } + } + return disposableHandle + } + + private fun loadFromResources(view: View): ConfigurationBasedDimensions { + return ConfigurationBasedDimensions( + defaultBurnInPreventionYOffsetPx = + view.resources.getDimensionPixelOffset(R.dimen.default_burn_in_prevention_offset), + indicationAreaPaddingPx = + view.resources.getDimensionPixelOffset(R.dimen.keyguard_indication_area_padding), + indicationTextSizePx = + view.resources.getDimensionPixelSize( + com.android.internal.R.dimen.text_size_small_material, + ), + ) + } + + private data class ConfigurationBasedDimensions( + val defaultBurnInPreventionYOffsetPx: Int, + val indicationAreaPaddingPx: Int, + val indicationTextSizePx: Int, + ) +} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/KeyguardIndicationArea.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/KeyguardIndicationArea.kt new file mode 100644 index 000000000000..890d565b03a5 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/KeyguardIndicationArea.kt @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.android.systemui.keyguard.ui.view + +import android.content.Context +import android.text.TextUtils +import android.util.AttributeSet +import android.view.Gravity +import android.view.View +import android.view.ViewGroup.LayoutParams.MATCH_PARENT +import android.view.ViewGroup.LayoutParams.WRAP_CONTENT +import android.widget.LinearLayout +import com.android.systemui.R +import com.android.systemui.statusbar.phone.KeyguardIndicationTextView + +class KeyguardIndicationArea( + context: Context, + private val attrs: AttributeSet?, +) : + LinearLayout( + context, + attrs, + ) { + + init { + setId(R.id.keyguard_indication_area) + orientation = LinearLayout.VERTICAL + + addView(indicationTopRow(), LinearLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT)) + addView( + indicationBottomRow(), + LinearLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT).apply { + gravity = Gravity.CENTER_HORIZONTAL + } + ) + } + + private fun indicationTopRow(): KeyguardIndicationTextView { + return KeyguardIndicationTextView(context, attrs).apply { + id = R.id.keyguard_indication_text + gravity = Gravity.CENTER + accessibilityLiveRegion = View.ACCESSIBILITY_LIVE_REGION_POLITE + setTextAppearance(R.style.TextAppearance_Keyguard_BottomArea) + + val padding = R.dimen.keyguard_indication_text_padding.dp() + setPaddingRelative(padding, 0, padding, 0) + } + } + + private fun indicationBottomRow(): KeyguardIndicationTextView { + return KeyguardIndicationTextView(context, attrs).apply { + id = R.id.keyguard_indication_text_bottom + gravity = Gravity.CENTER + accessibilityLiveRegion = View.ACCESSIBILITY_LIVE_REGION_POLITE + + setTextAppearance(R.style.TextAppearance_Keyguard_BottomArea) + setEllipsize(TextUtils.TruncateAt.END) + setAlpha(0.8f) + setMinHeight(R.dimen.keyguard_indication_text_min_height.dp()) + setMaxLines(2) + setVisibility(View.GONE) + + val padding = R.dimen.keyguard_indication_text_padding.dp() + setPaddingRelative(padding, 0, padding, 0) + } + } + + private fun Int.dp(): Int { + return context.resources.getDimensionPixelSize(this) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/KeyguardRootView.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/KeyguardRootView.kt new file mode 100644 index 000000000000..abf0e80bd87a --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/KeyguardRootView.kt @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.android.systemui.keyguard.ui.view + +import android.content.Context +import android.util.AttributeSet +import android.view.Gravity +import android.view.ViewGroup.LayoutParams.MATCH_PARENT +import android.view.ViewGroup.LayoutParams.WRAP_CONTENT +import android.widget.FrameLayout +import com.android.systemui.R + +/** Provides a container for all keyguard ui content. */ +class KeyguardRootView( + context: Context, + private val attrs: AttributeSet?, +) : + FrameLayout( + context, + attrs, + ) { + + init { + addIndicationTextArea() + } + + private fun addIndicationTextArea() { + val view = KeyguardIndicationArea(context, attrs) + addView( + view, + FrameLayout.LayoutParams( + MATCH_PARENT, + WRAP_CONTENT, + ) + .apply { + gravity = Gravity.BOTTOM or Gravity.CENTER_HORIZONTAL + bottomMargin = R.dimen.keyguard_indication_margin_bottom.dp() + } + ) + } + + private fun Int.dp(): Int { + return context.resources.getDimensionPixelSize(this) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModel.kt index 62a1a9e16fc6..3e6f8e68891a 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModel.kt @@ -57,7 +57,7 @@ constructor( * in the wallpaper picker application. This should _always_ be `false` for the real lock screen * experience. */ - private val previewMode = MutableStateFlow(PreviewMode()) + val previewMode = MutableStateFlow(PreviewMode()) /** * ID of the slot that's currently selected in the preview that renders exclusively in the @@ -101,12 +101,6 @@ constructor( bottomAreaInteractor.alpha.distinctUntilChanged() } } - /** An observable for whether the indication area should be padded. */ - val isIndicationAreaPadded: Flow<Boolean> = - combine(startButton, endButton) { startButtonModel, endButtonModel -> - startButtonModel.isVisible || endButtonModel.isVisible - } - .distinctUntilChanged() /** An observable for the x-offset by which the indication area should be translated. */ val indicationAreaTranslationX: Flow<Float> = bottomAreaInteractor.clockPosition.map { it.x.toFloat() }.distinctUntilChanged() 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 new file mode 100644 index 000000000000..389cf76c47ac --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModel.kt @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.keyguard.ui.viewmodel + +import com.android.systemui.doze.util.BurnInHelperWrapper +import com.android.systemui.keyguard.domain.interactor.KeyguardBottomAreaInteractor +import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor +import javax.inject.Inject +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.map + +/** View-model for the keyguard indication area view */ +@OptIn(ExperimentalCoroutinesApi::class) +class KeyguardIndicationAreaViewModel +@Inject +constructor( + private val keyguardInteractor: KeyguardInteractor, + private val bottomAreaInteractor: KeyguardBottomAreaInteractor, + private val keyguardBottomAreaViewModel: KeyguardBottomAreaViewModel, + private val burnInHelperWrapper: BurnInHelperWrapper, +) { + + /** Notifies when a new configuration is set */ + val configurationChange: Flow<Unit> = keyguardInteractor.configurationChange + + /** An observable for the alpha level for the entire bottom area. */ + val alpha: Flow<Float> = keyguardBottomAreaViewModel.alpha + + /** An observable for whether the indication area should be padded. */ + val isIndicationAreaPadded: Flow<Boolean> = + combine(keyguardBottomAreaViewModel.startButton, keyguardBottomAreaViewModel.endButton) { + startButtonModel, + endButtonModel -> + startButtonModel.isVisible || endButtonModel.isVisible + } + .distinctUntilChanged() + /** An observable for the x-offset by which the indication area should be translated. */ + val indicationAreaTranslationX: Flow<Float> = + bottomAreaInteractor.clockPosition.map { it.x.toFloat() }.distinctUntilChanged() + + /** Returns an observable for the y-offset by which the indication area should be translated. */ + fun indicationAreaTranslationY(defaultBurnInOffset: Int): Flow<Float> { + return keyguardInteractor.dozeAmount + .map { dozeAmount -> + dozeAmount * + (burnInHelperWrapper.burnInOffset( + /* amplitude = */ defaultBurnInOffset * 2, + /* xAxis= */ false, + ) - defaultBurnInOffset) + } + .distinctUntilChanged() + } +} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModel.kt index 68810f909016..44e1fd157cf9 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModel.kt @@ -17,10 +17,10 @@ package com.android.systemui.keyguard.ui.viewmodel import com.android.app.animation.Interpolators.EMPHASIZED_ACCELERATE +import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor import com.android.systemui.dagger.SysUISingleton import com.android.systemui.keyguard.domain.interactor.FromPrimaryBouncerTransitionInteractor.Companion.TO_GONE_DURATION import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor -import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerInteractor import com.android.systemui.keyguard.shared.model.ScrimAlpha import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow import com.android.systemui.statusbar.SysuiStatusBarStateController diff --git a/packages/SystemUI/src/com/android/systemui/log/BouncerLogger.kt b/packages/SystemUI/src/com/android/systemui/log/BouncerLogger.kt index 3be4499517b9..3226865d1d82 100644 --- a/packages/SystemUI/src/com/android/systemui/log/BouncerLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/log/BouncerLogger.kt @@ -16,8 +16,8 @@ package com.android.systemui.log +import com.android.systemui.bouncer.shared.model.BouncerMessageModel import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.keyguard.bouncer.shared.model.BouncerMessageModel import com.android.systemui.log.dagger.BouncerLog import javax.inject.Inject diff --git a/packages/SystemUI/src/com/android/systemui/log/ScreenDecorationsLogger.kt b/packages/SystemUI/src/com/android/systemui/log/ScreenDecorationsLogger.kt index f7277842c026..27301e92eca2 100644 --- a/packages/SystemUI/src/com/android/systemui/log/ScreenDecorationsLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/log/ScreenDecorationsLogger.kt @@ -21,9 +21,9 @@ import android.graphics.Rect import android.graphics.RectF import androidx.core.graphics.toRectF import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.log.LogBuffer import com.android.systemui.log.LogLevel.DEBUG import com.android.systemui.log.LogLevel.ERROR +import com.android.systemui.log.LogLevel.INFO import com.android.systemui.log.dagger.ScreenDecorationsLog import com.google.errorprone.annotations.CompileTimeConstant import javax.inject.Inject @@ -164,4 +164,49 @@ constructor( fun cameraProtectionEvent(@CompileTimeConstant cameraProtectionEvent: String) { logBuffer.log(TAG, DEBUG, cameraProtectionEvent) } + + fun logRotationChangeDeferred(currentRot: Int, newRot: Int) { + logBuffer.log( + TAG, + INFO, + { + int1 = currentRot + int2 = newRot + }, + { "Rotation changed, deferring $int2, staying at $int2" }, + ) + } + + fun logRotationChanged(oldRot: Int, newRot: Int) { + logBuffer.log( + TAG, + INFO, + { + int1 = oldRot + int2 = newRot + }, + { "Rotation changed from $int1 to $int2" } + ) + } + + fun logDisplayModeChanged(currentMode: Int, newMode: Int) { + logBuffer.log( + TAG, + INFO, + { + int1 = currentMode + int2 = newMode + }, + { "Resolution changed, deferring mode change to $int2, staying at $int1" }, + ) + } + + fun logUserSwitched(newUser: Int) { + logBuffer.log( + TAG, + DEBUG, + { int1 = newUser }, + { "UserSwitched newUserId=$int1. Updating color inversion setting" }, + ) + } } diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/DisableFlagsRepositoryLog.kt b/packages/SystemUI/src/com/android/systemui/log/dagger/DisableFlagsRepositoryLog.kt new file mode 100644 index 000000000000..e1412919ce46 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/log/dagger/DisableFlagsRepositoryLog.kt @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.systemui.log.dagger + +import com.android.systemui.log.LogBuffer +import javax.inject.Qualifier + +/** A [LogBuffer] for changes to [DisableFlagsRepository]. */ +@Qualifier +@MustBeDocumented +@Retention(AnnotationRetention.RUNTIME) +annotation class DisableFlagsRepositoryLog diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java index 0261ee53a0ff..3497285d6929 100644 --- a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java +++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java @@ -224,6 +224,15 @@ public class LogModule { false /* systrace */); } + /** Provides a logging buffer for the disable flags repository. */ + @Provides + @SysUISingleton + @DisableFlagsRepositoryLog + public static LogBuffer provideDisableFlagsRepositoryLogBuffer(LogBufferFactory factory) { + return factory.create("DisableFlagsRepository", 40 /* maxSize */, + false /* systrace */); + } + /** Provides a logging buffer for logs related to swipe up gestures. */ @Provides @SysUISingleton diff --git a/packages/SystemUI/src/com/android/systemui/log/table/TableLogBufferFactory.kt b/packages/SystemUI/src/com/android/systemui/log/table/TableLogBufferFactory.kt index 19e112487c46..1e2f71f01c35 100644 --- a/packages/SystemUI/src/com/android/systemui/log/table/TableLogBufferFactory.kt +++ b/packages/SystemUI/src/com/android/systemui/log/table/TableLogBufferFactory.kt @@ -61,7 +61,7 @@ constructor( bgDispatcher, coroutineScope, ) - dumpManager.registerNormalDumpable(name, tableBuffer) + dumpManager.registerTableLogBuffer(name, tableBuffer) tableBuffer.init() return tableBuffer } diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java index 1da8718b111f..99c591f25edb 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java @@ -51,7 +51,6 @@ import android.view.IWallpaperVisibilityListener; import android.view.IWindowManager; import android.view.View; import android.view.WindowInsets; -import android.view.WindowManagerGlobal; import android.view.accessibility.AccessibilityManager; import androidx.annotation.NonNull; @@ -69,10 +68,13 @@ import com.android.systemui.settings.DisplayTracker; import com.android.systemui.settings.UserTracker; import com.android.systemui.shared.system.QuickStepContract; import com.android.systemui.statusbar.CommandQueue; +import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.phone.BarTransitions.TransitionMode; import com.android.systemui.statusbar.phone.CentralSurfaces; import com.android.systemui.statusbar.policy.KeyguardStateController; +import dagger.Lazy; + import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; @@ -80,8 +82,6 @@ import java.util.Optional; import javax.inject.Inject; -import dagger.Lazy; - /** * Extracts shared elements between navbar and taskbar delegate to de-dupe logic and help them * experience the joys of friendship. @@ -111,6 +111,7 @@ public final class NavBarHelper implements private final AccessibilityButtonTargetsObserver mAccessibilityButtonTargetsObserver; private final List<NavbarTaskbarStateUpdater> mStateListeners = new ArrayList<>(); private final Context mContext; + private final NotificationShadeWindowController mNotificationShadeWindowController; private final CommandQueue mCommandQueue; private final ContentResolver mContentResolver; private final EdgeBackGestureHandler mEdgeBackGestureHandler; @@ -182,9 +183,11 @@ public final class NavBarHelper implements IWindowManager wm, UserTracker userTracker, DisplayTracker displayTracker, + NotificationShadeWindowController notificationShadeWindowController, DumpManager dumpManager, CommandQueue commandQueue) { mContext = context; + mNotificationShadeWindowController = notificationShadeWindowController; mCommandQueue = commandQueue; mContentResolver = mContext.getContentResolver(); mAccessibilityManager = accessibilityManager; @@ -459,12 +462,8 @@ public final class NavBarHelper implements * @return Whether the IME is shown on top of the screen given the {@code vis} flag of * {@link InputMethodService} and the keyguard states. */ - public boolean isImeShown(int vis) { - View shadeWindowView = null; - if (mCentralSurfacesOptionalLazy.get().isPresent()) { - shadeWindowView = - mCentralSurfacesOptionalLazy.get().get().getNotificationShadeWindowView(); - } + public boolean isImeShown(@InputMethodService.ImeWindowVisibility int vis) { + View shadeWindowView = mNotificationShadeWindowController.getWindowRootView(); boolean isKeyguardShowing = mKeyguardStateController.isShowing(); boolean imeVisibleOnShade = shadeWindowView != null && shadeWindowView.isAttachedToWindow() && shadeWindowView.getRootWindowInsets().isVisible(WindowInsets.Type.ime()); diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java index 682335e0b419..5bae1cba4ac4 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java @@ -66,6 +66,7 @@ import android.graphics.Point; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.Region; +import android.inputmethodservice.InputMethodService; import android.os.Binder; import android.os.Bundle; import android.os.Handler; @@ -1047,8 +1048,9 @@ public class NavigationBar extends ViewController<NavigationBarView> implements // ----- CommandQueue Callbacks ----- @Override - public void setImeWindowStatus(int displayId, IBinder token, int vis, int backDisposition, - boolean showImeSwitcher) { + public void setImeWindowStatus(int displayId, IBinder token, + @InputMethodService.ImeWindowVisibility int vis, + @InputMethodService.BackDispositionMode int backDisposition, boolean showImeSwitcher) { if (displayId != mDisplayId) { return; } diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java index 3b32313e76a0..cecf043c572e 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java @@ -338,8 +338,9 @@ public class TaskbarDelegate implements CommandQueue.Callbacks, } @Override - public void setImeWindowStatus(int displayId, IBinder token, int vis, int backDisposition, - boolean showImeSwitcher) { + public void setImeWindowStatus(int displayId, IBinder token, + @InputMethodService.ImeWindowVisibility int vis, + @InputMethodService.BackDispositionMode int backDisposition, boolean showImeSwitcher) { boolean imeShown = mNavBarHelper.isImeShown(vis); if (!imeShown) { // Count imperceptible changes as visible so we transition taskbar out quickly. diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java index 8b3d7a653232..7b86d0a6ebce 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java @@ -15,11 +15,13 @@ */ package com.android.systemui.navigationbar.gestural; +import static android.view.InputDevice.SOURCE_MOUSE; +import static android.view.InputDevice.SOURCE_TOUCHPAD; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_EXCLUDE_FROM_SCREEN_MAGNIFICATION; import static com.android.systemui.classifier.Classifier.BACK_GESTURE; -import static com.android.systemui.navigationbar.gestural.Utilities.isTrackpadFourFingerSwipe; -import static com.android.systemui.navigationbar.gestural.Utilities.isTrackpadMultiFingerSwipe; +import static com.android.systemui.navigationbar.gestural.Utilities.isTrackpadScroll; +import static com.android.systemui.navigationbar.gestural.Utilities.isTrackpadThreeFingerSwipe; import android.annotation.NonNull; import android.app.ActivityManager; @@ -37,6 +39,7 @@ import android.graphics.Rect; import android.graphics.Region; import android.hardware.input.InputManager; import android.icu.text.SimpleDateFormat; +import android.os.Handler; import android.os.Looper; import android.os.RemoteException; import android.os.SystemClock; @@ -195,6 +198,7 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack private final ViewConfiguration mViewConfiguration; private final WindowManager mWindowManager; private final IWindowManager mWindowManagerService; + private final InputManager mInputManager; private final Optional<Pip> mPipOptional; private final Optional<DesktopMode> mDesktopModeOptional; private final FalsingManager mFalsingManager; @@ -206,6 +210,7 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack private final int mDisplayId; private final Executor mMainExecutor; + private final Handler mMainHandler; private final Executor mBackgroundExecutor; private final Rect mPipExcludedBounds = new Rect(); @@ -249,13 +254,17 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack private boolean mDeferSetIsOnLeftEdge; private boolean mIsAttached; - private boolean mIsGesturalModeEnabled; + private boolean mIsGestureHandlingEnabled; + private boolean mIsTrackpadConnected; + private boolean mInGestureNavMode; + private boolean mUsingThreeButtonNav; private boolean mIsEnabled; private boolean mIsNavBarShownTransiently; private boolean mIsBackGestureAllowed; private boolean mGestureBlockingActivityRunning; private boolean mIsNewBackAffordanceEnabled; private boolean mIsTrackpadGestureFeaturesEnabled; + private boolean mIsTrackpadThreeFingerSwipe; private boolean mIsButtonForcedVisible; private InputMonitor mInputMonitor; @@ -349,12 +358,48 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack } }; + private final InputManager.InputDeviceListener mInputDeviceListener = + new InputManager.InputDeviceListener() { + + // Only one trackpad can be connected to a device at a time, since it takes over the + // only USB port. + private int mTrackpadDeviceId; + + @Override + public void onInputDeviceAdded(int deviceId) { + if (isTrackpadDevice(deviceId)) { + mTrackpadDeviceId = deviceId; + update(true /* isTrackpadConnected */); + } + } + + @Override + public void onInputDeviceChanged(int deviceId) { } + + @Override + public void onInputDeviceRemoved(int deviceId) { + if (mTrackpadDeviceId == deviceId) { + update(false /* isTrackpadConnected */); + } + } + + private void update(boolean isTrackpadConnected) { + boolean isPreviouslyTrackpadConnected = mIsTrackpadConnected; + mIsTrackpadConnected = isTrackpadConnected; + if (isPreviouslyTrackpadConnected != mIsTrackpadConnected) { + updateIsEnabled(); + updateCurrentUserResources(); + } + } + }; + EdgeBackGestureHandler( Context context, OverviewProxyService overviewProxyService, SysUiState sysUiState, PluginManager pluginManager, @Main Executor executor, + @Main Handler handler, @Background Executor backgroundExecutor, UserTracker userTracker, ProtoTracer protoTracer, @@ -363,6 +408,7 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack ViewConfiguration viewConfiguration, WindowManager windowManager, IWindowManager windowManagerService, + InputManager inputManager, Optional<Pip> pipOptional, Optional<DesktopMode> desktopModeOptional, FalsingManager falsingManager, @@ -373,6 +419,7 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack mContext = context; mDisplayId = context.getDisplayId(); mMainExecutor = executor; + mMainHandler = handler; mBackgroundExecutor = backgroundExecutor; mUserTracker = userTracker; mOverviewProxyService = overviewProxyService; @@ -384,6 +431,7 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack mViewConfiguration = viewConfiguration; mWindowManager = windowManager; mWindowManagerService = windowManagerService; + mInputManager = inputManager; mPipOptional = pipOptional; mDesktopModeOptional = desktopModeOptional; mFalsingManager = falsingManager; @@ -392,6 +440,8 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack mFeatureFlags = featureFlags; mLightBarControllerProvider = lightBarControllerProvider; mLastReportedConfig.setTo(mContext.getResources().getConfiguration()); + mIsTrackpadGestureFeaturesEnabled = mFeatureFlags.isEnabled( + Flags.TRACKPAD_GESTURE_FEATURES); ComponentName recentsComponentName = ComponentName.unflattenFromString( context.getString(com.android.internal.R.string.config_recentsComponentName)); if (recentsComponentName != null) { @@ -423,7 +473,7 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack ViewConfiguration.getLongPressTimeout()); mGestureNavigationSettingsObserver = new GestureNavigationSettingsObserver( - mContext.getMainThreadHandler(), mContext, this::onNavigationSettingsChanged); + mMainHandler, mContext, this::onNavigationSettingsChanged); updateCurrentUserResources(); } @@ -510,6 +560,13 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack mProtoTracer.add(this); mOverviewProxyService.addCallback(mQuickSwitchListener); mSysUiState.addCallback(mSysUiStateCallback); + if (mIsTrackpadGestureFeaturesEnabled) { + mInputManager.registerInputDeviceListener(mInputDeviceListener, mMainHandler); + int [] inputDevices = mInputManager.getInputDeviceIds(); + for (int inputDeviceId : inputDevices) { + mInputDeviceListener.onInputDeviceAdded(inputDeviceId); + } + } updateIsEnabled(); mUserTracker.addCallback(mUserChangedCallback, mMainExecutor); } @@ -522,6 +579,7 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack mProtoTracer.remove(this); mOverviewProxyService.removeCallback(mQuickSwitchListener); mSysUiState.removeCallback(mSysUiStateCallback); + mInputManager.unregisterInputDeviceListener(mInputDeviceListener); updateIsEnabled(); mUserTracker.removeCallback(mUserChangedCallback); } @@ -530,7 +588,8 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack * @see NavigationModeController.ModeChangedListener#onNavigationModeChanged */ public void onNavigationModeChanged(int mode) { - mIsGesturalModeEnabled = QuickStepContract.isGesturalMode(mode); + mUsingThreeButtonNav = QuickStepContract.isLegacyMode(mode); + mInGestureNavMode = QuickStepContract.isGesturalMode(mode); updateIsEnabled(); updateCurrentUserResources(); } @@ -553,81 +612,81 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack private void updateIsEnabled() { try { Trace.beginSection("EdgeBackGestureHandler#updateIsEnabled"); - updateIsEnabledTraced(); - } finally { - Trace.endSection(); - } - } - private void updateIsEnabledTraced() { - boolean isEnabled = mIsAttached && mIsGesturalModeEnabled; - if (isEnabled == mIsEnabled) { - return; - } - mIsEnabled = isEnabled; - disposeInputChannel(); - - if (mEdgeBackPlugin != null) { - mEdgeBackPlugin.onDestroy(); - mEdgeBackPlugin = null; - } - - if (!mIsEnabled) { - mGestureNavigationSettingsObserver.unregister(); - if (DEBUG_MISSING_GESTURE) { - Log.d(DEBUG_MISSING_GESTURE_TAG, "Unregister display listener"); + mIsGestureHandlingEnabled = + mInGestureNavMode || (mIsTrackpadGestureFeaturesEnabled && mUsingThreeButtonNav + && mIsTrackpadConnected); + boolean isEnabled = mIsAttached && mIsGestureHandlingEnabled; + if (isEnabled == mIsEnabled) { + return; } - mPluginManager.removePluginListener(this); - TaskStackChangeListeners.getInstance().unregisterTaskStackListener(mTaskStackListener); - DeviceConfig.removeOnPropertiesChangedListener(mOnPropertiesChangedListener); - mPipOptional.ifPresent(pip -> pip.setOnIsInPipStateChangedListener(null)); + mIsEnabled = isEnabled; + disposeInputChannel(); - try { - mWindowManagerService.unregisterSystemGestureExclusionListener( - mGestureExclusionListener, mDisplayId); - } catch (RemoteException | IllegalArgumentException e) { - Log.e(TAG, "Failed to unregister window manager callbacks", e); + if (mEdgeBackPlugin != null) { + mEdgeBackPlugin.onDestroy(); + mEdgeBackPlugin = null; } - } else { - mGestureNavigationSettingsObserver.register(); - updateDisplaySize(); - if (DEBUG_MISSING_GESTURE) { - Log.d(DEBUG_MISSING_GESTURE_TAG, "Register display listener"); - } - TaskStackChangeListeners.getInstance().registerTaskStackListener(mTaskStackListener); - DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_SYSTEMUI, - mMainExecutor::execute, mOnPropertiesChangedListener); - mPipOptional.ifPresent( - pip -> pip.setOnIsInPipStateChangedListener(mOnIsInPipStateChangedListener)); - mDesktopModeOptional.ifPresent( - dm -> dm.addDesktopGestureExclusionRegionListener( - mDesktopCornersChangedListener, mMainExecutor)); + if (!mIsEnabled) { + mGestureNavigationSettingsObserver.unregister(); + if (DEBUG_MISSING_GESTURE) { + Log.d(DEBUG_MISSING_GESTURE_TAG, "Unregister display listener"); + } + mPluginManager.removePluginListener(this); + TaskStackChangeListeners.getInstance().unregisterTaskStackListener( + mTaskStackListener); + DeviceConfig.removeOnPropertiesChangedListener(mOnPropertiesChangedListener); + mPipOptional.ifPresent(pip -> pip.setOnIsInPipStateChangedListener(null)); + + try { + mWindowManagerService.unregisterSystemGestureExclusionListener( + mGestureExclusionListener, mDisplayId); + } catch (RemoteException | IllegalArgumentException e) { + Log.e(TAG, "Failed to unregister window manager callbacks", e); + } - try { - mWindowManagerService.registerSystemGestureExclusionListener( - mGestureExclusionListener, mDisplayId); - } catch (RemoteException | IllegalArgumentException e) { - Log.e(TAG, "Failed to register window manager callbacks", e); - } + } else { + mGestureNavigationSettingsObserver.register(); + updateDisplaySize(); + if (DEBUG_MISSING_GESTURE) { + Log.d(DEBUG_MISSING_GESTURE_TAG, "Register display listener"); + } + TaskStackChangeListeners.getInstance().registerTaskStackListener( + mTaskStackListener); + DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_SYSTEMUI, + mMainExecutor::execute, mOnPropertiesChangedListener); + mPipOptional.ifPresent(pip -> pip.setOnIsInPipStateChangedListener( + mOnIsInPipStateChangedListener)); + mDesktopModeOptional.ifPresent( + dm -> dm.addDesktopGestureExclusionRegionListener( + mDesktopCornersChangedListener, mMainExecutor)); + + try { + mWindowManagerService.registerSystemGestureExclusionListener( + mGestureExclusionListener, mDisplayId); + } catch (RemoteException | IllegalArgumentException e) { + Log.e(TAG, "Failed to register window manager callbacks", e); + } - // Register input event receiver - mInputMonitor = mContext.getSystemService(InputManager.class).monitorGestureInput( - "edge-swipe", mDisplayId); - mInputEventReceiver = new InputChannelCompat.InputEventReceiver( - mInputMonitor.getInputChannel(), Looper.getMainLooper(), - Choreographer.getInstance(), this::onInputEvent); - - // Add a nav bar panel window - mIsNewBackAffordanceEnabled = mFeatureFlags.isEnabled(Flags.NEW_BACK_AFFORDANCE); - mIsTrackpadGestureFeaturesEnabled = mFeatureFlags.isEnabled( - Flags.TRACKPAD_GESTURE_FEATURES); - resetEdgeBackPlugin(); - mPluginManager.addPluginListener( - this, NavigationEdgeBackPlugin.class, /*allowMultiple=*/ false); + // Register input event receiver + mInputMonitor = mContext.getSystemService(InputManager.class).monitorGestureInput( + "edge-swipe", mDisplayId); + mInputEventReceiver = new InputChannelCompat.InputEventReceiver( + mInputMonitor.getInputChannel(), Looper.getMainLooper(), + Choreographer.getInstance(), this::onInputEvent); + + // Add a nav bar panel window + mIsNewBackAffordanceEnabled = mFeatureFlags.isEnabled(Flags.NEW_BACK_AFFORDANCE); + resetEdgeBackPlugin(); + mPluginManager.addPluginListener( + this, NavigationEdgeBackPlugin.class, /*allowMultiple=*/ false); + } + // Update the ML model resources. + updateMLModelState(); + } finally { + Trace.endSection(); } - // Update the ML model resources. - updateMLModelState(); } @Override @@ -704,9 +763,9 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack } private void updateMLModelState() { - boolean newState = - mIsGesturalModeEnabled && DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI, - SystemUiDeviceConfigFlags.USE_BACK_GESTURE_ML_MODEL, false); + boolean newState = mIsGestureHandlingEnabled && DeviceConfig.getBoolean( + DeviceConfig.NAMESPACE_SYSTEMUI, + SystemUiDeviceConfigFlags.USE_BACK_GESTURE_ML_MODEL, false); if (newState == mUseMLModel) { return; @@ -825,6 +884,15 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack mDisplaySize.y - insets.bottom); } + private boolean isTrackpadDevice(int deviceId) { + InputDevice inputDevice = mInputManager.getInputDevice(deviceId); + if (inputDevice == null) { + return false; + } + return inputDevice.getSources() == (InputDevice.SOURCE_MOUSE + | InputDevice.SOURCE_TOUCHPAD); + } + private boolean desktopExcludeRegionContains(int x, int y) { return mDesktopModeExcludeRegion.contains(x, y); } @@ -919,18 +987,21 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack (int) mEndPoint.x, (int) mEndPoint.y, mEdgeWidthLeft + mLeftInset, mDisplaySize.x - (mEdgeWidthRight + mRightInset), - mUseMLModel ? mMLResults : -2, logPackageName); + mUseMLModel ? mMLResults : -2, logPackageName, + mIsTrackpadThreeFingerSwipe ? SysUiStatsLog.BACK_GESTURE__INPUT_TYPE__TRACKPAD + : SysUiStatsLog.BACK_GESTURE__INPUT_TYPE__TOUCH); } private void onMotionEvent(MotionEvent ev) { int action = ev.getActionMasked(); - boolean isTrackpadMultiFingerSwipe = isTrackpadMultiFingerSwipe( - mIsTrackpadGestureFeaturesEnabled, ev); if (action == MotionEvent.ACTION_DOWN) { if (DEBUG_MISSING_GESTURE) { Log.d(DEBUG_MISSING_GESTURE_TAG, "Start gesture: " + ev); } + mIsTrackpadThreeFingerSwipe = isTrackpadThreeFingerSwipe( + mIsTrackpadGestureFeaturesEnabled, ev); + // ACTION_UP or ACTION_CANCEL is not guaranteed to be called before a new // ACTION_DOWN, in that case we should just reuse the old instance. mVelocityTracker.clear(); @@ -938,7 +1009,7 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack // Verify if this is in within the touch region and we aren't in immersive mode, and // either the bouncer is showing or the notification panel is hidden mInputEventReceiver.setBatchingEnabled(false); - if (isTrackpadMultiFingerSwipe) { + if (mIsTrackpadThreeFingerSwipe) { // Since trackpad gestures don't have zones, this will be determined later by the // direction of the gesture. {@code mIsOnLeftEdge} is set to false to begin with. mDeferSetIsOnLeftEdge = true; @@ -950,20 +1021,26 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack mLogGesture = false; mInRejectedExclusion = false; boolean isWithinInsets = isWithinInsets((int) ev.getX(), (int) ev.getY()); - // Trackpad back gestures don't have zones, so we don't need to check if the down event - // is within insets. - mAllowGesture = !mDisabledForQuickstep && mIsBackGestureAllowed - && (isTrackpadMultiFingerSwipe || isWithinInsets) + boolean isBackAllowedCommon = !mDisabledForQuickstep && mIsBackGestureAllowed && !mGestureBlockingActivityRunning && !QuickStepContract.isBackGestureDisabled(mSysUiFlags) - && (isValidTrackpadBackGesture(isTrackpadMultiFingerSwipe) - || isWithinTouchRegion((int) ev.getX(), (int) ev.getY())); + && !isTrackpadScroll(mIsTrackpadGestureFeaturesEnabled, ev); + if (mIsTrackpadThreeFingerSwipe) { + // Trackpad back gestures don't have zones, so we don't need to check if the down + // event is within insets. + mAllowGesture = isBackAllowedCommon && isValidTrackpadBackGesture( + true /* isTrackpadEvent */); + } else { + mAllowGesture = isBackAllowedCommon && !mUsingThreeButtonNav && isWithinInsets + && isWithinTouchRegion((int) ev.getX(), (int) ev.getY()) + && !isButtonPressFromTrackpad(ev); + } if (mAllowGesture) { mEdgeBackPlugin.setIsLeftPanel(mIsOnLeftEdge); mEdgeBackPlugin.onMotionEvent(ev); dispatchToBackAnimation(ev); } - if (mLogGesture || isTrackpadMultiFingerSwipe) { + if (mLogGesture || mIsTrackpadThreeFingerSwipe) { mDownPoint.set(ev.getX(), ev.getY()); mEndPoint.set(-1, -1); mThresholdCrossed = false; @@ -974,10 +1051,10 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack mTmpLogDate.setTime(curTime); String curTimeStr = mLogDateFormat.format(mTmpLogDate); (isWithinInsets ? mGestureLogInsideInsets : mGestureLogOutsideInsets).log(String.format( - "Gesture [%d [%s],alw=%B, mltf=%B, left=%B, defLeft=%B, backAlw=%B, disbld=%B," + "Gesture [%d [%s],alw=%B, t3fs=%B, left=%B, defLeft=%B, backAlw=%B, disbld=%B," + " qsDisbld=%b, blkdAct=%B, pip=%B," + " disp=%s, wl=%d, il=%d, wr=%d, ir=%d, excl=%s]", - curTime, curTimeStr, mAllowGesture, isTrackpadMultiFingerSwipe, + curTime, curTimeStr, mAllowGesture, mIsTrackpadThreeFingerSwipe, mIsOnLeftEdge, mDeferSetIsOnLeftEdge, mIsBackGestureAllowed, QuickStepContract.isBackGestureDisabled(mSysUiFlags), mDisabledForQuickstep, mGestureBlockingActivityRunning, mIsInPip, mDisplaySize, @@ -986,8 +1063,7 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack if (!mThresholdCrossed) { mEndPoint.x = (int) ev.getX(); mEndPoint.y = (int) ev.getY(); - if (action == MotionEvent.ACTION_POINTER_DOWN && (!isTrackpadMultiFingerSwipe - || isTrackpadFourFingerSwipe(mIsTrackpadGestureFeaturesEnabled, ev))) { + if (action == MotionEvent.ACTION_POINTER_DOWN && !mIsTrackpadThreeFingerSwipe) { if (mAllowGesture) { logGesture(SysUiStatsLog.BACK_GESTURE__TYPE__INCOMPLETE_MULTI_TOUCH); if (DEBUG_MISSING_GESTURE) { @@ -999,11 +1075,7 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack mLogGesture = false; return; } else if (action == MotionEvent.ACTION_MOVE) { - if (isTrackpadFourFingerSwipe(isTrackpadMultiFingerSwipe, ev)) { - cancelGesture(ev); - return; - } - if (isTrackpadMultiFingerSwipe && mDeferSetIsOnLeftEdge) { + if (mIsTrackpadThreeFingerSwipe && mDeferSetIsOnLeftEdge) { // mIsOnLeftEdge is determined by the relative position between the down // and the current motion event for trackpad gestures instead of zoning. mIsOnLeftEdge = mEndPoint.x > mDownPoint.x; @@ -1065,6 +1137,13 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack mProtoTracer.scheduleFrameUpdate(); } + private boolean isButtonPressFromTrackpad(MotionEvent ev) { + // We don't allow back for button press from the trackpad, and yet we do with a mouse. + int sources = InputManager.getInstance().getInputDevice(ev.getDeviceId()).getSources(); + int sourceTrackpad = (SOURCE_MOUSE | SOURCE_TOUCHPAD); + return (sources & sourceTrackpad) == sourceTrackpad && ev.getButtonState() != 0; + } + private void dispatchToBackAnimation(MotionEvent event) { if (mBackAnimation != null) { mVelocityTracker.addMovement(event); @@ -1159,7 +1238,7 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack pw.println(" mIsEnabled=" + mIsEnabled); pw.println(" mIsAttached=" + mIsAttached); pw.println(" mIsBackGestureAllowed=" + mIsBackGestureAllowed); - pw.println(" mIsGesturalModeEnabled=" + mIsGesturalModeEnabled); + pw.println(" mIsGestureHandlingEnabled=" + mIsGestureHandlingEnabled); pw.println(" mIsNavBarShownTransiently=" + mIsNavBarShownTransiently); pw.println(" mGestureBlockingActivityRunning=" + mGestureBlockingActivityRunning); pw.println(" mAllowGesture=" + mAllowGesture); @@ -1184,6 +1263,8 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack pw.println(" mPredictionLog=" + String.join("\n", mPredictionLog)); pw.println(" mGestureLogInsideInsets=" + String.join("\n", mGestureLogInsideInsets)); pw.println(" mGestureLogOutsideInsets=" + String.join("\n", mGestureLogOutsideInsets)); + pw.println(" mIsTrackpadConnected=" + mIsTrackpadConnected); + pw.println(" mUsingThreeButtonNav=" + mUsingThreeButtonNav); pw.println(" mEdgeBackPlugin=" + mEdgeBackPlugin); if (mEdgeBackPlugin != null) { mEdgeBackPlugin.dump(pw); @@ -1233,6 +1314,7 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack private final SysUiState mSysUiState; private final PluginManager mPluginManager; private final Executor mExecutor; + private final Handler mHandler; private final Executor mBackgroundExecutor; private final UserTracker mUserTracker; private final ProtoTracer mProtoTracer; @@ -1241,6 +1323,7 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack private final ViewConfiguration mViewConfiguration; private final WindowManager mWindowManager; private final IWindowManager mWindowManagerService; + private final InputManager mInputManager; private final Optional<Pip> mPipOptional; private final Optional<DesktopMode> mDesktopModeOptional; private final FalsingManager mFalsingManager; @@ -1255,6 +1338,7 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack SysUiState sysUiState, PluginManager pluginManager, @Main Executor executor, + @Main Handler handler, @Background Executor backgroundExecutor, UserTracker userTracker, ProtoTracer protoTracer, @@ -1263,6 +1347,7 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack ViewConfiguration viewConfiguration, WindowManager windowManager, IWindowManager windowManagerService, + InputManager inputManager, Optional<Pip> pipOptional, Optional<DesktopMode> desktopModeOptional, FalsingManager falsingManager, @@ -1275,6 +1360,7 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack mSysUiState = sysUiState; mPluginManager = pluginManager; mExecutor = executor; + mHandler = handler; mBackgroundExecutor = backgroundExecutor; mUserTracker = userTracker; mProtoTracer = protoTracer; @@ -1283,6 +1369,7 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack mViewConfiguration = viewConfiguration; mWindowManager = windowManager; mWindowManagerService = windowManagerService; + mInputManager = inputManager; mPipOptional = pipOptional; mDesktopModeOptional = desktopModeOptional; mFalsingManager = falsingManager; @@ -1300,6 +1387,7 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack mSysUiState, mPluginManager, mExecutor, + mHandler, mBackgroundExecutor, mUserTracker, mProtoTracer, @@ -1308,6 +1396,7 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack mViewConfiguration, mWindowManager, mWindowManagerService, + mInputManager, mPipOptional, mDesktopModeOptional, mFalsingManager, diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/Utilities.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/Utilities.java index 50e8aa7b2046..10a88c8b4839 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/Utilities.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/Utilities.java @@ -16,27 +16,24 @@ package com.android.systemui.navigationbar.gestural; +import static android.view.MotionEvent.AXIS_GESTURE_SWIPE_FINGER_COUNT; import static android.view.MotionEvent.CLASSIFICATION_MULTI_FINGER_SWIPE; +import static android.view.MotionEvent.CLASSIFICATION_TWO_FINGER_SWIPE; import android.view.MotionEvent; public final class Utilities { - public static boolean isTrackpadMultiFingerSwipe(boolean isTrackpadGestureFeaturesEnabled, + public static boolean isTrackpadScroll(boolean isTrackpadGestureFeaturesEnabled, MotionEvent event) { return isTrackpadGestureFeaturesEnabled - && event.getClassification() == CLASSIFICATION_MULTI_FINGER_SWIPE; + && event.getClassification() == CLASSIFICATION_TWO_FINGER_SWIPE; } public static boolean isTrackpadThreeFingerSwipe(boolean isTrackpadGestureFeaturesEnabled, MotionEvent event) { - return isTrackpadMultiFingerSwipe(isTrackpadGestureFeaturesEnabled, event) - && event.getPointerCount() == 3; - } - - public static boolean isTrackpadFourFingerSwipe(boolean isTrackpadGestureFeaturesEnabled, - MotionEvent event) { - return isTrackpadMultiFingerSwipe(isTrackpadGestureFeaturesEnabled, event) - && event.getPointerCount() == 4; + return isTrackpadGestureFeaturesEnabled + && event.getClassification() == CLASSIFICATION_MULTI_FINGER_SWIPE + && event.getAxisValue(AXIS_GESTURE_SWIPE_FINGER_COUNT) == 3; } } diff --git a/packages/SystemUI/src/com/android/systemui/notetask/INoteTaskBubblesService.aidl b/packages/SystemUI/src/com/android/systemui/notetask/INoteTaskBubblesService.aidl new file mode 100644 index 000000000000..3e947d9d2b16 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/notetask/INoteTaskBubblesService.aidl @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.notetask; + +import android.content.Intent; +import android.graphics.drawable.Icon; +import android.os.UserHandle; + +/** A service to help with controlling the state of notes app bubble through the system user. */ +interface INoteTaskBubblesService { + + boolean areBubblesAvailable(); + + void showOrHideAppBubble(in Intent intent, in UserHandle userHandle, in Icon icon); +} diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskBubblesController.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskBubblesController.kt new file mode 100644 index 000000000000..ec205f87d9fa --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskBubblesController.kt @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.notetask + +import android.app.Service +import android.content.Context +import android.content.Intent +import android.graphics.drawable.Icon +import android.os.IBinder +import android.os.UserHandle +import com.android.internal.infra.ServiceConnector +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.dagger.qualifiers.Background +import com.android.systemui.log.DebugLogger.debugLog +import com.android.wm.shell.bubbles.Bubbles +import java.util.Optional +import javax.inject.Inject +import kotlin.coroutines.resume +import kotlin.coroutines.suspendCoroutine +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.withContext + +/** + * A utility class to help interact with [Bubbles] as system user. The SysUI instance running as + * system user is the only instance that has the instance of [Bubbles] that manages the notes app + * bubble for all users. + * + * <p>Note: This class is made overridable so that a fake can be created for as mocking suspending + * functions is not supported by the Android tree's version of mockito. + */ +@SysUISingleton +open class NoteTaskBubblesController +@Inject +constructor( + @Application private val context: Context, + @Background private val bgDispatcher: CoroutineDispatcher +) { + + private val serviceConnector: ServiceConnector<INoteTaskBubblesService> = + ServiceConnector.Impl( + context, + Intent(context, NoteTaskBubblesService::class.java), + Context.BIND_AUTO_CREATE or Context.BIND_WAIVE_PRIORITY or Context.BIND_NOT_VISIBLE, + UserHandle.USER_SYSTEM, + INoteTaskBubblesService.Stub::asInterface + ) + + /** Returns whether notes app bubble is supported. */ + open suspend fun areBubblesAvailable(): Boolean = + withContext(bgDispatcher) { + suspendCoroutine { continuation -> + serviceConnector + .postForResult { it.areBubblesAvailable() } + .whenComplete { available, error -> + if (error != null) { + debugLog(error = error) { "Failed to query Bubbles as system user." } + } + continuation.resume(available ?: false) + } + } + } + + /** Calls the [Bubbles.showOrHideAppBubble] API as [UserHandle.USER_SYSTEM]. */ + open suspend fun showOrHideAppBubble( + intent: Intent, + userHandle: UserHandle, + icon: Icon + ) { + withContext(bgDispatcher) { + serviceConnector + .post { it.showOrHideAppBubble(intent, userHandle, icon) } + .whenComplete { _, error -> + if (error != null) { + debugLog(error = error) { + "Failed to show notes app bubble for intent $intent, " + + "user $userHandle, and icon $icon." + } + } else { + debugLog { + "Call to show notes app bubble for intent $intent, " + + "user $userHandle, and icon $icon successful." + } + } + } + } + } + + /** + * A helper service to call [Bubbles] APIs that should always be called from the system user + * instance of SysUI. + * + * <p>Note: This service always runs in the SysUI process running on the system user + * irrespective of which user started the service. This is required so that the correct instance + * of {@link Bubbles} is injected. This is set via attribute {@code android:singleUser=”true”} + * in AndroidManifest. + */ + class NoteTaskBubblesService + @Inject + constructor(private val mOptionalBubbles: Optional<Bubbles>) : Service() { + + override fun onBind(intent: Intent): IBinder { + return object : INoteTaskBubblesService.Stub() { + override fun areBubblesAvailable() = mOptionalBubbles.isPresent + + override fun showOrHideAppBubble( + intent: Intent, + userHandle: UserHandle, + icon: Icon + ) { + mOptionalBubbles.ifPresentOrElse( + { bubbles -> bubbles.showOrHideAppBubble(intent, userHandle, icon) }, + { + debugLog { + "Failed to show or hide bubble for intent $intent," + + "user $user, and icon $icon as bubble is empty." + } + } + ) + } + } + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt index ccfbaf1da7b6..48790c23e688 100644 --- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt +++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt @@ -33,25 +33,26 @@ import android.content.pm.ShortcutManager import android.graphics.drawable.Icon import android.os.UserHandle import android.os.UserManager +import android.provider.Settings import android.widget.Toast import androidx.annotation.VisibleForTesting import com.android.systemui.R import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.devicepolicy.areKeyguardShortcutsDisabled import com.android.systemui.log.DebugLogger.debugLog import com.android.systemui.notetask.NoteTaskRoleManagerExt.createNoteShortcutInfoAsUser import com.android.systemui.notetask.NoteTaskRoleManagerExt.getDefaultRoleHolderAsUser import com.android.systemui.notetask.shortcut.CreateNoteTaskShortcutActivity -import com.android.systemui.notetask.shortcut.LaunchNoteTaskManagedProfileProxyActivity import com.android.systemui.settings.UserTracker import com.android.systemui.shared.system.ActivityManagerKt.isInForeground -import com.android.systemui.util.kotlin.getOrNull +import com.android.systemui.util.settings.SecureSettings import com.android.wm.shell.bubbles.Bubble -import com.android.wm.shell.bubbles.Bubbles import com.android.wm.shell.bubbles.Bubbles.BubbleExpandListener -import java.util.Optional import java.util.concurrent.atomic.AtomicReference import javax.inject.Inject +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch /** * Entry point for creating and managing note. @@ -69,13 +70,15 @@ constructor( private val shortcutManager: ShortcutManager, private val resolver: NoteTaskInfoResolver, private val eventLogger: NoteTaskEventLogger, - private val optionalBubbles: Optional<Bubbles>, + private val noteTaskBubblesController: NoteTaskBubblesController, private val userManager: UserManager, private val keyguardManager: KeyguardManager, private val activityManager: ActivityManager, @NoteTaskEnabledKey private val isEnabled: Boolean, private val devicePolicyManager: DevicePolicyManager, private val userTracker: UserTracker, + private val secureSettings: SecureSettings, + @Application private val applicationScope: CoroutineScope ) { @VisibleForTesting val infoReference = AtomicReference<NoteTaskInfo?>() @@ -100,18 +103,6 @@ constructor( } } - /** Starts [LaunchNoteTaskProxyActivity] on the given [user]. */ - fun startNoteTaskProxyActivityForUser(user: UserHandle) { - context.startActivityAsUser( - Intent().apply { - component = - ComponentName(context, LaunchNoteTaskManagedProfileProxyActivity::class.java) - addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) - }, - user - ) - } - /** Starts the notes role setting. */ fun startNotesRoleSetting(activityContext: Context, entryPoint: NoteTaskEntryPoint?) { val user = @@ -146,7 +137,7 @@ constructor( userTracker.userProfiles.firstOrNull { userManager.isManagedProfile(it.id) }?.userHandle ?: userTracker.userHandle } else { - userTracker.userHandle + secureSettings.preferredUser } /** @@ -175,7 +166,19 @@ constructor( ) { if (!isEnabled) return - val bubbles = optionalBubbles.getOrNull() ?: return + applicationScope.launch { awaitShowNoteTaskAsUser(entryPoint, user) } + } + + private suspend fun awaitShowNoteTaskAsUser( + entryPoint: NoteTaskEntryPoint, + user: UserHandle, + ) { + if (!isEnabled) return + + if (!noteTaskBubblesController.areBubblesAvailable()) { + debugLog { "Bubbles not available in the system user SysUI instance" } + return + } // TODO(b/249954038): We should handle direct boot (isUserUnlocked). For now, we do nothing. if (!userManager.isUserUnlocked) return @@ -210,7 +213,7 @@ constructor( val intent = createNoteTaskIntent(info) val icon = Icon.createWithResource(context, R.drawable.ic_note_task_shortcut_widget) - bubbles.showOrHideAppBubble(intent, user, icon) + noteTaskBubblesController.showOrHideAppBubble(intent, user, icon) // App bubble logging happens on `onBubbleExpandChanged`. debugLog { "onShowNoteTask - opened as app bubble: $info" } } @@ -284,15 +287,55 @@ constructor( } /** + * Like [updateNoteTaskAsUser] but automatically apply to the current user and all its work + * profiles. + * + * @see updateNoteTaskAsUser + * @see UserTracker.userHandle + * @see UserTracker.userProfiles + */ + fun updateNoteTaskForCurrentUserAndManagedProfiles() { + updateNoteTaskAsUser(userTracker.userHandle) + for (profile in userTracker.userProfiles) { + if (userManager.isManagedProfile(profile.id)) { + updateNoteTaskAsUser(profile.userHandle) + } + } + } + + /** * Updates all [NoteTaskController] related information, including but not exclusively the * widget shortcut created by the [user] - by default it will use the current user. * + * If the user is not current user, the update will be dispatched to run in that user's process. + * * Keep in mind the shortcut API has a * [rate limiting](https://developer.android.com/develop/ui/views/launch/shortcuts/managing-shortcuts#rate-limiting) * and may not be updated in real-time. To reduce the chance of stale shortcuts, we run the * function during System UI initialization. */ fun updateNoteTaskAsUser(user: UserHandle) { + if (!userManager.isUserUnlocked(user)) { + debugLog { "updateNoteTaskAsUser call but user locked: user=$user" } + return + } + + if (user == userTracker.userHandle) { + updateNoteTaskAsUserInternal(user) + } else { + // TODO(b/278729185): Replace fire and forget service with a bounded service. + val intent = NoteTaskControllerUpdateService.createIntent(context) + context.startServiceAsUser(intent, user) + } + } + + @InternalNoteTaskApi + fun updateNoteTaskAsUserInternal(user: UserHandle) { + if (!userManager.isUserUnlocked(user)) { + debugLog { "updateNoteTaskAsUserInternal call but user locked: user=$user" } + return + } + val packageName = roleManager.getDefaultRoleHolderAsUser(ROLE_NOTES, user) val hasNotesRoleHolder = isEnabled && !packageName.isNullOrEmpty() @@ -310,20 +353,20 @@ constructor( /** @see OnRoleHoldersChangedListener */ fun onRoleHoldersChanged(roleName: String, user: UserHandle) { if (roleName != ROLE_NOTES) return - if (!userManager.isUserUnlocked(user)) { - debugLog { "onRoleHoldersChanged call but user locked: role=$roleName, user=$user" } - return - } - if (user == userTracker.userHandle) { - updateNoteTaskAsUser(user) - } else { - // TODO(b/278729185): Replace fire and forget service with a bounded service. - val intent = NoteTaskControllerUpdateService.createIntent(context) - context.startServiceAsUser(intent, user) - } + updateNoteTaskAsUser(user) } + private val SecureSettings.preferredUser: UserHandle + get() { + val userId = + secureSettings.getInt( + Settings.Secure.DEFAULT_NOTE_TASK_PROFILE, + userTracker.userHandle.identifier, + ) + return UserHandle.of(userId) + } + companion object { val TAG = NoteTaskController::class.simpleName.orEmpty() diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskControllerUpdateService.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskControllerUpdateService.kt index 26b35cc8ffd1..3e352afe3832 100644 --- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskControllerUpdateService.kt +++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskControllerUpdateService.kt @@ -44,7 +44,7 @@ constructor( override fun onCreate() { super.onCreate() // TODO(b/278729185): Replace fire and forget service with a bounded service. - controller.updateNoteTaskAsUser(user) + controller.updateNoteTaskAsUserInternal(user) stopSelf() } diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInitializer.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInitializer.kt index 221ff65e4dfe..fe1034a6aa32 100644 --- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInitializer.kt +++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInitializer.kt @@ -15,7 +15,10 @@ */ package com.android.systemui.notetask +import android.app.role.OnRoleHoldersChangedListener import android.app.role.RoleManager +import android.content.Context +import android.content.pm.UserInfo import android.os.UserHandle import android.view.KeyEvent import android.view.KeyEvent.KEYCODE_N @@ -54,6 +57,7 @@ constructor( initializeHandleSystemKey() initializeOnRoleHoldersChanged() initializeOnUserUnlocked() + initializeUserTracker() } /** @@ -79,7 +83,7 @@ constructor( private fun initializeOnRoleHoldersChanged() { roleManager.addOnRoleHoldersChangedListenerAsUser( backgroundExecutor, - controller::onRoleHoldersChanged, + callbacks, UserHandle.ALL, ) } @@ -93,18 +97,41 @@ constructor( */ private fun initializeOnUserUnlocked() { if (keyguardUpdateMonitor.isUserUnlocked(userTracker.userId)) { - controller.setNoteTaskShortcutEnabled(true, userTracker.userHandle) - } else { - keyguardUpdateMonitor.registerCallback(onUserUnlockedCallback) + controller.updateNoteTaskForCurrentUserAndManagedProfiles() } + keyguardUpdateMonitor.registerCallback(callbacks) } - // KeyguardUpdateMonitor.registerCallback uses a weak reference, so we need a hard reference. - private val onUserUnlockedCallback = - object : KeyguardUpdateMonitorCallback() { + private fun initializeUserTracker() { + userTracker.addCallback(callbacks, backgroundExecutor) + } + + // Some callbacks use a weak reference, so we play safe and keep a hard reference to them all. + private val callbacks = + object : + KeyguardUpdateMonitorCallback(), + CommandQueue.Callbacks, + UserTracker.Callback, + OnRoleHoldersChangedListener { + + override fun handleSystemKey(key: KeyEvent) { + key.toNoteTaskEntryPointOrNull()?.let(controller::showNoteTask) + } + + override fun onRoleHoldersChanged(roleName: String, user: UserHandle) { + controller.onRoleHoldersChanged(roleName, user) + } + override fun onUserUnlocked() { - controller.setNoteTaskShortcutEnabled(true, userTracker.userHandle) - keyguardUpdateMonitor.removeCallback(this) + controller.updateNoteTaskForCurrentUserAndManagedProfiles() + } + + override fun onUserChanged(newUser: Int, userContext: Context) { + controller.updateNoteTaskForCurrentUserAndManagedProfiles() + } + + override fun onProfilesChanged(profiles: List<UserInfo>) { + controller.updateNoteTaskForCurrentUserAndManagedProfiles() } } } diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskModule.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskModule.kt index 109cfeec0723..c0e688f0f82f 100644 --- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskModule.kt +++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskModule.kt @@ -26,7 +26,6 @@ import com.android.systemui.flags.Flags import com.android.systemui.notetask.quickaffordance.NoteTaskQuickAffordanceModule import com.android.systemui.notetask.shortcut.CreateNoteTaskShortcutActivity import com.android.systemui.notetask.shortcut.LaunchNoteTaskActivity -import com.android.systemui.notetask.shortcut.LaunchNoteTaskManagedProfileProxyActivity import dagger.Binds import dagger.Module import dagger.Provides @@ -40,12 +39,12 @@ interface NoteTaskModule { @[Binds IntoMap ClassKey(NoteTaskControllerUpdateService::class)] fun NoteTaskControllerUpdateService.bindNoteTaskControllerUpdateService(): Service + @[Binds IntoMap ClassKey(NoteTaskBubblesController.NoteTaskBubblesService::class)] + fun NoteTaskBubblesController.NoteTaskBubblesService.bindNoteTaskBubblesService(): Service + @[Binds IntoMap ClassKey(LaunchNoteTaskActivity::class)] fun LaunchNoteTaskActivity.bindNoteTaskLauncherActivity(): Activity - @[Binds IntoMap ClassKey(LaunchNoteTaskManagedProfileProxyActivity::class)] - fun LaunchNoteTaskManagedProfileProxyActivity.bindNoteTaskLauncherProxyActivity(): Activity - @[Binds IntoMap ClassKey(LaunchNotesRoleSettingsTrampolineActivity::class)] fun LaunchNotesRoleSettingsTrampolineActivity.bindLaunchNotesRoleSettingsTrampolineActivity(): Activity diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskRoleManagerExt.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskRoleManagerExt.kt index 754c3650a5ed..4d30634cda38 100644 --- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskRoleManagerExt.kt +++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskRoleManagerExt.kt @@ -19,6 +19,7 @@ package com.android.systemui.notetask import android.app.role.RoleManager import android.app.role.RoleManager.ROLE_NOTES import android.content.Context +import android.content.pm.PackageManager import android.content.pm.ShortcutInfo import android.graphics.drawable.Icon import android.os.PersistableBundle @@ -42,21 +43,42 @@ internal object NoteTaskRoleManagerExt { context: Context, user: UserHandle, ): ShortcutInfo { + val packageName = getDefaultRoleHolderAsUser(ROLE_NOTES, user) + val extras = PersistableBundle() - getDefaultRoleHolderAsUser(ROLE_NOTES, user)?.let { packageName -> + if (packageName != null) { // Set custom app badge using the icon from ROLES_NOTES default app. extras.putString(NoteTaskController.EXTRA_SHORTCUT_BADGE_OVERRIDE_PACKAGE, packageName) } + val shortLabel = context.getString(R.string.note_task_button_label) + + val applicationLabel = context.packageManager.getApplicationLabel(packageName) + val longLabel = + if (applicationLabel == null) { + shortLabel + } else { + context.getString( + R.string.note_task_shortcut_long_label, + applicationLabel, + ) + } + val icon = Icon.createWithResource(context, R.drawable.ic_note_task_shortcut_widget) return ShortcutInfo.Builder(context, NoteTaskController.SHORTCUT_ID) .setIntent(LaunchNoteTaskActivity.createIntent(context)) .setActivity(LaunchNoteTaskActivity.createComponent(context)) - .setShortLabel(context.getString(R.string.note_task_button_label)) + .setShortLabel(shortLabel) + .setLongLabel(longLabel) .setLongLived(true) .setIcon(icon) .setExtras(extras) .build() } + + private fun PackageManager.getApplicationLabel(packageName: String?): String? = + runCatching { getApplicationInfo(packageName, /* flags= */ 0)!! } + .getOrNull() + ?.let { info -> getApplicationLabel(info).toString() } } diff --git a/packages/SystemUI/src/com/android/systemui/notetask/shortcut/LaunchNoteTaskActivity.kt b/packages/SystemUI/src/com/android/systemui/notetask/shortcut/LaunchNoteTaskActivity.kt index 7ef149de2794..d00a79e6d2bc 100644 --- a/packages/SystemUI/src/com/android/systemui/notetask/shortcut/LaunchNoteTaskActivity.kt +++ b/packages/SystemUI/src/com/android/systemui/notetask/shortcut/LaunchNoteTaskActivity.kt @@ -20,67 +20,24 @@ import android.content.ComponentName import android.content.Context import android.content.Intent import android.os.Bundle -import android.os.UserHandle -import android.os.UserManager import androidx.activity.ComponentActivity -import com.android.systemui.log.DebugLogger.debugLog import com.android.systemui.notetask.NoteTaskController import com.android.systemui.notetask.NoteTaskEntryPoint -import com.android.systemui.settings.UserTracker import javax.inject.Inject /** Activity responsible for launching the note experience, and finish. */ -class LaunchNoteTaskActivity -@Inject -constructor( - private val controller: NoteTaskController, - private val userManager: UserManager, - private val userTracker: UserTracker, -) : ComponentActivity() { +class LaunchNoteTaskActivity @Inject constructor(private val controller: NoteTaskController) : + ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - - // Under the hood, notes app shortcuts are shown in a floating window, called Bubble. - // Bubble API is only available in the main user but not work profile. - // - // On devices with work profile (WP), SystemUI provides both personal notes app shortcuts & - // work profile notes app shortcuts. In order to make work profile notes app shortcuts to - // show in Bubble, a few redirections across users are required: - // 1. When `LaunchNoteTaskActivity` is started in the work profile user, we launch - // `LaunchNoteTaskManagedProfileProxyActivity` on the main user, which has access to the - // Bubble API. - // 2. `LaunchNoteTaskManagedProfileProxyActivity` calls `Bubble#showOrHideAppBubble` with - // the work profile user ID. - // 3. Bubble renders the work profile notes app activity in a floating window, which is - // hosted in the main user. - // - // WP main user - // ------------------------ ------------------------------------------- - // | LaunchNoteTaskActivity | -> | LaunchNoteTaskManagedProfileProxyActivity | - // ------------------------ ------------------------------------------- - // | - // main user | - // ---------------------------- | - // | Bubble#showOrHideAppBubble | <-------------- - // | (with WP user ID) | - // ---------------------------- - val mainUser: UserHandle? = userManager.mainUser - if (userManager.isManagedProfile) { - if (mainUser == null) { - debugLog { "Can't find the main user. Skipping the notes app launch." } + val entryPoint = + if (isInMultiWindowMode) { + NoteTaskEntryPoint.WIDGET_PICKER_SHORTCUT_IN_MULTI_WINDOW_MODE } else { - controller.startNoteTaskProxyActivityForUser(mainUser) + NoteTaskEntryPoint.WIDGET_PICKER_SHORTCUT } - } else { - val entryPoint = - if (isInMultiWindowMode) { - NoteTaskEntryPoint.WIDGET_PICKER_SHORTCUT_IN_MULTI_WINDOW_MODE - } else { - NoteTaskEntryPoint.WIDGET_PICKER_SHORTCUT - } - controller.showNoteTask(entryPoint) - } + controller.showNoteTaskAsUser(entryPoint, user) finish() } diff --git a/packages/SystemUI/src/com/android/systemui/notetask/shortcut/LaunchNoteTaskManagedProfileProxyActivity.kt b/packages/SystemUI/src/com/android/systemui/notetask/shortcut/LaunchNoteTaskManagedProfileProxyActivity.kt deleted file mode 100644 index 3259b0dcc53d..000000000000 --- a/packages/SystemUI/src/com/android/systemui/notetask/shortcut/LaunchNoteTaskManagedProfileProxyActivity.kt +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.notetask.shortcut - -import android.os.Build -import android.os.Bundle -import android.os.UserManager -import android.util.Log -import androidx.activity.ComponentActivity -import com.android.systemui.notetask.NoteTaskController -import com.android.systemui.notetask.NoteTaskEntryPoint -import com.android.systemui.settings.UserTracker -import javax.inject.Inject - -/** - * An internal proxy activity that starts notes app in the work profile. - * - * If there is no work profile, this activity finishes gracefully. - * - * This activity MUST NOT be exported because that would expose the INTERACT_ACROSS_USER privilege - * to any apps. - */ -class LaunchNoteTaskManagedProfileProxyActivity -@Inject -constructor( - private val controller: NoteTaskController, - private val userTracker: UserTracker, - private val userManager: UserManager, -) : ComponentActivity() { - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - - val managedProfileUser = - userTracker.userProfiles.firstOrNull { userManager.isManagedProfile(it.id) } - - if (managedProfileUser == null) { - logDebug { "Fail to find the work profile user." } - } else { - controller.showNoteTaskAsUser( - entryPoint = NoteTaskEntryPoint.WIDGET_PICKER_SHORTCUT, - user = managedProfileUser.userHandle - ) - } - finish() - } -} - -private inline fun logDebug(message: () -> String) { - if (Build.IS_DEBUGGABLE) { - Log.d(NoteTaskController.TAG, message()) - } -} diff --git a/packages/SystemUI/src/com/android/systemui/power/data/repository/PowerRepository.kt b/packages/SystemUI/src/com/android/systemui/power/data/repository/PowerRepository.kt index b2e04bb4f26f..69cb6119580b 100644 --- a/packages/SystemUI/src/com/android/systemui/power/data/repository/PowerRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/power/data/repository/PowerRepository.kt @@ -26,6 +26,8 @@ import com.android.systemui.broadcast.BroadcastDispatcher import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging 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.util.time.SystemClock import javax.inject.Inject import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow @@ -34,13 +36,18 @@ import kotlinx.coroutines.flow.Flow interface PowerRepository { /** Whether the device is interactive. Starts with the current state. */ val isInteractive: Flow<Boolean> + + /** Wakes up the device. */ + fun wakeUp(why: String, @PowerManager.WakeReason wakeReason: Int) } @SysUISingleton class PowerRepositoryImpl @Inject constructor( - manager: PowerManager, + private val manager: PowerManager, + @Application private val applicationContext: Context, + private val systemClock: SystemClock, dispatcher: BroadcastDispatcher, ) : PowerRepository { @@ -68,6 +75,14 @@ constructor( awaitClose { dispatcher.unregisterReceiver(receiver) } } + override fun wakeUp(why: String, wakeReason: Int) { + manager.wakeUp( + systemClock.uptimeMillis(), + wakeReason, + "${applicationContext.packageName}:$why", + ) + } + companion object { private const val TAG = "PowerRepository" } diff --git a/packages/SystemUI/src/com/android/systemui/power/domain/interactor/PowerInteractor.kt b/packages/SystemUI/src/com/android/systemui/power/domain/interactor/PowerInteractor.kt index 3f799f724fe7..c13476fbbe08 100644 --- a/packages/SystemUI/src/com/android/systemui/power/domain/interactor/PowerInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/power/domain/interactor/PowerInteractor.kt @@ -17,8 +17,12 @@ package com.android.systemui.power.domain.interactor +import android.os.PowerManager +import com.android.systemui.classifier.FalsingCollector import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.power.data.repository.PowerRepository +import com.android.systemui.statusbar.phone.ScreenOffAnimationController import javax.inject.Inject import kotlinx.coroutines.flow.Flow @@ -27,8 +31,27 @@ import kotlinx.coroutines.flow.Flow class PowerInteractor @Inject constructor( - repository: PowerRepository, + private val repository: PowerRepository, + private val falsingCollector: FalsingCollector, + private val screenOffAnimationController: ScreenOffAnimationController, + private val statusBarStateController: StatusBarStateController, ) { /** Whether the screen is on or off. */ val isInteractive: Flow<Boolean> = repository.isInteractive + + /** + * Wakes up the device if the device was dozing. + * + * @param why a string explaining why we're waking the device for debugging purposes. Should be + * in SCREAMING_SNAKE_CASE. + * @param wakeReason the PowerManager-based reason why we're waking the device. + */ + fun wakeUpIfDozing(why: String, @PowerManager.WakeReason wakeReason: Int) { + if ( + statusBarStateController.isDozing && screenOffAnimationController.allowWakeUpIfDozing() + ) { + repository.wakeUp(why, wakeReason) + falsingCollector.onScreenOnFromTouch() + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt index 79167f276576..c3b5db42e08d 100644 --- a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt +++ b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt @@ -25,7 +25,7 @@ import android.widget.ImageView import android.widget.LinearLayout import com.android.settingslib.Utils import com.android.systemui.R -import com.android.systemui.animation.LaunchableFrameLayout +import com.android.systemui.animation.view.LaunchableFrameLayout import com.android.systemui.statusbar.events.BackgroundAnimatableView class OngoingPrivacyChip @JvmOverloads constructor( diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java index b8c2fad8c10d..8d9475d9a53e 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java @@ -52,7 +52,6 @@ import com.android.systemui.animation.ShadeInterpolation; import com.android.systemui.compose.ComposeFacade; 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.MediaHost; import com.android.systemui.plugins.qs.QS; import com.android.systemui.plugins.qs.QSContainerController; @@ -755,8 +754,7 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca // Alpha progress should be linear on lockscreen shade expansion. return progress; } - if (mIsSmallScreen || !mFeatureFlags.isEnabled( - Flags.LARGE_SHADE_GRANULAR_ALPHA_INTERPOLATION)) { + if (mIsSmallScreen) { return ShadeInterpolation.getContentAlpha(progress); } else { return mLargeScreenShadeInterpolator.getQsAlpha(progress); diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragmentDisableFlagsLogger.kt b/packages/SystemUI/src/com/android/systemui/qs/QSFragmentDisableFlagsLogger.kt index cd52ec29177d..ac6aabb2e5bd 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSFragmentDisableFlagsLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragmentDisableFlagsLogger.kt @@ -34,7 +34,6 @@ class QSFragmentDisableFlagsLogger @Inject constructor( }, { disableFlagsLogger.getDisableFlagsString( - old = null, new = DisableFlagsLogger.DisableState(int1, int2), newAfterLocalModification = DisableFlagsLogger.DisableState(long1.toInt(), long2.toInt()) diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java index 59b94b7c4bd4..d2568ac79105 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java @@ -52,8 +52,8 @@ import com.android.systemui.qs.pipeline.data.repository.CustomTileAddedRepositor import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor; import com.android.systemui.settings.UserFileManager; import com.android.systemui.settings.UserTracker; +import com.android.systemui.shade.ShadeController; import com.android.systemui.statusbar.phone.AutoTileManager; -import com.android.systemui.statusbar.phone.CentralSurfaces; import com.android.systemui.tuner.TunerService; import com.android.systemui.tuner.TunerService.Tunable; import com.android.systemui.util.settings.SecureSettings; @@ -66,7 +66,6 @@ import java.util.Collection; import java.util.LinkedHashMap; import java.util.List; import java.util.Objects; -import java.util.Optional; import java.util.Set; import java.util.concurrent.Executor; import java.util.function.Predicate; @@ -108,7 +107,7 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, P private AutoTileManager mAutoTiles; private final ArrayList<QSFactory> mQsFactories = new ArrayList<>(); private int mCurrentUser; - private final Optional<CentralSurfaces> mCentralSurfacesOptional; + private final ShadeController mShadeController; private Context mUserContext; private UserTracker mUserTracker; private SecureSettings mSecureSettings; @@ -129,7 +128,7 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, P PluginManager pluginManager, TunerService tunerService, Provider<AutoTileManager> autoTiles, - Optional<CentralSurfaces> centralSurfacesOptional, + ShadeController shadeController, QSLogger qsLogger, UserTracker userTracker, SecureSettings secureSettings, @@ -148,7 +147,7 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, P mUserFileManager = userFileManager; mFeatureFlags = featureFlags; - mCentralSurfacesOptional = centralSurfacesOptional; + mShadeController = shadeController; mQsFactories.add(defaultFactory); pluginManager.addPluginListener(this, QSFactory.class, true); @@ -209,17 +208,17 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, P @Override public void collapsePanels() { - mCentralSurfacesOptional.ifPresent(CentralSurfaces::postAnimateCollapsePanels); + mShadeController.postAnimateCollapseShade(); } @Override public void forceCollapsePanels() { - mCentralSurfacesOptional.ifPresent(CentralSurfaces::postAnimateForceCollapsePanels); + mShadeController.postAnimateForceCollapseShade(); } @Override public void openPanels() { - mCentralSurfacesOptional.ifPresent(CentralSurfaces::postAnimateOpenPanels); + mShadeController.postAnimateExpandQs(); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/PanelInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/PanelInteractor.kt index 260caa767a5e..fa6de8dcdafe 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/PanelInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/PanelInteractor.kt @@ -16,8 +16,7 @@ package com.android.systemui.qs.pipeline.domain.interactor import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.statusbar.phone.CentralSurfaces -import java.util.Optional +import com.android.systemui.shade.ShadeController import javax.inject.Inject /** Encapsulates business logic for interacting with the QS panel. */ @@ -37,17 +36,17 @@ interface PanelInteractor { class PanelInteractorImpl @Inject constructor( - private val centralSurfaces: Optional<CentralSurfaces>, + private val shadeController: ShadeController, ) : PanelInteractor { override fun collapsePanels() { - centralSurfaces.ifPresent { it.postAnimateCollapsePanels() } + shadeController.postAnimateCollapseShade() } override fun forceCollapsePanels() { - centralSurfaces.ifPresent { it.postAnimateForceCollapsePanels() } + shadeController.postAnimateForceCollapseShade() } override fun openPanels() { - centralSurfaces.ifPresent { it.postAnimateOpenPanels() } + shadeController.postAnimateExpandQs() } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java index cd69f4edff63..e54168162de6 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java @@ -130,6 +130,11 @@ public class QSIconViewImpl extends QSIconView { d.setLayoutDirection(getLayoutDirection()); } + final Drawable lastDrawable = iv.getDrawable(); + if (lastDrawable instanceof Animatable2) { + ((Animatable2) lastDrawable).clearAnimationCallbacks(); + } + if (iv instanceof SlashImageView) { ((SlashImageView) iv).setAnimationEnabled(shouldAnimate); ((SlashImageView) iv).setState(null, d); diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DreamTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DreamTile.java index eef4c1dd4436..b5e6a0f5c60b 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DreamTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DreamTile.java @@ -166,17 +166,6 @@ public class DreamTile extends QSTileImpl<QSTile.BooleanState> { } @Override - protected void handleLongClick(@Nullable View view) { - try { - // Need to wake on long click so bouncer->settings works. - mDreamManager.awaken(); - } catch (RemoteException e) { - Log.e(LOG_TAG, "Can't awaken", e); - } - super.handleLongClick(view); - } - - @Override protected void handleUpdateState(BooleanState state, Object arg) { state.label = getTileLabel(); state.secondaryLabel = getActiveDreamName(); diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/FontScalingTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/FontScalingTile.kt index c013486b83e4..423fa80cacdb 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/FontScalingTile.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/FontScalingTile.kt @@ -38,7 +38,9 @@ import com.android.systemui.qs.QSHost import com.android.systemui.qs.QsEventLogger import com.android.systemui.qs.logging.QSLogger import com.android.systemui.qs.tileimpl.QSTileImpl +import com.android.systemui.settings.UserTracker import com.android.systemui.statusbar.phone.SystemUIDialog +import com.android.systemui.statusbar.policy.KeyguardStateController import com.android.systemui.util.concurrency.DelayableExecutor import com.android.systemui.util.settings.SecureSettings import com.android.systemui.util.settings.SystemSettings @@ -57,11 +59,13 @@ constructor( statusBarStateController: StatusBarStateController, activityStarter: ActivityStarter, qsLogger: QSLogger, + private val keyguardStateController: KeyguardStateController, private val dialogLaunchAnimator: DialogLaunchAnimator, private val systemSettings: SystemSettings, private val secureSettings: SecureSettings, private val systemClock: SystemClock, private val featureFlags: FeatureFlags, + private val userTracker: UserTracker, @Background private val backgroundDelayableExecutor: DelayableExecutor ) : QSTileImpl<QSTile.State?>( @@ -86,26 +90,40 @@ constructor( } override fun handleClick(view: View?) { - mUiHandler.post { + // We animate from the touched view only if we are not on the keyguard + val animateFromView: Boolean = view != null && !keyguardStateController.isShowing + + val runnable = Runnable { val dialog: SystemUIDialog = FontScalingDialog( mContext, systemSettings, secureSettings, systemClock, + userTracker, mainHandler, backgroundDelayableExecutor ) - if (view != null) { + if (animateFromView) { dialogLaunchAnimator.showFromView( dialog, - view, + view!!, DialogCuj(InteractionJankMonitor.CUJ_SHADE_DIALOG_OPEN, INTERACTION_JANK_TAG) ) } else { dialog.show() } } + + mainHandler.post { + mActivityStarter.executeRunnableDismissingKeyguard( + runnable, + /* cancelAction= */ null, + /* dismissShade= */ true, + /* afterKeyguardGone= */ true, + /* deferred= */ false + ) + } } override fun handleUpdateState(state: QSTile.State?, arg: Any?) { diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java index ad658458d330..6e1ef9108256 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java @@ -284,7 +284,6 @@ public class InternetDialog extends SystemUIDialog implements mHandler.removeCallbacks(mHideProgressBarRunnable); mHandler.removeCallbacks(mHideSearchingRunnable); mMobileNetworkLayout.setOnClickListener(null); - mMobileDataToggle.setOnCheckedChangeListener(null); mConnectedWifListLayout.setOnClickListener(null); if (mSecondaryMobileNetworkLayout != null) { mSecondaryMobileNetworkLayout.setOnClickListener(null); @@ -349,18 +348,16 @@ public class InternetDialog extends SystemUIDialog implements } mInternetDialogController.connectCarrierNetwork(); }); - mMobileDataToggle.setOnCheckedChangeListener( - (buttonView, isChecked) -> { - if (!isChecked && shouldShowMobileDialog()) { - showTurnOffMobileDialog(); - } else if (!shouldShowMobileDialog()) { - if (mInternetDialogController.isMobileDataEnabled() == isChecked) { - return; - } - mInternetDialogController.setMobileDataEnabled(mContext, mDefaultDataSubId, - isChecked, false); - } - }); + mMobileDataToggle.setOnClickListener(v -> { + boolean isChecked = mMobileDataToggle.isChecked(); + if (!isChecked && shouldShowMobileDialog()) { + mMobileDataToggle.setChecked(true); + showTurnOffMobileDialog(); + } else if (mInternetDialogController.isMobileDataEnabled() != isChecked) { + mInternetDialogController.setMobileDataEnabled(mContext, mDefaultDataSubId, + isChecked, false); + } + }); mConnectedWifListLayout.setOnClickListener(this::onClickConnectedWifi); mSeeAllLayout.setOnClickListener(this::onClickSeeMoreButton); mWiFiToggle.setOnCheckedChangeListener( @@ -686,9 +683,7 @@ public class InternetDialog extends SystemUIDialog implements mAlertDialog = new Builder(mContext) .setTitle(R.string.mobile_data_disable_title) .setMessage(mContext.getString(R.string.mobile_data_disable_message, carrierName)) - .setNegativeButton(android.R.string.cancel, (d, w) -> { - mMobileDataToggle.setChecked(true); - }) + .setNegativeButton(android.R.string.cancel, (d, w) -> {}) .setPositiveButton( com.android.internal.R.string.alert_windows_notification_turn_off_action, (d, w) -> { @@ -698,7 +693,6 @@ public class InternetDialog extends SystemUIDialog implements Prefs.putBoolean(mContext, QS_HAS_TURNED_OFF_MOBILE_DATA, true); }) .create(); - mAlertDialog.setOnCancelListener(dialog -> mMobileDataToggle.setChecked(true)); mAlertDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); SystemUIDialog.setShowForAllUsers(mAlertDialog, true); SystemUIDialog.registerDismissListener(mAlertDialog); 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 abeb5af352fc..ec1258049486 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java @@ -172,7 +172,9 @@ public class InternetDialogController implements AccessPointController.AccessPoi private Executor mExecutor; private AccessPointController mAccessPointController; private IntentFilter mConnectionStateFilter; - private InternetDialogCallback mCallback; + @VisibleForTesting + @Nullable + InternetDialogCallback mCallback; private UiEventLogger mUiEventLogger; private BroadcastDispatcher mBroadcastDispatcher; private KeyguardUpdateMonitor mKeyguardUpdateMonitor; @@ -215,12 +217,16 @@ public class InternetDialogController implements AccessPointController.AccessPoi new KeyguardUpdateMonitorCallback() { @Override public void onRefreshCarrierInfo() { - mCallback.onRefreshCarrierInfo(); + if (mCallback != null) { + mCallback.onRefreshCarrierInfo(); + } } @Override public void onSimStateChanged(int subId, int slotId, int simState) { - mCallback.onSimStateChanged(); + if (mCallback != null) { + mCallback.onSimStateChanged(); + } } }; @@ -331,6 +337,7 @@ public class InternetDialogController implements AccessPointController.AccessPoi mKeyguardUpdateMonitor.removeCallback(mKeyguardUpdateCallback); mConnectivityManager.unregisterNetworkCallback(mConnectivityManagerNetworkCallback); mConnectedWifiInternetMonitor.unregisterCallback(); + mCallback = null; } @VisibleForTesting @@ -724,7 +731,7 @@ public class InternetDialogController implements AccessPointController.AccessPoi ActivityLaunchAnimator.Controller controller = mDialogLaunchAnimator.createActivityLaunchController(view); - if (controller == null) { + if (controller == null && mCallback != null) { mCallback.dismissDialog(); } @@ -1095,7 +1102,9 @@ public class InternetDialogController implements AccessPointController.AccessPoi mHasWifiEntries = false; } - mCallback.onAccessPointsChanged(wifiEntries, connectedEntry, hasMoreWifiEntries); + if (mCallback != null) { + mCallback.onAccessPointsChanged(wifiEntries, connectedEntry, hasMoreWifiEntries); + } } @Override @@ -1117,34 +1126,46 @@ public class InternetDialogController implements AccessPointController.AccessPoi @Override public void onServiceStateChanged(@NonNull ServiceState serviceState) { - mCallback.onServiceStateChanged(serviceState); + if (mCallback != null) { + mCallback.onServiceStateChanged(serviceState); + } } @Override public void onDataConnectionStateChanged(int state, int networkType) { - mCallback.onDataConnectionStateChanged(state, networkType); + if (mCallback != null) { + mCallback.onDataConnectionStateChanged(state, networkType); + } } @Override public void onSignalStrengthsChanged(@NonNull SignalStrength signalStrength) { - mCallback.onSignalStrengthsChanged(signalStrength); + if (mCallback != null) { + mCallback.onSignalStrengthsChanged(signalStrength); + } } @Override public void onDisplayInfoChanged(@NonNull TelephonyDisplayInfo telephonyDisplayInfo) { mSubIdTelephonyDisplayInfoMap.put(mSubId, telephonyDisplayInfo); - mCallback.onDisplayInfoChanged(telephonyDisplayInfo); + if (mCallback != null) { + mCallback.onDisplayInfoChanged(telephonyDisplayInfo); + } } @Override public void onUserMobileDataStateChanged(boolean enabled) { - mCallback.onUserMobileDataStateChanged(enabled); + if (mCallback != null) { + mCallback.onUserMobileDataStateChanged(enabled); + } } @Override public void onCarrierNetworkChange(boolean active) { mCarrierNetworkChangeMode = active; - mCallback.onCarrierNetworkChange(active); + if (mCallback != null) { + mCallback.onCarrierNetworkChange(active); + } } } @@ -1171,14 +1192,18 @@ public class InternetDialogController implements AccessPointController.AccessPoi scanWifiAccessPoints(); } // update UI - mCallback.onCapabilitiesChanged(network, capabilities); + if (mCallback != null) { + mCallback.onCapabilitiesChanged(network, capabilities); + } } @Override @WorkerThread public void onLost(@NonNull Network network) { mHasEthernet = false; - mCallback.onLost(network); + if (mCallback != null) { + mCallback.onLost(network); + } } } diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java index 07259c2ff283..e7dde6617964 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java +++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java @@ -200,8 +200,8 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis // TODO: change the method signature to use (boolean inputFocusTransferStarted) @Override - public void onStatusBarMotionEvent(MotionEvent event) { - verifyCallerAndClearCallingIdentity("onStatusBarMotionEvent", () -> { + public void onStatusBarTouchEvent(MotionEvent event) { + verifyCallerAndClearCallingIdentity("onStatusBarTouchEvent", () -> { // TODO move this logic to message queue mCentralSurfacesOptionalLazy.get().ifPresent(centralSurfaces -> { if (event.getActionMasked() == ACTION_DOWN) { @@ -236,6 +236,13 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis } @Override + public void onStatusBarTrackpadEvent(MotionEvent event) { + verifyCallerAndClearCallingIdentityPostMain("onStatusBarTrackpadEvent", () -> + mCentralSurfacesOptionalLazy.get().ifPresent(centralSurfaces -> + centralSurfaces.onStatusBarTrackpadEvent(event))); + } + + @Override public void onBackPressed() { verifyCallerAndClearCallingIdentityPostMain("onBackPressed", () -> { sendEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BACK); @@ -345,7 +352,7 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis @Override public void expandNotificationPanel() { - verifyCallerAndClearCallingIdentity("expandNotificationPanel", + verifyCallerAndClearCallingIdentityPostMain("expandNotificationPanel", () -> mCommandQueue.handleSystemKey(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_SYSTEM_NAVIGATION_DOWN))); } @@ -353,7 +360,7 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis @Override public void toggleNotificationPanel() { verifyCallerAndClearCallingIdentityPostMain("toggleNotificationPanel", () -> - mCentralSurfacesOptionalLazy.get().ifPresent(CentralSurfaces::togglePanel)); + mCommandQueue.togglePanel()); } private boolean verifyCaller(String reason) { diff --git a/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt b/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt index 752471d83735..0a9839e2f18b 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt @@ -16,7 +16,7 @@ package com.android.systemui.scene -import com.android.systemui.scene.data.model.SceneContainerConfigModule +import com.android.systemui.scene.shared.model.SceneContainerConfigModule import com.android.systemui.scene.ui.composable.SceneModule import com.android.systemui.scene.ui.viewmodel.SceneContainerViewModelModule import dagger.Module diff --git a/packages/SystemUI/src/com/android/systemui/scene/data/repository/SceneContainerRepository.kt b/packages/SystemUI/src/com/android/systemui/scene/data/repository/SceneContainerRepository.kt index 61b162b014d8..1ebeced5fae6 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/data/repository/SceneContainerRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/data/repository/SceneContainerRepository.kt @@ -16,7 +16,7 @@ package com.android.systemui.scene.data.repository -import com.android.systemui.scene.data.model.SceneContainerConfig +import com.android.systemui.scene.shared.model.SceneContainerConfig import com.android.systemui.scene.shared.model.SceneKey import com.android.systemui.scene.shared.model.SceneModel import javax.inject.Inject @@ -28,11 +28,9 @@ import kotlinx.coroutines.flow.asStateFlow class SceneContainerRepository @Inject constructor( - containerConfigurations: Set<SceneContainerConfig>, + private val containerConfigByName: Map<String, SceneContainerConfig>, ) { - private val containerConfigByName: Map<String, SceneContainerConfig> = - containerConfigurations.associateBy { config -> config.name } private val containerVisibilityByName: Map<String, MutableStateFlow<Boolean>> = containerConfigByName .map { (containerName, _) -> containerName to MutableStateFlow(true) } @@ -48,21 +46,6 @@ constructor( .map { (containerName, _) -> containerName to MutableStateFlow(1f) } .toMap() - init { - val repeatedContainerNames = - containerConfigurations - .groupingBy { config -> config.name } - .eachCount() - .filter { (_, count) -> count > 1 } - check(repeatedContainerNames.isEmpty()) { - "Container names must be unique. The following container names appear more than once: ${ - repeatedContainerNames - .map { (name, count) -> "\"$name\" appears $count times" } - .joinToString(", ") - }" - } - } - /** * Returns the keys to all scenes in the container with the given name. * diff --git a/packages/SystemUI/src/com/android/systemui/scene/data/model/SceneContainerConfig.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/model/SceneContainerConfig.kt index d0769ebe941e..0327edbb06b4 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/data/model/SceneContainerConfig.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/shared/model/SceneContainerConfig.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023 The Android Open Source Project + * Copyright 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,9 +14,7 @@ * limitations under the License. */ -package com.android.systemui.scene.data.model - -import com.android.systemui.scene.shared.model.SceneKey +package com.android.systemui.scene.shared.model /** Models the configuration of a single scene container. */ data class SceneContainerConfig( diff --git a/packages/SystemUI/src/com/android/systemui/scene/data/model/SceneContainerConfigModule.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/model/SceneContainerConfigModule.kt index 0af80949f95e..7562a5a848d8 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/data/model/SceneContainerConfigModule.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/shared/model/SceneContainerConfigModule.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023 The Android Open Source Project + * Copyright 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,11 +14,9 @@ * limitations under the License. */ -package com.android.systemui.scene.data.model +package com.android.systemui.scene.shared.model import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.scene.shared.model.SceneContainerNames -import com.android.systemui.scene.shared.model.SceneKey import dagger.Module import dagger.Provides import javax.inject.Named diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootView.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootView.kt new file mode 100644 index 000000000000..9b5898f42279 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootView.kt @@ -0,0 +1,75 @@ +package com.android.systemui.scene.ui.view + +import android.content.Context +import android.util.AttributeSet +import androidx.activity.OnBackPressedDispatcher +import androidx.activity.OnBackPressedDispatcherOwner +import androidx.activity.setViewTreeOnBackPressedDispatcherOwner +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.repeatOnLifecycle +import com.android.systemui.compose.ComposeFacade +import com.android.systemui.lifecycle.repeatWhenAttached +import com.android.systemui.scene.shared.model.Scene +import com.android.systemui.scene.shared.model.SceneContainerConfig +import com.android.systemui.scene.shared.model.SceneKey +import com.android.systemui.scene.ui.viewmodel.SceneContainerViewModel +import kotlinx.coroutines.launch + +/** A root view of the main SysUI window that supports scenes. */ +class SceneWindowRootView( + context: Context, + attrs: AttributeSet?, +) : + WindowRootView( + context, + attrs, + ) { + fun init( + viewModel: SceneContainerViewModel, + containerConfig: SceneContainerConfig, + scenes: Set<Scene>, + ) { + val unsortedSceneByKey: Map<SceneKey, Scene> = scenes.associateBy { scene -> scene.key } + val sortedSceneByKey: Map<SceneKey, Scene> = buildMap { + containerConfig.sceneKeys.forEach { sceneKey -> + val scene = + checkNotNull(unsortedSceneByKey[sceneKey]) { + "Scene not found for key \"$sceneKey\"!" + } + + put(sceneKey, scene) + } + } + + repeatWhenAttached { + lifecycleScope.launch { + repeatOnLifecycle(Lifecycle.State.CREATED) { + setViewTreeOnBackPressedDispatcherOwner( + object : OnBackPressedDispatcherOwner { + override val onBackPressedDispatcher = + OnBackPressedDispatcher().apply { + setOnBackInvokedDispatcher(viewRootImpl.onBackInvokedDispatcher) + } + + override fun getLifecycle(): Lifecycle { + return this@repeatWhenAttached.lifecycle + } + } + ) + + addView( + ComposeFacade.createSceneContainerView( + context = context, + viewModel = viewModel, + sceneByKey = sortedSceneByKey, + ) + ) + } + + // Here when destroyed. + removeAllViews() + } + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/view/WindowRootView.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/view/WindowRootView.kt new file mode 100644 index 000000000000..636a30141021 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/scene/ui/view/WindowRootView.kt @@ -0,0 +1,52 @@ +package com.android.systemui.scene.ui.view + +import android.content.Context +import android.util.AttributeSet +import android.view.View +import android.widget.FrameLayout +import com.android.systemui.compose.ComposeFacade + +/** + * A view that can serve as the root of the main SysUI window (but might not, see below for more + * information regarding this confusing comment). + * + * Naturally, only one view may ever be at the root of a view hierarchy tree. Under certain + * conditions, the view hierarchy tree in the scene-containing window view may actually have one + * [WindowRootView] acting as the true root view and another [WindowRootView] which doesn't and is, + * instead, a child of the true root view. To discern which one is which, please use the [isRoot] + * method. + */ +open class WindowRootView( + context: Context, + attrs: AttributeSet?, +) : + FrameLayout( + context, + attrs, + ) { + + override fun onAttachedToWindow() { + super.onAttachedToWindow() + + if (ComposeFacade.isComposeAvailable() && isRoot()) { + ComposeFacade.composeInitializer().onAttachedToWindow(this) + } + } + + override fun onDetachedFromWindow() { + super.onDetachedFromWindow() + + if (ComposeFacade.isComposeAvailable() && isRoot()) { + ComposeFacade.composeInitializer().onDetachedFromWindow(this) + } + } + + /** + * Returns `true` if this view is the true root of the view-hierarchy; `false` otherwise. + * + * Please see the class-level documentation to understand why this is possible. + */ + private fun isRoot(): Boolean { + return parent.let { it !is View || it.id == android.R.id.content } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsCrossProcessHelper.java b/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsCrossProcessHelper.java index afc8bff91766..7de22b1a9c77 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsCrossProcessHelper.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsCrossProcessHelper.java @@ -19,7 +19,7 @@ package com.android.systemui.screenshot.appclips; import android.content.Context; import android.content.Intent; import android.graphics.Bitmap; -import android.os.UserManager; +import android.os.UserHandle; import androidx.annotation.Nullable; @@ -39,15 +39,14 @@ class AppClipsCrossProcessHelper { private final DisplayTracker mDisplayTracker; @Inject - AppClipsCrossProcessHelper(@Application Context context, UserManager userManager, - DisplayTracker displayTracker) { + AppClipsCrossProcessHelper(@Application Context context, DisplayTracker displayTracker) { // Start a service as main user so that even if the app clips activity is running as work // profile user the service is able to use correct instance of Bubbles to grab a screenshot // excluding the bubble layer. mProxyConnector = new ServiceConnector.Impl<>(context, new Intent(context, AppClipsScreenshotHelperService.class), Context.BIND_AUTO_CREATE | Context.BIND_WAIVE_PRIORITY - | Context.BIND_NOT_VISIBLE, userManager.getMainUser().getIdentifier(), + | Context.BIND_NOT_VISIBLE, UserHandle.USER_SYSTEM, IAppClipsScreenshotHelperService.Stub::asInterface); mDisplayTracker = displayTracker; } diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsScreenshotHelperService.java b/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsScreenshotHelperService.java index 4c013151c464..4cc2f6269bb2 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsScreenshotHelperService.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsScreenshotHelperService.java @@ -33,6 +33,11 @@ import javax.inject.Inject; /** * A helper service that runs in SysUI process and helps {@link AppClipsActivity} which runs in its * own separate process take a screenshot. + * + * <p>Note: This service always runs in the SysUI process running on the system user irrespective of + * which user started the service. This is required so that the correct instance of {@link Bubbles} + * instance is injected. This is set via attribute {@code android:singleUser=”true”} in + * AndroidManifest. */ public class AppClipsScreenshotHelperService extends Service { diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsService.java b/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsService.java index 394949297d6d..dce8c81c3462 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsService.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsService.java @@ -16,6 +16,11 @@ package com.android.systemui.screenshot.appclips; +import static android.content.Intent.CAPTURE_CONTENT_FOR_NOTE_BLOCKED_BY_ADMIN; +import static android.content.Intent.CAPTURE_CONTENT_FOR_NOTE_FAILED; +import static android.content.Intent.CAPTURE_CONTENT_FOR_NOTE_SUCCESS; +import static android.content.Intent.CAPTURE_CONTENT_FOR_NOTE_WINDOW_MODE_UNSUPPORTED; + import static com.android.systemui.flags.Flags.SCREENSHOT_APP_CLIPS; import android.app.Activity; @@ -25,17 +30,12 @@ import android.app.admin.DevicePolicyManager; import android.content.ComponentName; import android.content.Context; import android.content.Intent; +import android.content.Intent.CaptureContentForNoteStatusCodes; import android.content.res.Resources; import android.os.IBinder; -import android.os.UserHandle; -import android.os.UserManager; -import android.util.Log; import androidx.annotation.Nullable; -import androidx.annotation.VisibleForTesting; -import com.android.internal.infra.AndroidFuture; -import com.android.internal.infra.ServiceConnector; import com.android.internal.statusbar.IAppClipsService; import com.android.systemui.R; import com.android.systemui.dagger.qualifiers.Application; @@ -43,73 +43,36 @@ import com.android.systemui.flags.FeatureFlags; import com.android.wm.shell.bubbles.Bubbles; import java.util.Optional; -import java.util.concurrent.ExecutionException; import javax.inject.Inject; /** * A service that communicates with {@link StatusBarManager} to support the - * {@link StatusBarManager#canLaunchCaptureContentActivityForNote(Activity)} API. + * {@link StatusBarManager#canLaunchCaptureContentActivityForNote(Activity)} API. Also used by + * {@link AppClipsTrampolineActivity} to query if an app should be allowed to user App Clips. + * + * <p>Note: This service always runs in the SysUI process running on the system user irrespective of + * which user started the service. This is required so that the correct instance of {@link Bubbles} + * instance is injected. This is set via attribute {@code android:singleUser=”true”} in + * AndroidManifest. */ public class AppClipsService extends Service { - private static final String TAG = AppClipsService.class.getSimpleName(); - @Application private final Context mContext; private final FeatureFlags mFeatureFlags; private final Optional<Bubbles> mOptionalBubbles; private final DevicePolicyManager mDevicePolicyManager; - private final UserManager mUserManager; - private final boolean mAreTaskAndTimeIndependentPrerequisitesMet; - @VisibleForTesting() - @Nullable ServiceConnector<IAppClipsService> mProxyConnectorToMainProfile; - @Inject public AppClipsService(@Application Context context, FeatureFlags featureFlags, - Optional<Bubbles> optionalBubbles, DevicePolicyManager devicePolicyManager, - UserManager userManager) { + Optional<Bubbles> optionalBubbles, DevicePolicyManager devicePolicyManager) { mContext = context; mFeatureFlags = featureFlags; mOptionalBubbles = optionalBubbles; mDevicePolicyManager = devicePolicyManager; - mUserManager = userManager; - - // The consumer of this service are apps that call through StatusBarManager API to query if - // it can use app clips API. Since these apps can be launched as work profile users, this - // service will start as work profile user. SysUI doesn't share injected instances for - // different users. This is why the bubbles instance injected will be incorrect. As the apps - // don't generally have permission to connect to a service running as different user, we - // start a proxy connection to communicate with the main user's version of this service. - if (mUserManager.isManagedProfile()) { - // No need to check for prerequisites in this case as those are incorrect for work - // profile user instance of the service and the main user version of the service will - // take care of this check. - mAreTaskAndTimeIndependentPrerequisitesMet = false; - - // Get the main user so that we can connect to the main user's version of the service. - UserHandle mainUser = mUserManager.getMainUser(); - if (mainUser == null) { - // If main user is not available there isn't much we can do, no apps can use app - // clips. - return; - } - - // Set up the connection to be used later during onBind callback. - mProxyConnectorToMainProfile = - new ServiceConnector.Impl<>( - context, - new Intent(context, AppClipsService.class), - Context.BIND_AUTO_CREATE | Context.BIND_WAIVE_PRIORITY - | Context.BIND_NOT_VISIBLE, - mainUser.getIdentifier(), - IAppClipsService.Stub::asInterface); - return; - } mAreTaskAndTimeIndependentPrerequisitesMet = checkIndependentVariables(); - mProxyConnectorToMainProfile = null; } private boolean checkIndependentVariables() { @@ -144,40 +107,25 @@ public class AppClipsService extends Service { return new IAppClipsService.Stub() { @Override public boolean canLaunchCaptureContentActivityForNote(int taskId) { - // In case of managed profile, use the main user's instance of the service. Callers - // cannot directly connect to the main user's instance as they may not have the - // permission to interact across users. - if (mUserManager.isManagedProfile()) { - return canLaunchCaptureContentActivityForNoteFromMainUser(taskId); - } + return canLaunchCaptureContentActivityForNoteInternal(taskId) + == CAPTURE_CONTENT_FOR_NOTE_SUCCESS; + } + @Override + @CaptureContentForNoteStatusCodes + public int canLaunchCaptureContentActivityForNoteInternal(int taskId) { if (!mAreTaskAndTimeIndependentPrerequisitesMet) { - return false; + return CAPTURE_CONTENT_FOR_NOTE_FAILED; } if (!mOptionalBubbles.get().isAppBubbleTaskId(taskId)) { - return false; + return CAPTURE_CONTENT_FOR_NOTE_WINDOW_MODE_UNSUPPORTED; } - return !mDevicePolicyManager.getScreenCaptureDisabled(null); + return mDevicePolicyManager.getScreenCaptureDisabled(null) + ? CAPTURE_CONTENT_FOR_NOTE_BLOCKED_BY_ADMIN + : CAPTURE_CONTENT_FOR_NOTE_SUCCESS; } }; } - - /** Returns whether the app clips API can be used by querying the service as the main user. */ - private boolean canLaunchCaptureContentActivityForNoteFromMainUser(int taskId) { - if (mProxyConnectorToMainProfile == null) { - return false; - } - - try { - AndroidFuture<Boolean> future = mProxyConnectorToMainProfile.postForResult( - service -> service.canLaunchCaptureContentActivityForNote(taskId)); - return future.get(); - } catch (ExecutionException | InterruptedException e) { - Log.d(TAG, "Exception from service\n" + e); - } - - return false; - } } diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsTrampolineActivity.java b/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsTrampolineActivity.java index f00803c6d64b..6e5cef47400f 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsTrampolineActivity.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsTrampolineActivity.java @@ -22,41 +22,41 @@ import static android.content.Intent.CAPTURE_CONTENT_FOR_NOTE_SUCCESS; import static android.content.Intent.CAPTURE_CONTENT_FOR_NOTE_WINDOW_MODE_UNSUPPORTED; import static android.content.Intent.EXTRA_CAPTURE_CONTENT_FOR_NOTE_STATUS_CODE; -import static com.android.systemui.flags.Flags.SCREENSHOT_APP_CLIPS; import static com.android.systemui.screenshot.appclips.AppClipsEvent.SCREENSHOT_FOR_NOTE_TRIGGERED; import android.app.Activity; -import android.app.admin.DevicePolicyManager; import android.content.ActivityNotFoundException; import android.content.ComponentName; +import android.content.Context; import android.content.Intent; +import android.content.Intent.CaptureContentForNoteStatusCodes; import android.content.pm.PackageManager; import android.content.pm.PackageManager.ApplicationInfoFlags; import android.content.pm.PackageManager.NameNotFoundException; -import android.content.pm.UserInfo; -import android.content.res.Resources; import android.net.Uri; import android.os.Bundle; import android.os.Handler; import android.os.Parcel; import android.os.ResultReceiver; import android.os.UserHandle; -import android.os.UserManager; import android.util.Log; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; +import com.android.internal.infra.AndroidFuture; +import com.android.internal.infra.ServiceConnector; import com.android.internal.logging.UiEventLogger; +import com.android.internal.statusbar.IAppClipsService; import com.android.systemui.R; +import com.android.systemui.broadcast.BroadcastSender; +import com.android.systemui.dagger.qualifiers.Application; +import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; -import com.android.systemui.flags.FeatureFlags; import com.android.systemui.notetask.NoteTaskController; import com.android.systemui.notetask.NoteTaskEntryPoint; -import com.android.systemui.settings.UserTracker; -import com.android.wm.shell.bubbles.Bubbles; -import java.util.Optional; +import java.util.concurrent.Executor; import javax.inject.Inject; @@ -82,39 +82,57 @@ public class AppClipsTrampolineActivity extends Activity { private static final String TAG = AppClipsTrampolineActivity.class.getSimpleName(); static final String PERMISSION_SELF = "com.android.systemui.permission.SELF"; static final String EXTRA_SCREENSHOT_URI = TAG + "SCREENSHOT_URI"; - @VisibleForTesting - static final String EXTRA_USE_WP_USER = TAG + "USE_WP_USER"; static final String ACTION_FINISH_FROM_TRAMPOLINE = TAG + "FINISH_FROM_TRAMPOLINE"; static final String EXTRA_RESULT_RECEIVER = TAG + "RESULT_RECEIVER"; static final String EXTRA_CALLING_PACKAGE_NAME = TAG + "CALLING_PACKAGE_NAME"; private static final ApplicationInfoFlags APPLICATION_INFO_FLAGS = ApplicationInfoFlags.of(0); - private final DevicePolicyManager mDevicePolicyManager; - private final FeatureFlags mFeatureFlags; - private final Optional<Bubbles> mOptionalBubbles; private final NoteTaskController mNoteTaskController; private final PackageManager mPackageManager; - private final UserTracker mUserTracker; private final UiEventLogger mUiEventLogger; - private final UserManager mUserManager; + private final BroadcastSender mBroadcastSender; + @Background + private final Executor mBgExecutor; + @Main + private final Executor mMainExecutor; private final ResultReceiver mResultReceiver; + private final ServiceConnector<IAppClipsService> mAppClipsServiceConnector; + + private UserHandle mUserHandle; private Intent mKillAppClipsBroadcastIntent; - private UserHandle mNotesAppUser; @Inject - public AppClipsTrampolineActivity(DevicePolicyManager devicePolicyManager, FeatureFlags flags, - Optional<Bubbles> optionalBubbles, NoteTaskController noteTaskController, - PackageManager packageManager, UserTracker userTracker, UiEventLogger uiEventLogger, - UserManager userManager, @Main Handler mainHandler) { - mDevicePolicyManager = devicePolicyManager; - mFeatureFlags = flags; - mOptionalBubbles = optionalBubbles; + public AppClipsTrampolineActivity(@Application Context context, + NoteTaskController noteTaskController, PackageManager packageManager, + UiEventLogger uiEventLogger, BroadcastSender broadcastSender, + @Background Executor bgExecutor, @Main Executor mainExecutor, + @Main Handler mainHandler) { + mNoteTaskController = noteTaskController; + mPackageManager = packageManager; + mUiEventLogger = uiEventLogger; + mBroadcastSender = broadcastSender; + mBgExecutor = bgExecutor; + mMainExecutor = mainExecutor; + + mResultReceiver = createResultReceiver(mainHandler); + mAppClipsServiceConnector = createServiceConnector(context); + } + + /** A constructor used only for testing to verify interactions with {@link ServiceConnector}. */ + @VisibleForTesting + AppClipsTrampolineActivity(ServiceConnector<IAppClipsService> appClipsServiceConnector, + NoteTaskController noteTaskController, PackageManager packageManager, + UiEventLogger uiEventLogger, BroadcastSender broadcastSender, + @Background Executor bgExecutor, @Main Executor mainExecutor, + @Main Handler mainHandler) { + mAppClipsServiceConnector = appClipsServiceConnector; mNoteTaskController = noteTaskController; mPackageManager = packageManager; - mUserTracker = userTracker; mUiEventLogger = uiEventLogger; - mUserManager = userManager; + mBroadcastSender = broadcastSender; + mBgExecutor = bgExecutor; + mMainExecutor = mainExecutor; mResultReceiver = createResultReceiver(mainHandler); } @@ -127,62 +145,62 @@ public class AppClipsTrampolineActivity extends Activity { return; } - if (mUserManager.isManagedProfile()) { - maybeStartActivityForWPUser(); - finish(); - return; - } + mUserHandle = getUser(); - if (!mFeatureFlags.isEnabled(SCREENSHOT_APP_CLIPS)) { - finish(); - return; - } + mBgExecutor.execute(() -> { + AndroidFuture<Integer> statusCodeFuture = mAppClipsServiceConnector.postForResult( + service -> service.canLaunchCaptureContentActivityForNoteInternal(getTaskId())); + statusCodeFuture.whenCompleteAsync(this::handleAppClipsStatusCode, mMainExecutor); + }); + } - if (mOptionalBubbles.isEmpty()) { - setErrorResultAndFinish(CAPTURE_CONTENT_FOR_NOTE_FAILED); - return; + @Override + protected void onDestroy() { + if (isFinishing() && mKillAppClipsBroadcastIntent != null) { + mBroadcastSender.sendBroadcast(mKillAppClipsBroadcastIntent, PERMISSION_SELF); } - if (!mOptionalBubbles.get().isAppBubbleTaskId(getTaskId())) { - setErrorResultAndFinish(CAPTURE_CONTENT_FOR_NOTE_WINDOW_MODE_UNSUPPORTED); - return; - } + super.onDestroy(); + } - if (mDevicePolicyManager.getScreenCaptureDisabled(null)) { - setErrorResultAndFinish(CAPTURE_CONTENT_FOR_NOTE_BLOCKED_BY_ADMIN); + private void handleAppClipsStatusCode(@CaptureContentForNoteStatusCodes int statusCode, + Throwable error) { + if (isFinishing()) { + // It's too late, trampoline activity is finishing or already finished. Return early. return; } - ComponentName componentName; - try { - componentName = ComponentName.unflattenFromString( - getString(R.string.config_screenshotAppClipsActivityComponent)); - } catch (Resources.NotFoundException e) { - setErrorResultAndFinish(CAPTURE_CONTENT_FOR_NOTE_FAILED); + if (error != null) { + Log.d(TAG, "Error querying app clips service", error); + setErrorResultAndFinish(statusCode); return; } - if (componentName == null || componentName.getPackageName().isEmpty() - || componentName.getClassName().isEmpty()) { - setErrorResultAndFinish(CAPTURE_CONTENT_FOR_NOTE_FAILED); - return; - } + switch (statusCode) { + case CAPTURE_CONTENT_FOR_NOTE_SUCCESS: + launchAppClipsActivity(); + break; - mNotesAppUser = getUser(); - if (getIntent().getBooleanExtra(EXTRA_USE_WP_USER, /* defaultValue= */ false)) { - // Get the work profile user internally instead of passing around via intent extras as - // this activity is exported apps could potentially mess around with intent extras. - mNotesAppUser = getWorkProfileUser().orElse(mNotesAppUser); + case CAPTURE_CONTENT_FOR_NOTE_FAILED: + case CAPTURE_CONTENT_FOR_NOTE_WINDOW_MODE_UNSUPPORTED: + case CAPTURE_CONTENT_FOR_NOTE_BLOCKED_BY_ADMIN: + default: + setErrorResultAndFinish(statusCode); } + } + private void launchAppClipsActivity() { + ComponentName componentName = ComponentName.unflattenFromString( + getString(R.string.config_screenshotAppClipsActivityComponent)); String callingPackageName = getCallingPackage(); - Intent intent = new Intent().setComponent(componentName) + + Intent intent = new Intent() + .setComponent(componentName) .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) .putExtra(EXTRA_RESULT_RECEIVER, mResultReceiver) .putExtra(EXTRA_CALLING_PACKAGE_NAME, callingPackageName); try { - // Start the App Clips activity for the user corresponding to the notes app user. - startActivityAsUser(intent, mNotesAppUser); + startActivity(intent); // Set up the broadcast intent that will inform the above App Clips activity to finish // when this trampoline activity is finished. @@ -198,39 +216,6 @@ public class AppClipsTrampolineActivity extends Activity { } } - @Override - protected void onDestroy() { - super.onDestroy(); - - if (isFinishing() && mKillAppClipsBroadcastIntent != null) { - sendBroadcast(mKillAppClipsBroadcastIntent, PERMISSION_SELF); - } - } - - private Optional<UserHandle> getWorkProfileUser() { - return mUserTracker.getUserProfiles().stream() - .filter(profile -> mUserManager.isManagedProfile(profile.id)) - .findFirst() - .map(UserInfo::getUserHandle); - } - - private void maybeStartActivityForWPUser() { - UserHandle mainUser = mUserManager.getMainUser(); - if (mainUser == null) { - setErrorResultAndFinish(CAPTURE_CONTENT_FOR_NOTE_FAILED); - return; - } - - // Start the activity as the main user with activity result forwarding. Set the intent extra - // so that the newly started trampoline activity starts the actual app clips activity as the - // work profile user. Starting the app clips activity as the work profile user is required - // to save the screenshot in work profile user storage and grant read permission to the URI. - startActivityAsUser( - new Intent(this, AppClipsTrampolineActivity.class) - .putExtra(EXTRA_USE_WP_USER, /* value= */ true) - .addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT), mainUser); - } - private void setErrorResultAndFinish(int errorCode) { setResult(RESULT_OK, new Intent().putExtra(EXTRA_CAPTURE_CONTENT_FOR_NOTE_STATUS_CODE, errorCode)); @@ -241,7 +226,7 @@ public class AppClipsTrampolineActivity extends Activity { int callingPackageUid = 0; try { callingPackageUid = mPackageManager.getApplicationInfoAsUser(callingPackageName, - APPLICATION_INFO_FLAGS, mNotesAppUser.getIdentifier()).uid; + APPLICATION_INFO_FLAGS, mUserHandle.getIdentifier()).uid; } catch (NameNotFoundException e) { Log.d(TAG, "Couldn't find notes app UID " + e); } @@ -281,7 +266,7 @@ public class AppClipsTrampolineActivity extends Activity { mKillAppClipsBroadcastIntent = null; // Expand the note bubble before returning the result. - mNoteTaskController.showNoteTaskAsUser(NoteTaskEntryPoint.APP_CLIPS, mNotesAppUser); + mNoteTaskController.showNoteTaskAsUser(NoteTaskEntryPoint.APP_CLIPS, mUserHandle); setResult(RESULT_OK, convertedData); finish(); } @@ -298,11 +283,18 @@ public class AppClipsTrampolineActivity extends Activity { appClipsResultReceiver.writeToParcel(parcel, 0); parcel.setDataPosition(0); - ResultReceiver resultReceiver = ResultReceiver.CREATOR.createFromParcel(parcel); + ResultReceiver resultReceiver = ResultReceiver.CREATOR.createFromParcel(parcel); parcel.recycle(); return resultReceiver; } + private ServiceConnector<IAppClipsService> createServiceConnector( + @Application Context context) { + return new ServiceConnector.Impl<>(context, new Intent(context, AppClipsService.class), + Context.BIND_AUTO_CREATE | Context.BIND_WAIVE_PRIORITY | Context.BIND_NOT_VISIBLE, + UserHandle.USER_SYSTEM, IAppClipsService.Stub::asInterface); + } + /** This is a test only API for mocking response from {@link AppClipsActivity}. */ @VisibleForTesting public ResultReceiver getResultReceiverForTest() { diff --git a/packages/SystemUI/src/com/android/systemui/shade/CombinedShadeHeadersConstraintManagerImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/CombinedShadeHeadersConstraintManagerImpl.kt index 7e0f50400299..a9b3d0a1f3aa 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/CombinedShadeHeadersConstraintManagerImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/CombinedShadeHeadersConstraintManagerImpl.kt @@ -31,8 +31,7 @@ object CombinedShadeHeadersConstraintManagerImpl : CombinedShadeHeadersConstrain val constraintAlpha = if (visible) 0f else 1f return ConstraintsChanges( qqsConstraintsChanges = { - setAlpha(R.id.statusIcons, constraintAlpha) - setAlpha(R.id.batteryRemainingIcon, constraintAlpha) + setAlpha(R.id.shade_header_system_icons, constraintAlpha) } ) } @@ -45,14 +44,15 @@ object CombinedShadeHeadersConstraintManagerImpl : CombinedShadeHeadersConstrain R.id.barrier, ConstraintSet.START, 0, - R.id.statusIcons, + R.id.shade_header_system_icons, R.id.privacy_container ) - connect(R.id.statusIcons, ConstraintSet.START, R.id.date, ConstraintSet.END) + connect(R.id.shade_header_system_icons, ConstraintSet.START, R.id.date, + ConstraintSet.END) connect(R.id.privacy_container, ConstraintSet.START, R.id.date, ConstraintSet.END) - constrainWidth(R.id.statusIcons, ViewGroup.LayoutParams.WRAP_CONTENT) + constrainWidth(R.id.shade_header_system_icons, ViewGroup.LayoutParams.WRAP_CONTENT) constrainedWidth(R.id.date, true) - constrainedWidth(R.id.statusIcons, true) + constrainedWidth(R.id.shade_header_system_icons, true) } ) } @@ -84,7 +84,7 @@ object CombinedShadeHeadersConstraintManagerImpl : CombinedShadeHeadersConstrain setGuidelineEnd(centerEnd, offsetFromEdge) connect(R.id.date, ConstraintSet.END, centerStart, ConstraintSet.START) connect( - R.id.statusIcons, + R.id.shade_header_system_icons, ConstraintSet.START, centerEnd, ConstraintSet.END @@ -96,7 +96,7 @@ object CombinedShadeHeadersConstraintManagerImpl : CombinedShadeHeadersConstrain ConstraintSet.END ) constrainedWidth(R.id.date, true) - constrainedWidth(R.id.statusIcons, true) + constrainedWidth(R.id.shade_header_system_icons, true) }, qsConstraintsChanges = { setGuidelineBegin(centerStart, offsetFromEdge) diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java index 776a90d75a75..f7cacb9ecfa6 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java @@ -17,8 +17,6 @@ package com.android.systemui.shade; import static android.app.StatusBarManager.WINDOW_STATE_SHOWING; -import static android.view.MotionEvent.CLASSIFICATION_MULTI_FINGER_SWIPE; -import static android.view.MotionEvent.CLASSIFICATION_TWO_FINGER_SWIPE; import static android.view.View.INVISIBLE; import static android.view.View.VISIBLE; @@ -30,6 +28,8 @@ import static com.android.systemui.classifier.Classifier.BOUNCER_UNLOCK; import static com.android.systemui.classifier.Classifier.GENERIC; import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS; import static com.android.systemui.classifier.Classifier.UNLOCK; +import static com.android.systemui.navigationbar.gestural.Utilities.isTrackpadScroll; +import static com.android.systemui.navigationbar.gestural.Utilities.isTrackpadThreeFingerSwipe; import static com.android.systemui.shade.ShadeExpansionStateManagerKt.STATE_CLOSED; import static com.android.systemui.shade.ShadeExpansionStateManagerKt.STATE_OPEN; import static com.android.systemui.shade.ShadeExpansionStateManagerKt.STATE_OPENING; @@ -114,6 +114,8 @@ import com.android.systemui.R; import com.android.systemui.animation.ActivityLaunchAnimator; import com.android.systemui.animation.LaunchAnimator; import com.android.systemui.biometrics.AuthController; +import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor; +import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants; import com.android.systemui.classifier.Classifier; import com.android.systemui.classifier.FalsingCollector; import com.android.systemui.dagger.qualifiers.DisplayId; @@ -125,14 +127,14 @@ import com.android.systemui.flags.FeatureFlags; import com.android.systemui.flags.Flags; import com.android.systemui.fragments.FragmentService; import com.android.systemui.keyguard.KeyguardUnlockAnimationController; -import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor; +import com.android.systemui.keyguard.KeyguardViewConfigurator; import com.android.systemui.keyguard.domain.interactor.KeyguardBottomAreaInteractor; import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor; import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor; import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor; -import com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants; import com.android.systemui.keyguard.shared.model.TransitionState; import com.android.systemui.keyguard.shared.model.TransitionStep; +import com.android.systemui.keyguard.shared.model.WakefulnessModel; import com.android.systemui.keyguard.ui.binder.KeyguardLongPressViewBinder; import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel; import com.android.systemui.keyguard.ui.viewmodel.GoneToDreamingTransitionViewModel; @@ -222,8 +224,6 @@ import com.android.systemui.util.Utils; import com.android.systemui.util.time.SystemClock; import com.android.wm.shell.animation.FlingAnimationUtils; -import kotlin.Unit; - import java.io.PrintWriter; import java.util.ArrayList; import java.util.Collections; @@ -234,6 +234,8 @@ import java.util.function.Consumer; import javax.inject.Inject; import javax.inject.Provider; +import kotlin.Unit; + import kotlinx.coroutines.CoroutineDispatcher; @CentralSurfacesComponent.CentralSurfacesScope @@ -599,6 +601,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump private final KeyguardTransitionInteractor mKeyguardTransitionInteractor; private final KeyguardInteractor mKeyguardInteractor; + private final KeyguardViewConfigurator mKeyguardViewConfigurator; private final @Nullable MultiShadeInteractor mMultiShadeInteractor; private final CoroutineDispatcher mMainDispatcher; private boolean mIsAnyMultiShadeExpanded; @@ -741,6 +744,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump KeyguardLongPressViewModel keyguardLongPressViewModel, KeyguardInteractor keyguardInteractor, ActivityStarter activityStarter, + KeyguardViewConfigurator keyguardViewConfigurator, KeyguardFaceAuthInteractor keyguardFaceAuthInteractor) { mInteractionJankMonitor = interactionJankMonitor; keyguardStateController.addCallback(new KeyguardStateController.Callback() { @@ -763,6 +767,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump mLockscreenToOccludedTransitionViewModel = lockscreenToOccludedTransitionViewModel; mKeyguardTransitionInteractor = keyguardTransitionInteractor; mKeyguardInteractor = keyguardInteractor; + mKeyguardViewConfigurator = keyguardViewConfigurator; mView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() { @Override public void onViewAttachedToWindow(View v) { @@ -1321,7 +1326,6 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump mKeyguardBottomArea.initFrom(oldBottomArea); mView.addView(mKeyguardBottomArea, index); initBottomArea(); - mKeyguardIndicationController.setIndicationArea(mKeyguardBottomArea); mStatusBarStateListener.onDozeAmountChanged(mStatusBarStateController.getDozeAmount(), mStatusBarStateController.getInterpolatedDozeAmount()); @@ -1363,6 +1367,9 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump mKeyguardIndicationController.showTransientIndication(stringResourceId), mVibratorHelper, mActivityStarter); + + // Rebind (for now), as a new bottom area and indication area may have been created + mKeyguardViewConfigurator.bindIndicationArea(mKeyguardBottomArea); } @VisibleForTesting @@ -1400,7 +1407,6 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump private void setKeyguardBottomArea(KeyguardBottomAreaView keyguardBottomArea) { mKeyguardBottomArea = keyguardBottomArea; - mKeyguardIndicationController.setIndicationArea(mKeyguardBottomArea); } void setOpenCloseListener(OpenCloseListener openCloseListener) { @@ -2148,10 +2154,14 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump } int getFalsingThreshold() { - float factor = mCentralSurfaces.isWakeUpComingFromTouch() ? 1.5f : 1.0f; + float factor = ShadeViewController.getFalsingThresholdFactor(getWakefulness()); return (int) (mQsController.getFalsingThreshold() * factor); } + private WakefulnessModel getWakefulness() { + return mKeyguardInteractor.getWakefulnessModel().getValue(); + } + private void maybeAnimateBottomAreaAlpha() { mBottomAreaShadeAlphaAnimator.cancel(); if (mBarState == StatusBarState.SHADE_LOCKED) { @@ -2429,6 +2439,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump boolean isExpanded = !isFullyCollapsed() || mExpectingSynthesizedDown; if (mPanelExpanded != isExpanded) { mPanelExpanded = isExpanded; + updateSystemUiStateFlags(); mShadeExpansionStateManager.onShadeExpansionFullyChanged(isExpanded); if (!isExpanded) { mQsController.closeQsCustomizer(); @@ -2436,6 +2447,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump } } + @Override public boolean isPanelExpanded() { return mPanelExpanded; } @@ -2463,7 +2475,8 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump alpha = getFadeoutAlpha(); } if (mBarState == KEYGUARD && !mHintAnimationRunning - && !mKeyguardBypassController.getBypassEnabled()) { + && !mKeyguardBypassController.getBypassEnabled() + && !mQsController.getFullyExpanded()) { alpha *= mClockPositionResult.clockAlpha; } mNotificationStackScrollLayoutController.setAlpha(alpha); @@ -2571,7 +2584,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump this); return; } - if (mCentralSurfaces.getNotificationShadeWindowView() + if (mNotificationShadeWindowController.getWindowRootView() .isVisibleToUser()) { mView.getViewTreeObserver().removeOnGlobalLayoutListener( this); @@ -2893,9 +2906,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump float scrimMinFraction; if (mSplitShadeEnabled) { boolean highHun = mHeadsUpStartHeight * 2.5 - > - (mFeatureFlags.isEnabled(Flags.LARGE_SHADE_GRANULAR_ALPHA_INTERPOLATION) - ? mSplitShadeFullTransitionDistance : mSplitShadeScrimTransitionDistance); + > mSplitShadeFullTransitionDistance; // if HUN height is higher than 40% of predefined transition distance, it means HUN // is too high for regular transition. In that case we need to calculate transition // distance - here we take scrim transition distance as equal to shade transition @@ -3026,12 +3037,6 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump mKeyguardStatusViewController.setStatusAccessibilityImportance(mode); } - //TODO(b/254875405): this should be removed. - @Override - public KeyguardBottomAreaView getKeyguardBottomAreaView() { - return mKeyguardBottomArea; - } - @Override public void applyLaunchAnimationProgress(float linearProgress) { boolean hideIcons = LaunchAnimator.getProgress(ActivityLaunchAnimator.TIMINGS, @@ -3435,9 +3440,8 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump Log.d(TAG, "Updating panel sysui state flags: fullyExpanded=" + isFullyExpanded() + " inQs=" + mQsController.getExpanded()); } - boolean isPanelVisible = mCentralSurfaces != null && mCentralSurfaces.isPanelExpanded(); mSysUiState - .setFlag(SYSUI_STATE_NOTIFICATION_PANEL_VISIBLE, isPanelVisible) + .setFlag(SYSUI_STATE_NOTIFICATION_PANEL_VISIBLE, mPanelExpanded) .setFlag(SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED, isFullyExpanded() && !mQsController.getExpanded()) .setFlag(SYSUI_STATE_QUICK_SETTINGS_EXPANDED, @@ -3592,8 +3596,10 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump expand = flingExpands(vel, vectorVel, x, y); } - mDozeLog.traceFling(expand, mTouchAboveFalsingThreshold, - mCentralSurfaces.isWakeUpComingFromTouch()); + mDozeLog.traceFling( + expand, + mTouchAboveFalsingThreshold, + /* screenOnFromTouch=*/ getWakefulness().isDeviceInteractiveFromTapOrGesture()); // Log collapse gesture if on lock screen. if (!expand && onKeyguard) { float displayDensity = mCentralSurfaces.getDisplayDensity(); @@ -4660,6 +4666,9 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump final float x = event.getX(pointerIndex); final float y = event.getY(pointerIndex); boolean canCollapsePanel = canCollapsePanelOnTouch(); + final boolean isTrackpadTwoOrThreeFingerSwipe = isTrackpadScroll( + mTrackpadGestureFeaturesEnabled, event) || isTrackpadThreeFingerSwipe( + mTrackpadGestureFeaturesEnabled, event); switch (event.getActionMasked()) { case MotionEvent.ACTION_DOWN: @@ -4692,7 +4701,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump addMovement(event); break; case MotionEvent.ACTION_POINTER_UP: - if (isTrackpadMotionEvent(event)) { + if (isTrackpadTwoOrThreeFingerSwipe) { break; } final int upPointer = event.getPointerId(event.getActionIndex()); @@ -4708,7 +4717,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump mShadeLog.logMotionEventStatusBarState(event, mStatusBarStateController.getState(), "onInterceptTouchEvent: pointer down action"); - if (!isTrackpadMotionEvent(event) + if (!isTrackpadTwoOrThreeFingerSwipe && mStatusBarStateController.getState() == StatusBarState.KEYGUARD) { mMotionAborted = true; mVelocityTracker.clear(); @@ -4858,9 +4867,13 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump return false; } + final boolean isTrackpadTwoOrThreeFingerSwipe = isTrackpadScroll( + mTrackpadGestureFeaturesEnabled, event) || isTrackpadThreeFingerSwipe( + mTrackpadGestureFeaturesEnabled, event); + // On expanding, single mouse click expands the panel instead of dragging. if (isFullyCollapsed() && (event.isFromSource(InputDevice.SOURCE_MOUSE) - && !isTrackpadMotionEvent(event))) { + && !isTrackpadTwoOrThreeFingerSwipe)) { if (event.getAction() == MotionEvent.ACTION_UP) { expand(true /* animate */); } @@ -4920,7 +4933,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump break; case MotionEvent.ACTION_POINTER_UP: - if (isTrackpadMotionEvent(event)) { + if (isTrackpadTwoOrThreeFingerSwipe) { break; } final int upPointer = event.getPointerId(event.getActionIndex()); @@ -4939,7 +4952,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump mShadeLog.logMotionEventStatusBarState(event, mStatusBarStateController.getState(), "handleTouch: pointer down action"); - if (!isTrackpadMotionEvent(event) + if (!isTrackpadTwoOrThreeFingerSwipe && mStatusBarStateController.getState() == StatusBarState.KEYGUARD) { mMotionAborted = true; endMotionEvent(event, x, y, true /* forceCancel */); @@ -5013,12 +5026,6 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump } return !mGestureWaitForTouchSlop || mTracking; } - - private boolean isTrackpadMotionEvent(MotionEvent ev) { - return mTrackpadGestureFeaturesEnabled && ( - ev.getClassification() == CLASSIFICATION_MULTI_FINGER_SWIPE - || ev.getClassification() == CLASSIFICATION_TWO_FINGER_SWIPE); - } } private final class HeadsUpNotificationViewControllerImpl implements diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java index 0c800d456f3c..8105a145d15a 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java @@ -103,7 +103,7 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW private final KeyguardViewMediator mKeyguardViewMediator; private final KeyguardBypassController mKeyguardBypassController; private final AuthController mAuthController; - private ViewGroup mNotificationShadeView; + private ViewGroup mWindowRootView; private LayoutParams mLp; private boolean mHasTopUi; private boolean mHasTopUiChanged; @@ -262,7 +262,7 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW mLp.privateFlags |= PRIVATE_FLAG_BEHAVIOR_CONTROLLED; mLp.insetsFlags.behavior = BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE; - mWindowManager.addView(mNotificationShadeView, mLp); + mWindowManager.addView(mWindowRootView, mLp); mLpChanged.copyFrom(mLp); onThemeChanged(); @@ -274,13 +274,13 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW } @Override - public void setNotificationShadeView(ViewGroup view) { - mNotificationShadeView = view; + public void setWindowRootView(ViewGroup view) { + mWindowRootView = view; } @Override - public ViewGroup getNotificationShadeView() { - return mNotificationShadeView; + public ViewGroup getWindowRootView() { + return mWindowRootView; } @Override @@ -289,7 +289,7 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW } private void setKeyguardDark(boolean dark) { - int vis = mNotificationShadeView.getSystemUiVisibility(); + int vis = mWindowRootView.getSystemUiVisibility(); if (dark) { vis = vis | View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR; vis = vis | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR; @@ -297,7 +297,7 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW vis = vis & ~View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR; vis = vis & ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR; } - mNotificationShadeView.setSystemUiVisibility(vis); + mWindowRootView.setSystemUiVisibility(vis); } private void applyKeyguardFlags(NotificationShadeWindowState state) { @@ -413,11 +413,11 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW visible = true; mLogger.d("Visibility forced to be true"); } - if (mNotificationShadeView != null) { + if (mWindowRootView != null) { if (visible) { - mNotificationShadeView.setVisibility(View.VISIBLE); + mWindowRootView.setVisibility(View.VISIBLE); } else { - mNotificationShadeView.setVisibility(View.INVISIBLE); + mWindowRootView.setVisibility(View.INVISIBLE); } } } @@ -439,10 +439,10 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW private void applyFitsSystemWindows(NotificationShadeWindowState state) { boolean fitsSystemWindows = !state.isKeyguardShowingAndNotOccluded(); - if (mNotificationShadeView != null - && mNotificationShadeView.getFitsSystemWindows() != fitsSystemWindows) { - mNotificationShadeView.setFitsSystemWindows(fitsSystemWindows); - mNotificationShadeView.requestApplyInsets(); + if (mWindowRootView != null + && mWindowRootView.getFitsSystemWindows() != fitsSystemWindows) { + mWindowRootView.setFitsSystemWindows(fitsSystemWindows); + mWindowRootView.requestApplyInsets(); } } @@ -482,7 +482,7 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW if (mDeferWindowLayoutParams == 0 && mLp != null && mLp.copyFrom(mLpChanged) != 0) { mLogger.logApplyingWindowLayoutParams(mLp); Trace.beginSection("updateViewLayout"); - mWindowManager.updateViewLayout(mNotificationShadeView, mLp); + mWindowManager.updateViewLayout(mWindowRootView, mLp); Trace.endSection(); } } @@ -608,7 +608,7 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW try { final IWindowSession session = WindowManagerGlobal.getWindowSession(); session.updateTapExcludeRegion( - IWindow.Stub.asInterface(getNotificationShadeView().getWindowToken()), + IWindow.Stub.asInterface(getWindowRootView().getWindowToken()), region); } catch (RemoteException e) { Log.e(TAG, "could not update the tap exclusion region:" + e); @@ -847,8 +847,8 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW pw.println(" mKeyguardPreferredRefreshRate=" + mKeyguardPreferredRefreshRate); pw.println(" mDeferWindowLayoutParams=" + mDeferWindowLayoutParams); pw.println(mCurrentState); - if (mNotificationShadeView != null && mNotificationShadeView.getViewRootImpl() != null) { - mNotificationShadeView.getViewRootImpl().dump(" ", pw); + if (mWindowRootView != null && mWindowRootView.getViewRootImpl() != null) { + mWindowRootView.getViewRootImpl().dump(" ", pw); } new DumpsysTableLogger( TAG, @@ -864,7 +864,7 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW @Override public void onThemeChanged() { - if (mNotificationShadeView == null) { + if (mWindowRootView == null) { return; } diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowView.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowView.java index d75190e7289a..2b62b7d67c2a 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowView.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowView.java @@ -58,12 +58,14 @@ import android.widget.FrameLayout; import com.android.internal.view.FloatingActionMode; import com.android.internal.widget.floatingtoolbar.FloatingToolbar; import com.android.systemui.R; -import com.android.systemui.compose.ComposeFacade; +import com.android.systemui.scene.ui.view.WindowRootView; /** - * Combined keyguard and notification panel view. Also holding backdrop and scrims. + * Combined keyguard and notification panel view. Also holding backdrop and scrims. This view can + * serve as the root view of the main SysUI window, but because other views can also serve that + * purpose, users of this class cannot assume it is the root. */ -public class NotificationShadeWindowView extends FrameLayout { +public class NotificationShadeWindowView extends WindowRootView { public static final String TAG = "NotificationShadeWindowView"; private int mRightInset = 0; @@ -146,18 +148,6 @@ public class NotificationShadeWindowView extends FrameLayout { protected void onAttachedToWindow() { super.onAttachedToWindow(); setWillNotDraw(!DEBUG); - - if (ComposeFacade.INSTANCE.isComposeAvailable()) { - ComposeFacade.INSTANCE.composeInitializer().onAttachedToWindow(this); - } - } - - @Override - protected void onDetachedFromWindow() { - super.onDetachedFromWindow(); - if (ComposeFacade.INSTANCE.isComposeAvailable()) { - ComposeFacade.INSTANCE.composeInitializer().onDetachedFromWindow(this); - } } @Override diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java index 2f7644eb5c82..ade59d7d85bb 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java @@ -36,9 +36,14 @@ import androidx.annotation.Nullable; import com.android.internal.annotations.VisibleForTesting; import com.android.keyguard.AuthKeyguardMessageArea; +import com.android.keyguard.KeyguardMessageAreaController; import com.android.keyguard.LockIconViewController; import com.android.keyguard.dagger.KeyguardBouncerComponent; import com.android.systemui.R; +import com.android.systemui.back.domain.interactor.BackActionInteractor; +import com.android.systemui.bouncer.domain.interactor.BouncerMessageInteractor; +import com.android.systemui.bouncer.ui.binder.KeyguardBouncerViewBinder; +import com.android.systemui.bouncer.ui.viewmodel.KeyguardBouncerViewModel; import com.android.systemui.classifier.FalsingCollector; import com.android.systemui.compose.ComposeFacade; import com.android.systemui.dock.DockManager; @@ -48,9 +53,8 @@ import com.android.systemui.keyguard.KeyguardUnlockAnimationController; import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor; import com.android.systemui.keyguard.shared.model.TransitionState; import com.android.systemui.keyguard.shared.model.TransitionStep; -import com.android.systemui.keyguard.ui.binder.KeyguardBouncerViewBinder; -import com.android.systemui.keyguard.ui.viewmodel.KeyguardBouncerViewModel; import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel; +import com.android.systemui.log.BouncerLogger; import com.android.systemui.multishade.domain.interactor.MultiShadeInteractor; import com.android.systemui.multishade.domain.interactor.MultiShadeMotionEventInteractor; import com.android.systemui.multishade.ui.view.MultiShadeView; @@ -108,6 +112,7 @@ public class NotificationShadeWindowViewController { private NotificationStackScrollLayout mStackScrollLayout; private PhoneStatusBarViewController mStatusBarViewController; private final CentralSurfaces mService; + private final BackActionInteractor mBackActionInteractor; private final NotificationShadeWindowController mNotificationShadeWindowController; private DragDownHelper mDragDownHelper; private boolean mExpandingBelowNotch; @@ -141,6 +146,7 @@ public class NotificationShadeWindowViewController { StatusBarWindowStateController statusBarWindowStateController, LockIconViewController lockIconViewController, CentralSurfaces centralSurfaces, + BackActionInteractor backActionInteractor, NotificationShadeWindowController controller, Optional<UnfoldTransitionProgressProvider> unfoldTransitionProgressProvider, KeyguardUnlockAnimationController keyguardUnlockAnimationController, @@ -149,12 +155,15 @@ public class NotificationShadeWindowViewController { PulsingGestureListener pulsingGestureListener, KeyguardBouncerViewModel keyguardBouncerViewModel, KeyguardBouncerComponent.Factory keyguardBouncerComponentFactory, + KeyguardMessageAreaController.Factory messageAreaControllerFactory, KeyguardTransitionInteractor keyguardTransitionInteractor, PrimaryBouncerToGoneTransitionViewModel primaryBouncerToGoneTransitionViewModel, FeatureFlags featureFlags, Provider<MultiShadeInteractor> multiShadeInteractorProvider, SystemClock clock, - Provider<MultiShadeMotionEventInteractor> multiShadeMotionEventInteractorProvider) { + Provider<MultiShadeMotionEventInteractor> multiShadeMotionEventInteractorProvider, + BouncerMessageInteractor bouncerMessageInteractor, + BouncerLogger bouncerLogger) { mLockscreenShadeTransitionController = transitionController; mFalsingCollector = falsingCollector; mStatusBarStateController = statusBarStateController; @@ -167,6 +176,7 @@ public class NotificationShadeWindowViewController { mStatusBarKeyguardViewManager = statusBarKeyguardViewManager; mStatusBarWindowStateController = statusBarWindowStateController; mLockIconViewController = lockIconViewController; + mBackActionInteractor = backActionInteractor; mLockIconViewController.init(); mService = centralSurfaces; mNotificationShadeWindowController = controller; @@ -183,7 +193,11 @@ public class NotificationShadeWindowViewController { mView.findViewById(R.id.keyguard_bouncer_container), keyguardBouncerViewModel, primaryBouncerToGoneTransitionViewModel, - keyguardBouncerComponentFactory); + keyguardBouncerComponentFactory, + messageAreaControllerFactory, + bouncerMessageInteractor, + bouncerLogger, + featureFlags); collectFlow(mView, keyguardTransitionInteractor.getLockscreenToDreamingTransition(), mLockscreenToDreamingTransition); @@ -433,7 +447,7 @@ public class NotificationShadeWindowViewController { switch (event.getKeyCode()) { case KeyEvent.KEYCODE_BACK: if (!down) { - mService.onBackPressed(); + mBackActionInteractor.onBackRequested(); } return true; case KeyEvent.KEYCODE_MENU: diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt b/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt index 81fe3ca82257..fba01201190e 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt @@ -27,6 +27,7 @@ import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID import androidx.constraintlayout.widget.ConstraintSet.START import androidx.constraintlayout.widget.ConstraintSet.TOP import com.android.systemui.R +import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.fragments.FragmentService import com.android.systemui.navigationbar.NavigationModeController @@ -45,6 +46,7 @@ import kotlin.reflect.KMutableProperty0 @VisibleForTesting internal const val INSET_DEBOUNCE_MILLIS = 500L +@SysUISingleton class NotificationsQSContainerController @Inject constructor( view: NotificationsQuickSettingsContainer, private val navigationModeController: NavigationModeController, diff --git a/packages/SystemUI/src/com/android/systemui/shade/PulsingGestureListener.kt b/packages/SystemUI/src/com/android/systemui/shade/PulsingGestureListener.kt index fd82e2fc01fc..ee4e98e094fc 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/PulsingGestureListener.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/PulsingGestureListener.kt @@ -18,19 +18,18 @@ package com.android.systemui.shade import android.hardware.display.AmbientDisplayConfiguration import android.os.PowerManager -import android.os.SystemClock import android.provider.Settings import android.view.GestureDetector import android.view.MotionEvent import com.android.systemui.Dumpable +import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dock.DockManager import com.android.systemui.dump.DumpManager import com.android.systemui.plugins.FalsingManager import com.android.systemui.plugins.FalsingManager.LOW_PENALTY import com.android.systemui.plugins.statusbar.StatusBarStateController +import com.android.systemui.power.domain.interactor.PowerInteractor import com.android.systemui.settings.UserTracker -import com.android.systemui.statusbar.phone.CentralSurfaces -import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent import com.android.systemui.tuner.TunerService import com.android.systemui.tuner.TunerService.Tunable import java.io.PrintWriter @@ -45,12 +44,11 @@ import javax.inject.Inject * screen is still ON and not in the true AoD display state. When the device is in the true AoD * display state, wake-ups are handled by [com.android.systemui.doze.DozeSensors]. */ -@CentralSurfacesComponent.CentralSurfacesScope +@SysUISingleton class PulsingGestureListener @Inject constructor( - private val notificationShadeWindowView: NotificationShadeWindowView, private val falsingManager: FalsingManager, private val dockManager: DockManager, - private val centralSurfaces: CentralSurfaces, + private val powerInteractor: PowerInteractor, private val ambientDisplayConfiguration: AmbientDisplayConfiguration, private val statusBarStateController: StatusBarStateController, private val shadeLogger: ShadeLogger, @@ -88,11 +86,7 @@ class PulsingGestureListener @Inject constructor( shadeLogger.logSingleTapUpFalsingState(proximityIsNotNear, isNotAFalseTap) if (proximityIsNotNear && isNotAFalseTap) { shadeLogger.d("Single tap handled, requesting centralSurfaces.wakeUpIfDozing") - centralSurfaces.wakeUpIfDozing( - SystemClock.uptimeMillis(), - "PULSING_SINGLE_TAP", - PowerManager.WAKE_REASON_TAP - ) + powerInteractor.wakeUpIfDozing("PULSING_SINGLE_TAP", PowerManager.WAKE_REASON_TAP) } return true } @@ -113,11 +107,7 @@ class PulsingGestureListener @Inject constructor( !falsingManager.isProximityNear && !falsingManager.isFalseDoubleTap ) { - centralSurfaces.wakeUpIfDozing( - SystemClock.uptimeMillis(), - "PULSING_DOUBLE_TAP", - PowerManager.WAKE_REASON_TAP - ) + powerInteractor.wakeUpIfDozing("PULSING_DOUBLE_TAP", PowerManager.WAKE_REASON_TAP) return true } return false diff --git a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java index 7d66a0b431fa..91e2aa022b36 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java @@ -780,7 +780,6 @@ public class QuickSettingsController implements Dumpable { /** update Qs height state */ public void setExpansionHeight(float height) { - checkCorrectSplitShadeState(height); int maxHeight = getMaxExpansionHeight(); height = Math.min(Math.max( height, getMinExpansionHeight()), maxHeight); @@ -802,14 +801,6 @@ public class QuickSettingsController implements Dumpable { } } - /** TODO(b/269742565) Remove this logging */ - private void checkCorrectSplitShadeState(float height) { - if (mSplitShadeEnabled && height == 0 - && mPanelViewControllerLazy.get().isShadeFullyExpanded()) { - Log.wtfStack(TAG, "qsExpansion set to 0 while split shade is expanding or open"); - } - } - /** */ public void setHeightOverrideToDesiredHeight() { if (isSizeChangeAnimationRunning() && isQsFragmentCreated()) { @@ -1888,7 +1879,7 @@ public class QuickSettingsController implements Dumpable { target = getMaxExpansionHeight(); break; case FLING_COLLAPSE: - if (mSplitShadeEnabled) { // TODO:(b/269742565) remove below log + if (mSplitShadeEnabled) { Log.wtfStack(TAG, "FLING_COLLAPSE called in split shade"); } setExpandImmediate(false); diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeController.java b/packages/SystemUI/src/com/android/systemui/shade/ShadeController.java index d0a3cbbf0b02..9ed0e9a8b359 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ShadeController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeController.java @@ -38,23 +38,41 @@ public interface ShadeController { /** Collapse the shade instantly with no animation. */ void instantCollapseShade(); - /** See {@link #animateCollapsePanels(int, boolean, boolean, float)}. */ + /** See {@link #animateCollapseShade(int, boolean, boolean, float)}. */ void animateCollapseShade(); - /** See {@link #animateCollapsePanels(int, boolean, boolean, float)}. */ + /** See {@link #animateCollapseShade(int, boolean, boolean, float)}. */ void animateCollapseShade(int flags); - /** See {@link #animateCollapsePanels(int, boolean, boolean, float)}. */ + /** See {@link #animateCollapseShade(int, boolean, boolean, float)}. */ void animateCollapseShadeForced(); - /** See {@link #animateCollapsePanels(int, boolean, boolean, float)}. */ - void animateCollapseShadeDelayed(); + /** See {@link #animateCollapseShade(int, boolean, boolean, float)}. */ + void animateCollapseShadeForcedDelayed(); /** * Collapse the shade animated, showing the bouncer when on {@link StatusBarState#KEYGUARD} or * dismissing status bar when on {@link StatusBarState#SHADE}. */ - void animateCollapsePanels(int flags, boolean force, boolean delayed, float speedUpFactor); + void animateCollapseShade(int flags, boolean force, boolean delayed, float speedUpFactor); + + /** Expand the shade with an animation. */ + void animateExpandShade(); + + /** Expand the shade with quick settings expanded with an animation. */ + void animateExpandQs(); + + /** Posts a request to collapse the shade. */ + void postAnimateCollapseShade(); + + /** Posts a request to force collapse the shade. */ + void postAnimateForceCollapseShade(); + + /** Posts a request to expand the shade to quick settings. */ + void postAnimateExpandQs(); + + /** Cancels any ongoing expansion touch handling and collapses the shade. */ + void cancelExpansionAndCollapseShade(); /** * If the shade is not fully expanded, collapse it animated. @@ -115,6 +133,9 @@ public interface ShadeController { */ void collapseShade(boolean animate); + /** Calls #collapseShade if already on the main thread. If not, posts a call to it. */ + void collapseOnMainThread(); + /** Makes shade expanded but not visible. */ void makeExpandedInvisible(); @@ -127,8 +148,11 @@ public interface ShadeController { /** Handle status bar touch event. */ void onStatusBarTouch(MotionEvent event); - /** Called when the shade finishes collapsing. */ - void onClosingFinished(); + /** Called when a launch animation was cancelled. */ + void onLaunchAnimationCancelled(boolean isLaunchForActivity); + + /** Called when a launch animation ends. */ + void onLaunchAnimationEnd(boolean launchIsFullScreen); /** Sets the listener for when the visibility of the shade changes. */ void setVisibilityListener(ShadeVisibilityListener listener); diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java index d00dab633014..c9338b3614ea 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java @@ -17,6 +17,7 @@ package com.android.systemui.shade; import android.content.ComponentCallbacks2; +import android.os.Looper; import android.util.Log; import android.view.MotionEvent; import android.view.ViewTreeObserver; @@ -25,6 +26,7 @@ import android.view.WindowManagerGlobal; import com.android.systemui.assist.AssistManager; import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.NotificationPresenter; @@ -32,12 +34,14 @@ import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.notification.row.NotificationGutsManager; import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; +import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.window.StatusBarWindowController; import dagger.Lazy; import java.util.ArrayList; +import java.util.concurrent.Executor; import javax.inject.Inject; @@ -51,11 +55,13 @@ public final class ShadeControllerImpl implements ShadeController { private final int mDisplayId; private final CommandQueue mCommandQueue; + private final Executor mMainExecutor; private final KeyguardStateController mKeyguardStateController; private final NotificationShadeWindowController mNotificationShadeWindowController; private final StatusBarStateController mStatusBarStateController; private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; private final StatusBarWindowController mStatusBarWindowController; + private final DeviceProvisionedController mDeviceProvisionedController; private final Lazy<AssistManager> mAssistManagerLazy; private final Lazy<NotificationGutsManager> mGutsManager; @@ -72,18 +78,22 @@ public final class ShadeControllerImpl implements ShadeController { @Inject public ShadeControllerImpl( CommandQueue commandQueue, + @Main Executor mainExecutor, KeyguardStateController keyguardStateController, StatusBarStateController statusBarStateController, StatusBarKeyguardViewManager statusBarKeyguardViewManager, StatusBarWindowController statusBarWindowController, + DeviceProvisionedController deviceProvisionedController, NotificationShadeWindowController notificationShadeWindowController, WindowManager windowManager, Lazy<AssistManager> assistManagerLazy, Lazy<NotificationGutsManager> gutsManager ) { mCommandQueue = commandQueue; + mMainExecutor = mainExecutor; mStatusBarStateController = statusBarStateController; mStatusBarWindowController = statusBarWindowController; + mDeviceProvisionedController = deviceProvisionedController; mGutsManager = gutsManager; mNotificationShadeWindowController = notificationShadeWindowController; mStatusBarKeyguardViewManager = statusBarKeyguardViewManager; @@ -107,21 +117,21 @@ public final class ShadeControllerImpl implements ShadeController { @Override public void animateCollapseShade(int flags) { - animateCollapsePanels(flags, false, false, 1.0f); + animateCollapseShade(flags, false, false, 1.0f); } @Override public void animateCollapseShadeForced() { - animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE, true, false, 1.0f); + animateCollapseShade(CommandQueue.FLAG_EXCLUDE_NONE, true, false, 1.0f); } @Override - public void animateCollapseShadeDelayed() { - animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true, true, 1.0f); + public void animateCollapseShadeForcedDelayed() { + animateCollapseShade(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true, true, 1.0f); } @Override - public void animateCollapsePanels(int flags, boolean force, boolean delayed, + public void animateCollapseShade(int flags, boolean force, boolean delayed, float speedUpFactor) { if (!force && mStatusBarStateController.getState() != StatusBarState.SHADE) { runPostCollapseRunnables(); @@ -143,6 +153,25 @@ public final class ShadeControllerImpl implements ShadeController { } @Override + public void animateExpandShade() { + if (!mCommandQueue.panelsEnabled()) { + return; + } + mNotificationPanelViewController.expandToNotifications(); + } + + @Override + public void animateExpandQs() { + if (!mCommandQueue.panelsEnabled()) { + return; + } + // Settings are not available in setup + if (!mDeviceProvisionedController.isCurrentUserSetup()) return; + + mNotificationPanelViewController.expandToQs(); + } + + @Override public boolean closeShadeIfOpen() { if (!mNotificationPanelViewController.isFullyCollapsed()) { mCommandQueue.animateCollapsePanels( @@ -167,6 +196,20 @@ public final class ShadeControllerImpl implements ShadeController { public boolean isExpandingOrCollapsing() { return mNotificationPanelViewController.isExpandingOrCollapsing(); } + @Override + public void postAnimateCollapseShade() { + mMainExecutor.execute(this::animateCollapseShade); + } + + @Override + public void postAnimateForceCollapseShade() { + mMainExecutor.execute(this::animateCollapseShadeForced); + } + + @Override + public void postAnimateExpandQs() { + mMainExecutor.execute(this::animateExpandQs); + } @Override public void postOnShadeExpanded(Runnable executable) { @@ -202,7 +245,7 @@ public final class ShadeControllerImpl implements ShadeController { public boolean collapseShade() { if (!mNotificationPanelViewController.isFullyCollapsed()) { // close the shade if it was open - animateCollapseShadeDelayed(); + animateCollapseShadeForcedDelayed(); notifyVisibilityChanged(false); return true; @@ -227,6 +270,26 @@ public final class ShadeControllerImpl implements ShadeController { } @Override + public void cancelExpansionAndCollapseShade() { + if (mNotificationPanelViewController.isTracking()) { + mNotificationShadeWindowViewController.cancelCurrentTouch(); + } + if (mNotificationPanelViewController.isPanelExpanded() + && mStatusBarStateController.getState() == StatusBarState.SHADE) { + animateCollapseShade(); + } + } + + @Override + public void collapseOnMainThread() { + if (Looper.getMainLooper().isCurrentThread()) { + collapseShade(); + } else { + mMainExecutor.execute(this::collapseShade); + } + } + + @Override public void onStatusBarTouch(MotionEvent event) { if (event.getAction() == MotionEvent.ACTION_UP) { if (mExpandedVisible) { @@ -235,8 +298,7 @@ public final class ShadeControllerImpl implements ShadeController { } } - @Override - public void onClosingFinished() { + private void onClosingFinished() { runPostCollapseRunnables(); if (!mPresenter.isPresenterFullyCollapsed()) { // if we set it not to be focusable when collapsing, we have to undo it when we aborted @@ -246,6 +308,27 @@ public final class ShadeControllerImpl implements ShadeController { } @Override + public void onLaunchAnimationCancelled(boolean isLaunchForActivity) { + if (mPresenter.isPresenterFullyCollapsed() + && !mPresenter.isCollapsing() + && isLaunchForActivity) { + onClosingFinished(); + } else { + collapseShade(true /* animate */); + } + } + + @Override + public void onLaunchAnimationEnd(boolean launchIsFullScreen) { + if (!mPresenter.isCollapsing()) { + onClosingFinished(); + } + if (launchIsFullScreen) { + instantCollapseShade(); + } + } + + @Override public void instantCollapseShade() { mNotificationPanelViewController.instantCollapse(); runPostCollapseRunnables(); diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt index b7551cf3408e..a2b93516695a 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt @@ -16,6 +16,7 @@ package com.android.systemui.shade +import android.annotation.SuppressLint import android.content.ContentResolver import android.os.Handler import android.view.LayoutInflater @@ -28,12 +29,25 @@ import com.android.systemui.battery.BatteryMeterView import com.android.systemui.battery.BatteryMeterViewController import com.android.systemui.biometrics.AuthRippleController import com.android.systemui.biometrics.AuthRippleView +import com.android.systemui.compose.ComposeFacade import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.flags.FeatureFlags +import com.android.systemui.flags.Flags +import com.android.systemui.keyguard.ui.view.KeyguardRootView import com.android.systemui.privacy.OngoingPrivacyChip +import com.android.systemui.scene.shared.model.Scene +import com.android.systemui.scene.shared.model.SceneContainerConfig +import com.android.systemui.scene.shared.model.SceneContainerNames +import com.android.systemui.scene.ui.view.SceneWindowRootView +import com.android.systemui.scene.ui.view.WindowRootView +import com.android.systemui.scene.ui.viewmodel.SceneContainerViewModel import com.android.systemui.settings.UserTracker import com.android.systemui.statusbar.LightRevealScrim +import com.android.systemui.statusbar.NotificationShelf +import com.android.systemui.statusbar.NotificationShelfController +import com.android.systemui.statusbar.notification.row.dagger.NotificationShelfComponent +import com.android.systemui.statusbar.notification.shelf.ui.viewbinder.NotificationShelfViewBinderWrapperControllerImpl import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout import com.android.systemui.statusbar.phone.StatusIconContainer import com.android.systemui.statusbar.phone.TapAgainView @@ -46,6 +60,7 @@ import dagger.Provides import dagger.multibindings.ClassKey import dagger.multibindings.IntoMap import javax.inject.Named +import javax.inject.Provider /** Module for classes related to the notification shade. */ @Module @@ -59,19 +74,51 @@ abstract class ShadeModule { companion object { const val SHADE_HEADER = "large_screen_shade_header" + @SuppressLint("InflateParams") // Root views don't have parents. + @Provides + @SysUISingleton + fun providesWindowRootView( + layoutInflater: LayoutInflater, + featureFlags: FeatureFlags, + @Named(SceneContainerNames.SYSTEM_UI_DEFAULT) + viewModelProvider: Provider<SceneContainerViewModel>, + @Named(SceneContainerNames.SYSTEM_UI_DEFAULT) + containerConfigProvider: Provider<SceneContainerConfig>, + @Named(SceneContainerNames.SYSTEM_UI_DEFAULT) + scenesProvider: Provider<Set<@JvmSuppressWildcards Scene>>, + ): WindowRootView { + return if ( + featureFlags.isEnabled(Flags.SCENE_CONTAINER) && ComposeFacade.isComposeAvailable() + ) { + val sceneWindowRootView = + layoutInflater.inflate(R.layout.scene_window_root, null) as SceneWindowRootView + sceneWindowRootView.init( + viewModel = viewModelProvider.get(), + containerConfig = containerConfigProvider.get(), + scenes = scenesProvider.get(), + ) + sceneWindowRootView + } else { + layoutInflater.inflate(R.layout.super_notification_shade, null) + } + as WindowRootView? + ?: throw IllegalStateException("Window root view could not be properly inflated") + } + @Provides @SysUISingleton // TODO(b/277762009): Do something similar to // {@link StatusBarWindowModule.InternalWindowView} so that only // {@link NotificationShadeWindowViewController} can inject this view. fun providesNotificationShadeWindowView( - layoutInflater: LayoutInflater, + root: WindowRootView, + featureFlags: FeatureFlags, ): NotificationShadeWindowView { - return layoutInflater.inflate(R.layout.super_notification_shade, /* root= */ null) - as NotificationShadeWindowView? - ?: throw IllegalStateException( - "R.layout.super_notification_shade could not be properly inflated" - ) + if (featureFlags.isEnabled(Flags.SCENE_CONTAINER)) { + return root.findViewById(R.id.legacy_window_root) + } + return root as NotificationShadeWindowView? + ?: throw IllegalStateException("root view not a NotificationShadeWindowView") } // TODO(b/277762009): Only allow this view's controller to inject the view. See above. @@ -83,6 +130,32 @@ abstract class ShadeModule { return notificationShadeWindowView.findViewById(R.id.notification_stack_scroller) } + @Provides + @SysUISingleton + fun providesNotificationShelfController( + featureFlags: FeatureFlags, + newImpl: Provider<NotificationShelfViewBinderWrapperControllerImpl>, + notificationShelfComponentBuilder: NotificationShelfComponent.Builder, + layoutInflater: LayoutInflater, + notificationStackScrollLayout: NotificationStackScrollLayout, + ): NotificationShelfController { + return if (featureFlags.isEnabled(Flags.NOTIFICATION_SHELF_REFACTOR)) { + newImpl.get() + } else { + val shelfView = + layoutInflater.inflate( + R.layout.status_bar_notification_shelf, + notificationStackScrollLayout, + false + ) as NotificationShelf + val component = + notificationShelfComponentBuilder.notificationShelf(shelfView).build() + val notificationShelfController = component.notificationShelfController + notificationShelfController.init() + notificationShelfController + } + } + // TODO(b/277762009): Only allow this view's controller to inject the view. See above. @Provides @SysUISingleton @@ -100,6 +173,14 @@ abstract class ShadeModule { return notificationShadeWindowView.findViewById(R.id.light_reveal_scrim) } + @Provides + @SysUISingleton + fun providesKeyguardRootView( + notificationShadeWindowView: NotificationShadeWindowView, + ): KeyguardRootView { + return notificationShadeWindowView.findViewById(R.id.keyguard_root_view) + } + // TODO(b/277762009): Only allow this view's controller to inject the view. See above. @Provides @SysUISingleton @@ -130,6 +211,15 @@ abstract class ShadeModule { // TODO(b/277762009): Only allow this view's controller to inject the view. See above. @Provides @SysUISingleton + fun providesNotificationsQuickSettingsContainer( + notificationShadeWindowView: NotificationShadeWindowView, + ): NotificationsQuickSettingsContainer { + return notificationShadeWindowView.findViewById(R.id.notification_container_parent) + } + + // TODO(b/277762009): Only allow this view's controller to inject the view. See above. + @Provides + @SysUISingleton @Named(SHADE_HEADER) fun providesShadeHeaderView( notificationShadeWindowView: NotificationShadeWindowView, diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeSurface.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeSurface.kt index 5ac36bfc374f..8d5c30b51677 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ShadeSurface.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeSurface.kt @@ -36,33 +36,12 @@ interface ShadeSurface : ShadeViewController { headsUpManager: HeadsUpManagerPhone ) - /** - * Animate QS collapse by flinging it. If QS is expanded, it will collapse into QQS and stop. If - * in split shade, it will collapse the whole shade. - * - * @param fullyCollapse Do not stop when QS becomes QQS. Fling until QS isn't visible anymore. - */ - fun animateCollapseQs(fullyCollapse: Boolean) - - /** Returns whether the shade can be collapsed. */ - fun canBeCollapsed(): Boolean - /** Cancels any pending collapses. */ fun cancelPendingCollapse() /** Cancels the views current animation. */ fun cancelAnimation() - /** - * Close the keyguard user switcher if it is open and capable of closing. - * - * Has no effect if user switcher isn't supported, if the user switcher is already closed, or if - * the user switcher uses "simple" mode. The simple user switcher cannot be closed. - * - * @return true if the keyguard user switcher was open, and is now closed - */ - fun closeUserSwitcherIfOpen(): Boolean - /** Input focus transfer is about to happen. */ fun startWaitingForExpandGesture() @@ -116,9 +95,6 @@ interface ShadeSurface : ShadeViewController { /** Sets the view's alpha to max. */ fun resetAlpha() - /** Called when Back gesture has been committed (i.e. a back event has definitely occurred) */ - fun onBackPressed() - /** Sets progress of the predictive back animation. */ fun onBackProgressed(progressFraction: Float) diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewController.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewController.kt index f75047c2072a..05d2bc64c535 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewController.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewController.kt @@ -17,11 +17,11 @@ package com.android.systemui.shade import android.view.MotionEvent import android.view.ViewGroup +import com.android.systemui.keyguard.shared.model.WakefulnessModel import com.android.systemui.statusbar.RemoteInputController import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController import com.android.systemui.statusbar.phone.HeadsUpAppearanceController -import com.android.systemui.statusbar.phone.KeyguardBottomAreaView import com.android.systemui.statusbar.phone.KeyguardStatusBarView import com.android.systemui.statusbar.phone.KeyguardStatusBarViewController import java.util.function.Consumer @@ -60,7 +60,7 @@ interface ShadeViewController { * Returns whether the shade height is greater than zero or the shade is expecting a synthesized * down event. */ - @get:Deprecated("use {@link #isExpanded()} instead") val isPanelExpanded: Boolean + val isPanelExpanded: Boolean /** Returns whether the shade is fully expanded in either QS or QQS. */ val isShadeFullyExpanded: Boolean @@ -77,6 +77,17 @@ interface ShadeViewController { /** Collapses the shade with an animation duration in milliseconds. */ fun collapseWithDuration(animationDuration: Int) + /** + * Animate QS collapse by flinging it. If QS is expanded, it will collapse into QQS and stop. If + * in split shade, it will collapse the whole shade. + * + * @param fullyCollapse Do not stop when QS becomes QQS. Fling until QS isn't visible anymore. + */ + fun animateCollapseQs(fullyCollapse: Boolean) + + /** Returns whether the shade can be collapsed. */ + fun canBeCollapsed(): Boolean + /** Returns whether the shade is in the process of collapsing. */ val isCollapsing: Boolean @@ -120,19 +131,25 @@ interface ShadeViewController { /** Returns the StatusBarState. */ val barState: Int - /** - * Returns the bottom part of the keyguard, which contains quick affordances. - * - * TODO(b/275550429): this should be removed. - */ - val keyguardBottomAreaView: KeyguardBottomAreaView? - /** Returns the NSSL controller. */ val notificationStackScrollLayoutController: NotificationStackScrollLayoutController /** Sets the amount of progress in the status bar launch animation. */ fun applyLaunchAnimationProgress(linearProgress: Float) + /** + * Close the keyguard user switcher if it is open and capable of closing. + * + * Has no effect if user switcher isn't supported, if the user switcher is already closed, or if + * the user switcher uses "simple" mode. The simple user switcher cannot be closed. + * + * @return true if the keyguard user switcher was open, and is now closed + */ + fun closeUserSwitcherIfOpen(): Boolean + + /** Called when Back gesture has been committed (i.e. a back event has definitely occurred) */ + fun onBackPressed() + /** Sets whether the status bar launch animation is currently running. */ fun setIsLaunchAnimationRunning(running: Boolean) @@ -226,6 +243,16 @@ interface ShadeViewController { val shadeNotificationPresenter: ShadeNotificationPresenter companion object { + /** + * Returns a multiplicative factor to use when determining the falsing threshold for touches + * on the shade. The factor will be larger when the device is waking up due to a touch or + * gesture. + */ + @JvmStatic + fun getFalsingThresholdFactor(wakefulness: WakefulnessModel): Float { + return if (wakefulness.isDeviceInteractiveFromTapOrGesture()) 1.5f else 1.0f + } + const val WAKEUP_ANIMATION_DELAY_MS = 250 const val FLING_MAX_LENGTH_SECONDS = 0.6f const val FLING_SPEED_UP_FACTOR = 0.6f diff --git a/packages/SystemUI/src/com/android/systemui/shade/transition/ScrimShadeTransitionController.kt b/packages/SystemUI/src/com/android/systemui/shade/transition/ScrimShadeTransitionController.kt index 4e1c272ead99..abb69f691c47 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/transition/ScrimShadeTransitionController.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/transition/ScrimShadeTransitionController.kt @@ -16,24 +16,11 @@ package com.android.systemui.shade.transition -import android.content.res.Configuration -import android.content.res.Resources -import android.util.MathUtils.constrain -import com.android.systemui.R import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.dump.DumpManager -import com.android.systemui.flags.FeatureFlags -import com.android.systemui.flags.Flags import com.android.systemui.shade.PanelState -import com.android.systemui.shade.STATE_OPENING import com.android.systemui.shade.ShadeExpansionChangeEvent -import com.android.systemui.statusbar.StatusBarState -import com.android.systemui.statusbar.SysuiStatusBarStateController import com.android.systemui.statusbar.phone.ScrimController -import com.android.systemui.statusbar.policy.ConfigurationController -import com.android.systemui.statusbar.policy.HeadsUpManager -import com.android.systemui.util.LargeScreenUtils import java.io.PrintWriter import javax.inject.Inject @@ -42,37 +29,19 @@ import javax.inject.Inject class ScrimShadeTransitionController @Inject constructor( - configurationController: ConfigurationController, dumpManager: DumpManager, private val scrimController: ScrimController, - @Main private val resources: Resources, - private val statusBarStateController: SysuiStatusBarStateController, - private val headsUpManager: HeadsUpManager, - private val featureFlags: FeatureFlags, ) { - private var inSplitShade = false - private var splitShadeScrimTransitionDistance = 0 private var lastExpansionFraction: Float? = null private var lastExpansionEvent: ShadeExpansionChangeEvent? = null private var currentPanelState: Int? = null init { - updateResources() - configurationController.addCallback( - object : ConfigurationController.ConfigurationListener { - override fun onConfigChanged(newConfig: Configuration?) { - updateResources() - } - }) dumpManager.registerDumpable( - ScrimShadeTransitionController::class.java.simpleName, this::dump) - } - - private fun updateResources() { - inSplitShade = LargeScreenUtils.shouldUseSplitNotificationShade(resources) - splitShadeScrimTransitionDistance = - resources.getDimensionPixelSize(R.dimen.split_shade_scrim_transition_distance) + ScrimShadeTransitionController::class.java.simpleName, + this::dump + ) } fun onPanelStateChanged(@PanelState state: Int) { @@ -87,46 +56,25 @@ constructor( private fun onStateChanged() { val expansionEvent = lastExpansionEvent ?: return - val panelState = currentPanelState - val expansionFraction = calculateScrimExpansionFraction(expansionEvent, panelState) + val expansionFraction = calculateScrimExpansionFraction(expansionEvent) scrimController.setRawPanelExpansionFraction(expansionFraction) lastExpansionFraction = expansionFraction } - private fun calculateScrimExpansionFraction( - expansionEvent: ShadeExpansionChangeEvent, - @PanelState panelState: Int? - ): Float { - return if (canUseCustomFraction(panelState)) { - constrain(expansionEvent.dragDownPxAmount / splitShadeScrimTransitionDistance, 0f, 1f) - } else { - expansionEvent.fraction - } + private fun calculateScrimExpansionFraction(expansionEvent: ShadeExpansionChangeEvent): Float { + return expansionEvent.fraction } - private fun canUseCustomFraction(panelState: Int?) = - inSplitShade && isScreenUnlocked() && panelState == STATE_OPENING && - // in case of HUN we can't always use predefined distances to manage scrim - // transition because dragDownPxAmount can start from value bigger than - // splitShadeScrimTransitionDistance - !headsUpManager.isTrackingHeadsUp && - !featureFlags.isEnabled(Flags.LARGE_SHADE_GRANULAR_ALPHA_INTERPOLATION) - - private fun isScreenUnlocked() = - statusBarStateController.currentOrUpcomingState == StatusBarState.SHADE - private fun dump(printWriter: PrintWriter, args: Array<String>) { printWriter.println( """ ScrimShadeTransitionController: - Resources: - inSplitShade: $inSplitShade - isScreenUnlocked: ${isScreenUnlocked()} - splitShadeScrimTransitionDistance: $splitShadeScrimTransitionDistance State: currentPanelState: $currentPanelState lastExpansionFraction: $lastExpansionFraction lastExpansionEvent: $lastExpansionEvent - """.trimIndent()) + """ + .trimIndent() + ) } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java index a532195c5b9f..6c2c0cf12aad 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java @@ -19,7 +19,6 @@ package com.android.systemui.statusbar; import static android.app.StatusBarManager.DISABLE2_NONE; import static android.app.StatusBarManager.DISABLE_NONE; import static android.inputmethodservice.InputMethodService.BACK_DISPOSITION_DEFAULT; -import static android.inputmethodservice.InputMethodService.IME_INVISIBLE; import static android.view.Display.INVALID_DISPLAY; import android.annotation.Nullable; @@ -37,7 +36,7 @@ import android.hardware.biometrics.IBiometricContextListener; import android.hardware.biometrics.IBiometricSysuiReceiver; import android.hardware.biometrics.PromptInfo; import android.hardware.fingerprint.IUdfpsRefreshRateRequestCallback; -import android.inputmethodservice.InputMethodService.BackDispositionMode; +import android.inputmethodservice.InputMethodService; import android.media.INearbyMediaDevicesProvider; import android.media.MediaRoute2Info; import android.os.Binder; @@ -226,8 +225,10 @@ public class CommandQueue extends IStatusBar.Stub implements * @param backDisposition Disposition mode of back button. It should be one of below flags: * @param showImeSwitcher {@code true} to show IME switch button. */ - default void setImeWindowStatus(int displayId, IBinder token, int vis, - @BackDispositionMode int backDisposition, boolean showImeSwitcher) { } + default void setImeWindowStatus(int displayId, IBinder token, + @InputMethodService.ImeWindowVisibility int vis, + @InputMethodService.BackDispositionMode int backDisposition, + boolean showImeSwitcher) { } default void showRecentApps(boolean triggeredFromAltTab) { } default void hideRecentApps(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) { } default void toggleTaskbar() { } @@ -678,7 +679,9 @@ public class CommandQueue extends IStatusBar.Stub implements } @Override - public void setImeWindowStatus(int displayId, IBinder token, int vis, int backDisposition, + public void setImeWindowStatus(int displayId, IBinder token, + @InputMethodService.ImeWindowVisibility int vis, + @InputMethodService.BackDispositionMode int backDisposition, boolean showImeSwitcher) { synchronized (mLock) { mHandler.removeMessages(MSG_SHOW_IME_BUTTON); @@ -1092,7 +1095,9 @@ public class CommandQueue extends IStatusBar.Stub implements } } - private void handleShowImeButton(int displayId, IBinder token, int vis, int backDisposition, + private void handleShowImeButton(int displayId, IBinder token, + @InputMethodService.ImeWindowVisibility int vis, + @InputMethodService.BackDispositionMode int backDisposition, boolean showImeSwitcher) { if (displayId == INVALID_DISPLAY) return; @@ -1112,7 +1117,7 @@ public class CommandQueue extends IStatusBar.Stub implements private void sendImeInvisibleStatusForPrevNavBar() { for (int i = 0; i < mCallbacks.size(); i++) { mCallbacks.get(i).setImeWindowStatus(mLastUpdatedImeDisplayId, - null /* token */, IME_INVISIBLE, BACK_DISPOSITION_DEFAULT, + null /* token */, 0 /* vis */, BACK_DISPOSITION_DEFAULT, false /* showImeSwitcher */); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java index 06f43f1eeaa5..39181449aaa0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java @@ -56,7 +56,6 @@ import android.view.View.AccessibilityDelegate; import android.view.ViewGroup; import android.view.Window; import android.view.WindowManager; -import android.view.WindowManager.KeyboardShortcutsReceiver; import android.view.accessibility.AccessibilityNodeInfo; import android.widget.Button; import android.widget.EditText; @@ -337,6 +336,12 @@ public final class KeyboardShortcutListSearch { mSpecialCharacterNames.put(KeyEvent.KEYCODE_MUHENKAN, "無変換"); mSpecialCharacterNames.put(KeyEvent.KEYCODE_HENKAN, "変換"); mSpecialCharacterNames.put(KeyEvent.KEYCODE_KATAKANA_HIRAGANA, "かな"); + mSpecialCharacterNames.put(KeyEvent.KEYCODE_ALT_LEFT, "Alt"); + mSpecialCharacterNames.put(KeyEvent.KEYCODE_ALT_RIGHT, "Alt"); + mSpecialCharacterNames.put(KeyEvent.KEYCODE_CTRL_LEFT, "Ctrl"); + mSpecialCharacterNames.put(KeyEvent.KEYCODE_CTRL_RIGHT, "Ctrl"); + mSpecialCharacterNames.put(KeyEvent.KEYCODE_SHIFT_LEFT, "Shift"); + mSpecialCharacterNames.put(KeyEvent.KEYCODE_SHIFT_RIGHT, "Shift"); mModifierNames.put(KeyEvent.META_META_ON, "Meta"); mModifierNames.put(KeyEvent.META_CTRL_ON, "Ctrl"); @@ -411,27 +416,45 @@ public final class KeyboardShortcutListSearch { mKeyCharacterMap = mBackupKeyCharacterMap; } + private boolean mAppShortcutsReceived; + private boolean mImeShortcutsReceived; + @VisibleForTesting void showKeyboardShortcuts(int deviceId) { retrieveKeyCharacterMap(deviceId); - mWindowManager.requestAppKeyboardShortcuts(new KeyboardShortcutsReceiver() { - @Override - public void onKeyboardShortcutsReceived( - final List<KeyboardShortcutGroup> result) { - // Add specific app shortcuts - if (result.isEmpty()) { - mKeySearchResultMap.put(SHORTCUT_SPECIFICAPP_INDEX, false); - } else { - mSpecificAppGroup = reMapToKeyboardShortcutMultiMappingGroup(result); - mKeySearchResultMap.put(SHORTCUT_SPECIFICAPP_INDEX, true); - } - mFullShortsGroup.add(SHORTCUT_SYSTEM_INDEX, mSystemGroup); - mFullShortsGroup.add(SHORTCUT_INPUT_INDEX, mInputGroup); - mFullShortsGroup.add(SHORTCUT_OPENAPPS_INDEX, mOpenAppsGroup); - mFullShortsGroup.add(SHORTCUT_SPECIFICAPP_INDEX, mSpecificAppGroup); - showKeyboardShortcutSearchList(mFullShortsGroup); + mAppShortcutsReceived = false; + mImeShortcutsReceived = false; + mWindowManager.requestAppKeyboardShortcuts(result -> { + // Add specific app shortcuts + if (result.isEmpty()) { + mKeySearchResultMap.put(SHORTCUT_SPECIFICAPP_INDEX, false); + } else { + mSpecificAppGroup.addAll(reMapToKeyboardShortcutMultiMappingGroup(result)); + mKeySearchResultMap.put(SHORTCUT_SPECIFICAPP_INDEX, true); + } + mAppShortcutsReceived = true; + if (mImeShortcutsReceived) { + mergeAndShowKeyboardShortcutsGroups(); } }, deviceId); + mWindowManager.requestImeKeyboardShortcuts(result -> { + // Add specific Ime shortcuts + if (!result.isEmpty()) { + mInputGroup.addAll(reMapToKeyboardShortcutMultiMappingGroup(result)); + } + mImeShortcutsReceived = true; + if (mAppShortcutsReceived) { + mergeAndShowKeyboardShortcutsGroups(); + } + }, deviceId); + } + + private void mergeAndShowKeyboardShortcutsGroups() { + mFullShortsGroup.add(SHORTCUT_SYSTEM_INDEX, mSystemGroup); + mFullShortsGroup.add(SHORTCUT_INPUT_INDEX, mInputGroup); + mFullShortsGroup.add(SHORTCUT_OPENAPPS_INDEX, mOpenAppsGroup); + mFullShortsGroup.add(SHORTCUT_SPECIFICAPP_INDEX, mSpecificAppGroup); + showKeyboardShortcutSearchList(mFullShortsGroup); } // The original data structure is only for 1-to-1 shortcut mapping, so remap the old diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java index 43fbc7cbae03..a3fd82e9b140 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java @@ -56,7 +56,6 @@ import android.view.View.AccessibilityDelegate; import android.view.ViewGroup; import android.view.Window; import android.view.WindowManager; -import android.view.WindowManager.KeyboardShortcutsReceiver; import android.view.accessibility.AccessibilityNodeInfo; import android.widget.ImageView; import android.widget.LinearLayout; @@ -129,6 +128,9 @@ public final class KeyboardShortcuts { private KeyCharacterMap mKeyCharacterMap; private KeyCharacterMap mBackupKeyCharacterMap; + @Nullable private List<KeyboardShortcutGroup> mReceivedAppShortcutGroups = null; + @Nullable private List<KeyboardShortcutGroup> mReceivedImeShortcutGroups = null; + @VisibleForTesting KeyboardShortcuts(Context context, WindowManager windowManager) { this.mContext = new ContextThemeWrapper( @@ -324,6 +326,12 @@ public final class KeyboardShortcuts { mSpecialCharacterNames.put(KeyEvent.KEYCODE_MUHENKAN, "無変換"); mSpecialCharacterNames.put(KeyEvent.KEYCODE_HENKAN, "変換"); mSpecialCharacterNames.put(KeyEvent.KEYCODE_KATAKANA_HIRAGANA, "かな"); + mSpecialCharacterNames.put(KeyEvent.KEYCODE_ALT_LEFT, "Alt"); + mSpecialCharacterNames.put(KeyEvent.KEYCODE_ALT_RIGHT, "Alt"); + mSpecialCharacterNames.put(KeyEvent.KEYCODE_CTRL_LEFT, "Ctrl"); + mSpecialCharacterNames.put(KeyEvent.KEYCODE_CTRL_RIGHT, "Ctrl"); + mSpecialCharacterNames.put(KeyEvent.KEYCODE_SHIFT_LEFT, "Shift"); + mSpecialCharacterNames.put(KeyEvent.KEYCODE_SHIFT_RIGHT, "Shift"); mModifierNames.put(KeyEvent.META_META_ON, "Meta"); mModifierNames.put(KeyEvent.META_CTRL_ON, "Ctrl"); @@ -382,18 +390,36 @@ public final class KeyboardShortcuts { @VisibleForTesting void showKeyboardShortcuts(int deviceId) { retrieveKeyCharacterMap(deviceId); - mWindowManager.requestAppKeyboardShortcuts(new KeyboardShortcutsReceiver() { - @Override - public void onKeyboardShortcutsReceived( - final List<KeyboardShortcutGroup> result) { - result.add(getSystemShortcuts()); - final KeyboardShortcutGroup appShortcuts = getDefaultApplicationShortcuts(); - if (appShortcuts != null) { - result.add(appShortcuts); - } - showKeyboardShortcutsDialog(result); - } - }, deviceId); + mReceivedAppShortcutGroups = null; + mReceivedImeShortcutGroups = null; + mWindowManager.requestAppKeyboardShortcuts( + result -> { + mReceivedAppShortcutGroups = result; + maybeMergeAndShowKeyboardShortcuts(); + }, deviceId); + mWindowManager.requestImeKeyboardShortcuts( + result -> { + mReceivedImeShortcutGroups = result; + maybeMergeAndShowKeyboardShortcuts(); + }, deviceId); + } + + private void maybeMergeAndShowKeyboardShortcuts() { + if (mReceivedAppShortcutGroups == null || mReceivedImeShortcutGroups == null) { + return; + } + List<KeyboardShortcutGroup> shortcutGroups = mReceivedAppShortcutGroups; + shortcutGroups.addAll(mReceivedImeShortcutGroups); + mReceivedAppShortcutGroups = null; + mReceivedImeShortcutGroups = null; + + final KeyboardShortcutGroup defaultAppShortcuts = + getDefaultApplicationShortcuts(); + if (defaultAppShortcuts != null) { + shortcutGroups.add(defaultAppShortcuts); + } + shortcutGroups.add(getSystemShortcuts()); + showKeyboardShortcutsDialog(shortcutGroups); } private void dismissKeyboardShortcuts() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java index b847fb6ded83..795bcadc8272 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java @@ -95,7 +95,8 @@ import com.android.systemui.flags.FeatureFlags; import com.android.systemui.keyguard.KeyguardIndication; import com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController; import com.android.systemui.keyguard.ScreenLifecycle; -import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor; +import com.android.systemui.bouncer.domain.interactor.BouncerMessageInteractor; +import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor; import com.android.systemui.log.LogLevel; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; @@ -148,6 +149,7 @@ public class KeyguardIndicationController { private final AuthController mAuthController; private final KeyguardLogger mKeyguardLogger; private final UserTracker mUserTracker; + private final BouncerMessageInteractor mBouncerMessageInteractor; private ViewGroup mIndicationArea; private KeyguardIndicationTextView mTopIndicationView; private KeyguardIndicationTextView mLockScreenIndicationView; @@ -256,6 +258,7 @@ public class KeyguardIndicationController { AlternateBouncerInteractor alternateBouncerInteractor, AlarmManager alarmManager, UserTracker userTracker, + BouncerMessageInteractor bouncerMessageInteractor, FeatureFlags flags ) { mContext = context; @@ -281,6 +284,7 @@ public class KeyguardIndicationController { mScreenLifecycle.addObserver(mScreenObserver); mAlternateBouncerInteractor = alternateBouncerInteractor; mUserTracker = userTracker; + mBouncerMessageInteractor = bouncerMessageInteractor; mFeatureFlags = flags; mFaceAcquiredMessageDeferral = faceHelpMessageDeferral; @@ -1156,6 +1160,11 @@ public class KeyguardIndicationController { msgId, helpString); } else if (mStatusBarKeyguardViewManager.isBouncerShowing()) { + if (biometricSourceType == FINGERPRINT && !fpAuthFailed) { + mBouncerMessageInteractor.setFingerprintAcquisitionMessage(helpString); + } else if (faceAuthSoftError) { + mBouncerMessageInteractor.setFaceAcquisitionMessage(helpString); + } mStatusBarKeyguardViewManager.setKeyguardMessage(helpString, mInitialTextColorState); } else if (mScreenLifecycle.getScreenState() == SCREEN_ON) { @@ -1211,6 +1220,8 @@ public class KeyguardIndicationController { if (biometricSourceType == FACE) { mFaceAcquiredMessageDeferral.reset(); } + mBouncerMessageInteractor.setFaceAcquisitionMessage(null); + mBouncerMessageInteractor.setFingerprintAcquisitionMessage(null); } @Override @@ -1231,6 +1242,8 @@ public class KeyguardIndicationController { } else if (biometricSourceType == FINGERPRINT) { onFingerprintAuthError(msgId, errString); } + mBouncerMessageInteractor.setFaceAcquisitionMessage(null); + mBouncerMessageInteractor.setFingerprintAcquisitionMessage(null); } private void onFaceAuthError(int msgId, String errString) { @@ -1315,6 +1328,8 @@ public class KeyguardIndicationController { showActionToUnlock(); } } + mBouncerMessageInteractor.setFaceAcquisitionMessage(null); + mBouncerMessageInteractor.setFingerprintAcquisitionMessage(null); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LegacyNotificationShelfControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/LegacyNotificationShelfControllerImpl.java index dcd9dc3e7fa3..4ec5f46e7771 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/LegacyNotificationShelfControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/LegacyNotificationShelfControllerImpl.java @@ -52,7 +52,7 @@ public class LegacyNotificationShelfControllerImpl implements NotificationShelfC mActivatableNotificationViewController = activatableNotificationViewController; mKeyguardBypassController = keyguardBypassController; mStatusBarStateController = statusBarStateController; - mView.setSensitiveRevealAnimEndabled(featureFlags.isEnabled(Flags.SENSITIVE_REVEAL_ANIM)); + mView.setSensitiveRevealAnimEnabled(featureFlags.isEnabled(Flags.SENSITIVE_REVEAL_ANIM)); mOnAttachStateChangeListener = new View.OnAttachStateChangeListener() { @Override public void onViewAttachedToWindow(View v) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt index c098f455512a..e2d2ac0fcb58 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt @@ -6,7 +6,6 @@ import android.animation.ValueAnimator import android.content.Context import android.content.res.Configuration import android.os.PowerManager -import android.os.SystemClock import android.util.IndentingPrintWriter import android.util.MathUtils import android.view.MotionEvent @@ -30,6 +29,7 @@ import com.android.systemui.plugins.ActivityStarter.OnDismissAction import com.android.systemui.plugins.FalsingManager import com.android.systemui.plugins.qs.QS import com.android.systemui.plugins.statusbar.StatusBarStateController +import com.android.systemui.power.domain.interactor.PowerInteractor import com.android.systemui.shade.ShadeViewController import com.android.systemui.shade.data.repository.ShadeRepository import com.android.systemui.statusbar.notification.collection.NotificationEntry @@ -76,6 +76,7 @@ class LockscreenShadeTransitionController @Inject constructor( dumpManager: DumpManager, qsTransitionControllerFactory: LockscreenShadeQsTransitionController.Factory, private val shadeRepository: ShadeRepository, + private val powerInteractor: PowerInteractor, ) : Dumpable { private var pulseHeight: Float = 0f @get:VisibleForTesting @@ -278,11 +279,7 @@ class LockscreenShadeTransitionController @Inject constructor( // Bind the click listener of the shelf to go to the full shade notificationShelfController.setOnClickListener { if (statusBarStateController.state == StatusBarState.KEYGUARD) { - centralSurfaces.wakeUpIfDozing( - SystemClock.uptimeMillis(), - "SHADE_CLICK", - PowerManager.WAKE_REASON_GESTURE, - ) + powerInteractor.wakeUpIfDozing("SHADE_CLICK", PowerManager.WAKE_REASON_GESTURE) goToLockedShade(it) } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java index 1c9bc60deefe..5dcf6d1d8f28 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java @@ -43,6 +43,11 @@ public interface NotificationPresenter extends ExpandableNotificationRow.OnExpan void onUserSwitched(int newUserId); /** + * Called when a new row is created and bound to a notification. + */ + void onBindRow(ExpandableNotificationRow row); + + /** * @return true iff the device is in vr mode */ boolean isDeviceInVrMode(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java index c5191154377a..406db18c43dd 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java @@ -402,7 +402,7 @@ public class NotificationRemoteInputManager implements Dumpable { while (p != null) { if (p instanceof View) { View pv = (View) p; - if (pv.isRootNamespace()) { + if (pv.getId() == com.android.internal.R.id.status_bar_latest_event_content) { riv = findRemoteInputView(pv); row = (ExpandableNotificationRow) pv.getTag(R.id.row_tag_for_content_view); break; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowController.java index 2ca0b0054bf7..47a4641bcdd9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowController.java @@ -60,11 +60,11 @@ public interface NotificationShadeWindowController extends RemoteInputController default void attach() {} /** Sets the notification shade view. */ - default void setNotificationShadeView(ViewGroup view) {} + default void setWindowRootView(ViewGroup view) {} /** Gets the notification shade view. */ @Nullable - default ViewGroup getNotificationShadeView() { + default ViewGroup getWindowRootView() { return null; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java index 9d7f3be4dfe7..25a1dc6322ba 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java @@ -40,8 +40,6 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.policy.SystemBarUtils; import com.android.systemui.R; import com.android.systemui.animation.ShadeInterpolation; -import com.android.systemui.flags.FeatureFlags; -import com.android.systemui.flags.Flags; import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener; import com.android.systemui.shade.transition.LargeScreenShadeInterpolator; import com.android.systemui.statusbar.notification.NotificationUtils; @@ -97,7 +95,7 @@ public class NotificationShelf extends ActivatableNotificationView implements St private float mCornerAnimationDistance; private NotificationShelfController mController; private float mActualWidth = -1; - private boolean mSensitiveRevealAnimEndabled; + private boolean mSensitiveRevealAnimEnabled; private boolean mShelfRefactorFlagEnabled; private boolean mCanModifyColorOfNotifications; private boolean mCanInteract; @@ -226,9 +224,7 @@ public class NotificationShelf extends ActivatableNotificationView implements St if (ambientState.isBouncerInTransit()) { viewState.setAlpha(aboutToShowBouncerProgress(expansion)); } else { - FeatureFlags flags = ambientState.getFeatureFlags(); - if (ambientState.isSmallScreen() || !flags.isEnabled( - Flags.LARGE_SHADE_GRANULAR_ALPHA_INTERPOLATION)) { + if (ambientState.isSmallScreen()) { viewState.setAlpha(ShadeInterpolation.getContentAlpha(expansion)); } else { LargeScreenShadeInterpolator interpolator = @@ -265,21 +261,21 @@ public class NotificationShelf extends ActivatableNotificationView implements St viewState.hidden = true; } } - - final float stackEnd = ambientState.getStackY() + ambientState.getStackHeight(); - if (mSensitiveRevealAnimEndabled && viewState.hidden) { - // if the shelf is hidden, position it at the end of the stack (plus the clip - // padding), such that when it appears animated, it will smoothly move in from the - // bottom, without jump cutting any notifications - viewState.setYTranslation(stackEnd + mPaddingBetweenElements); - } else { - viewState.setYTranslation(stackEnd - viewState.height); - } } else { viewState.hidden = true; viewState.location = ExpandableViewState.LOCATION_GONE; viewState.hasItemsInStableShelf = false; } + + final float stackEnd = ambientState.getStackY() + ambientState.getStackHeight(); + if (mSensitiveRevealAnimEnabled && viewState.hidden) { + // if the shelf is hidden, position it at the end of the stack (plus the clip + // padding), such that when it appears animated, it will smoothly move in from the + // bottom, without jump cutting any notifications + viewState.setYTranslation(stackEnd + mPaddingBetweenElements); + } else { + viewState.setYTranslation(stackEnd - viewState.height); + } } private int getSpeedBumpIndex() { @@ -417,7 +413,7 @@ public class NotificationShelf extends ActivatableNotificationView implements St expandingAnimated, isLastChild, shelfClipStart); // TODO(b/172289889) scale mPaddingBetweenElements with expansion amount - if ((!mSensitiveRevealAnimEndabled && ((isLastChild && !child.isInShelf()) + if ((!mSensitiveRevealAnimEnabled && ((isLastChild && !child.isInShelf()) || backgroundForceHidden)) || aboveShelf) { notificationClipEnd = shelfStart + getIntrinsicHeight(); } else { @@ -466,7 +462,7 @@ public class NotificationShelf extends ActivatableNotificationView implements St // if the shelf is visible, but if the shelf is hidden, it causes incorrect curling. // notificationClipEnd handles the discrepancy between a visible and hidden shelf, // so we use that when on the keyguard (and while animating away) to reduce curling. - final float keyguardSafeShelfStart = !mSensitiveRevealAnimEndabled + final float keyguardSafeShelfStart = !mSensitiveRevealAnimEnabled && mAmbientState.isOnKeyguard() ? notificationClipEnd : shelfStart; updateCornerRoundnessOnScroll(anv, viewStart, keyguardSafeShelfStart); } @@ -1068,8 +1064,8 @@ public class NotificationShelf extends ActivatableNotificationView implements St * Set whether the sensitive reveal animation feature flag is enabled * @param enabled true if enabled */ - public void setSensitiveRevealAnimEndabled(boolean enabled) { - mSensitiveRevealAnimEndabled = enabled; + public void setSensitiveRevealAnimEnabled(boolean enabled) { + mSensitiveRevealAnimEnabled = enabled; } public void setRefactorFlagEnabled(boolean enabled) { 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 f6c9a5cae34a..73eba0ee9675 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java @@ -35,7 +35,7 @@ import com.android.systemui.dump.DumpHandler; import com.android.systemui.dump.DumpManager; import com.android.systemui.flags.FeatureFlags; import com.android.systemui.flags.Flags; -import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor; +import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor; import com.android.systemui.media.controls.pipeline.MediaDataManager; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.statusbar.StatusBarStateController; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/disableflags/DisableFlagsLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/disableflags/DisableFlagsLogger.kt index f04159cd631f..dc868695490b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/disableflags/DisableFlagsLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/disableflags/DisableFlagsLogger.kt @@ -71,15 +71,14 @@ class DisableFlagsLogger constructor( } /** - * Returns a string representing the, old, new, and new-after-modification disable flag states, - * as well as the differences between each of the states. + * Returns a string representing the new and new-after-modification disable flag states, + * as well as the differences between them (if there are any). * - * Example if [old], [new], and [newAfterLocalModification] are all different: - * Old: EnaiHbcRso.qINgr | New: EnaihBcRso.qiNGR (changed: hB.iGR) | New after local - * modification: EnaihBcRso.qInGR (changed: .n) + * Example if [new] and [newAfterLocalModification] are different: + * New: EnaihBcRso.qiNGR | New after local modification: EnaihBCRso.qInGR (changed: C.In) * - * Example if [old] and [new] are the same: - * EnaihBcRso.qiNGR (unchanged) + * Example if [new] and [newAfterLocalModification] are the same: + * New: EnaihBcRso.qiNGR * * A capital character signifies the flag is set and a lowercase character signifies that the * flag isn't set. The flag states will be logged in the same order as the passed-in lists. @@ -88,37 +87,17 @@ class DisableFlagsLogger constructor( * is no difference. the new-after-modification state also won't be included if there's no * difference from the new state. * - * @param old the disable state that had been previously sent. Null if we don't need to log the - * previously sent state. * @param new the new disable state that has just been sent. * @param newAfterLocalModification the new disable states after a class has locally modified * them. Null if the class does not locally modify. */ fun getDisableFlagsString( - old: DisableState? = null, new: DisableState, newAfterLocalModification: DisableState? = null ): String { val builder = StringBuilder("Received new disable state: ") - // This if/else has slightly repetitive code but is easier to read. - if (old != null && old != new) { - builder.append("Old: ") - builder.append(getFlagsString(old)) - builder.append(" | ") - builder.append("New: ") - builder.append(getFlagsString(new)) - builder.append(" ") - builder.append(getDiffString(old, new)) - } else if (old != null && old == new) { - // If old and new are the same, we only need to print one of them. - builder.append(getFlagsString(new)) - builder.append(" ") - builder.append(getDiffString(old, new)) - } else { // old == null - builder.append(getFlagsString(new)) - // Don't get a diff string because we have no [old] to compare with. - } + builder.append(getFlagsString(new)) if (newAfterLocalModification != null && new != newAfterLocalModification) { builder.append(" | New after local modification: ") diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/disableflags/data/model/DisableFlagsModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/disableflags/data/model/DisableFlagsModel.kt new file mode 100644 index 000000000000..bf4895cf1e3c --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/disableflags/data/model/DisableFlagsModel.kt @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package com.android.systemui.statusbar.disableflags.data.model + +import android.app.StatusBarManager.DISABLE2_NONE +import android.app.StatusBarManager.DISABLE_NONE +import android.app.StatusBarManager.DISABLE_NOTIFICATION_ALERTS +import com.android.systemui.log.LogBuffer +import com.android.systemui.log.LogLevel +import com.android.systemui.statusbar.disableflags.DisableFlagsLogger + +/** + * Model for the disable flags that come from [IStatusBar]. + * + * For clients of the disable flags: do *not* refer to the disable integers directly. Instead, + * re-use or define a helper method that internally processes the flags. (We want to hide the + * bitwise logic here so no one else has to worry about it.) + */ +data class DisableFlagsModel( + private val disable1: Int = DISABLE_NONE, + private val disable2: Int = DISABLE2_NONE, +) { + /** Returns true if notification alerts are allowed based on the flags. */ + fun areNotificationAlertsEnabled(): Boolean { + return (disable1 and DISABLE_NOTIFICATION_ALERTS) == 0 + } + + /** Logs the change to the provided buffer. */ + fun logChange(buffer: LogBuffer, disableFlagsLogger: DisableFlagsLogger) { + buffer.log( + TAG, + LogLevel.INFO, + { + int1 = disable1 + int2 = disable2 + }, + { + disableFlagsLogger.getDisableFlagsString( + new = DisableFlagsLogger.DisableState(int1, int2), + ) + } + ) + } + + private companion object { + const val TAG = "DisableFlagsModel" + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/disableflags/data/repository/DisableFlagsRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/disableflags/data/repository/DisableFlagsRepository.kt new file mode 100644 index 000000000000..9c419beb5248 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/disableflags/data/repository/DisableFlagsRepository.kt @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package com.android.systemui.statusbar.disableflags.data.repository + +import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.dagger.qualifiers.DisplayId +import com.android.systemui.log.LogBuffer +import com.android.systemui.log.dagger.DisableFlagsRepositoryLog +import com.android.systemui.statusbar.CommandQueue +import com.android.systemui.statusbar.disableflags.DisableFlagsLogger +import com.android.systemui.statusbar.disableflags.data.model.DisableFlagsModel +import javax.inject.Inject +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.flow.stateIn + +/** Repository for the disable flags received from external systems. See [IStatusBar.disable]. */ +@SysUISingleton +class DisableFlagsRepository +@Inject +constructor( + commandQueue: CommandQueue, + @DisplayId private val displayId: Int, + @Application scope: CoroutineScope, + @DisableFlagsRepositoryLog private val logBuffer: LogBuffer, + private val disableFlagsLogger: DisableFlagsLogger, +) { + val disableFlags: StateFlow<DisableFlagsModel> = + conflatedCallbackFlow { + val callback = + object : CommandQueue.Callbacks { + override fun disable( + displayId: Int, + state1: Int, + state2: Int, + animate: Boolean, + ) { + if (displayId != this@DisableFlagsRepository.displayId) { + return + } + trySend(DisableFlagsModel(state1, state2)) + } + } + commandQueue.addCallback(callback) + awaitClose { commandQueue.removeCallback(callback) } + } + .distinctUntilChanged() + .onEach { it.logChange(logBuffer, disableFlagsLogger) } + // Use Eagerly because we always need to know about disable flags + .stateIn(scope, SharingStarted.Eagerly, DisableFlagsModel()) +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt index 518825cea5e0..cf3903860e94 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt @@ -137,6 +137,19 @@ constructor( updateTextColorFromWallpaper() statusBarStateListener.onDozeAmountChanged(0f, statusBarStateController.dozeAmount) + + if (regionSamplingEnabled && (!regionSamplers.containsKey(v))) { + var regionSampler = RegionSampler( + v as View, + uiExecutor, + bgExecutor, + regionSamplingEnabled, + isLockscreen = true, + ) { updateTextColorFromRegionSampler() } + initializeTextColors(regionSampler) + regionSamplers[v] = regionSampler + regionSampler.startRegionSampler() + } } override fun onViewDetachedFromWindow(v: View) { @@ -171,23 +184,6 @@ constructor( val filteredTargets = targets.filter(::filterSmartspaceTarget) plugin?.onTargetsAvailable(filteredTargets) - if (!isRegionSamplersCreated) { - for (v in smartspaceViews) { - if (regionSamplingEnabled) { - var regionSampler = RegionSampler( - v as View, - uiExecutor, - bgExecutor, - regionSamplingEnabled, - isLockscreen = true, - ) { updateTextColorFromRegionSampler() } - initializeTextColors(regionSampler) - regionSamplers[v] = regionSampler - regionSampler.startRegionSampler() - } - } - isRegionSamplersCreated = true - } } private val userTrackerCallback = object : UserTracker.Callback { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java index bab553e18fdc..d10fac6ea3fc 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java @@ -17,15 +17,14 @@ package com.android.systemui.statusbar.notification; import android.app.Notification; import android.os.PowerManager; -import android.os.SystemClock; import android.service.notification.StatusBarNotification; import android.util.Log; import android.view.View; import com.android.systemui.DejankUtils; +import com.android.systemui.power.domain.interactor.PowerInteractor; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; -import com.android.systemui.statusbar.phone.CentralSurfaces; import com.android.wm.shell.bubbles.Bubbles; import java.util.Optional; @@ -40,7 +39,7 @@ public final class NotificationClicker implements View.OnClickListener { private static final String TAG = "NotificationClicker"; private final NotificationClickerLogger mLogger; - private final Optional<CentralSurfaces> mCentralSurfacesOptional; + private final PowerInteractor mPowerInteractor; private final Optional<Bubbles> mBubblesOptional; private final NotificationActivityStarter mNotificationActivityStarter; @@ -54,11 +53,11 @@ public final class NotificationClicker implements View.OnClickListener { private NotificationClicker( NotificationClickerLogger logger, - Optional<CentralSurfaces> centralSurfacesOptional, + PowerInteractor powerInteractor, Optional<Bubbles> bubblesOptional, NotificationActivityStarter notificationActivityStarter) { mLogger = logger; - mCentralSurfacesOptional = centralSurfacesOptional; + mPowerInteractor = powerInteractor; mBubblesOptional = bubblesOptional; mNotificationActivityStarter = notificationActivityStarter; } @@ -70,9 +69,7 @@ public final class NotificationClicker implements View.OnClickListener { return; } - mCentralSurfacesOptional.ifPresent(centralSurfaces -> centralSurfaces.wakeUpIfDozing( - SystemClock.uptimeMillis(), "NOTIFICATION_CLICK", - PowerManager.WAKE_REASON_GESTURE)); + mPowerInteractor.wakeUpIfDozing("NOTIFICATION_CLICK", PowerManager.WAKE_REASON_GESTURE); final ExpandableNotificationRow row = (ExpandableNotificationRow) v; final NotificationEntry entry = row.getEntry(); @@ -131,21 +128,22 @@ public final class NotificationClicker implements View.OnClickListener { /** Daggerized builder for NotificationClicker. */ public static class Builder { private final NotificationClickerLogger mLogger; + private final PowerInteractor mPowerInteractor; @Inject - public Builder(NotificationClickerLogger logger) { + public Builder(NotificationClickerLogger logger, PowerInteractor powerInteractor) { mLogger = logger; + mPowerInteractor = powerInteractor; } /** Builds an instance. */ public NotificationClicker build( - Optional<CentralSurfaces> centralSurfacesOptional, Optional<Bubbles> bubblesOptional, NotificationActivityStarter notificationActivityStarter ) { return new NotificationClicker( mLogger, - centralSurfacesOptional, + mPowerInteractor, bubblesOptional, notificationActivityStarter); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java index 8aeefeeac211..2796340c598f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java @@ -61,6 +61,7 @@ import android.service.notification.NotificationListenerService.Ranking; import android.service.notification.NotificationListenerService.RankingMap; import android.service.notification.StatusBarNotification; import android.util.ArrayMap; +import android.util.Log; import android.util.Pair; import androidx.annotation.NonNull; @@ -274,10 +275,7 @@ public class NotifCollection implements Dumpable, PipelineDumpable { Assert.isMainThread(); checkForReentrantCall(); - // TODO (b/206842750): This method is called from (silent) clear all and non-clear all - // contexts and should be checking the NO_CLEAR flag, rather than depending on NSSL - // to pass in a properly filtered list of notifications - + final int entryCount = entriesToDismiss.size(); final List<NotificationEntry> entriesToLocallyDismiss = new ArrayList<>(); for (int i = 0; i < entriesToDismiss.size(); i++) { NotificationEntry entry = entriesToDismiss.get(i).first; @@ -286,28 +284,34 @@ public class NotifCollection implements Dumpable, PipelineDumpable { requireNonNull(stats); NotificationEntry storedEntry = mNotificationSet.get(entry.getKey()); if (storedEntry == null) { - mLogger.logNonExistentNotifDismissed(entry); + mLogger.logDismissNonExistentNotif(entry, i, entryCount); continue; } if (entry != storedEntry) { throw mEulogizer.record( new IllegalStateException("Invalid entry: " + "different stored and dismissed entries for " + logKey(entry) + + " (" + i + "/" + entryCount + ")" + + " dismissed=@" + Integer.toHexString(entry.hashCode()) + " stored=@" + Integer.toHexString(storedEntry.hashCode()))); } if (entry.getDismissState() == DISMISSED) { + mLogger.logDismissAlreadyDismissedNotif(entry, i, entryCount); continue; + } else if (entry.getDismissState() == PARENT_DISMISSED) { + mLogger.logDismissAlreadyParentDismissedNotif(entry, i, entryCount); } updateDismissInterceptors(entry); if (isDismissIntercepted(entry)) { - mLogger.logNotifDismissedIntercepted(entry); + mLogger.logNotifDismissedIntercepted(entry, i, entryCount); continue; } entriesToLocallyDismiss.add(entry); if (!entry.isCanceled()) { + int finalI = i; // send message to system server if this notification hasn't already been cancelled mBgExecutor.execute(() -> { try { @@ -320,7 +324,7 @@ public class NotifCollection implements Dumpable, PipelineDumpable { stats.notificationVisibility); } catch (RemoteException e) { // system process is dead if we're here. - mLogger.logRemoteExceptionOnNotificationClear(entry, e); + mLogger.logRemoteExceptionOnNotificationClear(entry, finalI, entryCount, e); } }); } @@ -357,14 +361,16 @@ public class NotifCollection implements Dumpable, PipelineDumpable { } final List<NotificationEntry> entries = new ArrayList<>(getAllNotifs()); + final int initialEntryCount = entries.size(); for (int i = entries.size() - 1; i >= 0; i--) { NotificationEntry entry = entries.get(i); + if (!shouldDismissOnClearAll(entry, userId)) { // system server won't be removing these notifications, but we still give dismiss // interceptors the chance to filter the notification updateDismissInterceptors(entry); if (isDismissIntercepted(entry)) { - mLogger.logNotifClearAllDismissalIntercepted(entry); + mLogger.logNotifClearAllDismissalIntercepted(entry, i, initialEntryCount); } entries.remove(i); } @@ -380,25 +386,46 @@ public class NotifCollection implements Dumpable, PipelineDumpable { */ private void locallyDismissNotifications(List<NotificationEntry> entries) { final List<NotificationEntry> canceledEntries = new ArrayList<>(); - + final int entryCount = entries.size(); for (int i = 0; i < entries.size(); i++) { NotificationEntry entry = entries.get(i); + final NotificationEntry storedEntry = mNotificationSet.get(entry.getKey()); + if (storedEntry == null) { + mLogger.logLocallyDismissNonExistentNotif(entry, i, entryCount); + } else if (storedEntry != entry) { + mLogger.logLocallyDismissMismatchedEntry(entry, i, entryCount, storedEntry); + } + + if (entry.getDismissState() == DISMISSED) { + mLogger.logLocallyDismissAlreadyDismissedNotif(entry, i, entryCount); + } else if (entry.getDismissState() == PARENT_DISMISSED) { + mLogger.logLocallyDismissAlreadyParentDismissedNotif(entry, i, entryCount); + } + entry.setDismissState(DISMISSED); - mLogger.logNotifDismissed(entry); + mLogger.logLocallyDismissed(entry, i, entryCount); if (entry.isCanceled()) { canceledEntries.add(entry); - } else { - // Mark any children as dismissed as system server will auto-dismiss them as well - if (entry.getSbn().getNotification().isGroupSummary()) { - for (NotificationEntry otherEntry : mNotificationSet.values()) { - if (shouldAutoDismissChildren(otherEntry, entry.getSbn().getGroupKey())) { - otherEntry.setDismissState(PARENT_DISMISSED); - mLogger.logChildDismissed(otherEntry); - if (otherEntry.isCanceled()) { - canceledEntries.add(otherEntry); - } + continue; + } + + // Mark any children as dismissed as system server will auto-dismiss them as well + if (entry.getSbn().getNotification().isGroupSummary()) { + for (NotificationEntry otherEntry : mNotificationSet.values()) { + if (shouldAutoDismissChildren(otherEntry, entry.getSbn().getGroupKey())) { + if (otherEntry.getDismissState() == DISMISSED) { + mLogger.logLocallyDismissAlreadyDismissedChild( + otherEntry, entry, i, entryCount); + } else if (otherEntry.getDismissState() == PARENT_DISMISSED) { + mLogger.logLocallyDismissAlreadyParentDismissedChild( + otherEntry, entry, i, entryCount); + } + otherEntry.setDismissState(PARENT_DISMISSED); + mLogger.logLocallyDismissedChild(otherEntry, entry, i, entryCount); + if (otherEntry.isCanceled()) { + canceledEntries.add(otherEntry); } } } @@ -408,7 +435,7 @@ public class NotifCollection implements Dumpable, PipelineDumpable { // Immediately remove any dismissed notifs that have already been canceled by system server // (probably due to being lifetime-extended up until this point). for (NotificationEntry canceledEntry : canceledEntries) { - mLogger.logDismissOnAlreadyCanceledEntry(canceledEntry); + mLogger.logLocallyDismissedAlreadyCanceledEntry(canceledEntry); tryRemoveNotification(canceledEntry); } } @@ -517,10 +544,16 @@ public class NotifCollection implements Dumpable, PipelineDumpable { * @return True if the notification was removed, false otherwise. */ private boolean tryRemoveNotification(NotificationEntry entry) { - if (mNotificationSet.get(entry.getKey()) != entry) { + final NotificationEntry storedEntry = mNotificationSet.get(entry.getKey()); + if (storedEntry == null) { + Log.wtf(TAG, "TRY REMOVE non-existent notification " + logKey(entry)); + return false; + } else if (storedEntry != entry) { throw mEulogizer.record( - new IllegalStateException("No notification to remove with key " - + logKey(entry))); + new IllegalStateException("Mismatched stored and tryRemoved entries" + + " for key " + logKey(entry) + ":" + + " stored=@" + Integer.toHexString(storedEntry.hashCode()) + + " tryRemoved=@" + Integer.toHexString(entry.hashCode()))); } if (!entry.isCanceled()) { @@ -734,14 +767,16 @@ public class NotifCollection implements Dumpable, PipelineDumpable { } private void cancelLocalDismissal(NotificationEntry entry) { - if (entry.getDismissState() != NOT_DISMISSED) { - entry.setDismissState(NOT_DISMISSED); - if (entry.getSbn().getNotification().isGroupSummary()) { - for (NotificationEntry otherEntry : mNotificationSet.values()) { - if (otherEntry.getSbn().getGroupKey().equals(entry.getSbn().getGroupKey()) - && otherEntry.getDismissState() == PARENT_DISMISSED) { - otherEntry.setDismissState(NOT_DISMISSED); - } + if (entry.getDismissState() == NOT_DISMISSED) { + mLogger.logCancelLocalDismissalNotDismissedNotif(entry); + return; + } + entry.setDismissState(NOT_DISMISSED); + if (entry.getSbn().getNotification().isGroupSummary()) { + for (NotificationEntry otherEntry : mNotificationSet.values()) { + if (otherEntry.getSbn().getGroupKey().equals(entry.getSbn().getGroupKey()) + && otherEntry.getDismissState() == PARENT_DISMISSED) { + otherEntry.setDismissState(NOT_DISMISSED); } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java index bdb206beb123..38c37239a7d3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java @@ -71,7 +71,6 @@ public class NotificationRowBinderImpl implements NotificationRowBinder { private NotificationPresenter mPresenter; private NotificationListContainer mListContainer; - private BindRowCallback mBindRowCallback; private NotificationClicker mNotificationClicker; private FeatureFlags mFeatureFlags; @@ -103,11 +102,9 @@ public class NotificationRowBinderImpl implements NotificationRowBinder { * Sets up late-bound dependencies for this component. */ public void setUpWithPresenter(NotificationPresenter presenter, - NotificationListContainer listContainer, - BindRowCallback bindRowCallback) { + NotificationListContainer listContainer) { mPresenter = presenter; mListContainer = listContainer; - mBindRowCallback = bindRowCallback; mIconManager.attach(); } @@ -179,7 +176,7 @@ public class NotificationRowBinderImpl implements NotificationRowBinder { mNotificationRemoteInputManager.bindRow(row); entry.setRow(row); mNotifBindPipeline.manageRow(entry, row); - mBindRowCallback.onBindRow(row); + mPresenter.onBindRow(row); row.setInlineReplyAnimationFlagEnabled( mFeatureFlags.isEnabled(NOTIFICATION_INLINE_REPLY_ANIMATION)); } @@ -235,12 +232,4 @@ public class NotificationRowBinderImpl implements NotificationRowBinder { } }); } - - /** Callback for when a row is bound to an entry. */ - public interface BindRowCallback { - /** - * Called when a new row is created and bound to a notification. - */ - void onBindRow(ExpandableNotificationRow row); - } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionLogger.kt index 20de785bc9bd..73227ab9f4fb 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionLogger.kt @@ -108,27 +108,39 @@ class NotifCollectionLogger @Inject constructor( }) } - fun logNotifDismissed(entry: NotificationEntry) { + fun logLocallyDismissed(entry: NotificationEntry, index: Int, count: Int) { buffer.log(TAG, INFO, { str1 = entry.logKey + int1 = index + int2 = count }, { - "DISMISSED $str1" + "LOCALLY DISMISSED $str1 ($int1/$int2)" }) } - fun logNonExistentNotifDismissed(entry: NotificationEntry) { + fun logDismissNonExistentNotif(entry: NotificationEntry, index: Int, count: Int) { buffer.log(TAG, INFO, { str1 = entry.logKey + int1 = index + int2 = count }, { - "DISMISSED Non Existent $str1" + "DISMISS Non Existent $str1 ($int1/$int2)" }) } - fun logChildDismissed(entry: NotificationEntry) { + fun logLocallyDismissedChild( + child: NotificationEntry, + parent: NotificationEntry, + parentIndex: Int, + parentCount: Int + ) { buffer.log(TAG, DEBUG, { - str1 = entry.logKey + str1 = child.logKey + str2 = parent.logKey + int1 = parentIndex + int2 = parentCount }, { - "CHILD DISMISSED (inferred): $str1" + "LOCALLY DISMISSED CHILD (inferred): $str1 of parent $str2 ($int1/$int2)" }) } @@ -140,27 +152,31 @@ class NotifCollectionLogger @Inject constructor( }) } - fun logDismissOnAlreadyCanceledEntry(entry: NotificationEntry) { + fun logLocallyDismissedAlreadyCanceledEntry(entry: NotificationEntry) { buffer.log(TAG, DEBUG, { str1 = entry.logKey }, { - "Dismiss on $str1, which was already canceled. Trying to remove..." + "LOCALLY DISMISSED Already Canceled $str1. Trying to remove." }) } - fun logNotifDismissedIntercepted(entry: NotificationEntry) { + fun logNotifDismissedIntercepted(entry: NotificationEntry, index: Int, count: Int) { buffer.log(TAG, INFO, { str1 = entry.logKey + int1 = index + int2 = count }, { - "DISMISS INTERCEPTED $str1" + "DISMISS INTERCEPTED $str1 ($int1/$int2)" }) } - fun logNotifClearAllDismissalIntercepted(entry: NotificationEntry) { + fun logNotifClearAllDismissalIntercepted(entry: NotificationEntry, index: Int, count: Int) { buffer.log(TAG, INFO, { str1 = entry.logKey + int1 = index + int2 = count }, { - "CLEAR ALL DISMISSAL INTERCEPTED $str1" + "CLEAR ALL DISMISSAL INTERCEPTED $str1 ($int1/$int2)" }) } @@ -251,12 +267,19 @@ class NotifCollectionLogger @Inject constructor( }) } - fun logRemoteExceptionOnNotificationClear(entry: NotificationEntry, e: RemoteException) { + fun logRemoteExceptionOnNotificationClear( + entry: NotificationEntry, + index: Int, + count: Int, + e: RemoteException + ) { buffer.log(TAG, WTF, { str1 = entry.logKey + int1 = index + int2 = count str2 = e.toString() }, { - "RemoteException while attempting to clear $str1:\n$str2" + "RemoteException while attempting to clear $str1 ($int1/$int2):\n$str2" }) } @@ -387,6 +410,126 @@ class NotifCollectionLogger @Inject constructor( "Mismatch: current $str2 is $str3 for: $str1" }) } + + fun logDismissAlreadyDismissedNotif(entry: NotificationEntry, index: Int, count: Int) { + buffer.log(TAG, DEBUG, { + str1 = entry.logKey + int1 = index + int2 = count + }, { + "DISMISS Already Dismissed $str1 ($int1/$int2)" + }) + } + + fun logDismissAlreadyParentDismissedNotif( + childEntry: NotificationEntry, + childIndex: Int, + childCount: Int + ) { + buffer.log(TAG, DEBUG, { + str1 = childEntry.logKey + int1 = childIndex + int2 = childCount + str2 = childEntry.parent?.summary?.logKey ?: "(null)" + }, { + "DISMISS Already Parent-Dismissed $str1 ($int1/$int2) with summary $str2" + }) + } + + fun logLocallyDismissNonExistentNotif(entry: NotificationEntry, index: Int, count: Int) { + buffer.log(TAG, INFO, { + str1 = entry.logKey + int1 = index + int2 = count + }, { + "LOCALLY DISMISS Non Existent $str1 ($int1/$int2)" + }) + } + + fun logLocallyDismissMismatchedEntry( + entry: NotificationEntry, + index: Int, + count: Int, + storedEntry: NotificationEntry + ) { + buffer.log(TAG, INFO, { + str1 = entry.logKey + int1 = index + int2 = count + str2 = Integer.toHexString(entry.hashCode()) + str3 = Integer.toHexString(storedEntry.hashCode()) + }, { + "LOCALLY DISMISS Mismatch $str1 ($int1/$int2): dismissing @$str2 but stored @$str3" + }) + } + + fun logLocallyDismissAlreadyDismissedNotif( + entry: NotificationEntry, + index: Int, + count: Int + ) { + buffer.log(TAG, INFO, { + str1 = entry.logKey + int1 = index + int2 = count + }, { + "LOCALLY DISMISS Already Dismissed $str1 ($int1/$int2)" + }) + } + + fun logLocallyDismissAlreadyParentDismissedNotif( + entry: NotificationEntry, + index: Int, + count: Int + ) { + buffer.log(TAG, INFO, { + str1 = entry.logKey + int1 = index + int2 = count + }, { + "LOCALLY DISMISS Already Dismissed $str1 ($int1/$int2)" + }) + } + + fun logLocallyDismissAlreadyDismissedChild( + childEntry: NotificationEntry, + parentEntry: NotificationEntry, + parentIndex: Int, + parentCount: Int + ) { + buffer.log(TAG, INFO, { + str1 = childEntry.logKey + str2 = parentEntry.logKey + int1 = parentIndex + int2 = parentCount + }, { + "LOCALLY DISMISS Already Dismissed Child $str1 of parent $str2 ($int1/$int2)" + }) + } + + fun logLocallyDismissAlreadyParentDismissedChild( + childEntry: NotificationEntry, + parentEntry: NotificationEntry, + parentIndex: Int, + parentCount: Int + ) { + buffer.log(TAG, INFO, { + str1 = childEntry.logKey + str2 = parentEntry.logKey + int1 = parentIndex + int2 = parentCount + }, { + "LOCALLY DISMISS Already Parent-Dismissed Child $str1 of parent $str2 ($int1/$int2)" + }) + } + + fun logCancelLocalDismissalNotDismissedNotif(entry: NotificationEntry) { + buffer.log(TAG, INFO, { + str1 = entry.logKey + }, { + "CANCEL LOCAL DISMISS Not Dismissed $str1" + }) + } } private const val TAG = "NotifCollection" diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsInteractor.kt new file mode 100644 index 000000000000..8f7e269d34b0 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsInteractor.kt @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.notification.domain.interactor + +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.statusbar.disableflags.data.repository.DisableFlagsRepository +import javax.inject.Inject + +/** Interactor for notifications in general. */ +@SysUISingleton +class NotificationsInteractor +@Inject +constructor( + private val disableFlagsRepository: DisableFlagsRepository, +) { + /** Returns true if notification alerts are allowed. */ + fun areNotificationAlertsEnabled(): Boolean { + return disableFlagsRepository.disableFlags.value.areNotificationAlertsEnabled() + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsController.kt index a5278c3d0ad3..76e228bb54d2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsController.kt @@ -20,10 +20,8 @@ import android.service.notification.StatusBarNotification import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption import com.android.systemui.statusbar.NotificationPresenter import com.android.systemui.statusbar.notification.NotificationActivityStarter -import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl import com.android.systemui.statusbar.notification.collection.render.NotifStackController import com.android.systemui.statusbar.notification.stack.NotificationListContainer -import com.android.systemui.statusbar.phone.CentralSurfaces /** * The master controller for all notifications-related work @@ -33,12 +31,10 @@ import com.android.systemui.statusbar.phone.CentralSurfaces */ interface NotificationsController { fun initialize( - centralSurfaces: CentralSurfaces, presenter: NotificationPresenter, listContainer: NotificationListContainer, stackController: NotifStackController, notificationActivityStarter: NotificationActivityStarter, - bindRowCallback: NotificationRowBinderImpl.BindRowCallback ) fun resetUserExpandedStates() diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt index 0ed41758f215..f7bd177594a2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt @@ -42,7 +42,6 @@ import com.android.systemui.statusbar.notification.logging.NotificationLogger import com.android.systemui.statusbar.notification.logging.NotificationMemoryMonitor import com.android.systemui.statusbar.notification.row.NotifBindPipelineInitializer import com.android.systemui.statusbar.notification.stack.NotificationListContainer -import com.android.systemui.statusbar.phone.CentralSurfaces import com.android.wm.shell.bubbles.Bubbles import dagger.Lazy import java.util.Optional @@ -78,12 +77,10 @@ class NotificationsControllerImpl @Inject constructor( ) : NotificationsController { override fun initialize( - centralSurfaces: CentralSurfaces, presenter: NotificationPresenter, listContainer: NotificationListContainer, stackController: NotifStackController, notificationActivityStarter: NotificationActivityStarter, - bindRowCallback: NotificationRowBinderImpl.BindRowCallback ) { notificationListener.registerAsSystemService() @@ -94,13 +91,8 @@ class NotificationsControllerImpl @Inject constructor( }) notificationRowBinder.setNotificationClicker( - clickerBuilder.build( - Optional.ofNullable(centralSurfaces), bubblesOptional, - notificationActivityStarter)) - notificationRowBinder.setUpWithPresenter( - presenter, - listContainer, - bindRowCallback) + clickerBuilder.build(bubblesOptional, notificationActivityStarter)) + notificationRowBinder.setUpWithPresenter(presenter, listContainer) headsUpViewBinder.setPresenter(presenter) notifBindPipelineInitializer.initialize() animatedImageNotificationManager.bind() diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerStub.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerStub.kt index 14856dafdb11..65ba6de5b5cb 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerStub.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerStub.kt @@ -21,10 +21,8 @@ import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.Snoo import com.android.systemui.statusbar.NotificationListener import com.android.systemui.statusbar.NotificationPresenter import com.android.systemui.statusbar.notification.NotificationActivityStarter -import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl import com.android.systemui.statusbar.notification.collection.render.NotifStackController import com.android.systemui.statusbar.notification.stack.NotificationListContainer -import com.android.systemui.statusbar.phone.CentralSurfaces import javax.inject.Inject /** @@ -35,12 +33,10 @@ class NotificationsControllerStub @Inject constructor( ) : NotificationsController { override fun initialize( - centralSurfaces: CentralSurfaces, presenter: NotificationPresenter, listContainer: NotificationListContainer, stackController: NotifStackController, notificationActivityStarter: NotificationActivityStarter, - bindRowCallback: NotificationRowBinderImpl.BindRowCallback ) { // Always connect the listener even if notification-handling is disabled. Being a listener // grants special permissions and it's not clear if other things will break if we lose those diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java index 417d4acccf4e..8af488ea443d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java @@ -321,8 +321,7 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView protected void setBackgroundTintColor(int color) { if (color != mCurrentBackgroundTint) { mCurrentBackgroundTint = color; - // TODO(282173943): re-enable this tinting optimization when Resources are thread-safe - if (false && color == mNormalColor) { + if (color == mNormalColor) { // We don't need to tint a normal notification color = 0; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java index 9f397fe9ac0c..0bfd3c3c0b2b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java @@ -2673,7 +2673,12 @@ public class ExpandableNotificationRow extends ActivatableNotificationView } public boolean isExpanded(boolean allowOnKeyguard) { - return (!mOnKeyguard || allowOnKeyguard) + if (DEBUG) { + if (!mShowingPublicInitialized && !allowOnKeyguard) { + Log.d(TAG, "mShowingPublic is not initialized."); + } + } + return !mShowingPublic && (!mOnKeyguard || allowOnKeyguard) && (!hasUserChangedExpansion() && (isSystemExpanded() || isSystemChildExpanded()) || isUserExpanded()); } @@ -3620,6 +3625,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView pw.print(", mOnUserInteractionCallback null: " + (mOnUserInteractionCallback == null)); pw.print(", removed: " + isRemoved()); pw.print(", expandAnimationRunning: " + mExpandAnimationRunning); + pw.print(", mShowingPublic: " + mShowingPublic); + pw.print(", mShowingPublicInitialized: " + mShowingPublicInitialized); NotificationContentView showingLayout = getShowingLayout(); pw.print(", privateShowing: " + (showingLayout == mPrivateLayout)); pw.println(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragController.java index 7a2bee91e972..b95018777fea 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragController.java @@ -148,7 +148,7 @@ public class ExpandableNotificationRowDragController { private void dismissShade() { // Speed up dismissing the shade since the drag needs to be handled by // the shell layer underneath - mShadeController.animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE, true /* force */, + mShadeController.animateCollapseShade(CommandQueue.FLAG_EXCLUDE_NONE, true /* force */, false /* delayed */, 1.1f /* speedUpFactor */); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java index 0989df61a5e3..6bbeebfdb431 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java @@ -16,15 +16,11 @@ package com.android.systemui.statusbar.notification.row; -import static android.graphics.PorterDuff.Mode.SRC_ATOP; - import android.annotation.ColorInt; import android.content.Context; import android.content.res.ColorStateList; import android.content.res.Configuration; import android.content.res.Resources; -import android.graphics.ColorFilter; -import android.graphics.PorterDuffColorFilter; import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.util.IndentingPrintWriter; @@ -161,20 +157,10 @@ public class FooterView extends StackScrollerDecorView { */ public void updateColors() { Resources.Theme theme = mContext.getTheme(); - final @ColorInt int textColor = getResources().getColor(R.color.notif_pill_text, theme); - final Drawable clearAllBg = theme.getDrawable(R.drawable.notif_footer_btn_background); - final Drawable manageBg = theme.getDrawable(R.drawable.notif_footer_btn_background); - // TODO(b/282173943): Remove redundant tinting once Resources are thread-safe - final @ColorInt int buttonBgColor = - Utils.getColorAttrDefaultColor(mContext, com.android.internal.R.attr.colorSurface); - final ColorFilter bgColorFilter = new PorterDuffColorFilter(buttonBgColor, SRC_ATOP); - if (buttonBgColor != 0) { - clearAllBg.setColorFilter(bgColorFilter); - manageBg.setColorFilter(bgColorFilter); - } - mClearAllButton.setBackground(clearAllBg); + int textColor = getResources().getColor(R.color.notif_pill_text, theme); + mClearAllButton.setBackground(theme.getDrawable(R.drawable.notif_footer_btn_background)); mClearAllButton.setTextColor(textColor); - mManageButton.setBackground(manageBg); + mManageButton.setBackground(theme.getDrawable(R.drawable.notif_footer_btn_background)); mManageButton.setTextColor(textColor); final @ColorInt int labelTextColor = Utils.getColorAttrDefaultColor(mContext, android.R.attr.textColorPrimary); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java index b4bfded58e4b..13d1978ec8ff 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java @@ -474,7 +474,6 @@ public class NotificationContentInflater implements NotificationRowContentBinder parentLayout, remoteViewClickHandler); validateView(v, entry, row.getResources()); - v.setIsRootNamespace(true); applyCallback.setResultView(v); } else { newContentView.reapply( @@ -511,7 +510,6 @@ public class NotificationContentInflater implements NotificationRowContentBinder return; } if (isNewView) { - v.setIsRootNamespace(true); applyCallback.setResultView(v); } else if (existingWrapper != null) { existingWrapper.onReinflated(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java index 124df8c3b815..20f4429f294b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java @@ -208,6 +208,23 @@ public class NotificationContentView extends FrameLayout implements Notification mSmartReplyConstants = smartReplyConstants; mSmartReplyController = smartReplyController; mStatusBarService = statusBarService; + // We set root namespace so that we avoid searching children for id. Notification might + // contain custom view and their ids may clash with ids already existing in shade or + // notification panel + setIsRootNamespace(true); + } + + @Override + public View focusSearch(View focused, int direction) { + // This implementation is copied from ViewGroup but with removed special handling of + // setIsRootNamespace. This view is set as tree root using setIsRootNamespace and it + // causes focus to be stuck inside of it. We need to be root to avoid id conflicts + // but we don't want to behave like root when it comes to focusing. + if (mParent != null) { + return mParent.focusSearch(focused, direction); + } + Log.wtf(TAG, "NotificationContentView doesn't have parent"); + return null; } public void reinflate() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractor.kt index 014406fe49f9..4b896154c841 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractor.kt @@ -17,26 +17,24 @@ package com.android.systemui.statusbar.notification.shelf.domain.interactor import android.os.PowerManager +import com.android.systemui.dagger.SysUISingleton import com.android.systemui.keyguard.data.repository.DeviceEntryFaceAuthRepository import com.android.systemui.keyguard.data.repository.KeyguardRepository +import com.android.systemui.power.domain.interactor.PowerInteractor import com.android.systemui.statusbar.LockscreenShadeTransitionController import com.android.systemui.statusbar.NotificationShelf -import com.android.systemui.statusbar.phone.CentralSurfaces -import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent -import com.android.systemui.util.time.SystemClock import javax.inject.Inject import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine /** Interactor for the [NotificationShelf] */ -@CentralSurfacesComponent.CentralSurfacesScope +@SysUISingleton class NotificationShelfInteractor @Inject constructor( private val keyguardRepository: KeyguardRepository, private val deviceEntryFaceAuthRepository: DeviceEntryFaceAuthRepository, - private val centralSurfaces: CentralSurfaces, - private val systemClock: SystemClock, + private val powerInteractor: PowerInteractor, private val keyguardTransitionController: LockscreenShadeTransitionController, ) { /** Is the shelf showing on the keyguard? */ @@ -55,11 +53,7 @@ constructor( /** Transition keyguard to the locked shade, triggered by the shelf. */ fun goToLockedShadeFromShelf() { - centralSurfaces.wakeUpIfDozing( - systemClock.uptimeMillis(), - "SHADE_CLICK", - PowerManager.WAKE_REASON_GESTURE, - ) + powerInteractor.wakeUpIfDozing("SHADE_CLICK", PowerManager.WAKE_REASON_GESTURE) keyguardTransitionController.goToLockedShade(null) } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt index 12956ab9498a..23a58d252ba6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewbinder/NotificationShelfViewBinder.kt @@ -19,6 +19,7 @@ package com.android.systemui.statusbar.notification.shelf.ui.viewbinder import android.view.View import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle +import com.android.systemui.dagger.SysUISingleton import com.android.systemui.flags.FeatureFlags import com.android.systemui.flags.Flags import com.android.systemui.lifecycle.repeatWhenAttached @@ -32,7 +33,6 @@ import com.android.systemui.statusbar.notification.stack.AmbientState import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController import com.android.systemui.statusbar.phone.NotificationIconAreaController import com.android.systemui.statusbar.phone.NotificationIconContainer -import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent.CentralSurfacesScope import javax.inject.Inject import kotlinx.coroutines.awaitCancellation import kotlinx.coroutines.launch @@ -43,7 +43,7 @@ import kotlinx.coroutines.launch * [NotificationShelfController] interface. Once the [LegacyNotificationShelfControllerImpl] is * removed, this class can go away and the ViewBinder can be used directly. */ -@CentralSurfacesScope +@SysUISingleton class NotificationShelfViewBinderWrapperControllerImpl @Inject constructor() : NotificationShelfController { @@ -81,7 +81,7 @@ object NotificationShelfViewBinder { ActivatableNotificationViewBinder.bind(viewModel, shelf, falsingManager) shelf.apply { setRefactorFlagEnabled(true) - setSensitiveRevealAnimEndabled(featureFlags.isEnabled(Flags.SENSITIVE_REVEAL_ANIM)) + setSensitiveRevealAnimEnabled(featureFlags.isEnabled(Flags.SENSITIVE_REVEAL_ANIM)) // TODO(278765923): Replace with eventual NotificationIconContainerViewBinder#bind() notificationIconAreaController.setShelfIcons(shelfIcons) repeatWhenAttached { 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 df6fe3d5e434..d1413a275ff2 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 @@ -104,13 +104,13 @@ import com.android.systemui.statusbar.notification.NotificationUtils; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager; import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager; +import com.android.systemui.statusbar.notification.init.NotificationsController; import com.android.systemui.statusbar.notification.logging.NotificationLogger; import com.android.systemui.statusbar.notification.row.ActivatableNotificationView; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.ExpandableView; import com.android.systemui.statusbar.notification.row.FooterView; import com.android.systemui.statusbar.notification.row.StackScrollerDecorView; -import com.android.systemui.statusbar.phone.CentralSurfaces; import com.android.systemui.statusbar.phone.HeadsUpAppearanceController; import com.android.systemui.statusbar.phone.HeadsUpTouchHelper; import com.android.systemui.statusbar.phone.ScreenOffAnimationController; @@ -197,7 +197,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable */ private Set<Integer> mDebugTextUsedYPositions; private final boolean mDebugRemoveAnimation; - private final boolean mSimplifiedAppearFraction; private final boolean mSensitiveRevealAnimEndabled; private boolean mAnimatedInsets; @@ -316,7 +315,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable } }; private NotificationStackScrollLogger mLogger; - private CentralSurfaces mCentralSurfaces; + private NotificationsController mNotificationsController; private ActivityStarter mActivityStarter; private final int[] mTempInt2 = new int[2]; private boolean mGenerateChildOrderChangedEvent; @@ -621,7 +620,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable FeatureFlags featureFlags = Dependency.get(FeatureFlags.class); mDebugLines = featureFlags.isEnabled(Flags.NSSL_DEBUG_LINES); mDebugRemoveAnimation = featureFlags.isEnabled(Flags.NSSL_DEBUG_REMOVE_ANIMATION); - mSimplifiedAppearFraction = featureFlags.isEnabled(Flags.SIMPLIFIED_APPEAR_FRACTION); mSensitiveRevealAnimEndabled = featureFlags.isEnabled(Flags.SENSITIVE_REVEAL_ANIM); setAnimatedInsetsEnabled(featureFlags.isEnabled(Flags.ANIMATED_NOTIFICATION_SHADE_INSETS)); mSectionsManager = Dependency.get(NotificationSectionsManager.class); @@ -1638,14 +1636,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable return mAmbientState.getTrackedHeadsUpRow() != null; } - // TODO(b/246353296): remove it when Flags.SIMPLIFIED_APPEAR_FRACTION is removed - public float calculateAppearFractionOld(float height) { - float appearEndPosition = getAppearEndPosition(); - float appearStartPosition = getAppearStartPosition(); - return (height - appearStartPosition) - / (appearEndPosition - appearStartPosition); - } - /** * @param height the height of the panel * @return Fraction of the appear animation that has been performed. Normally follows expansion @@ -1653,33 +1643,24 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable * when HUN is swiped up. */ @FloatRange(from = -1.0, to = 1.0) - public float simplifiedAppearFraction(float height) { + public float calculateAppearFraction(float height) { if (isHeadsUpTransition()) { // HUN is a special case because fraction can go negative if swiping up. And for now // it must go negative as other pieces responsible for proper translation up assume // negative value for HUN going up. // This can't use expansion fraction as that goes only from 0 to 1. Also when // appear fraction for HUN is 0, expansion fraction will be already around 0.2-0.3 - // and that makes translation jump immediately. Let's use old implementation for now and - // see if we can figure out something better - return MathUtils.constrain(calculateAppearFractionOld(height), -1, 1); + // and that makes translation jump immediately. + float appearEndPosition = getAppearEndPosition(); + float appearStartPosition = getAppearStartPosition(); + float hunAppearFraction = (height - appearStartPosition) + / (appearEndPosition - appearStartPosition); + return MathUtils.constrain(hunAppearFraction, -1, 1); } else { return mAmbientState.getExpansionFraction(); } } - public float calculateAppearFraction(float height) { - if (mSimplifiedAppearFraction) { - return simplifiedAppearFraction(height); - } else if (mShouldUseSplitNotificationShade) { - // for split shade we want to always use the new way of calculating appear fraction - // because without it heads-up experience is very broken and it's less risky change - return simplifiedAppearFraction(height); - } else { - return calculateAppearFractionOld(height); - } - } - public float getStackTranslation() { return mStackTranslation; } @@ -4008,7 +3989,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable mAmbientState.setExpansionChanging(false); if (!mIsExpanded) { resetScrollPosition(); - mCentralSurfaces.resetUserExpandedStates(); + mNotificationsController.resetUserExpandedStates(); clearTemporaryViews(); clearUserLockedViews(); resetAllSwipeState(); @@ -4602,8 +4583,8 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable return max + getStackTranslation(); } - public void setCentralSurfaces(CentralSurfaces centralSurfaces) { - this.mCentralSurfaces = centralSurfaces; + public void setNotificationsController(NotificationsController notificationsController) { + this.mNotificationsController = notificationsController; } public void setActivityStarter(ActivityStarter activityStarter) { @@ -5070,6 +5051,9 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable println(pw, "intrinsicPadding", mIntrinsicPadding); println(pw, "topPadding", mTopPadding); println(pw, "bottomPadding", mBottomPadding); + println(pw, "translationX", getTranslationX()); + println(pw, "translationY", getTranslationY()); + println(pw, "translationZ", getTranslationZ()); mNotificationStackSizeCalculator.dump(pw, args); }); pw.println(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java index 9272c376d4fe..ad7cdc4eb50f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java @@ -56,11 +56,14 @@ import com.android.internal.logging.UiEventLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.systemui.ExpandHelper; import com.android.systemui.Gefingerpoken; +import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor; import com.android.systemui.classifier.Classifier; import com.android.systemui.classifier.FalsingCollector; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dump.DumpManager; import com.android.systemui.flags.FeatureFlags; +import com.android.systemui.flags.Flags; +import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor; import com.android.systemui.media.controls.ui.KeyguardMediaController; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.FalsingManager; @@ -69,6 +72,7 @@ import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.OnMenuEv import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.shade.ShadeController; +import com.android.systemui.shade.ShadeViewController; import com.android.systemui.statusbar.LockscreenShadeTransitionController; import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.NotificationLockscreenUserManager.UserChangedListener; @@ -98,6 +102,7 @@ import com.android.systemui.statusbar.notification.collection.render.NotifStats; import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider; import com.android.systemui.statusbar.notification.collection.render.SectionHeaderController; import com.android.systemui.statusbar.notification.dagger.SilentHeader; +import com.android.systemui.statusbar.notification.init.NotificationsController; import com.android.systemui.statusbar.notification.logging.NotificationLogger; import com.android.systemui.statusbar.notification.row.ActivatableNotificationView; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; @@ -107,7 +112,6 @@ import com.android.systemui.statusbar.notification.row.NotificationGutsManager; import com.android.systemui.statusbar.notification.row.NotificationSnooze; import com.android.systemui.statusbar.notification.stack.ui.viewbinder.NotificationListViewBinder; import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationListViewModel; -import com.android.systemui.statusbar.phone.CentralSurfaces; import com.android.systemui.statusbar.phone.HeadsUpAppearanceController; import com.android.systemui.statusbar.phone.HeadsUpManagerPhone; import com.android.systemui.statusbar.phone.HeadsUpTouchHelper; @@ -144,6 +148,7 @@ public class NotificationStackScrollLayoutController { private final boolean mAllowLongPress; private final NotificationGutsManager mNotificationGutsManager; + private final NotificationsController mNotificationsController; private final NotificationVisibilityProvider mVisibilityProvider; private final HeadsUpManagerPhone mHeadsUpManager; private final NotificationRoundnessManager mNotificationRoundnessManager; @@ -170,9 +175,9 @@ public class NotificationStackScrollLayoutController { private final KeyguardMediaController mKeyguardMediaController; private final SysuiStatusBarStateController mStatusBarStateController; private final KeyguardBypassController mKeyguardBypassController; + private final KeyguardInteractor mKeyguardInteractor; + private final PrimaryBouncerInteractor mPrimaryBouncerInteractor; private final NotificationLockscreenUserManager mLockscreenUserManager; - // TODO: CentralSurfaces should be encapsulated behind a Controller - private final CentralSurfaces mCentralSurfaces; private final SectionHeaderController mSilentHeaderController; private final LockscreenShadeTransitionController mLockscreenShadeTransitionController; private final InteractionJankMonitor mJankMonitor; @@ -190,6 +195,7 @@ public class NotificationStackScrollLayoutController { @Nullable private Boolean mHistoryEnabled; private int mBarState; + private boolean mIsBouncerShowingFromCentralSurfaces; private HeadsUpAppearanceController mHeadsUpAppearanceController; private final FeatureFlags mFeatureFlags; private final NotificationTargetsHelper mNotificationTargetsHelper; @@ -429,7 +435,7 @@ public class NotificationStackScrollLayoutController { @Override public void onSnooze(StatusBarNotification sbn, NotificationSwipeActionHelper.SnoozeOption snoozeOption) { - mCentralSurfaces.setNotificationSnoozed(sbn, snoozeOption); + mNotificationsController.setNotificationSnoozed(sbn, snoozeOption); } @Override @@ -559,7 +565,8 @@ public class NotificationStackScrollLayoutController { @Override public float getFalsingThresholdFactor() { - return mCentralSurfaces.isWakeUpComingFromTouch() ? 1.5f : 1.0f; + return ShadeViewController.getFalsingThresholdFactor( + mKeyguardInteractor.getWakefulnessModel().getValue()); } @Override @@ -613,6 +620,7 @@ public class NotificationStackScrollLayoutController { NotificationStackScrollLayout view, @Named(ALLOW_NOTIFICATION_LONG_PRESS_NAME) boolean allowLongPress, NotificationGutsManager notificationGutsManager, + NotificationsController notificationsController, NotificationVisibilityProvider visibilityProvider, HeadsUpManagerPhone headsUpManager, NotificationRoundnessManager notificationRoundnessManager, @@ -623,6 +631,8 @@ public class NotificationStackScrollLayoutController { SysuiStatusBarStateController statusBarStateController, KeyguardMediaController keyguardMediaController, KeyguardBypassController keyguardBypassController, + KeyguardInteractor keyguardInteractor, + PrimaryBouncerInteractor primaryBouncerInteractor, ZenModeController zenModeController, NotificationLockscreenUserManager lockscreenUserManager, Optional<NotificationListViewModel> nsslViewModel, @@ -632,7 +642,6 @@ public class NotificationStackScrollLayoutController { FalsingManager falsingManager, @Main Resources resources, NotificationSwipeHelper.Builder notificationSwipeHelperBuilder, - CentralSurfaces centralSurfaces, ScrimController scrimController, GroupExpansionManager groupManager, @SilentHeader SectionHeaderController silentHeaderController, @@ -660,6 +669,7 @@ public class NotificationStackScrollLayoutController { mLogger = logger; mAllowLongPress = allowLongPress; mNotificationGutsManager = notificationGutsManager; + mNotificationsController = notificationsController; mVisibilityProvider = visibilityProvider; mHeadsUpManager = headsUpManager; mNotificationRoundnessManager = notificationRoundnessManager; @@ -670,6 +680,8 @@ public class NotificationStackScrollLayoutController { mStatusBarStateController = statusBarStateController; mKeyguardMediaController = keyguardMediaController; mKeyguardBypassController = keyguardBypassController; + mKeyguardInteractor = keyguardInteractor; + mPrimaryBouncerInteractor = primaryBouncerInteractor; mZenModeController = zenModeController; mLockscreenUserManager = lockscreenUserManager; mViewModel = nsslViewModel; @@ -680,7 +692,6 @@ public class NotificationStackScrollLayoutController { mFalsingManager = falsingManager; mResources = resources; mNotificationSwipeHelperBuilder = notificationSwipeHelperBuilder; - mCentralSurfaces = centralSurfaces; mScrimController = scrimController; mJankMonitor = jankMonitor; mNotificationStackSizeCalculator = notificationStackSizeCalculator; @@ -709,7 +720,7 @@ public class NotificationStackScrollLayoutController { mView.setController(this); mView.setLogger(mLogger); mView.setTouchHandler(new TouchHandler()); - mView.setCentralSurfaces(mCentralSurfaces); + mView.setNotificationsController(mNotificationsController); mView.setActivityStarter(mActivityStarter); mView.setClearAllAnimationListener(this::onAnimationEnd); mView.setClearAllListener((selection) -> mUiEventLogger.log( @@ -1196,6 +1207,14 @@ public class NotificationStackScrollLayoutController { } /** + * Sets whether the bouncer is currently showing. Should only be called from + * {@link CentralSurfaces}. + */ + public void setBouncerShowingFromCentralSurfaces(boolean bouncerShowing) { + mIsBouncerShowingFromCentralSurfaces = bouncerShowing; + } + + /** * Set the visibility of the view, and propagate it to specific children. * * @param visible either the view is visible or not. @@ -1226,7 +1245,8 @@ public class NotificationStackScrollLayoutController { // That avoids "No Notifications" to blink when transitioning to AOD. // For more details, see: b/228790482 && !isInTransitionToKeyguard() - && !mCentralSurfaces.isBouncerShowing(); + // Don't show any notification content if the bouncer is showing. See b/267060171. + && !isBouncerShowing(); mView.updateEmptyShadeView(shouldShow, mZenModeController.areNotificationsHiddenInShade()); @@ -1234,6 +1254,24 @@ public class NotificationStackScrollLayoutController { } /** + * Returns whether the bouncer is currently showing. + * + * There's a possible timing difference between when CentralSurfaces marks the bouncer as not + * showing and when PrimaryBouncerInteractor marks the bouncer as not showing. (CentralSurfaces + * appears to mark the bouncer as showing for 10-200ms longer than PrimaryBouncerInteractor.) + * + * This timing difference could be load bearing, which is why we have a feature flag protecting + * where we fetch the value from. This flag is intended to be short-lived. + */ + private boolean isBouncerShowing() { + if (mFeatureFlags.isEnabled(Flags.USE_REPOS_FOR_BOUNCER_SHOWING)) { + return mPrimaryBouncerInteractor.isBouncerShowing(); + } else { + return mIsBouncerShowingFromCentralSurfaces; + } + } + + /** * Update the importantForAccessibility of NotificationStackScrollLayout. * <p> * We want the NSSL to be unimportant for accessibility when there's no diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java index efb7926b8b8b..b2d26d9dcf76 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java @@ -29,8 +29,6 @@ import com.android.internal.policy.SystemBarUtils; import com.android.keyguard.BouncerPanelExpansionCalculator; import com.android.systemui.R; import com.android.systemui.animation.ShadeInterpolation; -import com.android.systemui.flags.FeatureFlags; -import com.android.systemui.flags.Flags; import com.android.systemui.shade.transition.LargeScreenShadeInterpolator; import com.android.systemui.statusbar.EmptyShadeView; import com.android.systemui.statusbar.NotificationShelf; @@ -189,9 +187,7 @@ public class StackScrollAlgorithm { private float interpolateFooterAlpha(AmbientState ambientState) { float expansion = ambientState.getExpansionFraction(); - FeatureFlags flags = ambientState.getFeatureFlags(); - if (ambientState.isSmallScreen() - || !flags.isEnabled(Flags.LARGE_SHADE_GRANULAR_ALPHA_INTERPOLATION)) { + if (ambientState.isSmallScreen()) { return ShadeInterpolation.getContentAlpha(expansion); } LargeScreenShadeInterpolator interpolator = ambientState.getLargeScreenShadeInterpolator(); @@ -200,9 +196,7 @@ public class StackScrollAlgorithm { private float interpolateNotificationContentAlpha(AmbientState ambientState) { float expansion = ambientState.getExpansionFraction(); - FeatureFlags flags = ambientState.getFeatureFlags(); - if (ambientState.isSmallScreen() - || !flags.isEnabled(Flags.LARGE_SHADE_GRANULAR_ALPHA_INTERPOLATION)) { + if (ambientState.isSmallScreen()) { return ShadeInterpolation.getContentAlpha(expansion); } LargeScreenShadeInterpolator interpolator = ambientState.getLargeScreenShadeInterpolator(); @@ -772,8 +766,9 @@ public class StackScrollAlgorithm { boolean isTopEntry = topHeadsUpEntry == row; float unmodifiedEndLocation = childState.getYTranslation() + childState.height; if (mIsExpanded) { - if (row.mustStayOnScreen() && !childState.headsUpIsVisible - && !row.showingPulsing() && !ambientState.isOnKeyguard()) { + if (shouldHunBeVisibleWhenScrolled(row.mustStayOnScreen(), + childState.headsUpIsVisible, row.showingPulsing(), + ambientState.isOnKeyguard(), row.getEntry().isStickyAndNotDemoted())) { // Ensure that the heads up is always visible even when scrolled off clampHunToTop(mQuickQsOffsetHeight, ambientState.getStackTranslation(), row.getCollapsedHeight(), childState); @@ -821,7 +816,15 @@ public class StackScrollAlgorithm { } } - /** + @VisibleForTesting + boolean shouldHunBeVisibleWhenScrolled(boolean mustStayOnScreen, boolean headsUpIsVisible, + boolean showingPulsing, boolean isOnKeyguard, boolean headsUpOnKeyguard) { + return mustStayOnScreen && !headsUpIsVisible + && !showingPulsing + && (!isOnKeyguard || headsUpOnKeyguard); + } + + /** * When shade is open and we are scrolled to the bottom of notifications, * clamp incoming HUN in its collapsed form, right below qs offset. * Transition pinned collapsed HUN to full height when scrolling back up. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt index 1827a46958f8..730ef57f1972 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt @@ -45,6 +45,7 @@ import com.android.systemui.plugins.ActivityStarter.OnDismissAction import com.android.systemui.settings.UserTracker import com.android.systemui.shade.ShadeController import com.android.systemui.statusbar.NotificationLockscreenUserManager +import com.android.systemui.statusbar.NotificationShadeWindowController import com.android.systemui.statusbar.SysuiStatusBarStateController import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow import com.android.systemui.statusbar.policy.DeviceProvisionedController @@ -68,6 +69,7 @@ constructor( private val keyguardViewMediatorLazy: Lazy<KeyguardViewMediator>, private val shadeControllerLazy: Lazy<ShadeController>, private val statusBarKeyguardViewManagerLazy: Lazy<StatusBarKeyguardViewManager>, + private val notifShadeWindowControllerLazy: Lazy<NotificationShadeWindowController>, private val activityLaunchAnimator: ActivityLaunchAnimator, private val context: Context, private val lockScreenUserManager: NotificationLockscreenUserManager, @@ -387,6 +389,35 @@ constructor( } /** + * Whether we should animate an activity launch. + * + * Note: This method must be called *before* dismissing the keyguard. + */ + private fun shouldAnimateLaunch( + isActivityIntent: Boolean, + showOverLockscreen: Boolean, + ): Boolean { + // TODO(b/184121838): Support launch animations when occluded. + if (keyguardStateController.isOccluded) { + return false + } + + // Always animate if we are not showing the keyguard or if we animate over the lockscreen + // (without unlocking it). + if (showOverLockscreen || !keyguardStateController.isShowing) { + return true + } + + // We don't animate non-activity launches as they can break the animation. + // TODO(b/184121838): Support non activity launches on the lockscreen. + return isActivityIntent + } + + override fun shouldAnimateLaunch(isActivityIntent: Boolean): Boolean { + return shouldAnimateLaunch(isActivityIntent, false) + } + + /** * Encapsulates the activity logic for activity starter. * * Logic is duplicated in {@link CentralSurfacesImpl} @@ -417,7 +448,7 @@ constructor( val animate = animationController != null && !willLaunchResolverActivity && - centralSurfaces?.shouldAnimateLaunch(true /* isActivityIntent */) == true + shouldAnimateLaunch(isActivityIntent = true) val animController = wrapAnimationController( animationController = animationController, @@ -536,7 +567,7 @@ constructor( val animate = !willLaunchResolverActivity && animationController != null && - centralSurfaces?.shouldAnimateLaunch(intent.isActivity) == true + shouldAnimateLaunch(intent.isActivity) // If we animate, don't collapse the shade and defer the keyguard dismiss (in case we // run the animation on the keyguard). The animation will take care of (instantly) @@ -593,7 +624,7 @@ constructor( Log.w(TAG, "Sending intent failed: $e") if (!collapse) { // executeRunnableDismissingKeyguard did not collapse for us already. - centralSurfaces?.collapsePanelOnMainThread() + shadeControllerLazy.get().collapseOnMainThread() } // TODO: Dismiss Keyguard. } @@ -635,7 +666,7 @@ constructor( val animate = animationController != null && - centralSurfaces?.shouldAnimateLaunch( + shouldAnimateLaunch( /* isActivityIntent= */ true, showOverLockscreenWhenLocked ) == true @@ -713,7 +744,7 @@ constructor( } else if (dismissShade) { // The animation will take care of dismissing the shade at the end of the animation. // If we don't animate, collapse it directly. - centralSurfaces?.collapseShade() + shadeControllerLazy.get().cancelExpansionAndCollapseShade() } // We should exit the dream to prevent the activity from starting below the @@ -802,7 +833,7 @@ constructor( shadeControllerLazy.get().isExpandedVisible && !statusBarKeyguardViewManagerLazy.get().isBouncerShowing ) { - shadeControllerLazy.get().animateCollapseShadeDelayed() + shadeControllerLazy.get().animateCollapseShadeForcedDelayed() } else { // Do it after DismissAction has been processed to conserve the // needed ordering. @@ -865,7 +896,9 @@ constructor( if (dismissShade) { return StatusBarLaunchAnimatorController( animationController, - it, + it.shadeViewController, + shadeControllerLazy.get(), + notifShadeWindowControllerLazy.get(), isLaunchForActivity ) } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java index 86bf7cb6fa2f..630f1f43f8c7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java @@ -26,11 +26,10 @@ import android.content.pm.PackageManager; import android.os.Bundle; import android.os.PowerManager; import android.os.UserHandle; -import android.service.notification.StatusBarNotification; import android.view.KeyEvent; +import android.view.MotionEvent; import android.view.RemoteAnimationAdapter; import android.view.View; -import android.view.ViewGroup; import android.window.RemoteTransition; import android.window.SplashScreen; @@ -39,19 +38,15 @@ import androidx.lifecycle.Lifecycle; import androidx.lifecycle.LifecycleOwner; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.statusbar.RegisterStatusBarResult; import com.android.keyguard.AuthKeyguardMessageArea; import com.android.systemui.Dumpable; import com.android.systemui.animation.ActivityLaunchAnimator; import com.android.systemui.navigationbar.NavigationBarView; import com.android.systemui.plugins.ActivityStarter.OnDismissAction; -import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper; +import com.android.systemui.power.domain.interactor.PowerInteractor; import com.android.systemui.qs.QSPanelController; -import com.android.systemui.shade.NotificationShadeWindowView; -import com.android.systemui.shade.NotificationShadeWindowViewController; import com.android.systemui.shade.ShadeViewController; import com.android.systemui.shared.system.RemoteAnimationRunnerCompat; -import com.android.systemui.statusbar.LightRevealScrim; import com.android.systemui.statusbar.NotificationPresenter; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.util.Compile; @@ -69,14 +64,11 @@ public interface CentralSurfaces extends Dumpable, LifecycleOwner { String TAG = "CentralSurfaces"; boolean DEBUG = false; boolean SPEW = false; - boolean DUMPTRUCK = true; // extra dumpsys info boolean DEBUG_GESTURES = false; boolean DEBUG_MEDIA_FAKE_ARTWORK = false; boolean DEBUG_CAMERA_LIFT = false; boolean DEBUG_WINDOW_STATE = false; boolean DEBUG_WAKEUP_DELAY = Compile.IS_DEBUG; - // additional instrumentation for testing purposes; intended to be left on during development - boolean CHATTY = DEBUG; boolean SHOW_LOCKSCREEN_MEDIA_ARTWORK = true; String ACTION_FAKE_ARTWORK = "fake_artwork"; int FADE_KEYGUARD_START_DELAY = 100; @@ -194,14 +186,6 @@ public interface CentralSurfaces extends Dumpable, LifecycleOwner { return contextForUser.getPackageManager(); } - void animateExpandNotificationsPanel(); - - void animateExpandSettingsPanel(@Nullable String subpanel); - - void collapsePanelOnMainThread(); - - void togglePanel(); - void start(); boolean updateIsKeyguard(); @@ -214,13 +198,12 @@ public interface CentralSurfaces extends Dumpable, LifecycleOwner { /** * Wakes up the device if the device was dozing. + * + * @deprecated Use {@link PowerInteractor#wakeUpIfDozing(String, int)} instead. */ + @Deprecated void wakeUpIfDozing(long time, String why, @PowerManager.WakeReason int wakeReason); - NotificationShadeWindowView getNotificationShadeWindowView(); - - NotificationShadeWindowViewController getNotificationShadeWindowViewController(); - /** */ ShadeViewController getShadeViewController(); @@ -235,40 +218,30 @@ public interface CentralSurfaces extends Dumpable, LifecycleOwner { boolean isLaunchingActivityOverLockscreen(); - boolean isWakeUpComingFromTouch(); - void onKeyguardViewManagerStatesUpdated(); - ViewGroup getNotificationScrollLayout(); - boolean isPulsing(); boolean isOccluded(); - //TODO: These can / should probably be moved to NotificationPresenter or ShadeController - void onLaunchAnimationCancelled(boolean isLaunchForActivity); - - void onLaunchAnimationEnd(boolean launchIsFullScreen); - - boolean shouldAnimateLaunch(boolean isActivityIntent, boolean showOverLockscreen); - - boolean shouldAnimateLaunch(boolean isActivityIntent); - boolean isDeviceInVrMode(); NotificationPresenter getPresenter(); - void postAnimateCollapsePanels(); - - void postAnimateForceCollapsePanels(); - - void postAnimateOpenPanels(); - - boolean isPanelExpanded(); - + /** + * Used to dispatch initial touch events before crossing the threshold to pull down the + * notification shade. After that, since the launcher window is set to slippery, input + * frameworks take care of routing the events to the notification shade. + */ void onInputFocusTransfer(boolean start, boolean cancel, float velocity); - void animateCollapseQuickSettings(); + /** + * Dispatches status bar motion event to the notification shade. This is different from + * {@link #onInputFocusTransfer(boolean, boolean, float)} as it doesn't rely on setting the + * launcher window slippery to allow the frameworks to route those events after passing the + * initial threshold. + */ + default void onStatusBarTrackpadEvent(MotionEvent event) {} /** */ boolean getCommandQueuePanelsEnabled(); @@ -289,16 +262,12 @@ public interface CentralSurfaces extends Dumpable, LifecycleOwner { @Override void dump(PrintWriter pwOriginal, String[] args); - void createAndAddWindows(@Nullable RegisterStatusBarResult result); - float getDisplayWidth(); float getDisplayHeight(); void readyForKeyguardDone(); - void resetUserExpandedStates(); - void setLockscreenUser(int newUserId); void showKeyguard(); @@ -333,15 +302,11 @@ public interface CentralSurfaces extends Dumpable, LifecycleOwner { /** Should the keyguard be hidden immediately in response to a back press/gesture. */ boolean shouldKeyguardHideImmediately(); - boolean onBackPressed(); - boolean onSpacePressed(); void showBouncerWithDimissAndCancelIfKeyguard(OnDismissAction performAction, Runnable cancelAction); - LightRevealScrim getLightRevealScrim(); - // TODO: Figure out way to remove these. NavigationBarView getNavigationBarView(); @@ -353,10 +318,6 @@ public interface CentralSurfaces extends Dumpable, LifecycleOwner { void setBouncerShowing(boolean bouncerShowing); - void setBouncerShowingOverDream(boolean bouncerShowingOverDream); - - void collapseShade(); - int getWakefulnessState(); boolean isScreenFullyOff(); @@ -389,9 +350,6 @@ public interface CentralSurfaces extends Dumpable, LifecycleOwner { boolean isDeviceInteractive(); - void setNotificationSnoozed(StatusBarNotification sbn, - NotificationSwipeActionHelper.SnoozeOption snoozeOption); - void awakenDreams(); void clearNotificationEffects(); @@ -448,15 +406,10 @@ public interface CentralSurfaces extends Dumpable, LifecycleOwner { void setLaunchEmergencyActionOnFinishedWaking(boolean launch); QSPanelController getQSPanelController(); - - boolean areNotificationAlertsDisabled(); - float getDisplayDensity(); void extendDozePulse(); - boolean shouldDelayWakeUpAnimation(); - public static class KeyboardShortcutsMessage { final int mDeviceId; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java index 0ccc81981e58..7908ee38d49a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java @@ -208,7 +208,7 @@ public class CentralSurfacesCommandQueueCallbacks implements CommandQueue.Callba @Override public void animateCollapsePanels(int flags, boolean force) { - mShadeController.animateCollapsePanels(flags, force, false /* delayed */, + mShadeController.animateCollapseShade(flags, force, false /* delayed */, 1.0f /* speedUpFactor */); } @@ -218,11 +218,7 @@ public class CentralSurfacesCommandQueueCallbacks implements CommandQueue.Callba Log.d(CentralSurfaces.TAG, "animateExpand: mExpandedVisible=" + mShadeController.isExpandedVisible()); } - if (!mCommandQueue.panelsEnabled()) { - return; - } - - mShadeViewController.expandToNotifications(); + mShadeController.animateExpandShade(); } @Override @@ -231,14 +227,7 @@ public class CentralSurfacesCommandQueueCallbacks implements CommandQueue.Callba Log.d(CentralSurfaces.TAG, "animateExpand: mExpandedVisible=" + mShadeController.isExpandedVisible()); } - if (!mCommandQueue.panelsEnabled()) { - return; - } - - // Settings are not available in setup - if (!mDeviceProvisionedController.isCurrentUserSetup()) return; - - mShadeViewController.expandToQs(); + mShadeController.animateExpandQs(); } @Override @@ -255,8 +244,13 @@ public class CentralSurfacesCommandQueueCallbacks implements CommandQueue.Callba } /** * State is one or more of the DISABLE constants from StatusBarManager. + * + * @deprecated If you need to react to changes in disable flags, listen to + * {@link com.android.systemui.statusbar.disableflags.data.repository.DisableFlagsRepository} + * instead. */ @Override + @Deprecated public void disable(int displayId, int state1, int state2, boolean animate) { if (displayId != mDisplayId) { return; @@ -266,8 +260,6 @@ public class CentralSurfacesCommandQueueCallbacks implements CommandQueue.Callba state2 = mRemoteInputQuickSettingsDisabler.adjustDisableFlags(state2); Log.d(CentralSurfaces.TAG, mDisableFlagsLogger.getDisableFlagsString( - /* old= */ new DisableFlagsLogger.DisableState( - mCentralSurfaces.getDisabled1(), mCentralSurfaces.getDisabled2()), /* new= */ new DisableFlagsLogger.DisableState( state1, state2BeforeAdjustment), /* newStateAfterLocalModification= */ new DisableFlagsLogger.DisableState( @@ -288,7 +280,7 @@ public class CentralSurfacesCommandQueueCallbacks implements CommandQueue.Callba } if ((diff1 & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0) { - if (mCentralSurfaces.areNotificationAlertsDisabled()) { + if ((state1 & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0) { mHeadsUpManager.releaseAllImmediately(); } } @@ -556,10 +548,10 @@ public class CentralSurfacesCommandQueueCallbacks implements CommandQueue.Callba @Override public void togglePanel() { - if (mCentralSurfaces.isPanelExpanded()) { + if (mShadeViewController.isPanelExpanded()) { mShadeController.animateCollapseShade(); } else { - animateExpandNotificationsPanel(); + mShadeController.animateExpandShade(); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java index 648ece527bef..9bae739af32b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java @@ -69,7 +69,6 @@ import android.net.Uri; import android.os.Binder; import android.os.Bundle; import android.os.Handler; -import android.os.Looper; import android.os.PowerManager; import android.os.RemoteException; import android.os.ServiceManager; @@ -91,6 +90,7 @@ import android.view.Display; import android.view.IRemoteAnimationRunner; import android.view.IWindowManager; import android.view.KeyEvent; +import android.view.MotionEvent; import android.view.ThreadedRenderer; import android.view.View; import android.view.ViewGroup; @@ -135,7 +135,9 @@ import com.android.systemui.R; import com.android.systemui.accessibility.floatingmenu.AccessibilityFloatingMenuController; import com.android.systemui.animation.ActivityLaunchAnimator; import com.android.systemui.assist.AssistManager; +import com.android.systemui.back.domain.interactor.BackActionInteractor; import com.android.systemui.biometrics.AuthRippleController; +import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.camera.CameraIntents; import com.android.systemui.charging.WiredChargingRippleController; @@ -157,7 +159,6 @@ import com.android.systemui.keyguard.KeyguardUnlockAnimationController; import com.android.systemui.keyguard.KeyguardViewMediator; import com.android.systemui.keyguard.ScreenLifecycle; import com.android.systemui.keyguard.WakefulnessLifecycle; -import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor; import com.android.systemui.keyguard.ui.binder.LightRevealScrimViewBinder; import com.android.systemui.keyguard.ui.viewmodel.LightRevealScrimViewModel; import com.android.systemui.navigationbar.NavigationBarController; @@ -172,8 +173,8 @@ import com.android.systemui.plugins.PluginDependencyProvider; import com.android.systemui.plugins.PluginListener; import com.android.systemui.plugins.PluginManager; import com.android.systemui.plugins.qs.QS; -import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption; import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.power.domain.interactor.PowerInteractor; import com.android.systemui.qs.QSFragment; import com.android.systemui.qs.QSPanelController; import com.android.systemui.recents.ScreenPinningRequest; @@ -411,23 +412,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { return mQSPanelController; } - /** */ - @Override - public void animateExpandNotificationsPanel() { - mCommandQueueCallbacks.animateExpandNotificationsPanel(); - } - - /** */ - @Override - public void animateExpandSettingsPanel(@Nullable String subpanel) { - mCommandQueueCallbacks.animateExpandSettingsPanel(subpanel); - } - - /** */ - @Override - public void togglePanel() { - mCommandQueueCallbacks.togglePanel(); - } /** * The {@link StatusBarState} of the status bar. */ @@ -455,7 +439,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { private PhoneStatusBarTransitions mStatusBarTransitions; private final AuthRippleController mAuthRippleController; @WindowVisibleState private int mStatusBarWindowState = WINDOW_STATE_SHOWING; - protected final NotificationShadeWindowController mNotificationShadeWindowController; + private final NotificationShadeWindowController mNotificationShadeWindowController; private final StatusBarInitializer mStatusBarInitializer; private final StatusBarWindowController mStatusBarWindowController; private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; @@ -464,8 +448,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { private final LightRevealScrim mLightRevealScrim; private PowerButtonReveal mPowerButtonReveal; - private boolean mWakeUpComingFromTouch; - /** * Whether we should delay the wakeup animation (which shows the notifications and moves the * clock view). This is typically done when waking up from a 'press to unlock' gesture on a @@ -505,7 +487,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { private final AlternateBouncerInteractor mAlternateBouncerInteractor; private final PluginDependencyProvider mPluginDependencyProvider; - private final KeyguardDismissUtil mKeyguardDismissUtil; private final ExtensionController mExtensionController; private final UserInfoControllerImpl mUserInfoControllerImpl; private final DemoModeController mDemoModeController; @@ -627,6 +608,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { private final ViewMediatorCallback mKeyguardViewMediatorCallback; private final ScrimController mScrimController; protected DozeScrimController mDozeScrimController; + private final BackActionInteractor mBackActionInteractor; private final Executor mUiBgExecutor; protected boolean mDozing; @@ -655,7 +637,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { private final UserSwitcherController mUserSwitcherController; private final LifecycleRegistry mLifecycle = new LifecycleRegistry(this); protected final BatteryController mBatteryController; - protected boolean mPanelExpanded; private UiModeManager mUiModeManager; private LogMaker mStatusBarStateLog; protected final NotificationIconAreaController mNotificationIconAreaController; @@ -663,6 +644,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { private final SysuiColorExtractor mColorExtractor; private final ScreenLifecycle mScreenLifecycle; private final WakefulnessLifecycle mWakefulnessLifecycle; + protected final PowerInteractor mPowerInteractor; private boolean mNoAnimationOnNextBarModeChange; private final SysuiStatusBarStateController mStatusBarStateController; @@ -677,7 +659,9 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { private final Optional<StartingSurface> mStartingSurfaceOptional; private final ActivityIntentHelper mActivityIntentHelper; - private NotificationStackScrollLayoutController mStackScrollerController; + + @VisibleForTesting + protected NotificationStackScrollLayoutController mStackScrollerController; private final ColorExtractor.OnColorsChangedListener mOnColorsChangedListener = (extractor, which) -> updateTheme(); @@ -685,17 +669,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { private final InteractionJankMonitor mJankMonitor; /** Existing callback that handles back gesture invoked for the Shade. */ - private final OnBackInvokedCallback mOnBackInvokedCallback = () -> { - if (DEBUG) { - Log.d(TAG, "mOnBackInvokedCallback() called"); - } - onBackPressed(); - }; - - private boolean shouldBackBeHandled() { - return (mState != StatusBarState.KEYGUARD && mState != StatusBarState.SHADE_LOCKED - && !isBouncerShowingOverDream()); - } + private final OnBackInvokedCallback mOnBackInvokedCallback; /** * New callback that handles back gesture invoked, cancel, progress @@ -705,12 +679,12 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { private final OnBackAnimationCallback mOnBackAnimationCallback = new OnBackAnimationCallback() { @Override public void onBackInvoked() { - onBackPressed(); + mBackActionInteractor.onBackRequested(); } @Override public void onBackProgressed(BackEvent event) { - if (shouldBackBeHandled()) { + if (mBackActionInteractor.shouldBackBeHandled()) { if (mShadeSurface.canBeCollapsed()) { float fraction = event.getProgress(); mShadeSurface.onBackProgressed(fraction); @@ -764,6 +738,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { SysuiColorExtractor colorExtractor, ScreenLifecycle screenLifecycle, WakefulnessLifecycle wakefulnessLifecycle, + PowerInteractor powerInteractor, SysuiStatusBarStateController statusBarStateController, Optional<Bubbles> bubblesOptional, Lazy<NoteTaskController> noteTaskControllerLazy, @@ -773,12 +748,14 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { Lazy<AssistManager> assistManagerLazy, ConfigurationController configurationController, NotificationShadeWindowController notificationShadeWindowController, + NotificationShelfController notificationShelfController, DozeParameters dozeParameters, ScrimController scrimController, Lazy<LockscreenWallpaper> lockscreenWallpaperLazy, Lazy<BiometricUnlockController> biometricUnlockControllerLazy, AuthRippleController authRippleController, DozeServiceHost dozeServiceHost, + BackActionInteractor backActionInteractor, PowerManager powerManager, ScreenPinningRequest screenPinningRequest, DozeScrimController dozeScrimController, @@ -792,7 +769,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { InitController initController, @Named(TIME_TICK_HANDLER_NAME) Handler timeTickHandler, PluginDependencyProvider pluginDependencyProvider, - KeyguardDismissUtil keyguardDismissUtil, ExtensionController extensionController, UserInfoControllerImpl userInfoControllerImpl, PhoneStatusBarPolicy phoneStatusBarPolicy, @@ -839,6 +815,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { mKeyguardBypassController = keyguardBypassController; mKeyguardStateController = keyguardStateController; mHeadsUpManager = headsUpManagerPhone; + mBackActionInteractor = backActionInteractor; mKeyguardIndicationController = keyguardIndicationController; mStatusBarTouchableRegionManager = statusBarTouchableRegionManager; mFalsingCollector = falsingCollector; @@ -861,6 +838,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { mColorExtractor = colorExtractor; mScreenLifecycle = screenLifecycle; mWakefulnessLifecycle = wakefulnessLifecycle; + mPowerInteractor = powerInteractor; mStatusBarStateController = statusBarStateController; mBubblesOptional = bubblesOptional; mNoteTaskControllerLazy = noteTaskControllerLazy; @@ -870,6 +848,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { mAssistManagerLazy = assistManagerLazy; mConfigurationController = configurationController; mNotificationShadeWindowController = notificationShadeWindowController; + mNotificationShelfController = notificationShelfController; mDozeServiceHost = dozeServiceHost; mPowerManager = powerManager; mDozeParameters = dozeParameters; @@ -889,7 +868,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { mKeyguardViewMediatorCallback = viewMediatorCallback; mInitController = initController; mPluginDependencyProvider = pluginDependencyProvider; - mKeyguardDismissUtil = keyguardDismissUtil; mExtensionController = extensionController; mUserInfoControllerImpl = userInfoControllerImpl; mIconPolicy = phoneStatusBarPolicy; @@ -958,6 +936,12 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { } // Based on teamfood flag, enable predictive back animation for the Shade. mAnimateBack = mFeatureFlags.isEnabled(Flags.WM_SHADE_ANIMATE_BACK_GESTURE); + mOnBackInvokedCallback = () -> { + if (DEBUG) { + Log.d(TAG, "mOnBackInvokedCallback() called"); + } + mBackActionInteractor.onBackRequested(); + }; } private void initBubbles(Bubbles bubbles) { @@ -1151,7 +1135,8 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { @Override public void onPluginConnected(OverlayPlugin plugin, Context pluginContext) { mMainExecutor.execute( - () -> plugin.setup(getNotificationShadeWindowView(), + () -> plugin.setup( + mNotificationShadeWindowController.getWindowRootView(), getNavigationBarView(), new Callback(plugin), mDozeParameters)); } @@ -1551,22 +1536,15 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { @VisibleForTesting void onShadeExpansionFullyChanged(Boolean isExpanded) { - if (mPanelExpanded != isExpanded) { - mPanelExpanded = isExpanded; - if (getShadeViewController() != null) { - // Needed to update SYSUI_STATE_NOTIFICATION_PANEL_VISIBLE - getShadeViewController().updateSystemUiStateFlags(); - } - if (isExpanded && mStatusBarStateController.getState() != StatusBarState.KEYGUARD) { - if (DEBUG) { - Log.v(TAG, "clearing notification effects from Height"); - } - clearNotificationEffects(); + if (isExpanded && mStatusBarStateController.getState() != StatusBarState.KEYGUARD) { + if (DEBUG) { + Log.v(TAG, "clearing notification effects from Height"); } + clearNotificationEffects(); + } - if (!isExpanded) { - mRemoteInputManager.onPanelCollapsed(); - } + if (!isExpanded) { + mRemoteInputManager.onPanelCollapsed(); } } @@ -1604,12 +1582,10 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { mGutsManager.setNotificationActivityStarter(mNotificationActivityStarter); mShadeController.setNotificationPresenter(mPresenter); mNotificationsController.initialize( - this, mPresenter, mNotifListContainer, mStackScrollerController.getNotifStackController(), - mNotificationActivityStarter, - mCentralSurfacesComponent.getBindRowCallback()); + mNotificationActivityStarter); } /** @@ -1624,15 +1600,17 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { /** * Ask the display to wake up if currently dozing, else do nothing * + * @deprecated Use {@link PowerInteractor#wakeUpIfDozing(String, int)} instead. + * * @param time when to wake up * @param why the reason for the wake up */ @Override + @Deprecated public void wakeUpIfDozing(long time, String why, @PowerManager.WakeReason int wakeReason) { if (mDozing && mScreenOffAnimationController.allowWakeUpIfDozing()) { mPowerManager.wakeUp( time, wakeReason, "com.android.systemui:" + why); - mWakeUpComingFromTouch = true; mFalsingCollector.onScreenOnFromTouch(); } } @@ -1665,12 +1643,13 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { CollapsedStatusBarFragment.class, mCentralSurfacesComponent::createCollapsedStatusBarFragment); + ViewGroup windowRootView = mCentralSurfacesComponent.getWindowRootView(); mNotificationShadeWindowView = mCentralSurfacesComponent.getNotificationShadeWindowView(); mNotificationShadeWindowViewController = mCentralSurfacesComponent .getNotificationShadeWindowViewController(); // TODO(b/277762009): Inject [NotificationShadeWindowView] directly into the controller. // (Right now, there's a circular dependency.) - mNotificationShadeWindowController.setNotificationShadeView(mNotificationShadeWindowView); + mNotificationShadeWindowController.setWindowRootView(windowRootView); mNotificationShadeWindowViewController.setupExpandedStatusBar(); NotificationPanelViewController npvc = mCentralSurfacesComponent.getNotificationPanelViewController(); @@ -1681,11 +1660,11 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { mStackScrollerController = mCentralSurfacesComponent.getNotificationStackScrollLayoutController(); mQsController = mCentralSurfacesComponent.getQuickSettingsController(); + mBackActionInteractor.setup(mQsController, mShadeSurface); mStackScroller = mStackScrollerController.getView(); mNotifListContainer = mCentralSurfacesComponent.getNotificationListContainer(); mPresenter = mCentralSurfacesComponent.getNotificationPresenter(); mNotificationActivityStarter = mCentralSurfacesComponent.getNotificationActivityStarter(); - mNotificationShelfController = mCentralSurfacesComponent.getNotificationShelfController(); mHeadsUpManager.addListener(mCentralSurfacesComponent.getStatusBarHeadsUpChangeListener()); @@ -1746,21 +1725,10 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { mLightBarController.setBiometricUnlockController(mBiometricUnlockController); mMediaManager.setBiometricUnlockController(mBiometricUnlockController); - mKeyguardDismissUtil.setDismissHandler(this::executeWhenUnlocked); Trace.endSection(); } @Override - public NotificationShadeWindowView getNotificationShadeWindowView() { - return mNotificationShadeWindowView; - } - - @Override - public NotificationShadeWindowViewController getNotificationShadeWindowViewController() { - return mNotificationShadeWindowViewController; - } - - @Override public ShadeViewController getShadeViewController() { return mShadeSurface; } @@ -1805,11 +1773,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { && mFalsingCollector.isReportingEnabled() ? View.VISIBLE : View.INVISIBLE); } - @Override - public boolean areNotificationAlertsDisabled() { - return (mDisabled1 & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0; - } - /** * Whether we are currently animating an activity launch above the lockscreen (occluding * activity). @@ -1819,11 +1782,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { return mIsLaunchingActivityOverLockscreen; } - @Override - public boolean isWakeUpComingFromTouch() { - return mWakeUpComingFromTouch; - } - /** * To be called when there's a state change in StatusBarKeyguardViewManager. */ @@ -1833,11 +1791,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { } @Override - public ViewGroup getNotificationScrollLayout() { - return mStackScroller; - } - - @Override public boolean isPulsing() { return mDozeServiceHost.isPulsing(); } @@ -1853,58 +1806,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { return mKeyguardStateController.isOccluded(); } - /** A launch animation was cancelled. */ - //TODO: These can / should probably be moved to NotificationPresenter or ShadeController - @Override - public void onLaunchAnimationCancelled(boolean isLaunchForActivity) { - if (mPresenter.isPresenterFullyCollapsed() && !mPresenter.isCollapsing() - && isLaunchForActivity) { - mShadeController.onClosingFinished(); - } else { - mShadeController.collapseShade(true /* animate */); - } - } - - /** A launch animation ended. */ - @Override - public void onLaunchAnimationEnd(boolean launchIsFullScreen) { - if (!mPresenter.isCollapsing()) { - mShadeController.onClosingFinished(); - } - if (launchIsFullScreen) { - mShadeController.instantCollapseShade(); - } - } - - /** - * Whether we should animate an activity launch. - * - * Note: This method must be called *before* dismissing the keyguard. - */ - @Override - public boolean shouldAnimateLaunch(boolean isActivityIntent, boolean showOverLockscreen) { - // TODO(b/184121838): Support launch animations when occluded. - if (isOccluded()) { - return false; - } - - // Always animate if we are not showing the keyguard or if we animate over the lockscreen - // (without unlocking it). - if (showOverLockscreen || !mKeyguardStateController.isShowing()) { - return true; - } - - // We don't animate non-activity launches as they can break the animation. - // TODO(b/184121838): Support non activity launches on the lockscreen. - return isActivityIntent; - } - - /** Whether we should animate an activity launch. */ - @Override - public boolean shouldAnimateLaunch(boolean isActivityIntent) { - return shouldAnimateLaunch(isActivityIntent, false /* showOverLockscreen */); - } - @Override public boolean isDeviceInVrMode() { return mPresenter.isDeviceInVrMode(); @@ -1957,30 +1858,9 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { SystemClock.uptimeMillis(), PowerManager.WAKE_REASON_APPLICATION, "com.android.systemui:full_screen_intent"); - mWakeUpComingFromTouch = false; } } - @Override - public void postAnimateCollapsePanels() { - mMainExecutor.execute(mShadeController::animateCollapseShade); - } - - @Override - public void postAnimateForceCollapsePanels() { - mMainExecutor.execute(mShadeController::animateCollapseShadeForced); - } - - @Override - public void postAnimateOpenPanels() { - mMessageRouter.sendMessage(MSG_OPEN_SETTINGS_PANEL); - } - - @Override - public boolean isPanelExpanded() { - return mPanelExpanded; - } - /** * Called when another window is about to transfer it's input focus. */ @@ -1998,11 +1878,8 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { } @Override - public void animateCollapseQuickSettings() { - if (mState == StatusBarState.SHADE) { - mShadeSurface.collapse( - true, false /* delayed */, 1.0f /* speedUpFactor */); - } + public void onStatusBarTrackpadEvent(MotionEvent event) { + mCentralSurfacesComponent.getNotificationPanelViewController().handleExternalTouch(event); } private void onExpandedInvisible() { @@ -2256,8 +2133,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { + CameraIntents.getOverrideCameraPackage(mContext)); } - @Override - public void createAndAddWindows(@Nullable RegisterStatusBarResult result) { + private void createAndAddWindows(@Nullable RegisterStatusBarResult result) { makeStatusBarView(result); mNotificationShadeWindowController.attach(); mStatusBarWindowController.attach(); @@ -2341,7 +2217,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { mNotificationShadeWindowController.setNotTouchable(false); } finishBarAnimations(); - resetUserExpandedStates(); + mNotificationsController.resetUserExpandedStates(); } Trace.endSection(); } @@ -2360,20 +2236,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { } }; - @Override - public void resetUserExpandedStates() { - mNotificationsController.resetUserExpandedStates(); - } - - private void executeWhenUnlocked(OnDismissAction action, boolean requiresShadeOpen, - boolean afterKeyguardGone) { - if (mKeyguardStateController.isShowing() && requiresShadeOpen) { - mStatusBarStateController.setLeaveOpenOnKeyguardHide(true); - } - mActivityStarter.dismissKeyguardThenExecute(action, null /* cancelAction */, - afterKeyguardGone /* afterKeyguardGone */); - } - /** * Notify the shade controller that the current user changed * @@ -2889,7 +2751,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { case KeyEvent.KEYCODE_BACK: if (mState == StatusBarState.KEYGUARD && mStatusBarKeyguardViewManager.dispatchBackKeyEventPreIme()) { - return onBackPressed(); + return mBackActionInteractor.onBackRequested(); } } return false; @@ -2929,34 +2791,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { } @Override - public boolean onBackPressed() { - if (mStatusBarKeyguardViewManager.canHandleBackPressed()) { - mStatusBarKeyguardViewManager.onBackPressed(); - return true; - } - if (mQsController.isCustomizing()) { - mQsController.closeQsCustomizer(); - return true; - } - if (mQsController.getExpanded()) { - mShadeSurface.animateCollapseQs(false); - return true; - } - if (mShadeSurface.closeUserSwitcherIfOpen()) { - return true; - } - if (shouldBackBeHandled()) { - if (mShadeSurface.canBeCollapsed()) { - // this is the Shade dismiss animation, so make sure QQS closes when it ends. - mShadeSurface.onBackPressed(); - mShadeController.animateCollapseShade(); - } - return true; - } - return false; - } - - @Override public boolean onSpacePressed() { if (mDeviceInteractive && mState != StatusBarState.SHADE) { mShadeController.animateCollapseShadeForced(); @@ -2999,19 +2833,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { } /** - * Collapse the panel directly if we are on the main thread, post the collapsing on the main - * thread if we are not. - */ - @Override - public void collapsePanelOnMainThread() { - if (Looper.getMainLooper().isCurrentThread()) { - mShadeController.collapseShade(); - } else { - mContext.getMainExecutor().execute(mShadeController::collapseShade); - } - } - - /** * Updates the light reveal effect to reflect the reason we're waking or sleeping (for example, * from the power button). * @param wakingUp Whether we're updating because we're waking up (true) or going to sleep @@ -3047,11 +2868,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { } } - @Override - public LightRevealScrim getLightRevealScrim() { - return mLightRevealScrim; - } - // TODO: Figure out way to remove these. @Override public NavigationBarView getNavigationBarView() { @@ -3074,9 +2890,8 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { } protected ViewRootImpl getViewRootImpl() { - NotificationShadeWindowView nswv = getNotificationShadeWindowView(); - if (nswv != null) return nswv.getViewRootImpl(); - + View root = mNotificationShadeWindowController.getWindowRootView(); + if (root != null) return root.getViewRootImpl(); return null; } /** @@ -3087,6 +2902,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { mBouncerShowing = bouncerShowing; mKeyguardBypassController.setBouncerShowing(bouncerShowing); mPulseExpansionHandler.setBouncerShowing(bouncerShowing); + mStackScrollerController.setBouncerShowingFromCentralSurfaces(bouncerShowing); setBouncerShowingForStatusBarComponents(bouncerShowing); mStatusBarHideIconsForBouncerManager.setBouncerShowingAndTriggerUpdate(bouncerShowing); mCommandQueue.recomputeDisableFlags(mDisplayId, true /* animate */); @@ -3101,17 +2917,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { } /** - * Sets whether the bouncer over dream is showing. Note that the bouncer over dream is handled - * independently of the rest of the notification panel. As a result, setting this state via - * {@link #setBouncerShowing(boolean)} leads to unintended side effects from states modified - * behind the dream. - */ - @Override - public void setBouncerShowingOverDream(boolean bouncerShowingOverDream) { - mBouncerShowingOverDream = bouncerShowingOverDream; - } - - /** * Propagate the bouncer state to status bar components. * * Separate from {@link #setBouncerShowing} because we sometimes re-create the status bar and @@ -3128,19 +2933,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { mShadeSurface.setBouncerShowing(bouncerShowing); } - /** - * Collapses the notification shade if it is tracking or expanded. - */ - @Override - public void collapseShade() { - if (mShadeSurface.isTracking()) { - mNotificationShadeWindowViewController.cancelCurrentTouch(); - } - if (mPanelExpanded && mState == StatusBarState.SHADE) { - mShadeController.animateCollapseShade(); - } - } - @VisibleForTesting final WakefulnessLifecycle.Observer mWakefulnessObserver = new WakefulnessLifecycle.Observer() { @Override @@ -3149,7 +2941,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { releaseGestureWakeLock(); mLaunchCameraWhenFinishedWaking = false; mDeviceInteractive = false; - mWakeUpComingFromTouch = false; updateVisibleToUser(); updateNotificationPanelTouchState(); @@ -3505,7 +3296,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { mScrimController.setExpansionAffectsAlpha(!unlocking); if (mAlternateBouncerInteractor.isVisibleState()) { - if ((!isOccluded() || isPanelExpanded()) + if ((!isOccluded() || mShadeSurface.isPanelExpanded()) && (mState == StatusBarState.SHADE || mState == StatusBarState.SHADE_LOCKED || mTransitionToFullShadeProgress > 0f)) { mScrimController.transitionTo(ScrimState.AUTH_SCRIMMED_SHADE); @@ -3602,7 +3393,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { protected Display mDisplay; private int mDisplayId; - protected NotificationShelfController mNotificationShelfController; + private final NotificationShelfController mNotificationShelfController; private final Lazy<AssistManager> mAssistManagerLazy; @@ -3635,12 +3426,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { }; @Override - public void setNotificationSnoozed(StatusBarNotification sbn, SnoozeOption snoozeOption) { - mNotificationsController.setNotificationSnoozed(sbn, snoozeOption); - } - - - @Override public void awakenDreams() { mUiBgExecutor.execute(() -> { try { @@ -3841,8 +3626,9 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { if (userSetup != mUserSetup) { mUserSetup = userSetup; - if (!mUserSetup) { - animateCollapseQuickSettings(); + if (!mUserSetup && mState == StatusBarState.SHADE) { + mShadeSurface.collapse(true /* animate */, false /* delayed */, + 1.0f /* speedUpFactor */); } updateQsExpansionEnabled(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardDismissUtil.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardDismissUtil.java index 27b68f2ffb7d..1c90c0def95f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardDismissUtil.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardDismissUtil.java @@ -16,48 +16,41 @@ package com.android.systemui.statusbar.phone; -import android.util.Log; - import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.ActivityStarter.OnDismissAction; +import com.android.systemui.statusbar.SysuiStatusBarStateController; +import com.android.systemui.statusbar.policy.KeyguardStateController; import javax.inject.Inject; /** - * Executes actions that require the screen to be unlocked. Delegates the actual handling to an - * implementation passed via {@link #setDismissHandler}. + * Executes actions that require the screen to be unlocked. */ @SysUISingleton public class KeyguardDismissUtil implements KeyguardDismissHandler { - private static final String TAG = "KeyguardDismissUtil"; + private final KeyguardStateController mKeyguardStateController; - private volatile KeyguardDismissHandler mDismissHandler; + private final SysuiStatusBarStateController mStatusBarStateController; - @Inject - public KeyguardDismissUtil() { - } + private final ActivityStarter mActivityStarter; - /** Sets the actual {@link KeyguardDismissHandler} implementation. */ - public void setDismissHandler(KeyguardDismissHandler dismissHandler) { - mDismissHandler = dismissHandler; + @Inject + public KeyguardDismissUtil(KeyguardStateController keyguardStateController, + SysuiStatusBarStateController statusBarStateController, + ActivityStarter activityStarter) { + mKeyguardStateController = keyguardStateController; + mStatusBarStateController = statusBarStateController; + mActivityStarter = activityStarter; } - /** - * Executes an action that requires the screen to be unlocked. - * - * <p>Must be called after {@link #setDismissHandler}. - * - * @param requiresShadeOpen does the shade need to be forced open when hiding the keyguard? - */ @Override public void executeWhenUnlocked(OnDismissAction action, boolean requiresShadeOpen, boolean afterKeyguardGone) { - KeyguardDismissHandler dismissHandler = mDismissHandler; - if (dismissHandler == null) { - Log.wtf(TAG, "KeyguardDismissHandler not set."); - action.onDismiss(); - return; + if (mKeyguardStateController.isShowing() && requiresShadeOpen) { + mStatusBarStateController.setLeaveOpenOnKeyguardHide(true); } - dismissHandler.executeWhenUnlocked(action, requiresShadeOpen, afterKeyguardGone); + mActivityStarter.dismissKeyguardThenExecute(action, null /* cancelAction */, + afterKeyguardGone /* afterKeyguardGone */); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java index 25ecf1a424e0..47c4023ca8aa 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java @@ -59,7 +59,7 @@ import com.android.systemui.flags.FeatureFlags; import com.android.systemui.flags.Flags; import com.android.systemui.keyguard.KeyguardUnlockAnimationController; import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor; -import com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants; +import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants; import com.android.systemui.keyguard.shared.model.ScrimAlpha; import com.android.systemui.keyguard.shared.model.TransitionState; import com.android.systemui.keyguard.shared.model.TransitionStep; @@ -881,24 +881,11 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump mBehindAlpha = 1; mNotificationsAlpha = behindFraction * mDefaultScrimAlpha; } else { - if (mFeatureFlags.isEnabled(Flags.LARGE_SHADE_GRANULAR_ALPHA_INTERPOLATION)) { - mBehindAlpha = mLargeScreenShadeInterpolator.getBehindScrimAlpha( - mPanelExpansionFraction * mDefaultScrimAlpha); - mNotificationsAlpha = - mLargeScreenShadeInterpolator.getNotificationScrimAlpha( - mPanelExpansionFraction); - } else { - // Behind scrim will finish fading in at 30% expansion. - float behindFraction = MathUtils - .constrainedMap(0f, 1f, 0f, 0.3f, mPanelExpansionFraction); - mBehindAlpha = behindFraction * mDefaultScrimAlpha; - // Delay fade-in of notification scrim a bit further, to coincide with the - // behind scrim finishing fading in. - // Also to coincide with the view starting to fade in, otherwise the empty - // panel can be quite jarring. - mNotificationsAlpha = MathUtils - .constrainedMap(0f, 1f, 0.3f, 0.75f, mPanelExpansionFraction); - } + mBehindAlpha = mLargeScreenShadeInterpolator.getBehindScrimAlpha( + mPanelExpansionFraction * mDefaultScrimAlpha); + mNotificationsAlpha = + mLargeScreenShadeInterpolator.getNotificationScrimAlpha( + mPanelExpansionFraction); } mBehindTint = mState.getBehindTint(); mInFrontAlpha = 0; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java index 1bf63be3c8b0..e63875b92b64 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -18,7 +18,7 @@ package com.android.systemui.statusbar.phone; import static android.view.WindowInsets.Type.navigationBars; -import static com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants.EXPANSION_HIDDEN; +import static com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants.EXPANSION_HIDDEN; import static com.android.systemui.plugins.ActivityStarter.OnDismissAction; import static com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK; import static com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING; @@ -53,16 +53,16 @@ import com.android.keyguard.KeyguardUpdateMonitorCallback; import com.android.keyguard.KeyguardViewController; import com.android.keyguard.ViewMediatorCallback; import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor; +import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor; +import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInteractor; +import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInteractor.PrimaryBouncerExpansionCallback; +import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor; +import com.android.systemui.bouncer.ui.BouncerView; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dock.DockManager; import com.android.systemui.dreams.DreamOverlayStateController; import com.android.systemui.flags.FeatureFlags; import com.android.systemui.flags.Flags; -import com.android.systemui.keyguard.data.BouncerView; -import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor; -import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerCallbackInteractor; -import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerCallbackInteractor.PrimaryBouncerExpansionCallback; -import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerInteractor; import com.android.systemui.navigationbar.NavigationBarView; import com.android.systemui.navigationbar.NavigationModeController; import com.android.systemui.navigationbar.TaskbarDelegate; @@ -144,6 +144,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb // Local cache of expansion events, to avoid duplicates private float mFraction = -1f; private boolean mTracking = false; + private boolean mBouncerShowingOverDream; private final PrimaryBouncerExpansionCallback mExpansionCallback = new PrimaryBouncerExpansionCallback() { @@ -182,8 +183,8 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb @Override public void onVisibilityChanged(boolean isVisible) { - mCentralSurfaces.setBouncerShowingOverDream( - isVisible && mDreamOverlayStateController.isOverlayActive()); + mBouncerShowingOverDream = + isVisible && mDreamOverlayStateController.isOverlayActive(); if (!isVisible) { mCentralSurfaces.setPrimaryBouncerHiddenFraction(EXPANSION_HIDDEN); @@ -750,7 +751,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb @Override public void onStartedWakingUp() { - mCentralSurfaces.getNotificationShadeWindowView().getWindowInsetsController() + mNotificationShadeWindowController.getWindowRootView().getWindowInsetsController() .setAnimationsDisabled(false); NavigationBarView navBarView = mCentralSurfaces.getNavigationBarView(); if (navBarView != null) { @@ -764,7 +765,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb @Override public void onStartedGoingToSleep() { - mCentralSurfaces.getNotificationShadeWindowView().getWindowInsetsController() + mNotificationShadeWindowController.getWindowRootView().getWindowInsetsController() .setAnimationsDisabled(true); NavigationBarView navBarView = mCentralSurfaces.getNavigationBarView(); if (navBarView != null) { @@ -823,6 +824,11 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb } @Override + public boolean isBouncerShowingOverDream() { + return mBouncerShowingOverDream; + } + + @Override public void setOccluded(boolean occluded, boolean animate) { final boolean wasOccluded = mKeyguardStateController.isOccluded(); final boolean isOccluding = !wasOccluded && occluded; @@ -1114,7 +1120,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb if (view != null) { view.setVisibility(View.VISIBLE); } - mCentralSurfaces.getNotificationShadeWindowView().getWindowInsetsController() + mNotificationShadeWindowController.getWindowRootView().getWindowInsetsController() .show(navigationBars()); } }; @@ -1192,7 +1198,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb } } else { mNotificationContainer.removeCallbacks(mMakeNavigationBarVisibleRunnable); - mCentralSurfaces.getNotificationShadeWindowView().getWindowInsetsController() + mNotificationShadeWindowController.getWindowRootView().getWindowInsetsController() .hide(navigationBars()); } } @@ -1312,7 +1318,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb @Override public ViewRootImpl getViewRootImpl() { - ViewGroup viewGroup = mNotificationShadeWindowController.getNotificationShadeView(); + ViewGroup viewGroup = mNotificationShadeWindowController.getWindowRootView(); if (viewGroup != null) { return viewGroup.getViewRootImpl(); } else { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarLaunchAnimatorController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarLaunchAnimatorController.kt index 9f69db93e64a..b67ec581f8a2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarLaunchAnimatorController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarLaunchAnimatorController.kt @@ -3,6 +3,9 @@ package com.android.systemui.statusbar.phone import android.view.View import com.android.systemui.animation.ActivityLaunchAnimator import com.android.systemui.animation.LaunchAnimator +import com.android.systemui.shade.ShadeController +import com.android.systemui.shade.ShadeViewController +import com.android.systemui.statusbar.NotificationShadeWindowController /** * A [ActivityLaunchAnimator.Controller] that takes care of collapsing the status bar at the right @@ -10,36 +13,38 @@ import com.android.systemui.animation.LaunchAnimator */ class StatusBarLaunchAnimatorController( private val delegate: ActivityLaunchAnimator.Controller, - private val centralSurfaces: CentralSurfaces, + private val shadeViewController: ShadeViewController, + private val shadeController: ShadeController, + private val notificationShadeWindowController: NotificationShadeWindowController, private val isLaunchForActivity: Boolean = true ) : ActivityLaunchAnimator.Controller by delegate { // Always sync the opening window with the shade, given that we draw a hole punch in the shade // of the same size and position as the opening app to make it visible. override val openingWindowSyncView: View? - get() = centralSurfaces.notificationShadeWindowView + get() = notificationShadeWindowController.windowRootView override fun onIntentStarted(willAnimate: Boolean) { delegate.onIntentStarted(willAnimate) if (willAnimate) { - centralSurfaces.shadeViewController.setIsLaunchAnimationRunning(true) + shadeViewController.setIsLaunchAnimationRunning(true) } else { - centralSurfaces.collapsePanelOnMainThread() + shadeController.collapseOnMainThread() } } override fun onLaunchAnimationStart(isExpandingFullyAbove: Boolean) { delegate.onLaunchAnimationStart(isExpandingFullyAbove) - centralSurfaces.shadeViewController.setIsLaunchAnimationRunning(true) + shadeViewController.setIsLaunchAnimationRunning(true) if (!isExpandingFullyAbove) { - centralSurfaces.shadeViewController.collapseWithDuration( + shadeViewController.collapseWithDuration( ActivityLaunchAnimator.TIMINGS.totalDuration.toInt()) } } override fun onLaunchAnimationEnd(isExpandingFullyAbove: Boolean) { delegate.onLaunchAnimationEnd(isExpandingFullyAbove) - centralSurfaces.shadeViewController.setIsLaunchAnimationRunning(false) - centralSurfaces.onLaunchAnimationEnd(isExpandingFullyAbove) + shadeViewController.setIsLaunchAnimationRunning(false) + shadeController.onLaunchAnimationEnd(isExpandingFullyAbove) } override fun onLaunchAnimationProgress( @@ -48,12 +53,12 @@ class StatusBarLaunchAnimatorController( linearProgress: Float ) { delegate.onLaunchAnimationProgress(state, progress, linearProgress) - centralSurfaces.shadeViewController.applyLaunchAnimationProgress(linearProgress) + shadeViewController.applyLaunchAnimationProgress(linearProgress) } override fun onLaunchAnimationCancelled(newKeyguardOccludedState: Boolean?) { delegate.onLaunchAnimationCancelled() - centralSurfaces.shadeViewController.setIsLaunchAnimationRunning(false) - centralSurfaces.onLaunchAnimationCancelled(isLaunchForActivity) + shadeViewController.setIsLaunchAnimationRunning(false) + shadeController.onLaunchAnimationCancelled(isLaunchForActivity) } }
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java index 7bbb03b4bc94..f79a08173bf2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java @@ -63,6 +63,7 @@ import com.android.systemui.statusbar.NotificationClickNotifier; import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.NotificationPresenter; import com.android.systemui.statusbar.NotificationRemoteInputManager; +import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.notification.NotificationActivityStarter; import com.android.systemui.statusbar.notification.NotificationLaunchAnimatorControllerProvider; import com.android.systemui.statusbar.notification.collection.NotificationEntry; @@ -121,7 +122,8 @@ class StatusBarNotificationActivityStarter implements NotificationActivityStarte private final CentralSurfaces mCentralSurfaces; private final NotificationPresenter mPresenter; - private final ShadeViewController mNotificationPanel; + private final ShadeViewController mShadeViewController; + private final NotificationShadeWindowController mNotificationShadeWindowController; private final ActivityLaunchAnimator mActivityLaunchAnimator; private final NotificationLaunchAnimatorControllerProvider mNotificationAnimationProvider; private final UserTracker mUserTracker; @@ -156,7 +158,8 @@ class StatusBarNotificationActivityStarter implements NotificationActivityStarte OnUserInteractionCallback onUserInteractionCallback, CentralSurfaces centralSurfaces, NotificationPresenter presenter, - ShadeViewController panel, + ShadeViewController shadeViewController, + NotificationShadeWindowController notificationShadeWindowController, ActivityLaunchAnimator activityLaunchAnimator, NotificationLaunchAnimatorControllerProvider notificationAnimationProvider, LaunchFullScreenIntentProvider launchFullScreenIntentProvider, @@ -182,6 +185,7 @@ class StatusBarNotificationActivityStarter implements NotificationActivityStarte mLockPatternUtils = lockPatternUtils; mStatusBarRemoteInputCallback = remoteInputCallback; mActivityIntentHelper = activityIntentHelper; + mNotificationShadeWindowController = notificationShadeWindowController; mFeatureFlags = featureFlags; mMetricsLogger = metricsLogger; mLogger = logger; @@ -189,7 +193,7 @@ class StatusBarNotificationActivityStarter implements NotificationActivityStarte // TODO: use KeyguardStateController#isOccluded to remove this dependency mCentralSurfaces = centralSurfaces; mPresenter = presenter; - mNotificationPanel = panel; + mShadeViewController = shadeViewController; mActivityLaunchAnimator = activityLaunchAnimator; mNotificationAnimationProvider = notificationAnimationProvider; mUserTracker = userTracker; @@ -233,7 +237,7 @@ class StatusBarNotificationActivityStarter implements NotificationActivityStarte && mActivityIntentHelper.wouldPendingLaunchResolverActivity(intent, mLockscreenUserManager.getCurrentUserId()); final boolean animate = !willLaunchResolverActivity - && mCentralSurfaces.shouldAnimateLaunch(isActivityIntent); + && mActivityStarter.shouldAnimateLaunch(isActivityIntent); boolean showOverLockscreen = mKeyguardStateController.isShowing() && intent != null && mActivityIntentHelper.wouldPendingShowOverLockscreen(intent, mLockscreenUserManager.getCurrentUserId()); @@ -284,7 +288,7 @@ class StatusBarNotificationActivityStarter implements NotificationActivityStarte } // Always defer the keyguard dismiss when animating. - return animate || !mNotificationPanel.isFullyCollapsed(); + return animate || !mShadeViewController.isFullyCollapsed(); } private void handleNotificationClickAfterPanelCollapsed( @@ -319,7 +323,7 @@ class StatusBarNotificationActivityStarter implements NotificationActivityStarte removeHunAfterClick(row); // Show work challenge, do not run PendingIntent and // remove notification - collapseOnMainThread(); + mShadeController.collapseOnMainThread(); return; } } @@ -436,7 +440,9 @@ class StatusBarNotificationActivityStarter implements NotificationActivityStarte ActivityLaunchAnimator.Controller animationController = new StatusBarLaunchAnimatorController( mNotificationAnimationProvider.getAnimatorController(row, null), - mCentralSurfaces, + mShadeViewController, + mShadeController, + mNotificationShadeWindowController, isActivityIntent); mActivityLaunchAnimator.startPendingIntentWithAnimation( animationController, @@ -467,7 +473,7 @@ class StatusBarNotificationActivityStarter implements NotificationActivityStarte @Override public void startNotificationGutsIntent(final Intent intent, final int appUid, ExpandableNotificationRow row) { - boolean animate = mCentralSurfaces.shouldAnimateLaunch(true /* isActivityIntent */); + boolean animate = mActivityStarter.shouldAnimateLaunch(true /* isActivityIntent */); ActivityStarter.OnDismissAction onDismissAction = new ActivityStarter.OnDismissAction() { @Override public boolean onDismiss() { @@ -475,7 +481,10 @@ class StatusBarNotificationActivityStarter implements NotificationActivityStarte ActivityLaunchAnimator.Controller animationController = new StatusBarLaunchAnimatorController( mNotificationAnimationProvider.getAnimatorController(row), - mCentralSurfaces, true /* isActivityIntent */); + mShadeViewController, + mShadeController, + mNotificationShadeWindowController, + true /* isActivityIntent */); mActivityLaunchAnimator.startIntentWithAnimation( animationController, animate, intent.getPackage(), @@ -500,7 +509,7 @@ class StatusBarNotificationActivityStarter implements NotificationActivityStarte @Override public void startHistoryIntent(View view, boolean showHistory) { - boolean animate = mCentralSurfaces.shouldAnimateLaunch(true /* isActivityIntent */); + boolean animate = mActivityStarter.shouldAnimateLaunch(true /* isActivityIntent */); ActivityStarter.OnDismissAction onDismissAction = new ActivityStarter.OnDismissAction() { @Override public boolean onDismiss() { @@ -520,9 +529,12 @@ class StatusBarNotificationActivityStarter implements NotificationActivityStarte ); ActivityLaunchAnimator.Controller animationController = viewController == null ? null - : new StatusBarLaunchAnimatorController(viewController, - mCentralSurfaces, - true /* isActivityIntent */); + : new StatusBarLaunchAnimatorController( + viewController, + mShadeViewController, + mShadeController, + mNotificationShadeWindowController, + true /* isActivityIntent */); mActivityLaunchAnimator.startIntentWithAnimation(animationController, animate, intent.getPackage(), @@ -621,11 +633,4 @@ class StatusBarNotificationActivityStarter implements NotificationActivityStarte return true; } - private void collapseOnMainThread() { - if (Looper.getMainLooper().isCurrentThread()) { - mShadeController.collapseShade(); - } else { - mMainThreadHandler.post(mShadeController::collapseShade); - } - } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java index dfaee4c674ad..d366cbfaa39d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java @@ -40,7 +40,6 @@ import com.android.systemui.shade.NotificationShadeWindowView; import com.android.systemui.shade.QuickSettingsController; import com.android.systemui.shade.ShadeViewController; import com.android.systemui.statusbar.CommandQueue; -import com.android.systemui.statusbar.KeyguardIndicationController; import com.android.systemui.statusbar.LockscreenShadeTransitionController; import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.NotificationMediaManager; @@ -53,8 +52,8 @@ import com.android.systemui.statusbar.notification.AboveShelfObserver; import com.android.systemui.statusbar.notification.DynamicPrivacyController; import com.android.systemui.statusbar.notification.NotifPipelineFlags; import com.android.systemui.statusbar.notification.collection.NotificationEntry; -import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl; import com.android.systemui.statusbar.notification.collection.render.NotifShadeEventSource; +import com.android.systemui.statusbar.notification.domain.interactor.NotificationsInteractor; import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider; import com.android.systemui.statusbar.notification.interruption.NotificationInterruptSuppressor; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; @@ -69,9 +68,7 @@ import com.android.systemui.statusbar.policy.KeyguardStateController; import javax.inject.Inject; @CentralSurfacesComponent.CentralSurfacesScope -class StatusBarNotificationPresenter implements NotificationPresenter, - NotificationRowBinderImpl.BindRowCallback, - CommandQueue.Callbacks { +class StatusBarNotificationPresenter implements NotificationPresenter, CommandQueue.Callbacks { private static final String TAG = "StatusBarNotificationPresenter"; private final ActivityStarter mActivityStarter; @@ -86,8 +83,8 @@ class StatusBarNotificationPresenter implements NotificationPresenter, private final HeadsUpManagerPhone mHeadsUpManager; private final AboveShelfObserver mAboveShelfObserver; private final DozeScrimController mDozeScrimController; - private final KeyguardIndicationController mKeyguardIndicationController; private final CentralSurfaces mCentralSurfaces; + private final NotificationsInteractor mNotificationsInteractor; private final LockscreenShadeTransitionController mShadeTransitionController; private final CommandQueue mCommandQueue; @@ -115,8 +112,8 @@ class StatusBarNotificationPresenter implements NotificationPresenter, NotificationShadeWindowController notificationShadeWindowController, DynamicPrivacyController dynamicPrivacyController, KeyguardStateController keyguardStateController, - KeyguardIndicationController keyguardIndicationController, CentralSurfaces centralSurfaces, + NotificationsInteractor notificationsInteractor, LockscreenShadeTransitionController shadeTransitionController, CommandQueue commandQueue, NotificationLockscreenUserManager lockscreenUserManager, @@ -137,9 +134,9 @@ class StatusBarNotificationPresenter implements NotificationPresenter, mQsController = quickSettingsController; mHeadsUpManager = headsUp; mDynamicPrivacyController = dynamicPrivacyController; - mKeyguardIndicationController = keyguardIndicationController; // TODO: use KeyguardStateController#isOccluded to remove this dependency mCentralSurfaces = centralSurfaces; + mNotificationsInteractor = notificationsInteractor; mShadeTransitionController = shadeTransitionController; mCommandQueue = commandQueue; mLockscreenUserManager = lockscreenUserManager; @@ -173,7 +170,6 @@ class StatusBarNotificationPresenter implements NotificationPresenter, mNotificationPanel.getShadeNotificationPresenter().createRemoteInputDelegate()); initController.addPostInitTask(() -> { - mKeyguardIndicationController.init(); mNotifShadeEventSource.setShadeEmptiedCallback(this::maybeClosePanelForShadeEmptied); mNotifShadeEventSource.setNotifRemovedByUserCallback(this::maybeEndAmbientPulse); notificationInterruptStateProvider.addSuppressor(mInterruptSuppressor); @@ -341,7 +337,7 @@ class StatusBarNotificationPresenter implements NotificationPresenter, @Override public boolean suppressInterruptions(NotificationEntry entry) { - return mCentralSurfaces.areNotificationAlertsDisabled(); + return !mNotificationsInteractor.areNotificationAlertsEnabled(); } }; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterModule.java index 26c483ce18fa..d94e434c3339 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterModule.java @@ -17,7 +17,6 @@ package com.android.systemui.statusbar.phone; import com.android.systemui.statusbar.NotificationPresenter; -import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl; import dagger.Binds; import dagger.Module; @@ -26,8 +25,4 @@ import dagger.Module; public abstract class StatusBarNotificationPresenterModule { @Binds abstract NotificationPresenter bindPresenter(StatusBarNotificationPresenter impl); - - @Binds - abstract NotificationRowBinderImpl.BindRowCallback bindBindRowCallback( - StatusBarNotificationPresenter impl); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt index 592d1aa8f43f..96a4d900c160 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt @@ -22,6 +22,7 @@ import com.android.systemui.keyguard.KeyguardViewMediator import com.android.systemui.keyguard.WakefulnessLifecycle import com.android.systemui.statusbar.CircleReveal import com.android.systemui.statusbar.LightRevealScrim +import com.android.systemui.statusbar.NotificationShadeWindowController import com.android.systemui.statusbar.StatusBarState import com.android.systemui.statusbar.StatusBarStateControllerImpl import com.android.systemui.statusbar.notification.AnimatableProperty @@ -60,6 +61,7 @@ class UnlockedScreenOffAnimationController @Inject constructor( private val keyguardStateController: KeyguardStateController, private val dozeParameters: dagger.Lazy<DozeParameters>, private val globalSettings: GlobalSettings, + private val notifShadeWindowControllerLazy: dagger.Lazy<NotificationShadeWindowController>, private val interactionJankMonitor: InteractionJankMonitor, private val powerManager: PowerManager, private val handler: Handler = Handler() @@ -114,7 +116,7 @@ class UnlockedScreenOffAnimationController @Inject constructor( override fun onAnimationStart(animation: Animator?) { interactionJankMonitor.begin( - mCentralSurfaces.notificationShadeWindowView, CUJ_SCREEN_OFF) + notifShadeWindowControllerLazy.get().windowRootView, CUJ_SCREEN_OFF) } }) } @@ -218,7 +220,7 @@ class UnlockedScreenOffAnimationController @Inject constructor( .setCustomInterpolator(View.ALPHA, Interpolators.FAST_OUT_SLOW_IN), true /* animate */) interactionJankMonitor.begin( - mCentralSurfaces.notificationShadeWindowView, + notifShadeWindowControllerLazy.get().windowRootView, CUJ_SCREEN_OFF_SHOW_AOD ) } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/CentralSurfacesComponent.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/CentralSurfacesComponent.java index 273e78350f27..64c798b99a18 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/CentralSurfacesComponent.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/CentralSurfacesComponent.java @@ -20,15 +20,14 @@ import static com.android.systemui.statusbar.phone.dagger.StatusBarViewModule.ST import static java.lang.annotation.RetentionPolicy.RUNTIME; +import com.android.systemui.scene.ui.view.WindowRootView; import com.android.systemui.shade.NotificationPanelViewController; import com.android.systemui.shade.NotificationShadeWindowView; import com.android.systemui.shade.NotificationShadeWindowViewController; import com.android.systemui.shade.QuickSettingsController; import com.android.systemui.shade.ShadeHeaderController; import com.android.systemui.statusbar.NotificationPresenter; -import com.android.systemui.statusbar.NotificationShelfController; import com.android.systemui.statusbar.notification.NotificationActivityStarter; -import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl; import com.android.systemui.statusbar.notification.stack.NotificationListContainer; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutListContainerModule; @@ -80,15 +79,15 @@ public interface CentralSurfacesComponent { @Scope @interface CentralSurfacesScope {} + /** Creates the root view of the main SysUI window}. */ + WindowRootView getWindowRootView(); + /** - * Creates a {@link NotificationShadeWindowView}. + * Creates or returns a {@link NotificationShadeWindowView}. */ NotificationShadeWindowView getNotificationShadeWindowView(); /** */ - NotificationShelfController getNotificationShelfController(); - - /** */ NotificationStackScrollLayoutController getNotificationStackScrollLayoutController(); /** @@ -130,7 +129,5 @@ public interface CentralSurfacesComponent { NotificationPresenter getNotificationPresenter(); - NotificationRowBinderImpl.BindRowCallback getBindRowCallback(); - NotificationListContainer getNotificationListContainer(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java index 4ccbc5a12ea0..260d986db6c0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java @@ -23,24 +23,15 @@ import com.android.systemui.R; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dump.DumpManager; import com.android.systemui.flags.FeatureFlags; -import com.android.systemui.flags.Flags; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.shade.NotificationPanelView; import com.android.systemui.shade.NotificationPanelViewController; -import com.android.systemui.shade.NotificationShadeWindowView; -import com.android.systemui.shade.NotificationsQuickSettingsContainer; import com.android.systemui.shade.ShadeExpansionStateManager; import com.android.systemui.shade.ShadeViewController; import com.android.systemui.statusbar.CommandQueue; -import com.android.systemui.statusbar.LegacyNotificationShelfControllerImpl; -import com.android.systemui.statusbar.NotificationShelf; -import com.android.systemui.statusbar.NotificationShelfController; import com.android.systemui.statusbar.OperatorNameViewController; import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler; -import com.android.systemui.statusbar.notification.row.dagger.NotificationShelfComponent; import com.android.systemui.statusbar.notification.row.ui.viewmodel.ActivatableNotificationViewModelModule; -import com.android.systemui.statusbar.notification.shelf.ui.viewbinder.NotificationShelfViewBinderWrapperControllerImpl; -import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationListViewModelModule; import com.android.systemui.statusbar.phone.KeyguardBottomAreaView; import com.android.systemui.statusbar.phone.NotificationIconAreaController; @@ -68,7 +59,6 @@ import dagger.multibindings.IntoSet; import java.util.concurrent.Executor; import javax.inject.Named; -import javax.inject.Provider; @Module(subcomponents = StatusBarFragmentComponent.class, includes = { @@ -80,56 +70,11 @@ public abstract class StatusBarViewModule { public static final String STATUS_BAR_FRAGMENT = "status_bar_fragment"; /** */ - @Provides - @CentralSurfacesComponent.CentralSurfacesScope - public static NotificationShelf providesNotificationShelf(LayoutInflater layoutInflater, - NotificationStackScrollLayout notificationStackScrollLayout) { - NotificationShelf view = (NotificationShelf) layoutInflater.inflate( - R.layout.status_bar_notification_shelf, notificationStackScrollLayout, false); - - if (view == null) { - throw new IllegalStateException( - "R.layout.status_bar_notification_shelf could not be properly inflated"); - } - return view; - } - - /** */ - @Provides - @CentralSurfacesComponent.CentralSurfacesScope - public static NotificationShelfController providesStatusBarWindowView( - FeatureFlags featureFlags, - Provider<NotificationShelfViewBinderWrapperControllerImpl> newImpl, - NotificationShelfComponent.Builder notificationShelfComponentBuilder, - NotificationShelf notificationShelf) { - if (featureFlags.isEnabled(Flags.NOTIFICATION_SHELF_REFACTOR)) { - return newImpl.get(); - } else { - NotificationShelfComponent component = notificationShelfComponentBuilder - .notificationShelf(notificationShelf) - .build(); - LegacyNotificationShelfControllerImpl notificationShelfController = - component.getNotificationShelfController(); - notificationShelfController.init(); - - return notificationShelfController; - } - } - - /** */ @Binds @CentralSurfacesComponent.CentralSurfacesScope abstract ShadeViewController bindsShadeViewController( NotificationPanelViewController notificationPanelViewController); - /** */ - @Provides - @CentralSurfacesComponent.CentralSurfacesScope - public static NotificationsQuickSettingsContainer getNotificationsQuickSettingsContainer( - NotificationShadeWindowView notificationShadeWindowView) { - return notificationShadeWindowView.findViewById(R.id.notification_container_parent); - } - @Binds @IntoSet abstract StatusBarBoundsProvider.BoundsChangeListener sysBarAttrsListenerAsBoundsListener( 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 8c19fb4f43c9..f4ab408cc275 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 @@ -33,7 +33,6 @@ class CollapsedStatusBarFragmentLogger @Inject constructor( * modifications that were made to the flags locally. * * @param new see [DisableFlagsLogger.getDisableFlagsString] - * @param newAfterLocalModification see [DisableFlagsLogger.getDisableFlagsString] */ fun logDisableFlagChange( new: DisableFlagsLogger.DisableState, @@ -47,8 +46,7 @@ class CollapsedStatusBarFragmentLogger @Inject constructor( }, { disableFlagsLogger.getDisableFlagsString( - old = null, - new = DisableFlagsLogger.DisableState(int1, int2), + DisableFlagsLogger.DisableState(int1, int2), ) } ) diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java index 5144d1966222..4c7e6b007f38 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java @@ -334,8 +334,10 @@ public final class WMShell implements } @Override - public void setImeWindowStatus(int displayId, IBinder token, int vis, - int backDisposition, boolean showImeSwitcher) { + public void setImeWindowStatus(int displayId, IBinder token, + @InputMethodService.ImeWindowVisibility int vis, + @InputMethodService.BackDispositionMode int backDisposition, + boolean showImeSwitcher) { if (displayId == mDisplayTracker.getDefaultDisplayId() && (vis & InputMethodService.IME_VISIBLE) != 0) { oneHanded.stopOneHanded( diff --git a/packages/SystemUI/tests/AndroidManifest.xml b/packages/SystemUI/tests/AndroidManifest.xml index 080be6d8cf25..12da17f6a301 100644 --- a/packages/SystemUI/tests/AndroidManifest.xml +++ b/packages/SystemUI/tests/AndroidManifest.xml @@ -192,12 +192,6 @@ android:excludeFromRecents="true" /> <activity - android:name="com.android.systemui.notetask.shortcut.LaunchNoteTaskManagedProfileProxyActivity" - android:exported="false" - android:permission="com.android.systemui.permission.SELF" - android:excludeFromRecents="true" /> - - <activity android:name="com.android.systemui.notetask.LaunchNotesRoleSettingsTrampolineActivity" android:exported="false" android:permission="com.android.systemui.permission.SELF" diff --git a/packages/SystemUI/tests/src/com/android/keyguard/AdminSecondaryLockScreenControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/AdminSecondaryLockScreenControllerTest.java index f5cd0ca7ab3b..319a02d911ed 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/AdminSecondaryLockScreenControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/AdminSecondaryLockScreenControllerTest.java @@ -50,6 +50,7 @@ import com.android.systemui.SysuiTestCase; import org.junit.After; import org.junit.Before; +import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; @@ -59,6 +60,7 @@ import org.mockito.MockitoAnnotations; @RunWithLooper @RunWith(AndroidTestingRunner.class) @SmallTest +@Ignore("b/286245842") public class AdminSecondaryLockScreenControllerTest extends SysuiTestCase { private static final int TARGET_USER_ID = KeyguardUpdateMonitor.getCurrentUser(); diff --git a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt index e9a9f3a6b007..0dcd404d2fc5 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt @@ -23,7 +23,7 @@ import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.broadcast.BroadcastDispatcher import com.android.systemui.flags.FeatureFlags -import com.android.systemui.keyguard.data.repository.FakeKeyguardBouncerRepository +import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor @@ -54,6 +54,7 @@ import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith +import org.mockito.ArgumentMatchers.anyBoolean import org.mockito.ArgumentMatchers.anyFloat import org.mockito.ArgumentMatchers.anyInt import org.mockito.Mock @@ -63,6 +64,7 @@ import org.mockito.Mockito.verify import org.mockito.junit.MockitoJUnit import java.util.TimeZone import java.util.concurrent.Executor +import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository import org.mockito.Mockito.`when` as whenever @RunWith(AndroidTestingRunner::class) @@ -118,6 +120,7 @@ class ClockEventControllerTest : SysuiTestCase() { commandQueue = commandQueue, featureFlags = featureFlags, bouncerRepository = bouncerRepository, + configurationRepository = FakeConfigurationRepository(), ), KeyguardTransitionInteractor(transitionRepository, TestScope().backgroundScope), broadcastDispatcher, @@ -156,9 +159,8 @@ class ClockEventControllerTest : SysuiTestCase() { @Test fun themeChanged_verifyClockPaletteUpdated() = runBlocking(IMMEDIATE) { - // TODO(b/266103601): delete this test and add more coverage for updateColors() - // verify(smallClockEvents).onRegionDarknessChanged(anyBoolean()) - // verify(largeClockEvents).onRegionDarknessChanged(anyBoolean()) + verify(smallClockEvents).onRegionDarknessChanged(anyBoolean()) + verify(largeClockEvents).onRegionDarknessChanged(anyBoolean()) val captor = argumentCaptor<ConfigurationController.ConfigurationListener>() verify(configurationController).addCallback(capture(captor)) diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java index 061340e385a5..65c677ea7e5a 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java @@ -16,6 +16,7 @@ package com.android.keyguard; +import static android.view.View.INVISIBLE; import static android.view.View.VISIBLE; import static com.android.keyguard.KeyguardClockSwitch.LARGE; @@ -192,6 +193,7 @@ public class KeyguardClockSwitchTest extends SysuiTestCase { assertThat(mLargeClockFrame.getAlpha()).isEqualTo(1); assertThat(mLargeClockFrame.getVisibility()).isEqualTo(VISIBLE); assertThat(mSmallClockFrame.getAlpha()).isEqualTo(0); + assertThat(mSmallClockFrame.getVisibility()).isEqualTo(INVISIBLE); } @Test @@ -201,6 +203,7 @@ public class KeyguardClockSwitchTest extends SysuiTestCase { assertThat(mLargeClockFrame.getAlpha()).isEqualTo(1); assertThat(mLargeClockFrame.getVisibility()).isEqualTo(VISIBLE); assertThat(mSmallClockFrame.getAlpha()).isEqualTo(0); + assertThat(mSmallClockFrame.getVisibility()).isEqualTo(INVISIBLE); } @Test @@ -216,6 +219,7 @@ public class KeyguardClockSwitchTest extends SysuiTestCase { // only big clock is removed at switch assertThat(mLargeClockFrame.getParent()).isNull(); assertThat(mLargeClockFrame.getAlpha()).isEqualTo(0); + assertThat(mLargeClockFrame.getVisibility()).isEqualTo(INVISIBLE); } @Test @@ -227,6 +231,7 @@ public class KeyguardClockSwitchTest extends SysuiTestCase { // only big clock is removed at switch assertThat(mLargeClockFrame.getParent()).isNull(); assertThat(mLargeClockFrame.getAlpha()).isEqualTo(0); + assertThat(mLargeClockFrame.getVisibility()).isEqualTo(INVISIBLE); } @Test diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java index 2f20f76ccc50..e561f1f233b4 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java @@ -18,7 +18,7 @@ package com.android.keyguard; import static com.android.keyguard.KeyguardSecurityContainer.MODE_DEFAULT; import static com.android.keyguard.KeyguardSecurityContainer.MODE_ONE_HANDED; -import static com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants.EXPANSION_VISIBLE; +import static com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants.EXPANSION_VISIBLE; import static com.google.common.truth.Truth.assertThat; @@ -67,6 +67,7 @@ import com.android.systemui.classifier.FalsingCollector; import com.android.systemui.flags.FakeFeatureFlags; import com.android.systemui.flags.FeatureFlags; import com.android.systemui.flags.Flags; +import com.android.systemui.bouncer.domain.interactor.BouncerMessageInteractor; import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor; import com.android.systemui.log.SessionTracker; import com.android.systemui.plugins.ActivityStarter; @@ -216,7 +217,8 @@ public class KeyguardSecurityContainerControllerTest extends SysuiTestCase { mUserSwitcherController, mFeatureFlags, mGlobalSettings, mSessionTracker, Optional.of(mSideFpsController), mFalsingA11yDelegate, mTelephonyManager, mViewMediatorCallback, mAudioManager, - mock(KeyguardFaceAuthInteractor.class)); + mock(KeyguardFaceAuthInteractor.class), + mock(BouncerMessageInteractor.class)); } @Test diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java index 1e675f8e4540..b0576e02f998 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java @@ -38,6 +38,7 @@ import static com.android.keyguard.KeyguardUpdateMonitor.BIOMETRIC_STATE_CANCELL import static com.android.keyguard.KeyguardUpdateMonitor.DEFAULT_CANCEL_SIGNAL_TIMEOUT; import static com.android.keyguard.KeyguardUpdateMonitor.HAL_POWER_PRESS_TIMEOUT; import static com.android.keyguard.KeyguardUpdateMonitor.getCurrentUser; +import static com.android.systemui.flags.Flags.FP_LISTEN_OCCLUDING_APPS; import static com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_CLOSED; import static com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_OPENED; import static com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_UNKNOWN; @@ -134,6 +135,7 @@ import com.android.systemui.biometrics.AuthController; import com.android.systemui.biometrics.FingerprintInteractiveToAuthProvider; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dump.DumpManager; +import com.android.systemui.flags.FakeFeatureFlags; import com.android.systemui.log.SessionTracker; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.settings.UserTracker; @@ -278,6 +280,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { private final Executor mBackgroundExecutor = Runnable::run; private final Executor mMainExecutor = Runnable::run; private TestableLooper mTestableLooper; + private FakeFeatureFlags mFeatureFlags; private Handler mHandler; private TestableKeyguardUpdateMonitor mKeyguardUpdateMonitor; private MockitoSession mMockitoSession; @@ -326,6 +329,8 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { mTestableLooper = TestableLooper.get(this); allowTestableLooperAsMainThread(); + mFeatureFlags = new FakeFeatureFlags(); + mFeatureFlags.set(FP_LISTEN_OCCLUDING_APPS, false); when(mSecureSettings.getUriFor(anyString())).thenReturn(mURI); @@ -1555,6 +1560,23 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { } @Test + public void testOccludingAppFingerprintListeningState_featureFlagEnabled() { + mFeatureFlags.set(FP_LISTEN_OCCLUDING_APPS, true); + + // GIVEN keyguard isn't visible (app occluding) + mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON); + mKeyguardUpdateMonitor.setKeyguardShowing(true, true); + when(mStrongAuthTracker.hasUserAuthenticatedSinceBoot()).thenReturn(true); + + // THEN we SHOULD listen for non-UDFPS fingerprint + assertThat(mKeyguardUpdateMonitor.shouldListenForFingerprint(false)).isEqualTo(true); + + // THEN we should listen for udfps (hiding of mechanism to actually auth is + // controlled by UdfpsKeyguardViewController) + assertThat(mKeyguardUpdateMonitor.shouldListenForFingerprint(true)).isEqualTo(true); + } + + @Test public void testOccludingAppFingerprintListeningState() { // GIVEN keyguard isn't visible (app occluding) mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON); @@ -3262,7 +3284,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { mDreamManager, mDevicePolicyManager, mSensorPrivacyManager, mTelephonyManager, mPackageManager, mFaceManager, mFingerprintManager, mBiometricManager, mFaceWakeUpTriggersConfig, mDevicePostureController, - Optional.of(mInteractiveToAuthProvider)); + Optional.of(mInteractiveToAuthProvider), mFeatureFlags); setStrongAuthTracker(KeyguardUpdateMonitorTest.this.mStrongAuthTracker); } diff --git a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java index 84e58be2150f..c88c4d65f412 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java @@ -18,6 +18,7 @@ package com.android.keyguard; import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; import static com.android.systemui.flags.Flags.DOZING_MIGRATION_1; +import static com.android.systemui.flags.Flags.FACE_AUTH_REFACTOR; import static org.mockito.Mockito.any; import static org.mockito.Mockito.anyInt; @@ -42,15 +43,12 @@ import com.android.systemui.biometrics.AuthController; import com.android.systemui.biometrics.AuthRippleController; import com.android.systemui.doze.util.BurnInHelperKt; import com.android.systemui.dump.DumpManager; -import com.android.systemui.flags.FeatureFlags; -import com.android.systemui.keyguard.data.repository.FakeKeyguardBouncerRepository; -import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository; +import com.android.systemui.flags.FakeFeatureFlags; import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository; -import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor; +import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory; import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; -import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.VibratorHelper; import com.android.systemui.statusbar.policy.ConfigurationController; @@ -91,10 +89,9 @@ public class LockIconViewControllerBaseTest extends SysuiTestCase { protected @Mock ConfigurationController mConfigurationController; protected @Mock VibratorHelper mVibrator; protected @Mock AuthRippleController mAuthRippleController; - protected @Mock FeatureFlags mFeatureFlags; protected @Mock KeyguardTransitionRepository mTransitionRepository; - protected @Mock CommandQueue mCommandQueue; protected FakeExecutor mDelayableExecutor = new FakeExecutor(new FakeSystemClock()); + protected FakeFeatureFlags mFeatureFlags; protected LockIconViewController mUnderTest; @@ -144,6 +141,8 @@ public class LockIconViewControllerBaseTest extends SysuiTestCase { when(mStatusBarStateController.isDozing()).thenReturn(false); when(mStatusBarStateController.getState()).thenReturn(StatusBarState.KEYGUARD); + mFeatureFlags = new FakeFeatureFlags(); + mFeatureFlags.set(FACE_AUTH_REFACTOR, false); mUnderTest = new LockIconViewController( mLockIconView, mStatusBarStateController, @@ -161,12 +160,7 @@ public class LockIconViewControllerBaseTest extends SysuiTestCase { mResources, new KeyguardTransitionInteractor(mTransitionRepository, TestScopeProvider.getTestScope().getBackgroundScope()), - new KeyguardInteractor( - new FakeKeyguardRepository(), - mCommandQueue, - mFeatureFlags, - new FakeKeyguardBouncerRepository() - ), + KeyguardInteractorFactory.create(mFeatureFlags).getKeyguardInteractor(), mFeatureFlags ); } @@ -227,7 +221,7 @@ public class LockIconViewControllerBaseTest extends SysuiTestCase { } protected void init(boolean useMigrationFlag) { - when(mFeatureFlags.isEnabled(DOZING_MIGRATION_1)).thenReturn(useMigrationFlag); + mFeatureFlags.set(DOZING_MIGRATION_1, useMigrationFlag); mUnderTest.init(); verify(mLockIconView, atLeast(1)).addOnAttachStateChangeListener(mAttachCaptor.capture()); diff --git a/packages/SystemUI/tests/src/com/android/keyguard/clock/AnalogClockControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/clock/AnalogClockControllerTest.java deleted file mode 100644 index ef3af8a3c9bc..000000000000 --- a/packages/SystemUI/tests/src/com/android/keyguard/clock/AnalogClockControllerTest.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.keyguard.clock; - -import static com.google.common.truth.Truth.assertThat; - -import android.content.res.Resources; -import android.graphics.Color; -import android.test.suitebuilder.annotation.SmallTest; -import android.testing.AndroidTestingRunner; -import android.testing.TestableLooper.RunWithLooper; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.TextView; - -import com.android.systemui.SysuiTestCase; -import com.android.systemui.colorextraction.SysuiColorExtractor; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -@SmallTest -@RunWith(AndroidTestingRunner.class) -@RunWithLooper -public final class AnalogClockControllerTest extends SysuiTestCase { - - private AnalogClockController mClockController; - @Mock SysuiColorExtractor mMockColorExtractor; - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - - Resources res = getContext().getResources(); - LayoutInflater layoutInflater = LayoutInflater.from(getContext()); - mClockController = new AnalogClockController(res, layoutInflater, - mMockColorExtractor); - } - - @Test - public void setDarkAmount_AOD() { - ViewGroup smallClockFrame = (ViewGroup) mClockController.getView(); - View smallClock = smallClockFrame.getChildAt(0); - // WHEN dark amount is set to AOD - mClockController.setDarkAmount(1f); - // THEN small clock should be shown. - assertThat(smallClock.getVisibility()).isEqualTo(View.VISIBLE); - } - - @Test - public void setColorPalette_setDigitalClock() { - ViewGroup smallClock = (ViewGroup) mClockController.getView(); - // WHEN color palette is set - mClockController.setColorPalette(true, new int[]{Color.RED}); - // THEN child of small clock should have text color set. - TextView digitalClock = (TextView) smallClock.getChildAt(0); - assertThat(digitalClock.getCurrentTextColor()).isEqualTo(Color.RED); - } -} diff --git a/packages/SystemUI/tests/src/com/android/keyguard/clock/BubbleClockControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/clock/BubbleClockControllerTest.java deleted file mode 100644 index b56986eb80d0..000000000000 --- a/packages/SystemUI/tests/src/com/android/keyguard/clock/BubbleClockControllerTest.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.keyguard.clock; - -import static com.google.common.truth.Truth.assertThat; - -import android.content.res.Resources; -import android.graphics.Color; -import android.test.suitebuilder.annotation.SmallTest; -import android.testing.AndroidTestingRunner; -import android.testing.TestableLooper.RunWithLooper; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.TextView; - -import com.android.systemui.SysuiTestCase; -import com.android.systemui.colorextraction.SysuiColorExtractor; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -@SmallTest -@RunWith(AndroidTestingRunner.class) -@RunWithLooper -public final class BubbleClockControllerTest extends SysuiTestCase { - - private BubbleClockController mClockController; - @Mock SysuiColorExtractor mMockColorExtractor; - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - - Resources res = getContext().getResources(); - LayoutInflater layoutInflater = LayoutInflater.from(getContext()); - mClockController = new BubbleClockController(res, layoutInflater, mMockColorExtractor); - } - - @Test - public void setDarkAmount_AOD() { - ViewGroup smallClockFrame = (ViewGroup) mClockController.getView(); - View smallClock = smallClockFrame.getChildAt(0); - // WHEN dark amount is set to AOD - mClockController.setDarkAmount(1f); - // THEN small clock should not be shown. - assertThat(smallClock.getVisibility()).isEqualTo(View.VISIBLE); - } - - @Test - public void setColorPalette_setDigitalClock() { - ViewGroup smallClock = (ViewGroup) mClockController.getView(); - // WHEN text color is set - mClockController.setColorPalette(true, new int[]{Color.RED}); - // THEN child of small clock should have text color set. - TextView digitalClock = (TextView) smallClock.getChildAt(0); - assertThat(digitalClock.getCurrentTextColor()).isEqualTo(Color.RED); - } -} diff --git a/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockInfoTest.java b/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockInfoTest.java deleted file mode 100644 index 4c0890a73853..000000000000 --- a/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockInfoTest.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.keyguard.clock; - -import static com.google.common.truth.Truth.assertThat; - -import static org.mockito.Mockito.verify; - -import android.graphics.Bitmap; -import android.test.suitebuilder.annotation.SmallTest; -import android.testing.AndroidTestingRunner; -import android.testing.TestableLooper.RunWithLooper; - -import com.android.systemui.SysuiTestCase; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -import java.util.function.Supplier; - -@SmallTest -@RunWith(AndroidTestingRunner.class) -@RunWithLooper -public final class ClockInfoTest extends SysuiTestCase { - - @Mock - private Supplier<Bitmap> mMockSupplier; - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - } - - @Test - public void testGetName() { - final String name = "name"; - ClockInfo info = ClockInfo.builder().setName(name).build(); - assertThat(info.getName()).isEqualTo(name); - } - - @Test - public void testGetTitle() { - final String title = "title"; - ClockInfo info = ClockInfo.builder().setTitle(() -> title).build(); - assertThat(info.getTitle()).isEqualTo(title); - } - - @Test - public void testGetId() { - final String id = "id"; - ClockInfo info = ClockInfo.builder().setId(id).build(); - assertThat(info.getId()).isEqualTo(id); - } - - @Test - public void testGetThumbnail() { - ClockInfo info = ClockInfo.builder().setThumbnail(mMockSupplier).build(); - info.getThumbnail(); - verify(mMockSupplier).get(); - } - - @Test - public void testGetPreview() { - ClockInfo info = ClockInfo.builder().setPreview(mMockSupplier).build(); - info.getPreview(); - verify(mMockSupplier).get(); - } -} diff --git a/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockManagerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockManagerTest.java deleted file mode 100644 index 7a5b772e2f1b..000000000000 --- a/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockManagerTest.java +++ /dev/null @@ -1,258 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.keyguard.clock; - -import static com.google.common.truth.Truth.assertThat; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.content.ContentResolver; -import android.database.ContentObserver; -import android.net.Uri; -import android.test.suitebuilder.annotation.SmallTest; -import android.testing.AndroidTestingRunner; -import android.testing.TestableLooper.RunWithLooper; -import android.view.LayoutInflater; - -import com.android.systemui.SysuiTestCase; -import com.android.systemui.colorextraction.SysuiColorExtractor; -import com.android.systemui.dock.DockManager; -import com.android.systemui.dock.DockManagerFake; -import com.android.systemui.plugins.ClockPlugin; -import com.android.systemui.plugins.PluginManager; -import com.android.systemui.settings.UserTracker; -import com.android.systemui.util.concurrency.FakeExecutor; -import com.android.systemui.util.time.FakeSystemClock; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -import java.util.Arrays; - -@SmallTest -@RunWith(AndroidTestingRunner.class) -// Need to run tests on main looper to allow for onClockChanged operation to happen synchronously. -@RunWithLooper(setAsMainLooper = true) -public final class ClockManagerTest extends SysuiTestCase { - - private static final String BUBBLE_CLOCK = BubbleClockController.class.getName(); - private static final Class<?> BUBBLE_CLOCK_CLASS = BubbleClockController.class; - private static final int MAIN_USER_ID = 0; - private static final int SECONDARY_USER_ID = 11; - private static final Uri SETTINGS_URI = null; - - private final FakeSystemClock mFakeSystemClock = new FakeSystemClock(); - private final FakeExecutor mMainExecutor = new FakeExecutor(mFakeSystemClock); - private ClockManager mClockManager; - private ContentObserver mContentObserver; - private DockManagerFake mFakeDockManager; - private ArgumentCaptor<UserTracker.Callback> mUserTrackerCallbackCaptor; - @Mock PluginManager mMockPluginManager; - @Mock SysuiColorExtractor mMockColorExtractor; - @Mock ContentResolver mMockContentResolver; - @Mock UserTracker mUserTracker; - @Mock SettingsWrapper mMockSettingsWrapper; - @Mock ClockManager.ClockChangedListener mMockListener1; - @Mock ClockManager.ClockChangedListener mMockListener2; - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - - LayoutInflater inflater = LayoutInflater.from(getContext()); - - mFakeDockManager = new DockManagerFake(); - - when(mUserTracker.getUserId()).thenReturn(MAIN_USER_ID); - mUserTrackerCallbackCaptor = ArgumentCaptor.forClass(UserTracker.Callback.class); - - mClockManager = new ClockManager(getContext(), inflater, - mMockPluginManager, mMockColorExtractor, mMockContentResolver, - mUserTracker, mMainExecutor, mMockSettingsWrapper, mFakeDockManager); - - mClockManager.addBuiltinClock(() -> new BubbleClockController( - getContext().getResources(), inflater, mMockColorExtractor)); - mClockManager.addOnClockChangedListener(mMockListener1); - mClockManager.addOnClockChangedListener(mMockListener2); - verify(mUserTracker).addCallback(mUserTrackerCallbackCaptor.capture(), any()); - reset(mMockListener1, mMockListener2); - - mContentObserver = mClockManager.getContentObserver(); - } - - @After - public void tearDown() { - mClockManager.removeOnClockChangedListener(mMockListener1); - mClockManager.removeOnClockChangedListener(mMockListener2); - } - - @Test - public void dockEvent() { - mFakeDockManager.setDockEvent(DockManager.STATE_DOCKED); - assertThat(mClockManager.isDocked()).isTrue(); - } - - @Test - public void undockEvent() { - mFakeDockManager.setDockEvent(DockManager.STATE_NONE); - assertThat(mClockManager.isDocked()).isFalse(); - } - - @Test - public void getCurrentClock_default() { - // GIVEN that settings doesn't contain any values - when(mMockSettingsWrapper.getLockScreenCustomClockFace(anyInt())).thenReturn(null); - when(mMockSettingsWrapper.getDockedClockFace(anyInt())).thenReturn(null); - // WHEN settings change event is fired - mContentObserver.onChange(false, Arrays.asList(SETTINGS_URI), 0, MAIN_USER_ID); - // THEN the result is null, indicated the default clock face should be used. - assertThat(mClockManager.getCurrentClock()).isNull(); - } - - @Test - public void getCurrentClock_customClock() { - // GIVEN that settings is set to the bubble clock face - when(mMockSettingsWrapper.getLockScreenCustomClockFace(anyInt())).thenReturn(BUBBLE_CLOCK); - // WHEN settings change event is fired - mContentObserver.onChange(false, Arrays.asList(SETTINGS_URI), 0, MAIN_USER_ID); - // THEN the plugin is the bubble clock face. - assertThat(mClockManager.getCurrentClock()).isInstanceOf(BUBBLE_CLOCK_CLASS); - } - - @Test - public void onClockChanged_customClock() { - // GIVEN that settings is set to the bubble clock face - when(mMockSettingsWrapper.getLockScreenCustomClockFace(anyInt())).thenReturn(BUBBLE_CLOCK); - // WHEN settings change event is fired - mContentObserver.onChange(false, Arrays.asList(SETTINGS_URI), 0, MAIN_USER_ID); - // THEN the plugin is the bubble clock face. - ArgumentCaptor<ClockPlugin> captor = ArgumentCaptor.forClass(ClockPlugin.class); - verify(mMockListener1).onClockChanged(captor.capture()); - assertThat(captor.getValue()).isInstanceOf(BUBBLE_CLOCK_CLASS); - } - - @Test - public void onClockChanged_uniqueInstances() { - // GIVEN that settings is set to the bubble clock face - when(mMockSettingsWrapper.getLockScreenCustomClockFace(anyInt())).thenReturn(BUBBLE_CLOCK); - // WHEN settings change event is fired - mContentObserver.onChange(false, Arrays.asList(SETTINGS_URI), 0, MAIN_USER_ID); - // THEN the listeners receive separate instances of the Bubble clock plugin. - ArgumentCaptor<ClockPlugin> captor1 = ArgumentCaptor.forClass(ClockPlugin.class); - ArgumentCaptor<ClockPlugin> captor2 = ArgumentCaptor.forClass(ClockPlugin.class); - verify(mMockListener1).onClockChanged(captor1.capture()); - verify(mMockListener2).onClockChanged(captor2.capture()); - assertThat(captor1.getValue()).isInstanceOf(BUBBLE_CLOCK_CLASS); - assertThat(captor2.getValue()).isInstanceOf(BUBBLE_CLOCK_CLASS); - assertThat(captor1.getValue()).isNotSameInstanceAs(captor2.getValue()); - } - - @Test - public void getCurrentClock_badSettingsValue() { - // GIVEN that settings contains a value that doesn't correspond to a - // custom clock face. - when(mMockSettingsWrapper.getLockScreenCustomClockFace(anyInt())).thenReturn("bad value"); - // WHEN settings change event is fired - mContentObserver.onChange(false, Arrays.asList(SETTINGS_URI), 0, MAIN_USER_ID); - // THEN the result is null. - assertThat(mClockManager.getCurrentClock()).isNull(); - } - - @Test - public void getCurrentClock_dockedDefault() { - // WHEN dock event is fired - mFakeDockManager.setDockEvent(DockManager.STATE_DOCKED); - // THEN the result is null, indicating the default clock face. - assertThat(mClockManager.getCurrentClock()).isNull(); - } - - @Test - public void getCurrentClock_dockedCustomClock() { - // GIVEN settings is set to the bubble clock face - when(mMockSettingsWrapper.getDockedClockFace(anyInt())).thenReturn(BUBBLE_CLOCK); - // WHEN dock event fires - mFakeDockManager.setDockEvent(DockManager.STATE_DOCKED); - // THEN the plugin is the bubble clock face. - assertThat(mClockManager.getCurrentClock()).isInstanceOf(BUBBLE_CLOCK_CLASS); - } - - @Test - public void getCurrentClock_badDockedSettingsValue() { - // GIVEN settings contains a value that doesn't correspond to an available clock face. - when(mMockSettingsWrapper.getDockedClockFace(anyInt())).thenReturn("bad value"); - // WHEN dock event fires - mFakeDockManager.setDockEvent(DockManager.STATE_DOCKED); - // THEN the result is null. - assertThat(mClockManager.getCurrentClock()).isNull(); - } - - @Test - public void getCurrentClock_badDockedSettingsFallback() { - // GIVEN settings contains a value that doesn't correspond to an available clock face, but - // locked screen settings is set to bubble clock. - when(mMockSettingsWrapper.getDockedClockFace(anyInt())).thenReturn("bad value"); - when(mMockSettingsWrapper.getLockScreenCustomClockFace(anyInt())).thenReturn(BUBBLE_CLOCK); - // WHEN dock event is fired - mFakeDockManager.setDockEvent(DockManager.STATE_DOCKED); - // THEN the plugin is the bubble clock face. - assertThat(mClockManager.getCurrentClock()).isInstanceOf(BUBBLE_CLOCK_CLASS); - } - - @Test - public void onUserChanged_defaultClock() { - // WHEN the user changes - switchUser(SECONDARY_USER_ID); - // THEN the plugin is null for the default clock face - assertThat(mClockManager.getCurrentClock()).isNull(); - } - - @Test - public void onUserChanged_customClock() { - // GIVEN that a second user has selected the bubble clock face - when(mMockSettingsWrapper.getLockScreenCustomClockFace(SECONDARY_USER_ID)).thenReturn( - BUBBLE_CLOCK); - // WHEN the user changes - switchUser(SECONDARY_USER_ID); - // THEN the plugin is the bubble clock face. - assertThat(mClockManager.getCurrentClock()).isInstanceOf(BUBBLE_CLOCK_CLASS); - } - - @Test - public void onUserChanged_docked() { - // GIVEN device is docked - mFakeDockManager.setDockEvent(DockManager.STATE_DOCKED); - // AND the second user as selected the bubble clock for the dock - when(mMockSettingsWrapper.getDockedClockFace(SECONDARY_USER_ID)).thenReturn(BUBBLE_CLOCK); - // WHEN the user changes - switchUser(SECONDARY_USER_ID); - // THEN the plugin is the bubble clock face. - assertThat(mClockManager.getCurrentClock()).isInstanceOf(BUBBLE_CLOCK_CLASS); - } - - private void switchUser(int newUser) { - when(mUserTracker.getUserId()).thenReturn(newUser); - mUserTrackerCallbackCaptor.getValue().onUserChanged(newUser, mContext); - } -} diff --git a/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockOptionsProviderTest.java b/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockOptionsProviderTest.java deleted file mode 100644 index d2832fb98460..000000000000 --- a/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockOptionsProviderTest.java +++ /dev/null @@ -1,189 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.keyguard.clock; - -import static com.google.common.truth.Truth.assertThat; - -import static org.mockito.Mockito.verify; - -import android.database.Cursor; -import android.graphics.Bitmap; -import android.net.Uri; -import android.test.suitebuilder.annotation.SmallTest; -import android.testing.AndroidTestingRunner; -import android.testing.TestableLooper.RunWithLooper; - -import com.android.systemui.SysuiTestCase; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -import java.util.ArrayList; -import java.util.List; -import java.util.function.Supplier; - -@SmallTest -@RunWith(AndroidTestingRunner.class) -@RunWithLooper -public final class ClockOptionsProviderTest extends SysuiTestCase { - - private static final String CONTENT_SCHEME = "content"; - private static final String AUTHORITY = "com.android.keyguard.clock"; - private static final String LIST_OPTIONS = "list_options"; - private static final String PREVIEW = "preview"; - private static final String THUMBNAIL = "thumbnail"; - private static final String MIME_TYPE_LIST_OPTIONS = "vnd.android.cursor.dir/clock_faces"; - private static final String MIME_TYPE_PNG = "image/png"; - private static final String NAME_COLUMN = "name"; - private static final String TITLE_COLUMN = "title"; - private static final String ID_COLUMN = "id"; - private static final String PREVIEW_COLUMN = "preview"; - private static final String THUMBNAIL_COLUMN = "thumbnail"; - - private ClockOptionsProvider mProvider; - private Supplier<List<ClockInfo>> mMockSupplier; - private List<ClockInfo> mClocks; - private Uri mListOptionsUri; - @Mock - private Supplier<Bitmap> mMockBitmapSupplier; - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - mClocks = new ArrayList<>(); - mProvider = new ClockOptionsProvider(() -> mClocks); - mListOptionsUri = new Uri.Builder() - .scheme(CONTENT_SCHEME) - .authority(AUTHORITY) - .appendPath(LIST_OPTIONS) - .build(); - } - - @Test - public void testGetType_listOptions() { - Uri uri = new Uri.Builder() - .scheme(CONTENT_SCHEME) - .authority(AUTHORITY) - .appendPath(LIST_OPTIONS) - .build(); - assertThat(mProvider.getType(uri)).isEqualTo(MIME_TYPE_LIST_OPTIONS); - } - - @Test - public void testGetType_preview() { - Uri uri = new Uri.Builder() - .scheme(CONTENT_SCHEME) - .authority(AUTHORITY) - .appendPath(PREVIEW) - .appendPath("id") - .build(); - assertThat(mProvider.getType(uri)).isEqualTo(MIME_TYPE_PNG); - } - - @Test - public void testGetType_thumbnail() { - Uri uri = new Uri.Builder() - .scheme(CONTENT_SCHEME) - .authority(AUTHORITY) - .appendPath(THUMBNAIL) - .appendPath("id") - .build(); - assertThat(mProvider.getType(uri)).isEqualTo(MIME_TYPE_PNG); - } - - @Test - public void testQuery_noClocks() { - Cursor cursor = mProvider.query(mListOptionsUri, null, null, null); - assertThat(cursor.getCount()).isEqualTo(0); - } - - @Test - public void testQuery_listOptions() { - mClocks.add(ClockInfo.builder() - .setName("name_a") - .setTitle(() -> "title_a") - .setId("id_a") - .build()); - mClocks.add(ClockInfo.builder() - .setName("name_b") - .setTitle(() -> "title_b") - .setId("id_b") - .build()); - Cursor cursor = mProvider.query(mListOptionsUri, null, null, null); - assertThat(cursor.getCount()).isEqualTo(2); - cursor.moveToFirst(); - assertThat(cursor.getString( - cursor.getColumnIndex(NAME_COLUMN))).isEqualTo("name_a"); - assertThat(cursor.getString( - cursor.getColumnIndex(TITLE_COLUMN))).isEqualTo("title_a"); - assertThat(cursor.getString( - cursor.getColumnIndex(ID_COLUMN))).isEqualTo("id_a"); - assertThat(cursor.getString( - cursor.getColumnIndex(PREVIEW_COLUMN))) - .isEqualTo("content://com.android.keyguard.clock/preview/id_a"); - assertThat(cursor.getString( - cursor.getColumnIndex(THUMBNAIL_COLUMN))) - .isEqualTo("content://com.android.keyguard.clock/thumbnail/id_a"); - cursor.moveToNext(); - assertThat(cursor.getString( - cursor.getColumnIndex(NAME_COLUMN))).isEqualTo("name_b"); - assertThat(cursor.getString( - cursor.getColumnIndex(TITLE_COLUMN))).isEqualTo("title_b"); - assertThat(cursor.getString( - cursor.getColumnIndex(ID_COLUMN))).isEqualTo("id_b"); - assertThat(cursor.getString( - cursor.getColumnIndex(PREVIEW_COLUMN))) - .isEqualTo("content://com.android.keyguard.clock/preview/id_b"); - assertThat(cursor.getString( - cursor.getColumnIndex(THUMBNAIL_COLUMN))) - .isEqualTo("content://com.android.keyguard.clock/thumbnail/id_b"); - } - - @Test - public void testOpenFile_preview() throws Exception { - mClocks.add(ClockInfo.builder() - .setId("id") - .setPreview(mMockBitmapSupplier) - .build()); - Uri uri = new Uri.Builder() - .scheme(CONTENT_SCHEME) - .authority(AUTHORITY) - .appendPath(PREVIEW) - .appendPath("id") - .build(); - mProvider.openFile(uri, "r").close(); - verify(mMockBitmapSupplier).get(); - } - - @Test - public void testOpenFile_thumbnail() throws Exception { - mClocks.add(ClockInfo.builder() - .setId("id") - .setThumbnail(mMockBitmapSupplier) - .build()); - Uri uri = new Uri.Builder() - .scheme(CONTENT_SCHEME) - .authority(AUTHORITY) - .appendPath(THUMBNAIL) - .appendPath("id") - .build(); - mProvider.openFile(uri, "r").close(); - verify(mMockBitmapSupplier).get(); - } -} diff --git a/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockPaletteTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockPaletteTest.kt deleted file mode 100644 index 347b26deacd4..000000000000 --- a/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockPaletteTest.kt +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -package com.android.keyguard.clock - -import android.graphics.Color -import android.testing.AndroidTestingRunner -import androidx.test.filters.SmallTest -import com.android.systemui.SysuiTestCase -import com.google.common.truth.Truth.assertThat -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith - -@RunWith(AndroidTestingRunner::class) -@SmallTest -class ClockPaletteTest : SysuiTestCase() { - - private lateinit var clockPalette: ClockPalette - private lateinit var colors: IntArray - - @Before - fun setUp() { - clockPalette = ClockPalette() - // colors used are reds from light to dark. - val hsv: FloatArray = FloatArray(3) - Color.colorToHSV(Color.RED, hsv) - colors = IntArray(10) - val step: Float = (0f - hsv[2]) / colors.size - for (i in 0 until colors.size) { - hsv[2] += step - colors[i] = Color.HSVToColor(hsv) - } - } - - @Test - fun testDark() { - // GIVEN on AOD - clockPalette.setDarkAmount(1f) - // AND GIVEN that wallpaper doesn't support dark text - clockPalette.setColorPalette(false, colors) - // THEN the secondary color should be lighter than the primary color - assertThat(value(clockPalette.getPrimaryColor())) - .isGreaterThan(value(clockPalette.getSecondaryColor())) - } - - @Test - fun testDarkText() { - // GIVEN on lock screen - clockPalette.setDarkAmount(0f) - // AND GIVEN that wallpaper supports dark text - clockPalette.setColorPalette(true, colors) - // THEN the secondary color should be darker the primary color - assertThat(value(clockPalette.getPrimaryColor())) - .isLessThan(value(clockPalette.getSecondaryColor())) - } - - @Test - fun testLightText() { - // GIVEN on lock screen - clockPalette.setDarkAmount(0f) - // AND GIVEN that wallpaper doesn't support dark text - clockPalette.setColorPalette(false, colors) - // THEN the secondary color should be darker than the primary color - assertThat(value(clockPalette.getPrimaryColor())) - .isGreaterThan(value(clockPalette.getSecondaryColor())) - } - - @Test - fun testNullColors() { - // GIVEN on AOD - clockPalette.setDarkAmount(1f) - // AND GIVEN that wallpaper colors are null - clockPalette.setColorPalette(false, null) - // THEN the primary color should be whilte - assertThat(clockPalette.getPrimaryColor()).isEqualTo(Color.WHITE) - } - - private fun value(color: Int): Float { - val hsv: FloatArray = FloatArray(3) - Color.colorToHSV(color, hsv) - return hsv[2] - } -} diff --git a/packages/SystemUI/tests/src/com/android/keyguard/clock/CrossFadeDarkControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/clock/CrossFadeDarkControllerTest.java deleted file mode 100644 index fd7657ff18cc..000000000000 --- a/packages/SystemUI/tests/src/com/android/keyguard/clock/CrossFadeDarkControllerTest.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.keyguard.clock; - -import static com.google.common.truth.Truth.assertThat; - -import android.test.suitebuilder.annotation.SmallTest; -import android.testing.AndroidTestingRunner; -import android.testing.TestableLooper.RunWithLooper; -import android.view.View; -import android.widget.TextView; - -import com.android.systemui.SysuiTestCase; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; - -@SmallTest -@RunWith(AndroidTestingRunner.class) -@RunWithLooper -public final class CrossFadeDarkControllerTest extends SysuiTestCase { - - private View mViewFadeIn; - private View mViewFadeOut; - private CrossFadeDarkController mDarkController; - - @Before - public void setUp() { - mViewFadeIn = new TextView(getContext()); - mViewFadeIn.setVisibility(View.VISIBLE); - mViewFadeIn.setAlpha(1f); - mViewFadeOut = new TextView(getContext()); - mViewFadeOut.setVisibility(View.VISIBLE); - mViewFadeOut.setAlpha(1f); - - mDarkController = new CrossFadeDarkController(mViewFadeIn, mViewFadeOut); - } - - @Test - public void setDarkAmount_fadeIn() { - // WHEN dark amount corresponds to AOD - mDarkController.setDarkAmount(1f); - // THEN fade in view should be faded in and fade out view faded out. - assertThat(mViewFadeIn.getAlpha()).isEqualTo(1f); - assertThat(mViewFadeIn.getVisibility()).isEqualTo(View.VISIBLE); - assertThat(mViewFadeOut.getAlpha()).isEqualTo(0f); - assertThat(mViewFadeOut.getVisibility()).isEqualTo(View.GONE); - } - - @Test - public void setDarkAmount_fadeOut() { - // WHEN dark amount corresponds to lock screen - mDarkController.setDarkAmount(0f); - // THEN fade out view should bed faded out and fade in view faded in. - assertThat(mViewFadeIn.getAlpha()).isEqualTo(0f); - assertThat(mViewFadeIn.getVisibility()).isEqualTo(View.GONE); - assertThat(mViewFadeOut.getAlpha()).isEqualTo(1f); - assertThat(mViewFadeOut.getVisibility()).isEqualTo(View.VISIBLE); - } - - @Test - public void setDarkAmount_partialFadeIn() { - // WHEN dark amount corresponds to a partial transition - mDarkController.setDarkAmount(0.9f); - // THEN views should have intermediate alpha value. - assertThat(mViewFadeIn.getAlpha()).isGreaterThan(0f); - assertThat(mViewFadeIn.getAlpha()).isLessThan(1f); - assertThat(mViewFadeIn.getVisibility()).isEqualTo(View.VISIBLE); - } - - @Test - public void setDarkAmount_partialFadeOut() { - // WHEN dark amount corresponds to a partial transition - mDarkController.setDarkAmount(0.1f); - // THEN views should have intermediate alpha value. - assertThat(mViewFadeOut.getAlpha()).isGreaterThan(0f); - assertThat(mViewFadeOut.getAlpha()).isLessThan(1f); - assertThat(mViewFadeOut.getVisibility()).isEqualTo(View.VISIBLE); - } -} diff --git a/packages/SystemUI/tests/src/com/android/keyguard/clock/SettingsWrapperTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/clock/SettingsWrapperTest.kt deleted file mode 100644 index 573581dae3b1..000000000000 --- a/packages/SystemUI/tests/src/com/android/keyguard/clock/SettingsWrapperTest.kt +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -package com.android.keyguard.clock - -import android.testing.AndroidTestingRunner -import androidx.test.filters.SmallTest -import com.android.systemui.SysuiTestCase -import com.google.common.truth.Truth.assertThat -import org.json.JSONObject -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith -import org.mockito.Mockito.mock -import org.mockito.Mockito.never -import org.mockito.Mockito.verify - -private const val PACKAGE = "com.android.keyguard.clock.Clock" -private const val CLOCK_FIELD = "clock" -private const val TIMESTAMP_FIELD = "_applied_timestamp" -private const val USER_ID = 0 - -@RunWith(AndroidTestingRunner::class) -@SmallTest -class SettingsWrapperTest : SysuiTestCase() { - - private lateinit var wrapper: SettingsWrapper - private lateinit var migration: SettingsWrapper.Migration - - @Before - fun setUp() { - migration = mock(SettingsWrapper.Migration::class.java) - wrapper = SettingsWrapper(getContext().contentResolver, migration) - } - - @Test - fun testDecodeUnnecessary() { - // GIVEN a settings value that doesn't need to be decoded - val value = PACKAGE - // WHEN the value is decoded - val decoded = wrapper.decode(value, USER_ID) - // THEN the same value is returned, because decoding isn't necessary. - // TODO(b/135674383): Null should be returned when the migration code in removed. - assertThat(decoded).isEqualTo(value) - // AND the value is migrated to JSON format - verify(migration).migrate(value, USER_ID) - } - - @Test - fun testDecodeJSON() { - // GIVEN a settings value that is encoded in JSON - val json: JSONObject = JSONObject() - json.put(CLOCK_FIELD, PACKAGE) - json.put(TIMESTAMP_FIELD, System.currentTimeMillis()) - val value = json.toString() - // WHEN the value is decoded - val decoded = wrapper.decode(value, USER_ID) - // THEN the clock field should have been extracted - assertThat(decoded).isEqualTo(PACKAGE) - } - - @Test - fun testDecodeJSONWithoutClockField() { - // GIVEN a settings value that doesn't contain the CLOCK_FIELD - val json: JSONObject = JSONObject() - json.put(TIMESTAMP_FIELD, System.currentTimeMillis()) - val value = json.toString() - // WHEN the value is decoded - val decoded = wrapper.decode(value, USER_ID) - // THEN null is returned - assertThat(decoded).isNull() - // AND the value is not migrated to JSON format - verify(migration, never()).migrate(value, USER_ID) - } - - @Test - fun testDecodeNullJSON() { - assertThat(wrapper.decode(null, USER_ID)).isNull() - } -} diff --git a/packages/SystemUI/tests/src/com/android/keyguard/clock/SmallClockPositionTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/clock/SmallClockPositionTest.kt deleted file mode 100644 index 3a27e356ed84..000000000000 --- a/packages/SystemUI/tests/src/com/android/keyguard/clock/SmallClockPositionTest.kt +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -package com.android.keyguard.clock - -import android.testing.AndroidTestingRunner -import androidx.test.filters.SmallTest -import com.android.systemui.SysuiTestCase -import com.google.common.truth.Truth.assertThat -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith - -@RunWith(AndroidTestingRunner::class) -@SmallTest -class SmallClockPositionTest : SysuiTestCase() { - - private val statusBarHeight = 100 - private val lockPadding = 15 - private val lockHeight = 35 - private val burnInY = 20 - - private lateinit var position: SmallClockPosition - - @Before - fun setUp() { - position = SmallClockPosition(statusBarHeight, lockPadding, lockHeight, burnInY) - } - - @Test - fun loadResources() { - // Cover constructor taking Resources object. - position = SmallClockPosition(context) - position.setDarkAmount(1f) - assertThat(position.preferredY).isGreaterThan(0) - } - - @Test - fun darkPosition() { - // GIVEN on AOD - position.setDarkAmount(1f) - // THEN Y is sum of statusBarHeight, lockPadding, lockHeight, lockPadding, burnInY - assertThat(position.preferredY).isEqualTo(185) - } - - @Test - fun lockPosition() { - // GIVEN on lock screen - position.setDarkAmount(0f) - // THEN Y position is statusBarHeight + lockPadding + lockHeight + lockPadding - // (100 + 15 + 35 + 15 = 165) - assertThat(position.preferredY).isEqualTo(165) - } -} diff --git a/packages/SystemUI/tests/src/com/android/keyguard/clock/ViewPreviewerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/clock/ViewPreviewerTest.kt deleted file mode 100644 index 5ece6ef1a794..000000000000 --- a/packages/SystemUI/tests/src/com/android/keyguard/clock/ViewPreviewerTest.kt +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.keyguard.clock - -import android.content.Context -import com.google.common.truth.Truth.assertThat - -import android.graphics.Canvas -import android.graphics.Color -import android.testing.AndroidTestingRunner -import android.view.View -import androidx.test.filters.SmallTest -import com.android.systemui.SysuiTestCase -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith - -@RunWith(AndroidTestingRunner::class) -@SmallTest -class ViewPreviewerTest : SysuiTestCase() { - - private lateinit var previewer: ViewPreviewer - private lateinit var view: View - - @Before - fun setUp() { - previewer = ViewPreviewer() - view = TestView(context) - } - - @Test - fun testCreatePreview() { - val width = 100 - val height = 100 - // WHEN a preview image is created - val bitmap = previewer.createPreview(view, width, height)!! - // THEN the bitmap has the expected width and height - assertThat(bitmap.height).isEqualTo(height) - assertThat(bitmap.width).isEqualTo(width) - assertThat(bitmap.getPixel(0, 0)).isEqualTo(Color.RED) - } - - class TestView(context: Context) : View(context) { - override fun onDraw(canvas: Canvas?) { - super.onDraw(canvas) - canvas?.drawColor(Color.RED) - } - } -} diff --git a/packages/SystemUI/tests/src/com/android/systemui/DependencyTest.java b/packages/SystemUI/tests/src/com/android/systemui/DependencyTest.java index a0fdc8f1555e..24a5e8072796 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/DependencyTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/DependencyTest.java @@ -48,8 +48,7 @@ public class DependencyTest extends SysuiTestCase { @Test public void testInitDependency() throws ExecutionException, InterruptedException { Dependency.clearDependencies(); - SystemUIInitializer initializer = - SystemUIInitializerFactory.createFromConfigNoAssert(mContext); + SystemUIInitializer initializer = new SystemUIInitializerImpl(mContext); initializer.init(true); Dependency dependency = initializer.getSysUIComponent().createDependency(); dependency.start(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorHwcLayerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorHwcLayerTest.kt index 8207fa6958f3..d500b5aebcbf 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorHwcLayerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorHwcLayerTest.kt @@ -74,7 +74,8 @@ class ScreenDecorHwcLayerTest : SysuiTestCase() { val decorationSupport = DisplayDecorationSupport() decorationSupport.format = PixelFormat.R_8 - decorHwcLayer = Mockito.spy(ScreenDecorHwcLayer(mContext, decorationSupport)) + decorHwcLayer = + Mockito.spy(ScreenDecorHwcLayer(mContext, decorationSupport, /* debug */ false)) whenever(decorHwcLayer.width).thenReturn(displayWidth) whenever(decorHwcLayer.height).thenReturn(displayHeight) whenever(decorHwcLayer.context).thenReturn(mockContext) diff --git a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java index 4cf5a4be0b60..79c87cfd1f3e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java @@ -43,7 +43,6 @@ import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; -import static org.mockito.Mockito.reset; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -63,6 +62,7 @@ import android.os.Handler; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.testing.TestableLooper.RunWithLooper; +import android.util.PathParser; import android.util.Size; import android.view.Display; import android.view.DisplayCutout; @@ -83,6 +83,7 @@ import com.android.systemui.biometrics.AuthController; import com.android.systemui.decor.CornerDecorProvider; import com.android.systemui.decor.CutoutDecorProviderFactory; import com.android.systemui.decor.CutoutDecorProviderImpl; +import com.android.systemui.decor.DebugRoundedCornerModel; import com.android.systemui.decor.DecorProvider; import com.android.systemui.decor.DecorProviderFactory; import com.android.systemui.decor.FaceScanningOverlayProviderImpl; @@ -96,7 +97,6 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.settings.FakeDisplayTracker; import com.android.systemui.settings.UserTracker; import com.android.systemui.statusbar.events.PrivacyDotViewController; -import com.android.systemui.tuner.TunerService; import com.android.systemui.util.concurrency.FakeExecutor; import com.android.systemui.util.concurrency.FakeThreadFactory; import com.android.systemui.util.settings.FakeSettings; @@ -139,8 +139,6 @@ public class ScreenDecorationsTest extends SysuiTestCase { @Mock private Display mDisplay; @Mock - private TunerService mTunerService; - @Mock private UserTracker mUserTracker; @Mock private PrivacyDotViewController mDotViewController; @@ -234,7 +232,7 @@ public class ScreenDecorationsTest extends SysuiTestCase { new ScreenDecorationsLogger(logcatLogBuffer("TestLogBuffer")))); mScreenDecorations = spy(new ScreenDecorations(mContext, mExecutor, mSecureSettings, - mTunerService, mUserTracker, mDisplayTracker, mDotViewController, mThreadFactory, + mUserTracker, mDisplayTracker, mDotViewController, mThreadFactory, mPrivacyDotDecorProviderFactory, mFaceScanningProviderFactory, new ScreenDecorationsLogger(logcatLogBuffer("TestLogBuffer")), mAuthController) { @@ -251,12 +249,6 @@ public class ScreenDecorationsTest extends SysuiTestCase { } @Override - public void onTuningChanged(String key, String newValue) { - super.onTuningChanged(key, newValue); - mExecutor.runAllReady(); - } - - @Override protected void updateOverlayWindowVisibilityIfViewExists(@Nullable View view) { super.updateOverlayWindowVisibilityIfViewExists(view); mExecutor.runAllReady(); @@ -268,9 +260,10 @@ public class ScreenDecorationsTest extends SysuiTestCase { } }); mScreenDecorations.mDisplayInfo = mDisplayInfo; + // Make sure tests are never run starting in debug mode + mScreenDecorations.setDebug(false); doReturn(1f).when(mScreenDecorations).getPhysicalPixelDisplaySizeRatio(); doNothing().when(mScreenDecorations).updateOverlayProviderViews(any()); - reset(mTunerService); try { mPrivacyDotShowingListener = mScreenDecorations.mPrivacyDotShowingListener.getClass() @@ -464,8 +457,6 @@ public class ScreenDecorationsTest extends SysuiTestCase { mScreenDecorations.start(); // No views added. verifyOverlaysExistAndAdded(false, false, false, false, null); - // No Tuners tuned. - verify(mTunerService, never()).addTunable(any(), any()); // No dot controller init verify(mDotViewController, never()).initialize(any(), any(), any(), any()); } @@ -497,8 +488,6 @@ public class ScreenDecorationsTest extends SysuiTestCase { // Face scanning doesn't exist verifyFaceScanningViewExists(false); - // One tunable. - verify(mTunerService, times(1)).addTunable(any(), any()); // Dot controller init verify(mDotViewController, times(1)).initialize( isA(View.class), isA(View.class), isA(View.class), isA(View.class)); @@ -528,8 +517,6 @@ public class ScreenDecorationsTest extends SysuiTestCase { // Face scanning doesn't exist verifyFaceScanningViewExists(false); - // One tunable. - verify(mTunerService, times(1)).addTunable(any(), any()); // No dot controller init verify(mDotViewController, never()).initialize(any(), any(), any(), any()); } @@ -560,8 +547,6 @@ public class ScreenDecorationsTest extends SysuiTestCase { // Face scanning doesn't exist verifyFaceScanningViewExists(false); - // One tunable. - verify(mTunerService, times(1)).addTunable(any(), any()); // Dot controller init verify(mDotViewController, times(1)).initialize( isA(View.class), isA(View.class), isA(View.class), isA(View.class)); @@ -1073,18 +1058,89 @@ public class ScreenDecorationsTest extends SysuiTestCase { } @Test + public void testDebugRoundedCorners_noDeviceCornersSet() { + setupResources(0 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */, + null /* roundedTopDrawable */, null /* roundedBottomDrawable */, + 0 /* roundedPadding */, false /* privacyDot */, false /* faceScanning */); + + mScreenDecorations.start(); + // No rounded corners exist at this point + verifyOverlaysExistAndAdded(false, false, false, false, View.VISIBLE); + + // Path from rounded.xml, scaled by 10x to produce 80x80 corners + Path debugPath = PathParser.createPathFromPathData("M8,0H0v8C0,3.6,3.6,0,8,0z"); + // WHEN debug corners are added to the delegate + DebugRoundedCornerModel debugCorner = new DebugRoundedCornerModel( + debugPath, + 80, + 80, + 10f, + 10f + ); + mScreenDecorations.mDebugRoundedCornerDelegate + .applyNewDebugCorners(debugCorner, debugCorner); + + // AND debug mode is entered + mScreenDecorations.setDebug(true); + mExecutor.runAllReady(); + + // THEN the debug corners provide decor + List<DecorProvider> providers = mScreenDecorations.getProviders(false); + assertEquals(4, providers.size()); + + // Top and bottom overlays contain the debug rounded corners + verifyOverlaysExistAndAdded(false, true, false, true, View.VISIBLE); + } + + @Test + public void testDebugRoundedCornersRemoved_noDeviceCornersSet() { + // GIVEN a device with no rounded corners defined + setupResources(0 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */, + null /* roundedTopDrawable */, null /* roundedBottomDrawable */, + 0 /* roundedPadding */, false /* privacyDot */, false /* faceScanning */); + + mScreenDecorations.start(); + // No rounded corners exist at this point + verifyOverlaysExistAndAdded(false, false, false, false, View.VISIBLE); + + // Path from rounded.xml, scaled by 10x to produce 80x80 corners + Path debugPath = PathParser.createPathFromPathData("M8,0H0v8C0,3.6,3.6,0,8,0z"); + // WHEN debug corners are added to the delegate + DebugRoundedCornerModel debugCorner = new DebugRoundedCornerModel( + debugPath, + 80, + 80, + 10f, + 10f + ); + mScreenDecorations.mDebugRoundedCornerDelegate + .applyNewDebugCorners(debugCorner, debugCorner); + + // AND debug mode is entered + mScreenDecorations.setDebug(true); + mExecutor.runAllReady(); + + // Top and bottom overlays contain the debug rounded corners + verifyOverlaysExistAndAdded(false, true, false, true, View.VISIBLE); + + // WHEN debug is exited + mScreenDecorations.setDebug(false); + mExecutor.runAllReady(); + + // THEN the decor is removed + verifyOverlaysExistAndAdded(false, false, false, false, View.VISIBLE); + assertThat(mScreenDecorations.mDebugRoundedCornerDelegate.getHasBottom()).isFalse(); + assertThat(mScreenDecorations.mDebugRoundedCornerDelegate.getHasTop()).isFalse(); + } + + @Test public void testRegistration_From_NoOverlay_To_HasOverlays() { doReturn(false).when(mScreenDecorations).hasOverlays(); mScreenDecorations.start(); - verify(mTunerService, times(0)).addTunable(any(), any()); - verify(mTunerService, times(1)).removeTunable(any()); assertThat(mScreenDecorations.mIsRegistered, is(false)); - reset(mTunerService); doReturn(true).when(mScreenDecorations).hasOverlays(); mScreenDecorations.onConfigurationChanged(new Configuration()); - verify(mTunerService, times(1)).addTunable(any(), any()); - verify(mTunerService, times(0)).removeTunable(any()); assertThat(mScreenDecorations.mIsRegistered, is(true)); } @@ -1093,14 +1149,9 @@ public class ScreenDecorationsTest extends SysuiTestCase { doReturn(true).when(mScreenDecorations).hasOverlays(); mScreenDecorations.start(); - verify(mTunerService, times(1)).addTunable(any(), any()); - verify(mTunerService, times(0)).removeTunable(any()); assertThat(mScreenDecorations.mIsRegistered, is(true)); - reset(mTunerService); mScreenDecorations.onConfigurationChanged(new Configuration()); - verify(mTunerService, times(0)).addTunable(any(), any()); - verify(mTunerService, times(0)).removeTunable(any()); assertThat(mScreenDecorations.mIsRegistered, is(true)); } @@ -1109,15 +1160,10 @@ public class ScreenDecorationsTest extends SysuiTestCase { doReturn(true).when(mScreenDecorations).hasOverlays(); mScreenDecorations.start(); - verify(mTunerService, times(1)).addTunable(any(), any()); - verify(mTunerService, times(0)).removeTunable(any()); assertThat(mScreenDecorations.mIsRegistered, is(true)); - reset(mTunerService); doReturn(false).when(mScreenDecorations).hasOverlays(); mScreenDecorations.onConfigurationChanged(new Configuration()); - verify(mTunerService, times(0)).addTunable(any(), any()); - verify(mTunerService, times(1)).removeTunable(any()); assertThat(mScreenDecorations.mIsRegistered, is(false)); } @@ -1181,7 +1227,7 @@ public class ScreenDecorationsTest extends SysuiTestCase { when(mFaceScanningProviderFactory.getProviders()).thenReturn(mFaceScanningProviders); when(mFaceScanningProviderFactory.getHasProviders()).thenReturn(true); ScreenDecorations screenDecorations = new ScreenDecorations(mContext, mExecutor, - mSecureSettings, mTunerService, mUserTracker, mDisplayTracker, mDotViewController, + mSecureSettings, mUserTracker, mDisplayTracker, mDotViewController, mThreadFactory, mPrivacyDotDecorProviderFactory, mFaceScanningProviderFactory, new ScreenDecorationsLogger(logcatLogBuffer("TestLogBuffer")), mAuthController); screenDecorations.start(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationSettingsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationSettingsControllerTest.java index 665246bd7f7d..62a176c94d67 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationSettingsControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationSettingsControllerTest.java @@ -73,9 +73,9 @@ public class MagnificationSettingsControllerTest extends SysuiTestCase { @Test public void testShowSettingsPanel() { - mMagnificationSettingsController.showMagnificationSettings(); + mMagnificationSettingsController.toggleSettingsPanelVisibility(); - verify(mWindowMagnificationSettings).showSettingPanel(); + verify(mWindowMagnificationSettings).toggleSettingsPanelVisibility(); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java index 9c36af3a35e4..31c09b8e7ec4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java @@ -16,6 +16,7 @@ package com.android.systemui.accessibility; +import static android.content.pm.PackageManager.FEATURE_WINDOW_MAGNIFICATION; import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; import static android.content.res.Configuration.ORIENTATION_PORTRAIT; import static android.content.res.Configuration.ORIENTATION_UNDEFINED; @@ -96,6 +97,7 @@ import com.android.systemui.utils.os.FakeHandler; import com.google.common.util.concurrent.AtomicDouble; import org.junit.After; +import org.junit.Assume; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -147,8 +149,15 @@ public class WindowMagnificationControllerTest extends SysuiTestCase { private View.OnTouchListener mTouchListener; private MotionEventHelper mMotionEventHelper = new MotionEventHelper(); + /** + * return whether window magnification is supported for current test context. + */ + private boolean isWindowModeSupported() { + return getContext().getPackageManager().hasSystemFeature(FEATURE_WINDOW_MAGNIFICATION); + } + @Before - public void setUp() throws RemoteException { + public void setUp() throws Exception { MockitoAnnotations.initMocks(this); mContext = Mockito.spy(getContext()); mHandler = new FakeHandler(TestableLooper.get(this).getLooper()); @@ -202,6 +211,9 @@ public class WindowMagnificationControllerTest extends SysuiTestCase { return null; }).when(mSpyView).setOnTouchListener( any(View.OnTouchListener.class)); + + // skip test if window magnification is not supported to prevent fail results. (b/279820875) + Assume.assumeTrue(isWindowModeSupported()); } @After diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationSettingsTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationSettingsTest.java index ce96708039ae..275723be3859 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationSettingsTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationSettingsTest.java @@ -16,10 +16,10 @@ package com.android.systemui.accessibility; +import static android.provider.Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE; import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_CAPABILITY; import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL; import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN; -import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_NONE; import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW; import static com.google.common.truth.Truth.assertThat; @@ -28,8 +28,10 @@ import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertNotNull; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -37,6 +39,7 @@ import android.annotation.IdRes; import android.content.Context; import android.content.pm.ActivityInfo; import android.database.ContentObserver; +import android.graphics.Rect; import android.os.UserHandle; import android.provider.Settings; import android.testing.AndroidTestingRunner; @@ -49,8 +52,10 @@ import android.widget.Button; import android.widget.CompoundButton; import android.widget.LinearLayout; +import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; +import com.android.internal.accessibility.common.MagnificationConstants; import com.android.internal.graphics.SfVsyncFrameCallbackProvider; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; @@ -60,12 +65,13 @@ import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @SmallTest @RunWith(AndroidTestingRunner.class) -@TestableLooper.RunWithLooper(setAsMainLooper = true) +@TestableLooper.RunWithLooper public class WindowMagnificationSettingsTest extends SysuiTestCase { private static final int MAGNIFICATION_SIZE_SMALL = 1; @@ -85,6 +91,11 @@ public class WindowMagnificationSettingsTest extends SysuiTestCase { private WindowMagnificationSettings mWindowMagnificationSettings; private MotionEventHelper mMotionEventHelper = new MotionEventHelper(); + private ArgumentCaptor<Float> mSecureSettingsScaleCaptor; + private ArgumentCaptor<String> mSecureSettingsNameCaptor; + private ArgumentCaptor<Integer> mSecureSettingsUserHandleCaptor; + private ArgumentCaptor<Float> mCallbackMagnifierScaleCaptor; + @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); @@ -100,6 +111,10 @@ public class WindowMagnificationSettingsTest extends SysuiTestCase { mSecureSettings); mSettingView = mWindowMagnificationSettings.getSettingView(); + mSecureSettingsScaleCaptor = ArgumentCaptor.forClass(Float.class); + mSecureSettingsNameCaptor = ArgumentCaptor.forClass(String.class); + mSecureSettingsUserHandleCaptor = ArgumentCaptor.forClass(Integer.class); + mCallbackMagnifierScaleCaptor = ArgumentCaptor.forClass(Float.class); } @After @@ -275,6 +290,39 @@ public class WindowMagnificationSettingsTest extends SysuiTestCase { } @Test + public void onScreenSizeChanged_resetPositionToRightBottomCorner() { + setupMagnificationCapabilityAndMode( + /* capability= */ ACCESSIBILITY_MAGNIFICATION_MODE_ALL, + /* mode= */ ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW); + mWindowMagnificationSettings.showSettingPanel(); + + // move the panel to the center of draggable window bounds + mWindowMagnificationSettings.mParams.x = + mWindowMagnificationSettings.mDraggableWindowBounds.centerX(); + mWindowMagnificationSettings.mParams.y = + mWindowMagnificationSettings.mDraggableWindowBounds.centerY(); + mWindowMagnificationSettings.updateButtonViewLayoutIfNeeded(); + + final Rect testWindowBounds = new Rect( + mWindowManager.getCurrentWindowMetrics().getBounds()); + testWindowBounds.set(testWindowBounds.left, testWindowBounds.top, + testWindowBounds.right + 200, testWindowBounds.bottom + 50); + mWindowManager.setWindowBounds(testWindowBounds); + + InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { + mWindowMagnificationSettings.onConfigurationChanged(ActivityInfo.CONFIG_SCREEN_SIZE); + }); + + // the panel position should be reset to the bottom-right corner + assertEquals( + mWindowMagnificationSettings.mParams.x, + mWindowMagnificationSettings.mDraggableWindowBounds.right); + assertEquals( + mWindowMagnificationSettings.mParams.y, + mWindowMagnificationSettings.mDraggableWindowBounds.bottom); + } + + @Test public void showSettingsPanel_observerRegistered() { setupMagnificationCapabilityAndMode( /* capability= */ ACCESSIBILITY_MAGNIFICATION_MODE_ALL, @@ -289,6 +337,20 @@ public class WindowMagnificationSettingsTest extends SysuiTestCase { } @Test + public void showSettingsPanel_observerForMagnificationScaleRegistered() { + setupMagnificationCapabilityAndMode( + /* capability= */ ACCESSIBILITY_MAGNIFICATION_MODE_ALL, + /* mode= */ ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW); + + mWindowMagnificationSettings.showSettingPanel(); + + verify(mSecureSettings).registerContentObserverForUser( + eq(ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE), + any(ContentObserver.class), + eq(UserHandle.USER_CURRENT)); + } + + @Test public void hideSettingsPanel_observerUnregistered() { setupMagnificationCapabilityAndMode( /* capability= */ ACCESSIBILITY_MAGNIFICATION_MODE_ALL, @@ -297,7 +359,162 @@ public class WindowMagnificationSettingsTest extends SysuiTestCase { mWindowMagnificationSettings.showSettingPanel(); mWindowMagnificationSettings.hideSettingPanel(); - verify(mSecureSettings).unregisterContentObserver(any(ContentObserver.class)); + verify(mSecureSettings, times(2)).unregisterContentObserver(any(ContentObserver.class)); + } + + @Test + public void seekbarProgress_justInflated_maxValueAndProgressSetCorrectly() { + setupScaleInSecureSettings(0f); + assertThat(mWindowMagnificationSettings.mZoomSeekbar.getProgress()).isEqualTo(0); + assertThat(mWindowMagnificationSettings.mZoomSeekbar.getMax()).isEqualTo(70); + } + + @Test + public void seekbarProgress_minMagnification_seekbarProgressIsCorrect() { + setupScaleInSecureSettings(0f); + setupMagnificationCapabilityAndMode( + /* capability= */ ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW, + /* mode= */ ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW); + + mWindowMagnificationSettings.showSettingPanel(); + + // Seekbar index from 0 to 70. 1.0f scale (A11Y_SCALE_MIN_VALUE) would correspond to 0. + assertThat(mWindowMagnificationSettings.mZoomSeekbar.getProgress()).isEqualTo(0); + } + + @Test + public void seekbarProgress_belowMinMagnification_seekbarProgressIsZero() { + setupScaleInSecureSettings(0f); + setupMagnificationCapabilityAndMode( + /* capability= */ ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW, + /* mode= */ ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW); + + mWindowMagnificationSettings.showSettingPanel(); + + assertThat(mWindowMagnificationSettings.mZoomSeekbar.getProgress()).isEqualTo(0); + } + + @Test + public void seekbarProgress_magnificationBefore_seekbarProgressIsHalf() { + setupScaleInSecureSettings(4f); + setupMagnificationCapabilityAndMode( + /* capability= */ ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW, + /* mode= */ ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW); + + mWindowMagnificationSettings.showSettingPanel(); + + // float scale : from 1.0f to 8.0f, seekbar index from 0 to 70. + // 4.0f would correspond to 30. + assertThat(mWindowMagnificationSettings.mZoomSeekbar.getProgress()).isEqualTo(30); + } + + @Test + public void seekbarProgress_maxMagnificationBefore_seekbarProgressIsMax() { + setupScaleInSecureSettings(8f); + setupMagnificationCapabilityAndMode( + /* capability= */ ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW, + /* mode= */ ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW); + + mWindowMagnificationSettings.showSettingPanel(); + + // 8.0f is max magnification {@link MagnificationScaleProvider#MAX_SCALE}. + // Max zoom seek bar is 70. + assertThat(mWindowMagnificationSettings.mZoomSeekbar.getProgress()).isEqualTo(70); + } + + @Test + public void seekbarProgress_aboveMaxMagnificationBefore_seekbarProgressIsMax() { + setupScaleInSecureSettings(9f); + setupMagnificationCapabilityAndMode( + /* capability= */ ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW, + /* mode= */ ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW); + + mWindowMagnificationSettings.showSettingPanel(); + + // Max zoom seek bar is 70. + assertThat(mWindowMagnificationSettings.mZoomSeekbar.getProgress()).isEqualTo(70); + } + + @Test + public void seekbarProgress_progressChangedRoughlyHalf_scaleAndCallbackUpdated() { + setupMagnificationCapabilityAndMode( + /* capability= */ ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW, + /* mode= */ ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW); + mWindowMagnificationSettings.showSettingPanel(); + + mWindowMagnificationSettings.mZoomSeekbar.setProgress(30); + + verifyScaleUpdatedInSecureSettings(4f); + verifyCallbackOnMagnifierScale(4f); + } + + @Test + public void seekbarProgress_minProgress_callbackUpdated() { + setupMagnificationCapabilityAndMode( + /* capability= */ ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW, + /* mode= */ ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW); + mWindowMagnificationSettings.showSettingPanel(); + // Set progress to non-zero first so onProgressChanged can be triggered upon setting to 0. + mWindowMagnificationSettings.mZoomSeekbar.setProgress(30); + + mWindowMagnificationSettings.mZoomSeekbar.setProgress(0); + + // For now, secure settings will not be updated for values < 1.3f. Follow up on this later. + verify(mWindowMagnificationSettingsCallback, times(2)) + .onMagnifierScale(mCallbackMagnifierScaleCaptor.capture()); + var capturedArgs = mCallbackMagnifierScaleCaptor.getAllValues(); + assertThat(capturedArgs).hasSize(2); + assertThat(capturedArgs.get(1)).isWithin(0.01f).of(1f); + } + + @Test + public void seekbarProgress_maxProgress_scaleAndCallbackUpdated() { + setupMagnificationCapabilityAndMode( + /* capability= */ ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW, + /* mode= */ ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW); + mWindowMagnificationSettings.showSettingPanel(); + + mWindowMagnificationSettings.mZoomSeekbar.setProgress(70); + + verifyScaleUpdatedInSecureSettings(8f); + verifyCallbackOnMagnifierScale(8f); + } + + @Test + public void seekbarProgress_scaleUpdatedAfterSettingPanelOpened_progressAlsoUpdated() { + setupMagnificationCapabilityAndMode( + /* capability= */ ACCESSIBILITY_MAGNIFICATION_MODE_ALL, + /* mode= */ ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW); + var contentObserverCaptor = ArgumentCaptor.forClass(ContentObserver.class); + mWindowMagnificationSettings.showSettingPanel(); + verify(mSecureSettings).registerContentObserverForUser( + eq(ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE), + contentObserverCaptor.capture(), + eq(UserHandle.USER_CURRENT)); + + // Simulate outside changes. + setupScaleInSecureSettings(4f); + // Simulate callback due to outside change. + contentObserverCaptor.getValue().onChange(/* selfChange= */ false); + + assertThat(mWindowMagnificationSettings.mZoomSeekbar.getProgress()).isEqualTo(30); + } + + private void verifyScaleUpdatedInSecureSettings(float scale) { + verify(mSecureSettings).putFloatForUser( + mSecureSettingsNameCaptor.capture(), + mSecureSettingsScaleCaptor.capture(), + mSecureSettingsUserHandleCaptor.capture()); + assertThat(mSecureSettingsScaleCaptor.getValue()).isWithin(0.01f).of(scale); + assertThat(mSecureSettingsNameCaptor.getValue()) + .isEqualTo(Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE); + assertThat(mSecureSettingsUserHandleCaptor.getValue()).isEqualTo(UserHandle.USER_CURRENT); + } + + private void verifyCallbackOnMagnifierScale(float scale) { + verify(mWindowMagnificationSettingsCallback) + .onMagnifierScale(mCallbackMagnifierScaleCaptor.capture()); + assertThat(mCallbackMagnifierScaleCaptor.getValue()).isWithin(0.01f).of(scale); } private <T extends View> T getInternalView(@IdRes int idRes) { @@ -308,12 +525,19 @@ public class WindowMagnificationSettingsTest extends SysuiTestCase { private void setupMagnificationCapabilityAndMode(int capability, int mode) { when(mSecureSettings.getIntForUser( - Settings.Secure.ACCESSIBILITY_MAGNIFICATION_CAPABILITY, - ACCESSIBILITY_MAGNIFICATION_MODE_NONE, - UserHandle.USER_CURRENT)).thenReturn(capability); + eq(Settings.Secure.ACCESSIBILITY_MAGNIFICATION_CAPABILITY), + anyInt(), + eq(UserHandle.USER_CURRENT))).thenReturn(capability); when(mSecureSettings.getIntForUser( - Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE, - ACCESSIBILITY_MAGNIFICATION_MODE_NONE, - UserHandle.USER_CURRENT)).thenReturn(mode); + eq(Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE), + anyInt(), + eq(UserHandle.USER_CURRENT))).thenReturn(mode); + } + + private void setupScaleInSecureSettings(float scale) { + when(mSecureSettings.getFloatForUser( + ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE, + MagnificationConstants.SCALE_MIN_VALUE, + UserHandle.USER_CURRENT)).thenReturn(scale); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationTest.java index 38ecec0b4602..db580742a68f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationTest.java @@ -110,7 +110,7 @@ public class WindowMagnificationTest extends SysuiTestCase { mWindowMagnification.mMagnificationSettingsControllerCallback .onSettingsPanelVisibilityChanged(TEST_DISPLAY, /* shown= */ true); return null; - }).when(mMagnificationSettingsController).showMagnificationSettings(); + }).when(mMagnificationSettingsController).toggleSettingsPanelVisibility(); doAnswer(invocation -> { mWindowMagnification.mMagnificationSettingsControllerCallback .onSettingsPanelVisibilityChanged(TEST_DISPLAY, /* shown= */ false); @@ -198,7 +198,7 @@ public class WindowMagnificationTest extends SysuiTestCase { mWindowMagnification.mWindowMagnifierCallback.onClickSettingsButton(TEST_DISPLAY); waitForIdleSync(); - verify(mMagnificationSettingsController).showMagnificationSettings(); + verify(mMagnificationSettingsController).toggleSettingsPanelVisibility(); verify(mA11yLogger).log( eq(MagnificationSettingsEvent.MAGNIFICATION_SETTINGS_PANEL_OPENED)); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/DismissAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/DismissAnimationControllerTest.java index f6ca93831ccd..fd258e38a00f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/DismissAnimationControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/DismissAnimationControllerTest.java @@ -29,7 +29,8 @@ import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; import com.android.systemui.util.settings.SecureSettings; -import com.android.wm.shell.bubbles.DismissView; +import com.android.wm.shell.bubbles.DismissViewUtils; +import com.android.wm.shell.common.bubbles.DismissView; import org.junit.Before; import org.junit.Rule; @@ -63,6 +64,7 @@ public class DismissAnimationControllerTest extends SysuiTestCase { final MenuView stubMenuView = new MenuView(mContext, stubMenuViewModel, stubMenuViewAppearance); mDismissView = spy(new DismissView(mContext)); + DismissViewUtils.setup(mDismissView); mDismissAnimationController = new DismissAnimationController(mDismissView, stubMenuView); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java index d4efbe46ebfa..98be49f9d493 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java @@ -40,7 +40,8 @@ import com.android.internal.accessibility.dialog.AccessibilityTarget; import com.android.systemui.SysuiTestCase; import com.android.systemui.accessibility.MotionEventHelper; import com.android.systemui.util.settings.SecureSettings; -import com.android.wm.shell.bubbles.DismissView; +import com.android.wm.shell.bubbles.DismissViewUtils; +import com.android.wm.shell.common.bubbles.DismissView; import org.junit.After; import org.junit.Before; @@ -88,6 +89,7 @@ public class MenuListViewTouchHandlerTest extends SysuiTestCase { mStubMenuView.setTranslationY(0); mMenuAnimationController = spy(new MenuAnimationController(mStubMenuView)); mDismissView = spy(new DismissView(mContext)); + DismissViewUtils.setup(mDismissView); mDismissAnimationController = spy(new DismissAnimationController(mDismissView, mStubMenuView)); mTouchHandler = new MenuListViewTouchHandler(mMenuAnimationController, diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/fontscaling/FontScalingDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/accessibility/fontscaling/FontScalingDialogTest.kt index f10c21bc514a..a10f5dd4878d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/fontscaling/FontScalingDialogTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/fontscaling/FontScalingDialogTest.kt @@ -25,6 +25,7 @@ import androidx.test.filters.SmallTest import com.android.systemui.R import com.android.systemui.SysuiTestCase import com.android.systemui.common.ui.view.SeekBarWithIconButtonsView +import com.android.systemui.settings.UserTracker import com.android.systemui.util.concurrency.FakeExecutor import com.android.systemui.util.mockito.capture import com.android.systemui.util.mockito.whenever @@ -38,6 +39,7 @@ import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentCaptor import org.mockito.Captor +import org.mockito.Mock import org.mockito.Mockito.spy import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @@ -63,6 +65,7 @@ class FontScalingDialogTest : SysuiTestCase() { .getResources() .getStringArray(com.android.settingslib.R.array.entryvalues_font_size) + @Mock private lateinit var userTracker: UserTracker @Captor private lateinit var seekBarChangeCaptor: ArgumentCaptor<SeekBar.OnSeekBarChangeListener> @@ -72,7 +75,7 @@ class FontScalingDialogTest : SysuiTestCase() { val mainHandler = Handler(TestableLooper.get(this).getLooper()) systemSettings = FakeSettings() // Guarantee that the systemSettings always starts with the default font scale. - systemSettings.putFloat(Settings.System.FONT_SCALE, 1.0f) + systemSettings.putFloatForUser(Settings.System.FONT_SCALE, 1.0f, userTracker.userId) secureSettings = FakeSettings() systemClock = FakeSystemClock() backgroundDelayableExecutor = FakeExecutor(systemClock) @@ -82,6 +85,7 @@ class FontScalingDialogTest : SysuiTestCase() { systemSettings, secureSettings, systemClock, + userTracker, mainHandler, backgroundDelayableExecutor ) @@ -93,7 +97,12 @@ class FontScalingDialogTest : SysuiTestCase() { val seekBar: SeekBar = fontScalingDialog.findViewById<SeekBar>(R.id.seekbar)!! val progress: Int = seekBar.getProgress() - val currentScale = systemSettings.getFloat(Settings.System.FONT_SCALE, /* def= */ 1.0f) + val currentScale = + systemSettings.getFloatForUser( + Settings.System.FONT_SCALE, + /* def= */ 1.0f, + userTracker.userId + ) assertThat(currentScale).isEqualTo(fontSizeValueArray[progress].toFloat()) @@ -119,7 +128,12 @@ class FontScalingDialogTest : SysuiTestCase() { backgroundDelayableExecutor.advanceClockToNext() backgroundDelayableExecutor.runAllReady() - val currentScale = systemSettings.getFloat(Settings.System.FONT_SCALE, /* def= */ 1.0f) + val currentScale = + systemSettings.getFloatForUser( + Settings.System.FONT_SCALE, + /* def= */ 1.0f, + userTracker.userId + ) assertThat(seekBar.getProgress()).isEqualTo(1) assertThat(currentScale).isEqualTo(fontSizeValueArray[1].toFloat()) @@ -145,7 +159,12 @@ class FontScalingDialogTest : SysuiTestCase() { backgroundDelayableExecutor.advanceClockToNext() backgroundDelayableExecutor.runAllReady() - val currentScale = systemSettings.getFloat(Settings.System.FONT_SCALE, /* def= */ 1.0f) + val currentScale = + systemSettings.getFloatForUser( + Settings.System.FONT_SCALE, + /* def= */ 1.0f, + userTracker.userId + ) assertThat(seekBar.getProgress()).isEqualTo(fontSizeValueArray.size - 2) assertThat(currentScale) .isEqualTo(fontSizeValueArray[fontSizeValueArray.size - 2].toFloat()) @@ -159,16 +178,21 @@ class FontScalingDialogTest : SysuiTestCase() { val seekBarWithIconButtonsView: SeekBarWithIconButtonsView = fontScalingDialog.findViewById(R.id.font_scaling_slider)!! - secureSettings.putInt(Settings.Secure.ACCESSIBILITY_FONT_SCALING_HAS_BEEN_CHANGED, OFF) + secureSettings.putIntForUser( + Settings.Secure.ACCESSIBILITY_FONT_SCALING_HAS_BEEN_CHANGED, + OFF, + userTracker.userId + ) // Default seekbar progress for font size is 1, set it to another progress 0 seekBarWithIconButtonsView.setProgress(0) backgroundDelayableExecutor.runAllReady() val currentSettings = - secureSettings.getInt( + secureSettings.getIntForUser( Settings.Secure.ACCESSIBILITY_FONT_SCALING_HAS_BEEN_CHANGED, - /* def = */ OFF + /* def = */ OFF, + userTracker.userId ) assertThat(currentSettings).isEqualTo(ON) @@ -199,7 +223,12 @@ class FontScalingDialogTest : SysuiTestCase() { backgroundDelayableExecutor.runAllReady() // Verify that the scale of font size remains the default value 1.0f. - var systemScale = systemSettings.getFloat(Settings.System.FONT_SCALE, /* def= */ 1.0f) + var systemScale = + systemSettings.getFloatForUser( + Settings.System.FONT_SCALE, + /* def= */ 1.0f, + userTracker.userId + ) assertThat(systemScale).isEqualTo(1.0f) // Simulate releasing the finger from the seekbar. @@ -209,7 +238,12 @@ class FontScalingDialogTest : SysuiTestCase() { backgroundDelayableExecutor.runAllReady() // Verify that the scale of font size has been updated. - systemScale = systemSettings.getFloat(Settings.System.FONT_SCALE, /* def= */ 1.0f) + systemScale = + systemSettings.getFloatForUser( + Settings.System.FONT_SCALE, + /* def= */ 1.0f, + userTracker.userId + ) assertThat(systemScale).isEqualTo(fontSizeValueArray[0].toFloat()) fontScalingDialog.dismiss() diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/GhostedViewLaunchAnimatorControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/GhostedViewLaunchAnimatorControllerTest.kt index 0798d73cc2f2..d1ac0e861d26 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/animation/GhostedViewLaunchAnimatorControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/animation/GhostedViewLaunchAnimatorControllerTest.kt @@ -21,6 +21,7 @@ import android.testing.TestableLooper import android.widget.FrameLayout import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase +import com.android.systemui.animation.view.LaunchableFrameLayout import org.junit.Assert.assertThrows import org.junit.Test import org.junit.runner.RunWith diff --git a/packages/SystemUI/tests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt index 1990c8f644b4..ac2d492f3320 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt @@ -16,6 +16,7 @@ package com.android.systemui.authentication.domain.interactor +import android.app.admin.DevicePolicyManager import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.authentication.data.repository.AuthenticationRepository @@ -48,7 +49,7 @@ class AuthenticationInteractorTest : SysuiTestCase() { fun authMethod() = testScope.runTest { val authMethod by collectLastValue(underTest.authenticationMethod) - assertThat(authMethod).isEqualTo(AuthenticationMethodModel.PIN(1234)) + assertThat(authMethod).isEqualTo(AuthenticationMethodModel.Pin(1234)) underTest.setAuthenticationMethod(AuthenticationMethodModel.Password("password")) assertThat(authMethod).isEqualTo(AuthenticationMethodModel.Password("password")) @@ -147,7 +148,7 @@ class AuthenticationInteractorTest : SysuiTestCase() { testScope.runTest { val failedAttemptCount by collectLastValue(underTest.failedAuthenticationAttempts) val isUnlocked by collectLastValue(underTest.isUnlocked) - underTest.setAuthenticationMethod(AuthenticationMethodModel.PIN(1234)) + underTest.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234)) assertThat(isUnlocked).isFalse() assertThat(underTest.authenticate(listOf(1, 2, 3, 4))).isTrue() @@ -160,7 +161,7 @@ class AuthenticationInteractorTest : SysuiTestCase() { testScope.runTest { val failedAttemptCount by collectLastValue(underTest.failedAuthenticationAttempts) val isUnlocked by collectLastValue(underTest.isUnlocked) - underTest.setAuthenticationMethod(AuthenticationMethodModel.PIN(1234)) + underTest.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234)) assertThat(isUnlocked).isFalse() assertThat(underTest.authenticate(listOf(9, 8, 7))).isFalse() @@ -169,6 +170,51 @@ class AuthenticationInteractorTest : SysuiTestCase() { } @Test + fun authenticate_withEmptyPin_returnsFalseAndDoesNotUnlockDevice() = + testScope.runTest { + val failedAttemptCount by collectLastValue(underTest.failedAuthenticationAttempts) + val isUnlocked by collectLastValue(underTest.isUnlocked) + underTest.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234)) + assertThat(isUnlocked).isFalse() + + assertThat(underTest.authenticate(listOf())).isFalse() + assertThat(isUnlocked).isFalse() + assertThat(failedAttemptCount).isEqualTo(1) + } + + @Test + fun authenticate_withCorrectMaxLengthPin_returnsTrueAndUnlocksDevice() = + testScope.runTest { + val failedAttemptCount by collectLastValue(underTest.failedAuthenticationAttempts) + val isUnlocked by collectLastValue(underTest.isUnlocked) + underTest.setAuthenticationMethod(AuthenticationMethodModel.Pin(9999999999999999)) + assertThat(isUnlocked).isFalse() + + assertThat(underTest.authenticate(List(16) { 9 })).isTrue() + assertThat(isUnlocked).isTrue() + assertThat(failedAttemptCount).isEqualTo(0) + } + + @Test + fun authenticate_withCorrectTooLongPin_returnsFalseAndDoesNotUnlockDevice() = + testScope.runTest { + // Max pin length is 16 digits. To avoid issues with overflows, this test ensures + // that all pins > 16 decimal digits are rejected. + + // If the policy changes, there is work to do in SysUI. + assertThat(DevicePolicyManager.MAX_PASSWORD_LENGTH).isLessThan(17) + + val failedAttemptCount by collectLastValue(underTest.failedAuthenticationAttempts) + val isUnlocked by collectLastValue(underTest.isUnlocked) + underTest.setAuthenticationMethod(AuthenticationMethodModel.Pin(99999999999999999)) + assertThat(isUnlocked).isFalse() + + assertThat(underTest.authenticate(List(17) { 9 })).isFalse() + assertThat(isUnlocked).isFalse() + assertThat(failedAttemptCount).isEqualTo(1) + } + + @Test fun authenticate_withCorrectPassword_returnsTrueAndUnlocksDevice() = testScope.runTest { val failedAttemptCount by collectLastValue(underTest.failedAuthenticationAttempts) @@ -291,6 +337,110 @@ class AuthenticationInteractorTest : SysuiTestCase() { } @Test + fun tryAutoConfirm_withAutoConfirmPinAndEmptyInput_returnsNullAndHasNoEffect() = + testScope.runTest { + val failedAttemptCount by collectLastValue(underTest.failedAuthenticationAttempts) + val isUnlocked by collectLastValue(underTest.isUnlocked) + underTest.setAuthenticationMethod( + AuthenticationMethodModel.Pin(1234, autoConfirm = true) + ) + assertThat(isUnlocked).isFalse() + + assertThat(underTest.authenticate(listOf(), tryAutoConfirm = true)).isNull() + assertThat(isUnlocked).isFalse() + assertThat(failedAttemptCount).isEqualTo(0) + } + + @Test + fun tryAutoConfirm_withAutoConfirmPinAndShorterPin_returnsNullAndHasNoEffect() = + testScope.runTest { + val failedAttemptCount by collectLastValue(underTest.failedAuthenticationAttempts) + val isUnlocked by collectLastValue(underTest.isUnlocked) + underTest.setAuthenticationMethod( + AuthenticationMethodModel.Pin(1234, autoConfirm = true) + ) + assertThat(isUnlocked).isFalse() + + assertThat(underTest.authenticate(listOf(1, 2, 3), tryAutoConfirm = true)).isNull() + assertThat(isUnlocked).isFalse() + assertThat(failedAttemptCount).isEqualTo(0) + } + + @Test + fun tryAutoConfirm_withAutoConfirmWrongPinCorrectLength_returnsFalseAndDoesNotUnlockDevice() = + testScope.runTest { + val failedAttemptCount by collectLastValue(underTest.failedAuthenticationAttempts) + val isUnlocked by collectLastValue(underTest.isUnlocked) + underTest.setAuthenticationMethod( + AuthenticationMethodModel.Pin(1234, autoConfirm = true) + ) + assertThat(isUnlocked).isFalse() + + assertThat(underTest.authenticate(listOf(1, 2, 4, 4), tryAutoConfirm = true)).isFalse() + assertThat(isUnlocked).isFalse() + assertThat(failedAttemptCount).isEqualTo(1) + } + + @Test + fun tryAutoConfirm_withAutoConfirmLongerPin_returnsFalseAndDoesNotUnlockDevice() = + testScope.runTest { + val failedAttemptCount by collectLastValue(underTest.failedAuthenticationAttempts) + val isUnlocked by collectLastValue(underTest.isUnlocked) + underTest.setAuthenticationMethod( + AuthenticationMethodModel.Pin(1234, autoConfirm = true) + ) + assertThat(isUnlocked).isFalse() + + assertThat(underTest.authenticate(listOf(1, 2, 3, 4, 5), tryAutoConfirm = true)) + .isFalse() + assertThat(isUnlocked).isFalse() + assertThat(failedAttemptCount).isEqualTo(1) + } + + @Test + fun tryAutoConfirm_withAutoConfirmCorrectPin_returnsTrueAndUnlocksDevice() = + testScope.runTest { + val failedAttemptCount by collectLastValue(underTest.failedAuthenticationAttempts) + val isUnlocked by collectLastValue(underTest.isUnlocked) + underTest.setAuthenticationMethod( + AuthenticationMethodModel.Pin(1234, autoConfirm = true) + ) + assertThat(isUnlocked).isFalse() + + assertThat(underTest.authenticate(listOf(1, 2, 4, 4), tryAutoConfirm = true)).isFalse() + assertThat(isUnlocked).isFalse() + assertThat(failedAttemptCount).isEqualTo(1) + } + + @Test + fun tryAutoConfirm_withoutAutoConfirmButCorrectPin_returnsNullAndHasNoEffects() = + testScope.runTest { + val failedAttemptCount by collectLastValue(underTest.failedAuthenticationAttempts) + val isUnlocked by collectLastValue(underTest.isUnlocked) + underTest.setAuthenticationMethod( + AuthenticationMethodModel.Pin(1234, autoConfirm = false) + ) + assertThat(isUnlocked).isFalse() + + assertThat(underTest.authenticate(listOf(1, 2, 3, 4), tryAutoConfirm = true)).isNull() + assertThat(isUnlocked).isFalse() + assertThat(failedAttemptCount).isEqualTo(0) + } + + @Test + fun tryAutoConfirm_withoutCorrectPassword_returnsNullAndHasNoEffects() = + testScope.runTest { + val failedAttemptCount by collectLastValue(underTest.failedAuthenticationAttempts) + val isUnlocked by collectLastValue(underTest.isUnlocked) + underTest.setAuthenticationMethod(AuthenticationMethodModel.Password("password")) + assertThat(isUnlocked).isFalse() + + assertThat(underTest.authenticate("password".toList(), tryAutoConfirm = true)).isNull() + assertThat(isUnlocked).isFalse() + assertThat(failedAttemptCount).isEqualTo(0) + } + + @Test fun unlocksDevice_whenAuthMethodBecomesNone() = testScope.runTest { val isUnlocked by collectLastValue(underTest.isUnlocked) diff --git a/packages/SystemUI/tests/src/com/android/systemui/back/domain/interactor/BackActionInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/back/domain/interactor/BackActionInteractorTest.kt new file mode 100644 index 000000000000..88c710aedf93 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/back/domain/interactor/BackActionInteractorTest.kt @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.back.domain.interactor + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.plugins.statusbar.StatusBarStateController +import com.android.systemui.shade.QuickSettingsController +import com.android.systemui.shade.ShadeController +import com.android.systemui.shade.ShadeViewController +import com.android.systemui.statusbar.StatusBarState +import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager +import com.android.systemui.util.mockito.whenever +import junit.framework.Assert.assertFalse +import junit.framework.Assert.assertTrue +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.Mockito.anyBoolean +import org.mockito.Mockito.atLeastOnce +import org.mockito.Mockito.never +import org.mockito.Mockito.verify +import org.mockito.junit.MockitoJUnit + +@SmallTest +@RunWith(AndroidJUnit4::class) +class BackActionInteractorTest : SysuiTestCase() { + @JvmField @Rule var mockitoRule = MockitoJUnit.rule() + + @Mock private lateinit var statusBarStateController: StatusBarStateController + @Mock private lateinit var statusBarKeyguardViewManager: StatusBarKeyguardViewManager + @Mock private lateinit var shadeController: ShadeController + @Mock private lateinit var qsController: QuickSettingsController + @Mock private lateinit var shadeViewController: ShadeViewController + + private lateinit var backActionInteractor: BackActionInteractor + + @Before + fun setup() { + backActionInteractor = + BackActionInteractor( + statusBarStateController, + statusBarKeyguardViewManager, + shadeController, + ) + backActionInteractor.setup(qsController, shadeViewController) + } + + @Test + fun testOnBackRequested_keyguardCanHandleBackPressed() { + whenever(statusBarKeyguardViewManager.canHandleBackPressed()).thenReturn(true) + + val result = backActionInteractor.onBackRequested() + + assertTrue(result) + verify(statusBarKeyguardViewManager, atLeastOnce()).onBackPressed() + } + + @Test + fun testOnBackRequested_quickSettingsIsCustomizing() { + whenever(qsController.isCustomizing).thenReturn(true) + + val result = backActionInteractor.onBackRequested() + + assertTrue(result) + verify(qsController, atLeastOnce()).closeQsCustomizer() + verify(statusBarKeyguardViewManager, never()).onBackPressed() + } + + @Test + fun testOnBackRequested_quickSettingsExpanded() { + whenever(qsController.expanded).thenReturn(true) + + val result = backActionInteractor.onBackRequested() + + assertTrue(result) + verify(shadeViewController, atLeastOnce()).animateCollapseQs(anyBoolean()) + verify(statusBarKeyguardViewManager, never()).onBackPressed() + } + + @Test + fun testOnBackRequested_closeUserSwitcherIfOpen() { + whenever(shadeViewController.closeUserSwitcherIfOpen()).thenReturn(true) + + val result = backActionInteractor.onBackRequested() + + assertTrue(result) + verify(statusBarKeyguardViewManager, never()).onBackPressed() + verify(shadeViewController, never()).animateCollapseQs(anyBoolean()) + } + + @Test + fun testOnBackRequested_returnsFalse() { + // make shouldBackBeHandled return false + whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD) + + val result = backActionInteractor.onBackRequested() + + assertFalse(result) + verify(statusBarKeyguardViewManager, never()).onBackPressed() + verify(shadeViewController, never()).animateCollapseQs(anyBoolean()) + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java index b9f92a064bc8..b4a4a11a81a1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java @@ -954,6 +954,25 @@ public class AuthControllerTest extends SysuiTestCase { eq(null) /* credentialAttestation */); } + @Test + public void testShowDialog_whenOwnerNotInForeground() { + PromptInfo promptInfo = createTestPromptInfo(); + promptInfo.setAllowBackgroundAuthentication(false); + switchTask("other_package"); + mAuthController.showAuthenticationDialog(promptInfo, + mReceiver /* receiver */, + new int[]{1} /* sensorIds */, + false /* credentialAllowed */, + true /* requireConfirmation */, + 0 /* userId */, + 0 /* operationId */, + "testPackage", + REQUEST_ID); + + assertNull(mAuthController.mCurrentDialog); + verify(mDialog1, never()).show(any(), any()); + } + private void showDialog(int[] sensorIds, boolean credentialAllowed) { mAuthController.showAuthenticationDialog(createTestPromptInfo(), mReceiver /* receiver */, diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricNotificationServiceTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricNotificationServiceTest.java index 38c9caf085e2..9cb3b1aa9a55 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricNotificationServiceTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricNotificationServiceTest.java @@ -30,7 +30,11 @@ import android.app.Notification; import android.app.NotificationManager; import android.hardware.biometrics.BiometricFaceConstants; import android.hardware.biometrics.BiometricSourceType; +import android.hardware.biometrics.BiometricStateListener; +import android.hardware.face.FaceManager; +import android.hardware.fingerprint.FingerprintManager; import android.os.Handler; +import android.os.UserHandle; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; @@ -69,6 +73,10 @@ public class BiometricNotificationServiceTest extends SysuiTestCase { Optional<FingerprintReEnrollNotification> mFingerprintReEnrollNotificationOptional; @Mock FingerprintReEnrollNotification mFingerprintReEnrollNotification; + @Mock + FingerprintManager mFingerprintManager; + @Mock + FaceManager mFaceManager; private static final String TAG = "BiometricNotificationService"; private static final int FACE_NOTIFICATION_ID = 1; @@ -81,6 +89,8 @@ public class BiometricNotificationServiceTest extends SysuiTestCase { private TestableLooper mLooper; private KeyguardUpdateMonitorCallback mKeyguardUpdateMonitorCallback; private KeyguardStateController.Callback mKeyguardStateControllerCallback; + private BiometricStateListener mFaceStateListener; + private BiometricStateListener mFingerprintStateListener; @Before public void setUp() { @@ -99,25 +109,37 @@ public class BiometricNotificationServiceTest extends SysuiTestCase { mKeyguardUpdateMonitor, mKeyguardStateController, handler, mNotificationManager, broadcastReceiver, - mFingerprintReEnrollNotificationOptional); + mFingerprintReEnrollNotificationOptional, + mFingerprintManager, + mFaceManager); biometricNotificationService.start(); ArgumentCaptor<KeyguardUpdateMonitorCallback> updateMonitorCallbackArgumentCaptor = ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback.class); ArgumentCaptor<KeyguardStateController.Callback> stateControllerCallbackArgumentCaptor = ArgumentCaptor.forClass(KeyguardStateController.Callback.class); + ArgumentCaptor<BiometricStateListener> faceStateListenerArgumentCaptor = + ArgumentCaptor.forClass(BiometricStateListener.class); + ArgumentCaptor<BiometricStateListener> fingerprintStateListenerArgumentCaptor = + ArgumentCaptor.forClass(BiometricStateListener.class); verify(mKeyguardUpdateMonitor).registerCallback( updateMonitorCallbackArgumentCaptor.capture()); verify(mKeyguardStateController).addCallback( stateControllerCallbackArgumentCaptor.capture()); + verify(mFaceManager).registerBiometricStateListener( + faceStateListenerArgumentCaptor.capture()); + verify(mFingerprintManager).registerBiometricStateListener( + fingerprintStateListenerArgumentCaptor.capture()); + mFaceStateListener = faceStateListenerArgumentCaptor.getValue(); + mFingerprintStateListener = fingerprintStateListenerArgumentCaptor.getValue(); mKeyguardUpdateMonitorCallback = updateMonitorCallbackArgumentCaptor.getValue(); mKeyguardStateControllerCallback = stateControllerCallbackArgumentCaptor.getValue(); } @Test - public void testShowFingerprintReEnrollNotification() { + public void testShowFingerprintReEnrollNotification_onAcquiredReEnroll() { when(mKeyguardStateController.isShowing()).thenReturn(false); mKeyguardUpdateMonitorCallback.onBiometricHelp( @@ -139,7 +161,7 @@ public class BiometricNotificationServiceTest extends SysuiTestCase { .isEqualTo(ACTION_SHOW_FINGERPRINT_REENROLL_DIALOG); } @Test - public void testShowFaceReEnrollNotification() { + public void testShowFaceReEnrollNotification_onErrorReEnroll() { when(mKeyguardStateController.isShowing()).thenReturn(false); mKeyguardUpdateMonitorCallback.onBiometricError( @@ -161,4 +183,52 @@ public class BiometricNotificationServiceTest extends SysuiTestCase { .isEqualTo(ACTION_SHOW_FACE_REENROLL_DIALOG); } + @Test + public void testCancelReEnrollmentNotification_onFaceEnrollmentStateChange() { + when(mKeyguardStateController.isShowing()).thenReturn(false); + + mKeyguardUpdateMonitorCallback.onBiometricError( + BiometricFaceConstants.BIOMETRIC_ERROR_RE_ENROLL, + "Testing Face Re-enrollment" /* errString */, + BiometricSourceType.FACE + ); + mKeyguardStateControllerCallback.onKeyguardShowingChanged(); + + mLooper.moveTimeForward(SHOW_NOTIFICATION_DELAY_MS); + mLooper.processAllMessages(); + + verify(mNotificationManager).notifyAsUser(eq(TAG), eq(FACE_NOTIFICATION_ID), + mNotificationArgumentCaptor.capture(), any()); + + mFaceStateListener.onEnrollmentsChanged(0 /* userId */, 0 /* sensorId */, + false /* hasEnrollments */); + + verify(mNotificationManager).cancelAsUser(eq(TAG), eq(FACE_NOTIFICATION_ID), + eq(UserHandle.CURRENT)); + } + + @Test + public void testCancelReEnrollmentNotification_onFingerprintEnrollmentStateChange() { + when(mKeyguardStateController.isShowing()).thenReturn(false); + + mKeyguardUpdateMonitorCallback.onBiometricHelp( + FINGERPRINT_ACQUIRED_RE_ENROLL, + "Testing Fingerprint Re-enrollment" /* errString */, + BiometricSourceType.FINGERPRINT + ); + mKeyguardStateControllerCallback.onKeyguardShowingChanged(); + + mLooper.moveTimeForward(SHOW_NOTIFICATION_DELAY_MS); + mLooper.processAllMessages(); + + verify(mNotificationManager).notifyAsUser(eq(TAG), eq(FINGERPRINT_NOTIFICATION_ID), + mNotificationArgumentCaptor.capture(), any()); + + mFingerprintStateListener.onEnrollmentsChanged(0 /* userId */, 0 /* sensorId */, + false /* hasEnrollments */); + + verify(mNotificationManager).cancelAsUser(eq(TAG), eq(FINGERPRINT_NOTIFICATION_ID), + eq(UserHandle.CURRENT)); + } + } diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/OWNERS b/packages/SystemUI/tests/src/com/android/systemui/biometrics/OWNERS index adb10f01b5e1..5420c377be39 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/OWNERS +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/OWNERS @@ -1,4 +1,5 @@ set noparent +# Bug component: 879035 include /services/core/java/com/android/server/biometrics/OWNERS beverlyt@google.com diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt index 2908e753c2fe..d022653a33c8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt @@ -58,11 +58,11 @@ import com.android.systemui.SysuiTestableContext import com.android.systemui.biometrics.data.repository.FakeRearDisplayStateRepository import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractor import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractorImpl +import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository +import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor import com.android.systemui.dump.DumpManager import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository -import com.android.systemui.keyguard.data.repository.FakeKeyguardBouncerRepository -import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.statusbar.policy.KeyguardStateController import com.android.systemui.util.concurrency.FakeExecutor diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt index 9dcdc46a394a..224875590d75 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt @@ -46,8 +46,8 @@ import com.android.systemui.animation.ActivityLaunchAnimator import com.android.systemui.dump.DumpManager import com.android.systemui.flags.FeatureFlags import com.android.systemui.flags.Flags -import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor -import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerInteractor +import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor +import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.shade.ShadeExpansionStateManager import com.android.systemui.statusbar.LockscreenShadeTransitionController @@ -114,6 +114,8 @@ class UdfpsControllerOverlayTest : SysuiTestCase() { @Mock private lateinit var primaryBouncerInteractor: PrimaryBouncerInteractor @Mock private lateinit var alternateBouncerInteractor: AlternateBouncerInteractor @Mock private lateinit var udfpsUtils: UdfpsUtils + @Mock private lateinit var udfpsKeyguardAccessibilityDelegate: + UdfpsKeyguardAccessibilityDelegate @Captor private lateinit var layoutParamsCaptor: ArgumentCaptor<WindowManager.LayoutParams> private val onTouch = { _: View, _: MotionEvent, _: Boolean -> true } @@ -144,7 +146,8 @@ class UdfpsControllerOverlayTest : SysuiTestCase() { configurationController, keyguardStateController, unlockedScreenOffAnimationController, udfpsDisplayMode, secureSettings, REQUEST_ID, reason, controllerCallback, onTouch, activityLaunchAnimator, featureFlags, - primaryBouncerInteractor, alternateBouncerInteractor, isDebuggable, udfpsUtils + primaryBouncerInteractor, alternateBouncerInteractor, isDebuggable, udfpsUtils, + udfpsKeyguardAccessibilityDelegate, ) block() } diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java index b2ccd60216d7..821c2cbbafca 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java @@ -87,9 +87,9 @@ import com.android.systemui.dump.DumpManager; import com.android.systemui.flags.FeatureFlags; import com.android.systemui.flags.Flags; import com.android.systemui.keyguard.ScreenLifecycle; -import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor; +import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor; import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor; -import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerInteractor; +import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor; import com.android.systemui.log.SessionTracker; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; @@ -217,6 +217,8 @@ public class UdfpsControllerTest extends SysuiTestCase { private AlternateBouncerInteractor mAlternateBouncerInteractor; @Mock private SecureSettings mSecureSettings; + @Mock + private UdfpsKeyguardAccessibilityDelegate mUdfpsKeyguardAccessibilityDelegate; // Capture listeners so that they can be used to send events @Captor @@ -315,7 +317,8 @@ public class UdfpsControllerTest extends SysuiTestCase { mActivityLaunchAnimator, alternateTouchProvider, mBiometricExecutor, mPrimaryBouncerInteractor, mSinglePointerTouchProcessor, mSessionTracker, mAlternateBouncerInteractor, mSecureSettings, mInputManager, mUdfpsUtils, - mock(KeyguardFaceAuthInteractor.class)); + mock(KeyguardFaceAuthInteractor.class), + mUdfpsKeyguardAccessibilityDelegate); verify(mFingerprintManager).setUdfpsOverlayController(mOverlayCaptor.capture()); mOverlayController = mOverlayCaptor.getValue(); verify(mScreenLifecycle).addObserver(mScreenObserverCaptor.capture()); @@ -440,6 +443,16 @@ public class UdfpsControllerTest extends SysuiTestCase { } @Test + public void showUdfpsOverlay_callsListener() throws RemoteException { + mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId, + BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); + mFgExecutor.runAllReady(); + + verify(mFingerprintManager).onUdfpsUiEvent(FingerprintManager.UDFPS_UI_OVERLAY_SHOWN, + TEST_REQUEST_ID, mOpticalProps.sensorId); + } + + @Test public void testSubscribesToOrientationChangesWhenShowingOverlay() throws Exception { mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, mOpticalProps.sensorId, BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); @@ -759,17 +772,20 @@ public class UdfpsControllerTest extends SysuiTestCase { inOrder.verify(mAlternateTouchProvider).onUiReady(); inOrder.verify(mLatencyTracker).onActionEnd( eq(LatencyTracker.ACTION_UDFPS_ILLUMINATE)); - verify(mFingerprintManager, never()).onUiReady(anyLong(), anyInt()); + verify(mFingerprintManager, never()).onUdfpsUiEvent( + eq(FingerprintManager.UDFPS_UI_READY), anyLong(), anyInt()); } else { InOrder inOrder = inOrder(mFingerprintManager, mLatencyTracker); - inOrder.verify(mFingerprintManager).onUiReady(eq(TEST_REQUEST_ID), + inOrder.verify(mFingerprintManager).onUdfpsUiEvent( + eq(FingerprintManager.UDFPS_UI_READY), eq(TEST_REQUEST_ID), eq(testParams.sensorProps.sensorId)); inOrder.verify(mLatencyTracker).onActionEnd( eq(LatencyTracker.ACTION_UDFPS_ILLUMINATE)); verify(mAlternateTouchProvider, never()).onUiReady(); } } else { - verify(mFingerprintManager, never()).onUiReady(anyLong(), anyInt()); + verify(mFingerprintManager, never()).onUdfpsUiEvent( + eq(FingerprintManager.UDFPS_UI_READY), anyLong(), anyInt()); verify(mAlternateTouchProvider, never()).onUiReady(); verify(mLatencyTracker, never()).onActionEnd( eq(LatencyTracker.ACTION_UDFPS_ILLUMINATE)); diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardAccessibilityDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardAccessibilityDelegateTest.kt new file mode 100644 index 000000000000..921ff098753e --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardAccessibilityDelegateTest.kt @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.biometrics + +import android.testing.TestableLooper +import android.view.View +import android.view.accessibility.AccessibilityNodeInfo +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager +import com.android.systemui.util.mockito.argumentCaptor +import org.junit.Assert.assertEquals +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.ArgumentMatchers.anyBoolean +import org.mockito.Mock +import org.mockito.Mockito.mock +import org.mockito.Mockito.verify +import org.mockito.MockitoAnnotations + +@RunWith(AndroidJUnit4::class) +@SmallTest +@TestableLooper.RunWithLooper +class UdfpsKeyguardAccessibilityDelegateTest : SysuiTestCase() { + + @Mock private lateinit var keyguardViewManager: StatusBarKeyguardViewManager + @Mock private lateinit var hostView: View + private lateinit var underTest: UdfpsKeyguardAccessibilityDelegate + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + underTest = + UdfpsKeyguardAccessibilityDelegate( + context.resources, + keyguardViewManager, + ) + } + + @Test + fun onInitializeAccessibilityNodeInfo_clickActionAdded() { + // WHEN node is initialized + val mockedNodeInfo = mock(AccessibilityNodeInfo::class.java) + underTest.onInitializeAccessibilityNodeInfo(hostView, mockedNodeInfo) + + // THEN a11y action is added + val argumentCaptor = argumentCaptor<AccessibilityNodeInfo.AccessibilityAction>() + verify(mockedNodeInfo).addAction(argumentCaptor.capture()) + + // AND the a11y action is a click action + assertEquals( + AccessibilityNodeInfo.AccessibilityAction.ACTION_CLICK.id, + argumentCaptor.value.id + ) + } + + @Test + fun performAccessibilityAction_actionClick_showsPrimaryBouncer() { + // WHEN click action is performed + val mockedNodeInfo = mock(AccessibilityNodeInfo::class.java) + underTest.performAccessibilityAction( + hostView, + AccessibilityNodeInfo.AccessibilityAction.ACTION_CLICK.id, + null + ) + + // THEN primary bouncer shows + verify(keyguardViewManager).showPrimaryBouncer(anyBoolean()) + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerBaseTest.java index 0d3b394eabdb..032753a19044 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerBaseTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerBaseTest.java @@ -31,8 +31,8 @@ import com.android.systemui.dump.DumpManager; import com.android.systemui.flags.FakeFeatureFlags; import com.android.systemui.flags.Flags; import com.android.systemui.keyguard.KeyguardViewMediator; -import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor; -import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerInteractor; +import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor; +import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.shade.ShadeExpansionChangeEvent; import com.android.systemui.shade.ShadeExpansionListener; @@ -73,6 +73,7 @@ public class UdfpsKeyguardViewLegacyControllerBaseTest extends SysuiTestCase { protected @Mock ActivityLaunchAnimator mActivityLaunchAnimator; protected @Mock PrimaryBouncerInteractor mPrimaryBouncerInteractor; protected @Mock AlternateBouncerInteractor mAlternateBouncerInteractor; + protected @Mock UdfpsKeyguardAccessibilityDelegate mUdfpsKeyguardAccessibilityDelegate; protected FakeFeatureFlags mFeatureFlags = new FakeFeatureFlags(); @@ -167,7 +168,8 @@ public class UdfpsKeyguardViewLegacyControllerBaseTest extends SysuiTestCase { mActivityLaunchAnimator, mFeatureFlags, mPrimaryBouncerInteractor, - mAlternateBouncerInteractor); + mAlternateBouncerInteractor, + mUdfpsKeyguardAccessibilityDelegate); return controller; } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt index ffad32626db3..9df06dc9e18f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt @@ -22,20 +22,20 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.keyguard.KeyguardSecurityModel import com.android.systemui.RoboPilotTest +import com.android.systemui.bouncer.data.repository.KeyguardBouncerRepository +import com.android.systemui.bouncer.data.repository.KeyguardBouncerRepositoryImpl +import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor +import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInteractor +import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor +import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants +import com.android.systemui.bouncer.ui.BouncerView import com.android.systemui.classifier.FalsingCollector import com.android.systemui.flags.FakeFeatureFlags import com.android.systemui.flags.Flags import com.android.systemui.keyguard.DismissCallbackRegistry -import com.android.systemui.keyguard.data.BouncerView import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository import com.android.systemui.keyguard.data.repository.DeviceEntryFingerprintAuthRepository import com.android.systemui.keyguard.data.repository.FakeTrustRepository -import com.android.systemui.keyguard.data.repository.KeyguardBouncerRepository -import com.android.systemui.keyguard.data.repository.KeyguardBouncerRepositoryImpl -import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor -import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerCallbackInteractor -import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerInteractor -import com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants import com.android.systemui.log.table.TableLogBuffer import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.statusbar.StatusBarState diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/bouncer/data/factory/BouncerMessageFactoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/data/factory/BouncerMessageFactoryTest.kt index 0ef77e8b9216..992ee1a83254 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/bouncer/data/factory/BouncerMessageFactoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/data/factory/BouncerMessageFactoryTest.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.keyguard.bouncer.data.factory +package com.android.systemui.bouncer.data.factory import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest @@ -26,7 +26,7 @@ import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_DEFAULT import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_INCORRECT_PRIMARY_AUTH_INPUT import com.android.keyguard.KeyguardUpdateMonitor import com.android.systemui.SysuiTestCase -import com.android.systemui.keyguard.bouncer.shared.model.BouncerMessageModel +import com.android.systemui.bouncer.shared.model.BouncerMessageModel import com.android.systemui.util.mockito.whenever import com.google.common.truth.StringSubject import com.google.common.truth.Truth.assertThat @@ -34,7 +34,6 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runTest import org.junit.Before -import org.junit.Ignore import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock @@ -43,7 +42,6 @@ import org.mockito.MockitoAnnotations @OptIn(ExperimentalCoroutinesApi::class) @SmallTest @RunWith(AndroidJUnit4::class) -@Ignore("b/236891644") class BouncerMessageFactoryTest : SysuiTestCase() { private lateinit var underTest: BouncerMessageFactory diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/bouncer/data/repository/BouncerMessageRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/data/repo/BouncerMessageRepositoryTest.kt index a729de5eced5..de712da9d99b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/bouncer/data/repository/BouncerMessageRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/data/repo/BouncerMessageRepositoryTest.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.keyguard.bouncer.data.repository +package com.android.systemui.bouncer.data.repo import android.content.pm.UserInfo import android.hardware.biometrics.BiometricSourceType @@ -45,10 +45,12 @@ import com.android.systemui.R.string.kg_prompt_reason_restart_pin import com.android.systemui.R.string.kg_prompt_unattended_update import com.android.systemui.R.string.kg_trust_agent_disabled import com.android.systemui.SysuiTestCase +import com.android.systemui.bouncer.data.factory.BouncerMessageFactory +import com.android.systemui.bouncer.data.repository.BouncerMessageRepository +import com.android.systemui.bouncer.data.repository.BouncerMessageRepositoryImpl +import com.android.systemui.bouncer.shared.model.BouncerMessageModel +import com.android.systemui.bouncer.shared.model.Message import com.android.systemui.coroutines.collectLastValue -import com.android.systemui.keyguard.bouncer.data.factory.BouncerMessageFactory -import com.android.systemui.keyguard.bouncer.shared.model.BouncerMessageModel -import com.android.systemui.keyguard.bouncer.shared.model.Message import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository import com.android.systemui.keyguard.data.repository.FakeTrustRepository @@ -61,7 +63,6 @@ import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Before -import org.junit.Ignore import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentCaptor @@ -74,7 +75,6 @@ import org.mockito.MockitoAnnotations @SmallTest @TestableLooper.RunWithLooper(setAsMainLooper = true) @RunWith(AndroidJUnit4::class) -@Ignore("b/236891644") class BouncerMessageRepositoryTest : SysuiTestCase() { @Mock private lateinit var updateMonitor: KeyguardUpdateMonitor diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardBouncerRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/data/repository/KeyguardBouncerRepositoryTest.kt index b3104b7de4b9..a04919185350 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardBouncerRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/data/repository/KeyguardBouncerRepositoryTest.kt @@ -1,20 +1,4 @@ -/* - * 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.keyguard.data.repository +package com.android.systemui.bouncer.data.repository import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest @@ -24,14 +8,14 @@ import com.android.systemui.log.table.TableLogBuffer import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.eq import com.android.systemui.util.time.SystemClock -import kotlinx.coroutines.runBlocking import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.runBlocking import kotlinx.coroutines.test.TestCoroutineScope import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock -import org.mockito.Mockito.verify +import org.mockito.Mockito import org.mockito.MockitoAnnotations @OptIn(ExperimentalCoroutinesApi::class) @@ -59,6 +43,7 @@ class KeyguardBouncerRepositoryTest : SysuiTestCase() { @Test fun changingFlowValueTriggersLogging() = runBlocking { underTest.setPrimaryShow(true) - verify(bouncerLogger).logChange(eq(""), eq("PrimaryBouncerShow"), value = eq(false), any()) + Mockito.verify(bouncerLogger) + .logChange(eq(""), eq("PrimaryBouncerShow"), value = eq(false), any()) } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/AlternateBouncerInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorTest.kt index ca6b8d51e9e6..37b9ca49ef57 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/AlternateBouncerInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorTest.kt @@ -14,16 +14,16 @@ * limitations under the License. */ -package com.android.systemui.keyguard.domain.interactor +package com.android.systemui.bouncer.domain.interactor import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase +import com.android.systemui.bouncer.data.repository.KeyguardBouncerRepository +import com.android.systemui.bouncer.data.repository.KeyguardBouncerRepositoryImpl import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository -import com.android.systemui.keyguard.data.repository.KeyguardBouncerRepository -import com.android.systemui.keyguard.data.repository.KeyguardBouncerRepositoryImpl import com.android.systemui.log.table.TableLogBuffer import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.statusbar.policy.KeyguardStateController diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt index 374c28d6dce8..9483667909d5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt @@ -69,7 +69,7 @@ class BouncerInteractorTest : SysuiTestCase() { val currentScene by collectLastValue(sceneInteractor.currentScene("container1")) val message by collectLastValue(underTest.message) - authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.PIN(1234)) + authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234)) authenticationInteractor.lockDevice() underTest.showOrUnlockDevice("container1") assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) @@ -95,6 +95,61 @@ class BouncerInteractorTest : SysuiTestCase() { } @Test + fun pinAuthMethod_tryAutoConfirm_withAutoConfirmPin() = + testScope.runTest { + val currentScene by collectLastValue(sceneInteractor.currentScene("container1")) + val message by collectLastValue(underTest.message) + + authenticationInteractor.setAuthenticationMethod( + AuthenticationMethodModel.Pin(1234, autoConfirm = true) + ) + authenticationInteractor.lockDevice() + underTest.showOrUnlockDevice("container1") + assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) + assertThat(message).isEqualTo(MESSAGE_ENTER_YOUR_PIN) + underTest.clearMessage() + + // Incomplete input. + assertThat(underTest.authenticate(listOf(1, 2), tryAutoConfirm = true)).isNull() + assertThat(message).isEmpty() + assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) + + // Wrong 4-digit pin + assertThat(underTest.authenticate(listOf(1, 2, 3, 5), tryAutoConfirm = true)).isFalse() + assertThat(message).isEqualTo(MESSAGE_WRONG_PIN) + assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) + + // Correct input. + assertThat(underTest.authenticate(listOf(1, 2, 3, 4), tryAutoConfirm = true)).isTrue() + assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Gone)) + } + + @Test + fun pinAuthMethod_tryAutoConfirm_withoutAutoConfirmPin() = + testScope.runTest { + val currentScene by collectLastValue(sceneInteractor.currentScene("container1")) + val message by collectLastValue(underTest.message) + + authenticationInteractor.setAuthenticationMethod( + AuthenticationMethodModel.Pin(1234, autoConfirm = false) + ) + authenticationInteractor.lockDevice() + underTest.showOrUnlockDevice("container1") + assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) + underTest.clearMessage() + + // Incomplete input. + assertThat(underTest.authenticate(listOf(1, 2), tryAutoConfirm = true)).isNull() + assertThat(message).isEmpty() + assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) + + // Correct input. + assertThat(underTest.authenticate(listOf(1, 2, 3, 4), tryAutoConfirm = true)).isNull() + assertThat(message).isEmpty() + assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) + } + + @Test fun passwordAuthMethod() = testScope.runTest { val currentScene by collectLastValue(sceneInteractor.currentScene("container1")) @@ -167,7 +222,7 @@ class BouncerInteractorTest : SysuiTestCase() { fun showOrUnlockDevice_notLocked_switchesToGoneScene() = testScope.runTest { val currentScene by collectLastValue(sceneInteractor.currentScene("container1")) - authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.PIN(1234)) + authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234)) authenticationInteractor.unlockDevice() runCurrent() @@ -211,7 +266,7 @@ class BouncerInteractorTest : SysuiTestCase() { val throttling by collectLastValue(underTest.throttling) val message by collectLastValue(underTest.message) val isUnlocked by collectLastValue(authenticationInteractor.isUnlocked) - authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.PIN(1234)) + authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234)) assertThat(throttling).isNull() assertThat(message).isEqualTo("") assertThat(isUnlocked).isFalse() diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/bouncer/domain/interactor/BouncerMessageInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractorTest.kt index 3a87680bb24c..8e5256e970a3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/bouncer/domain/interactor/BouncerMessageInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractorTest.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.keyguard.bouncer.domain.interactor +package com.android.systemui.bouncer.domain.interactor import android.content.pm.UserInfo import android.testing.TestableLooper @@ -26,14 +26,14 @@ import com.android.keyguard.KeyguardUpdateMonitor import com.android.systemui.R.string.keyguard_enter_pin import com.android.systemui.R.string.kg_too_many_failed_attempts_countdown import com.android.systemui.SysuiTestCase +import com.android.systemui.bouncer.data.factory.BouncerMessageFactory +import com.android.systemui.bouncer.data.repository.FakeBouncerMessageRepository +import com.android.systemui.bouncer.shared.model.BouncerMessageModel +import com.android.systemui.bouncer.shared.model.Message import com.android.systemui.coroutines.FlowValue import com.android.systemui.coroutines.collectLastValue import com.android.systemui.flags.FakeFeatureFlags import com.android.systemui.flags.Flags -import com.android.systemui.keyguard.bouncer.data.factory.BouncerMessageFactory -import com.android.systemui.keyguard.bouncer.shared.model.BouncerMessageModel -import com.android.systemui.keyguard.bouncer.shared.model.Message -import com.android.systemui.keyguard.data.repository.FakeBouncerMessageRepository import com.android.systemui.user.data.repository.FakeUserRepository import com.android.systemui.util.mockito.KotlinArgumentCaptor import com.android.systemui.util.mockito.whenever @@ -42,7 +42,6 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runTest import org.junit.Before -import org.junit.Ignore import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentMatchers.eq @@ -54,7 +53,6 @@ import org.mockito.MockitoAnnotations @SmallTest @TestableLooper.RunWithLooper(setAsMainLooper = true) @RunWith(AndroidJUnit4::class) -@Ignore("b/236891644") class BouncerMessageInteractorTest : SysuiTestCase() { @Mock private lateinit var securityModel: KeyguardSecurityModel diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerCallbackInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerCallbackInteractorTest.kt index 2b135cc406bf..a81ca86f338d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerCallbackInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerCallbackInteractorTest.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.keyguard.domain.interactor +package com.android.systemui.bouncer.domain.interactor import android.view.View import androidx.test.ext.junit.runners.AndroidJUnit4 diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorTest.kt index b5cb44aafd29..f892453b3a57 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorTest.kt @@ -14,7 +14,7 @@ * limitations under the License */ -package com.android.systemui.keyguard.domain.interactor +package com.android.systemui.bouncer.domain.interactor import android.hardware.biometrics.BiometricSourceType import android.testing.AndroidTestingRunner @@ -27,17 +27,17 @@ import com.android.keyguard.KeyguardUpdateMonitor import com.android.systemui.DejankUtils import com.android.systemui.R import com.android.systemui.SysuiTestCase +import com.android.systemui.bouncer.data.repository.KeyguardBouncerRepository +import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants.EXPANSION_HIDDEN +import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants.EXPANSION_VISIBLE +import com.android.systemui.bouncer.shared.model.BouncerShowMessageModel +import com.android.systemui.bouncer.ui.BouncerView +import com.android.systemui.bouncer.ui.BouncerViewDelegate import com.android.systemui.classifier.FalsingCollector import com.android.systemui.flags.FakeFeatureFlags import com.android.systemui.flags.Flags import com.android.systemui.keyguard.DismissCallbackRegistry -import com.android.systemui.keyguard.data.BouncerView -import com.android.systemui.keyguard.data.BouncerViewDelegate import com.android.systemui.keyguard.data.repository.FakeTrustRepository -import com.android.systemui.keyguard.data.repository.KeyguardBouncerRepository -import com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants.EXPANSION_HIDDEN -import com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants.EXPANSION_VISIBLE -import com.android.systemui.keyguard.shared.model.BouncerShowMessageModel import com.android.systemui.plugins.ActivityStarter import com.android.systemui.statusbar.policy.KeyguardStateController import com.android.systemui.util.mockito.any @@ -407,6 +407,7 @@ class PrimaryBouncerInteractorTest : SysuiTestCase() { whenever(keyguardStateController.isFaceAuthEnabled).thenReturn(true) whenever(keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(BiometricSourceType.FACE)) .thenReturn(true) + whenever(keyguardUpdateMonitor.doesCurrentPostureAllowFaceAuth()).thenReturn(true) // WHEN bouncer show is requested underTest.show(true) @@ -424,6 +425,25 @@ class PrimaryBouncerInteractorTest : SysuiTestCase() { } @Test + fun noDelayBouncer_biometricsAllowed_postureDoesNotAllowFaceAuth() { + mainHandler.setMode(FakeHandler.Mode.QUEUEING) + + // GIVEN bouncer should not be delayed because device isn't in the right posture for + // face auth + whenever(keyguardStateController.isFaceAuthEnabled).thenReturn(true) + whenever(keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(BiometricSourceType.FACE)) + .thenReturn(true) + whenever(keyguardUpdateMonitor.doesCurrentPostureAllowFaceAuth()).thenReturn(false) + + // WHEN bouncer show is requested + underTest.show(true) + + // THEN primary show & primary showing soon are updated immediately + verify(repository).setPrimaryShow(true) + verify(repository).setPrimaryShowingSoon(false) + } + + @Test fun delayBouncerWhenActiveUnlockPossible() { testScope.run { mainHandler.setMode(FakeHandler.Mode.QUEUEING) diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractorWithCoroutinesTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorWithCoroutinesTest.kt index b288fbcc0678..665456d5cd80 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractorWithCoroutinesTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorWithCoroutinesTest.kt @@ -14,7 +14,7 @@ * limitations under the License */ -package com.android.systemui.keyguard.domain.interactor +package com.android.systemui.bouncer.domain.interactor import android.os.Looper import androidx.test.ext.junit.runners.AndroidJUnit4 @@ -23,13 +23,13 @@ import com.android.keyguard.KeyguardSecurityModel import com.android.keyguard.KeyguardUpdateMonitor import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase +import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository +import com.android.systemui.bouncer.ui.BouncerView import com.android.systemui.classifier.FalsingCollector import com.android.systemui.coroutines.collectLastValue import com.android.systemui.flags.FakeFeatureFlags import com.android.systemui.flags.Flags import com.android.systemui.keyguard.DismissCallbackRegistry -import com.android.systemui.keyguard.data.BouncerView -import com.android.systemui.keyguard.data.repository.FakeKeyguardBouncerRepository import com.android.systemui.keyguard.data.repository.TrustRepository import com.android.systemui.statusbar.phone.KeyguardBypassController import com.android.systemui.statusbar.policy.KeyguardStateController diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt index 1642410e5a3f..b53e03419df3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt @@ -55,7 +55,7 @@ class AuthMethodBouncerViewModelTest : SysuiTestCase() { @Test fun animateFailure() = testScope.runTest { - authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.PIN(1234)) + authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234)) val animateFailure by collectLastValue(underTest.animateFailure) assertThat(animateFailure).isFalse() diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt index e8c946cdd59d..c6074962ee8e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt @@ -97,7 +97,7 @@ class BouncerViewModelTest : SysuiTestCase() { testScope.runTest { val message by collectLastValue(underTest.message) val throttling by collectLastValue(bouncerInteractor.throttling) - authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.PIN(1234)) + authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234)) assertThat(message?.isUpdateAnimated).isTrue() repeat(BouncerInteractor.THROTTLE_EVERY) { @@ -120,7 +120,7 @@ class BouncerViewModelTest : SysuiTestCase() { } ) val throttling by collectLastValue(bouncerInteractor.throttling) - authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.PIN(1234)) + authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234)) assertThat(isInputEnabled).isTrue() repeat(BouncerInteractor.THROTTLE_EVERY) { @@ -137,7 +137,7 @@ class BouncerViewModelTest : SysuiTestCase() { fun throttlingDialogMessage() = testScope.runTest { val throttlingDialogMessage by collectLastValue(underTest.throttlingDialogMessage) - authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.PIN(1234)) + authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234)) repeat(BouncerInteractor.THROTTLE_EVERY) { // Wrong PIN. @@ -154,7 +154,7 @@ class BouncerViewModelTest : SysuiTestCase() { return listOf( AuthenticationMethodModel.None, AuthenticationMethodModel.Swipe, - AuthenticationMethodModel.PIN(1234), + AuthenticationMethodModel.Pin(1234), AuthenticationMethodModel.Password("password"), AuthenticationMethodModel.Pattern( listOf(AuthenticationMethodModel.Pattern.PatternCoordinate(1, 1)) diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModelTest.kt index 15707c93146c..8236165b62d8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBouncerViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModelTest.kt @@ -14,7 +14,7 @@ * limitations under the License */ -package com.android.systemui.keyguard.ui.viewmodel +package com.android.systemui.bouncer.ui.viewmodel import android.os.Looper import androidx.test.ext.junit.runners.AndroidJUnit4 @@ -23,16 +23,16 @@ import com.android.keyguard.KeyguardSecurityModel import com.android.keyguard.KeyguardUpdateMonitor import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase +import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository +import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInteractor +import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor +import com.android.systemui.bouncer.shared.model.BouncerShowMessageModel +import com.android.systemui.bouncer.ui.BouncerView import com.android.systemui.classifier.FalsingCollector import com.android.systemui.flags.FakeFeatureFlags import com.android.systemui.flags.Flags import com.android.systemui.keyguard.DismissCallbackRegistry -import com.android.systemui.keyguard.data.BouncerView -import com.android.systemui.keyguard.data.repository.FakeKeyguardBouncerRepository import com.android.systemui.keyguard.data.repository.TrustRepository -import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerCallbackInteractor -import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerInteractor -import com.android.systemui.keyguard.shared.model.BouncerShowMessageModel import com.android.systemui.statusbar.phone.KeyguardBypassController import com.android.systemui.statusbar.policy.KeyguardStateController import com.android.systemui.utils.os.FakeHandler diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt index 3bdaf0590888..7e358d27046b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt @@ -30,7 +30,6 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.test.TestScope -import kotlinx.coroutines.test.advanceTimeBy import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test @@ -85,8 +84,8 @@ class PinBouncerViewModelTest : SysuiTestCase() { val isUnlocked by collectLastValue(authenticationInteractor.isUnlocked) val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_NAME)) val message by collectLastValue(bouncerViewModel.message) - val pinLengths by collectLastValue(underTest.pinLengths) - authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.PIN(1234)) + val entries by collectLastValue(underTest.pinEntries) + authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234)) authenticationInteractor.lockDevice() sceneInteractor.setCurrentScene(CONTAINER_NAME, SceneModel(SceneKey.Bouncer)) assertThat(isUnlocked).isFalse() @@ -95,7 +94,7 @@ class PinBouncerViewModelTest : SysuiTestCase() { underTest.onShown() assertThat(message?.text).isEqualTo(ENTER_YOUR_PIN) - assertThat(pinLengths).isEqualTo(0 to 0) + assertThat(entries).hasSize(0) assertThat(isUnlocked).isFalse() assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) } @@ -106,8 +105,8 @@ class PinBouncerViewModelTest : SysuiTestCase() { val isUnlocked by collectLastValue(authenticationInteractor.isUnlocked) val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_NAME)) val message by collectLastValue(bouncerViewModel.message) - val pinLengths by collectLastValue(underTest.pinLengths) - authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.PIN(1234)) + val entries by collectLastValue(underTest.pinEntries) + authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234)) authenticationInteractor.lockDevice() sceneInteractor.setCurrentScene(CONTAINER_NAME, SceneModel(SceneKey.Bouncer)) assertThat(isUnlocked).isFalse() @@ -117,7 +116,8 @@ class PinBouncerViewModelTest : SysuiTestCase() { underTest.onPinButtonClicked(1) assertThat(message?.text).isEmpty() - assertThat(pinLengths).isEqualTo(0 to 1) + assertThat(entries).hasSize(1) + assertThat(entries?.map { it.input }).containsExactly(1) assertThat(isUnlocked).isFalse() assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) } @@ -128,32 +128,59 @@ class PinBouncerViewModelTest : SysuiTestCase() { val isUnlocked by collectLastValue(authenticationInteractor.isUnlocked) val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_NAME)) val message by collectLastValue(bouncerViewModel.message) - val pinLengths by collectLastValue(underTest.pinLengths) - authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.PIN(1234)) + val entries by collectLastValue(underTest.pinEntries) + authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234)) authenticationInteractor.lockDevice() sceneInteractor.setCurrentScene(CONTAINER_NAME, SceneModel(SceneKey.Bouncer)) assertThat(isUnlocked).isFalse() assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) underTest.onShown() underTest.onPinButtonClicked(1) - assertThat(pinLengths).isEqualTo(0 to 1) + assertThat(entries).hasSize(1) underTest.onBackspaceButtonClicked() assertThat(message?.text).isEmpty() - assertThat(pinLengths).isEqualTo(1 to 0) + assertThat(entries).hasSize(0) assertThat(isUnlocked).isFalse() assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) } @Test + fun onPinEdit() = + testScope.runTest { + val isUnlocked by collectLastValue(authenticationInteractor.isUnlocked) + val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_NAME)) + val message by collectLastValue(bouncerViewModel.message) + val entries by collectLastValue(underTest.pinEntries) + authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234)) + authenticationInteractor.lockDevice() + sceneInteractor.setCurrentScene(CONTAINER_NAME, SceneModel(SceneKey.Bouncer)) + assertThat(isUnlocked).isFalse() + assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) + underTest.onShown() + + underTest.onPinButtonClicked(1) + underTest.onPinButtonClicked(2) + underTest.onPinButtonClicked(3) + underTest.onBackspaceButtonClicked() + underTest.onBackspaceButtonClicked() + underTest.onPinButtonClicked(4) + underTest.onPinButtonClicked(5) + + assertThat(entries).hasSize(3) + assertThat(entries?.map { it.input }).containsExactly(1, 4, 5).inOrder() + assertThat(entries?.map { it.sequenceNumber }).isInStrictOrder() + } + + @Test fun onBackspaceButtonLongPressed() = testScope.runTest { val isUnlocked by collectLastValue(authenticationInteractor.isUnlocked) val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_NAME)) val message by collectLastValue(bouncerViewModel.message) - val pinLengths by collectLastValue(underTest.pinLengths) - authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.PIN(1234)) + val entries by collectLastValue(underTest.pinEntries) + authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234)) authenticationInteractor.lockDevice() sceneInteractor.setCurrentScene(CONTAINER_NAME, SceneModel(SceneKey.Bouncer)) assertThat(isUnlocked).isFalse() @@ -165,13 +192,9 @@ class PinBouncerViewModelTest : SysuiTestCase() { underTest.onPinButtonClicked(4) underTest.onBackspaceButtonLongPressed() - repeat(4) { index -> - assertThat(pinLengths).isEqualTo(4 - index to 3 - index) - advanceTimeBy(PinBouncerViewModel.BACKSPACE_LONG_PRESS_DELAY_MS) - } assertThat(message?.text).isEmpty() - assertThat(pinLengths).isEqualTo(1 to 0) + assertThat(entries).hasSize(0) assertThat(isUnlocked).isFalse() assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) } @@ -181,7 +204,7 @@ class PinBouncerViewModelTest : SysuiTestCase() { testScope.runTest { val isUnlocked by collectLastValue(authenticationInteractor.isUnlocked) val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_NAME)) - authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.PIN(1234)) + authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234)) authenticationInteractor.lockDevice() sceneInteractor.setCurrentScene(CONTAINER_NAME, SceneModel(SceneKey.Bouncer)) assertThat(isUnlocked).isFalse() @@ -204,8 +227,8 @@ class PinBouncerViewModelTest : SysuiTestCase() { val isUnlocked by collectLastValue(authenticationInteractor.isUnlocked) val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_NAME)) val message by collectLastValue(bouncerViewModel.message) - val pinLengths by collectLastValue(underTest.pinLengths) - authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.PIN(1234)) + val entries by collectLastValue(underTest.pinEntries) + authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234)) authenticationInteractor.lockDevice() sceneInteractor.setCurrentScene(CONTAINER_NAME, SceneModel(SceneKey.Bouncer)) assertThat(isUnlocked).isFalse() @@ -219,7 +242,7 @@ class PinBouncerViewModelTest : SysuiTestCase() { underTest.onAuthenticateButtonClicked() - assertThat(pinLengths).isEqualTo(0 to 0) + assertThat(entries).hasSize(0) assertThat(message?.text).isEqualTo(WRONG_PIN) assertThat(isUnlocked).isFalse() assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) @@ -231,8 +254,8 @@ class PinBouncerViewModelTest : SysuiTestCase() { val isUnlocked by collectLastValue(authenticationInteractor.isUnlocked) val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_NAME)) val message by collectLastValue(bouncerViewModel.message) - val pinLengths by collectLastValue(underTest.pinLengths) - authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.PIN(1234)) + val entries by collectLastValue(underTest.pinEntries) + authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234)) authenticationInteractor.lockDevice() sceneInteractor.setCurrentScene(CONTAINER_NAME, SceneModel(SceneKey.Bouncer)) assertThat(isUnlocked).isFalse() @@ -245,7 +268,7 @@ class PinBouncerViewModelTest : SysuiTestCase() { underTest.onPinButtonClicked(5) // PIN is now wrong! underTest.onAuthenticateButtonClicked() assertThat(message?.text).isEqualTo(WRONG_PIN) - assertThat(pinLengths).isEqualTo(0 to 0) + assertThat(entries).hasSize(0) assertThat(isUnlocked).isFalse() assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) @@ -262,6 +285,157 @@ class PinBouncerViewModelTest : SysuiTestCase() { assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Gone)) } + @Test + fun onAutoConfirm_whenCorrect() = + testScope.runTest { + val isUnlocked by collectLastValue(authenticationInteractor.isUnlocked) + val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_NAME)) + authenticationInteractor.setAuthenticationMethod( + AuthenticationMethodModel.Pin(1234, autoConfirm = true) + ) + authenticationInteractor.lockDevice() + sceneInteractor.setCurrentScene(CONTAINER_NAME, SceneModel(SceneKey.Bouncer)) + assertThat(isUnlocked).isFalse() + assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) + underTest.onShown() + underTest.onPinButtonClicked(1) + underTest.onPinButtonClicked(2) + underTest.onPinButtonClicked(3) + underTest.onPinButtonClicked(4) + + assertThat(isUnlocked).isTrue() + assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Gone)) + } + + @Test + fun onAutoConfirm_whenWrong() = + testScope.runTest { + val isUnlocked by collectLastValue(authenticationInteractor.isUnlocked) + val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_NAME)) + val message by collectLastValue(bouncerViewModel.message) + val entries by collectLastValue(underTest.pinEntries) + authenticationInteractor.setAuthenticationMethod( + AuthenticationMethodModel.Pin(1234, autoConfirm = true) + ) + authenticationInteractor.lockDevice() + sceneInteractor.setCurrentScene(CONTAINER_NAME, SceneModel(SceneKey.Bouncer)) + assertThat(isUnlocked).isFalse() + assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) + underTest.onShown() + underTest.onPinButtonClicked(1) + underTest.onPinButtonClicked(2) + underTest.onPinButtonClicked(3) + underTest.onPinButtonClicked(5) // PIN is now wrong! + + assertThat(entries).hasSize(0) + assertThat(message?.text).isEqualTo(WRONG_PIN) + assertThat(isUnlocked).isFalse() + assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) + } + + @Test + fun backspaceButtonAppearance_withoutAutoConfirm_alwaysShown() = + testScope.runTest { + val backspaceButtonAppearance by collectLastValue(underTest.backspaceButtonAppearance) + + authenticationInteractor.setAuthenticationMethod( + AuthenticationMethodModel.Pin(1234, autoConfirm = false) + ) + + assertThat(backspaceButtonAppearance).isEqualTo(ActionButtonAppearance.Shown) + } + + @Test + fun backspaceButtonAppearance_withAutoConfirmButNoInput_isHidden() = + testScope.runTest { + val backspaceButtonAppearance by collectLastValue(underTest.backspaceButtonAppearance) + authenticationInteractor.setAuthenticationMethod( + AuthenticationMethodModel.Pin(1234, autoConfirm = true) + ) + + assertThat(backspaceButtonAppearance).isEqualTo(ActionButtonAppearance.Hidden) + } + + @Test + fun backspaceButtonAppearance_withAutoConfirmAndInput_isShownQuiet() = + testScope.runTest { + val backspaceButtonAppearance by collectLastValue(underTest.backspaceButtonAppearance) + authenticationInteractor.setAuthenticationMethod( + AuthenticationMethodModel.Pin(1234, autoConfirm = true) + ) + + underTest.onPinButtonClicked(1) + + assertThat(backspaceButtonAppearance).isEqualTo(ActionButtonAppearance.Subtle) + } + + @Test + fun confirmButtonAppearance_withoutAutoConfirm_alwaysShown() = + testScope.runTest { + val confirmButtonAppearance by collectLastValue(underTest.confirmButtonAppearance) + + authenticationInteractor.setAuthenticationMethod( + AuthenticationMethodModel.Pin(1234, autoConfirm = false) + ) + + assertThat(confirmButtonAppearance).isEqualTo(ActionButtonAppearance.Shown) + } + + @Test + fun confirmButtonAppearance_withAutoConfirm_isHidden() = + testScope.runTest { + val confirmButtonAppearance by collectLastValue(underTest.confirmButtonAppearance) + authenticationInteractor.setAuthenticationMethod( + AuthenticationMethodModel.Pin(1234, autoConfirm = true) + ) + + assertThat(confirmButtonAppearance).isEqualTo(ActionButtonAppearance.Hidden) + } + + @Test + fun hintedPinLength_withoutAutoConfirm_isNull() = + testScope.runTest { + val hintedPinLength by collectLastValue(underTest.hintedPinLength) + authenticationInteractor.setAuthenticationMethod( + AuthenticationMethodModel.Pin(1234, autoConfirm = false) + ) + + assertThat(hintedPinLength).isNull() + } + + @Test + fun hintedPinLength_withAutoConfirmPinLessThanSixDigits_isNull() = + testScope.runTest { + val hintedPinLength by collectLastValue(underTest.hintedPinLength) + authenticationInteractor.setAuthenticationMethod( + AuthenticationMethodModel.Pin(12345, autoConfirm = true) + ) + + assertThat(hintedPinLength).isNull() + } + + @Test + fun hintedPinLength_withAutoConfirmPinExactlySixDigits_isSix() = + testScope.runTest { + val hintedPinLength by collectLastValue(underTest.hintedPinLength) + authenticationInteractor.setAuthenticationMethod( + AuthenticationMethodModel.Pin(123456, autoConfirm = true) + ) + + assertThat(hintedPinLength).isEqualTo(6) + } + + @Test + fun hintedPinLength_withAutoConfirmPinMoreThanSixDigits_isNull() = + testScope.runTest { + val hintedPinLength by collectLastValue(underTest.hintedPinLength) + authenticationInteractor.setAuthenticationMethod( + AuthenticationMethodModel.Pin(1234567, autoConfirm = true) + ) + + assertThat(hintedPinLength).isNull() + } + companion object { private const val CONTAINER_NAME = "container1" private const val ENTER_YOUR_PIN = "Enter your pin" diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineFalsingManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineFalsingManagerTest.java index 292fdff0027d..f770a3885aad 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineFalsingManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineFalsingManagerTest.java @@ -187,4 +187,11 @@ public class BrightLineFalsingManagerTest extends SysuiTestCase { when(mFalsingDataProvider.isUnfolded()).thenReturn(true); assertThat(mBrightLineFalsingManager.isFalseTouch(Classifier.GENERIC)).isFalse(); } + + @Test + public void testTrackpadGesture() { + assertThat(mBrightLineFalsingManager.isFalseTouch(Classifier.GENERIC)).isTrue(); + when(mFalsingDataProvider.isFromTrackpad()).thenReturn(true); + assertThat(mBrightLineFalsingManager.isFalseTouch(Classifier.GENERIC)).isFalse(); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/ClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/ClassifierTest.java index 925461765546..4da151c3ca04 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/classifier/ClassifierTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/ClassifierTest.java @@ -18,8 +18,11 @@ package com.android.systemui.classifier; import android.hardware.devicestate.DeviceStateManager.FoldStateListener; import android.util.DisplayMetrics; +import android.view.InputDevice; import android.view.MotionEvent; +import androidx.test.uiautomator.Configurator; + import com.android.systemui.SysuiTestCase; import com.android.systemui.dock.DockManagerFake; import com.android.systemui.statusbar.policy.BatteryController; @@ -80,6 +83,10 @@ public class ClassifierTest extends SysuiTestCase { mDataProvider.onSessionEnd(); } + protected static int getPointerAction(int actionType, int index) { + return actionType + (index << MotionEvent.ACTION_POINTER_INDEX_SHIFT); + } + protected MotionEvent appendDownEvent(float x, float y) { return appendMotionEvent(MotionEvent.ACTION_DOWN, x, y); } @@ -124,4 +131,55 @@ public class ClassifierTest extends SysuiTestCase { return motionEvent; } + + protected MotionEvent appendTrackpadDownEvent(float x, float y) { + return appendTrackpadMotionEvent(MotionEvent.ACTION_DOWN, x, y, 1); + } + + protected MotionEvent appendTrackpadMoveEvent(float x, float y, int pointerCount) { + return appendTrackpadMotionEvent(MotionEvent.ACTION_MOVE, x, y, pointerCount); + } + + protected MotionEvent appendTrackpadPointerDownEvent(int actionType, float x, float y, + int pointerCount) { + return appendTrackpadMotionEvent(actionType, x, y, pointerCount); + } + + private MotionEvent appendTrackpadMotionEvent(int actionType, float x, float y, + int pointerCount) { + long eventTime = mMotionEvents.isEmpty() ? 1 : mMotionEvents.get( + mMotionEvents.size() - 1).getEventTime() + 1; + return appendTrackpadMotionEvent(actionType, x, y, pointerCount, eventTime); + } + + private MotionEvent appendTrackpadMotionEvent(int actionType, float x, float y, + int pointerCount, long eventTime) { + MotionEvent.PointerProperties[] pointerProperties = + new MotionEvent.PointerProperties[pointerCount]; + MotionEvent.PointerCoords[] pointerCoords = new MotionEvent.PointerCoords[pointerCount]; + for (int i = 0; i < pointerCount; i++) { + pointerProperties[i] = getPointerProperties(i); + pointerCoords[i] = getPointerCoords(x, y); + } + return MotionEvent.obtain(1, eventTime, actionType, pointerCount, pointerProperties, + pointerCoords, 0, 0, 1.0f, 1.0f, 0, 0, + InputDevice.SOURCE_TOUCHPAD | InputDevice.SOURCE_MOUSE, 0, 0, + MotionEvent.CLASSIFICATION_MULTI_FINGER_SWIPE); + } + + private static MotionEvent.PointerProperties getPointerProperties(int pointerId) { + MotionEvent.PointerProperties properties = new MotionEvent.PointerProperties(); + properties.id = pointerId; + properties.toolType = Configurator.getInstance().getToolType(); + return properties; + } + + private static MotionEvent.PointerCoords getPointerCoords(float x, float y) { + MotionEvent.PointerCoords coords = new MotionEvent.PointerCoords(); + coords.pressure = 1; + coords.size = 1; + coords.x = x; + coords.y = y; + return coords; + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingDataProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingDataProviderTest.java index 7e066808b5bd..0353f87ae9b4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingDataProviderTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingDataProviderTest.java @@ -282,6 +282,22 @@ public class FalsingDataProviderTest extends ClassifierTest { } @Test + public void test_IsFromTrackpad() { + MotionEvent motionEventOrigin = appendTrackpadDownEvent(0, 0); + + mDataProvider.onMotionEvent(motionEventOrigin); + mDataProvider.onMotionEvent( + appendTrackpadPointerDownEvent(getPointerAction(MotionEvent.ACTION_POINTER_DOWN, 1), + 0, 0, 2)); + mDataProvider.onMotionEvent( + appendTrackpadPointerDownEvent(getPointerAction(MotionEvent.ACTION_POINTER_DOWN, 2), + 0, 0, 3)); + mDataProvider.onMotionEvent(appendTrackpadMoveEvent(1, -1, 3)); + assertThat(mDataProvider.isFromTrackpad()).isTrue(); + mDataProvider.onSessionEnd(); + } + + @Test public void test_isWirelessCharging() { assertThat(mDataProvider.isDocked()).isFalse(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerTest.java index 39fb7b4cda2c..967196689650 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardOverlayControllerTest.java @@ -26,6 +26,7 @@ import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBO import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_SHOWN_MINIMIZED; import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_SWIPE_DISMISSED; import static com.android.systemui.flags.Flags.CLIPBOARD_IMAGE_TIMEOUT; +import static com.android.systemui.flags.Flags.CLIPBOARD_SHARED_TRANSITIONS; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; @@ -93,12 +94,16 @@ public class ClipboardOverlayControllerTest extends SysuiTestCase { @Mock private ClipboardImageLoader mClipboardImageLoader; @Mock + private ClipboardTransitionExecutor mClipboardTransitionExecutor; + @Mock private UiEventLogger mUiEventLogger; private FakeDisplayTracker mDisplayTracker = new FakeDisplayTracker(mContext); private FakeFeatureFlags mFeatureFlags = new FakeFeatureFlags(); @Mock private Animator mAnimator; + private ArgumentCaptor<Animator.AnimatorListener> mAnimatorListenerCaptor = + ArgumentCaptor.forClass(Animator.AnimatorListener.class); private ClipData mSampleClipData; @@ -117,6 +122,7 @@ public class ClipboardOverlayControllerTest extends SysuiTestCase { when(mClipboardOverlayView.getEnterAnimation()).thenReturn(mAnimator); when(mClipboardOverlayView.getExitAnimation()).thenReturn(mAnimator); + when(mClipboardOverlayView.getFadeOutAnimation()).thenReturn(mAnimator); when(mClipboardOverlayWindow.getWindowInsets()).thenReturn( getImeInsets(new Rect(0, 0, 0, 0))); @@ -124,7 +130,16 @@ public class ClipboardOverlayControllerTest extends SysuiTestCase { new ClipData.Item("Test Item")); mFeatureFlags.set(CLIPBOARD_IMAGE_TIMEOUT, true); // turned off for legacy tests + mFeatureFlags.set(CLIPBOARD_SHARED_TRANSITIONS, true); // turned off for old tests + } + /** + * Needs to be done after setting flags for legacy tests, since the value of + * CLIPBOARD_SHARED_TRANSITIONS is checked during construction. This can be moved back into + * the setup method once CLIPBOARD_SHARED_TRANSITIONS is fully released and the tests where it + * is false are removed.[ + */ + private void initController() { mOverlayController = new ClipboardOverlayController( mContext, mClipboardOverlayView, @@ -136,6 +151,7 @@ public class ClipboardOverlayControllerTest extends SysuiTestCase { mClipboardUtils, mExecutor, mClipboardImageLoader, + mClipboardTransitionExecutor, mUiEventLogger); verify(mClipboardOverlayView).setCallbacks(mOverlayCallbacksCaptor.capture()); mCallbacks = mOverlayCallbacksCaptor.getValue(); @@ -148,6 +164,8 @@ public class ClipboardOverlayControllerTest extends SysuiTestCase { @Test public void test_setClipData_invalidImageData_legacy() { + initController(); + ClipData clipData = new ClipData("", new String[]{"image/png"}, new ClipData.Item(Uri.parse(""))); mFeatureFlags.set(CLIPBOARD_IMAGE_TIMEOUT, false); @@ -161,6 +179,8 @@ public class ClipboardOverlayControllerTest extends SysuiTestCase { @Test public void test_setClipData_nonImageUri_legacy() { + initController(); + ClipData clipData = new ClipData("", new String[]{"resource/png"}, new ClipData.Item(Uri.parse(""))); mFeatureFlags.set(CLIPBOARD_IMAGE_TIMEOUT, false); @@ -175,6 +195,8 @@ public class ClipboardOverlayControllerTest extends SysuiTestCase { @Test public void test_setClipData_textData_legacy() { mFeatureFlags.set(CLIPBOARD_IMAGE_TIMEOUT, false); + initController(); + mOverlayController.setClipData(mSampleClipData, "abc"); verify(mClipboardOverlayView, times(1)).showTextPreview("Test Item", false); @@ -186,6 +208,8 @@ public class ClipboardOverlayControllerTest extends SysuiTestCase { @Test public void test_setClipData_sensitiveTextData_legacy() { mFeatureFlags.set(CLIPBOARD_IMAGE_TIMEOUT, false); + initController(); + ClipDescription description = mSampleClipData.getDescription(); PersistableBundle b = new PersistableBundle(); b.putBoolean(ClipDescription.EXTRA_IS_SENSITIVE, true); @@ -202,6 +226,7 @@ public class ClipboardOverlayControllerTest extends SysuiTestCase { public void test_setClipData_repeatedCalls_legacy() { when(mAnimator.isRunning()).thenReturn(true); mFeatureFlags.set(CLIPBOARD_IMAGE_TIMEOUT, false); + initController(); mOverlayController.setClipData(mSampleClipData, ""); mOverlayController.setClipData(mSampleClipData, ""); @@ -211,6 +236,8 @@ public class ClipboardOverlayControllerTest extends SysuiTestCase { @Test public void test_setClipData_invalidImageData() { + initController(); + ClipData clipData = new ClipData("", new String[]{"image/png"}, new ClipData.Item(Uri.parse(""))); @@ -223,6 +250,7 @@ public class ClipboardOverlayControllerTest extends SysuiTestCase { @Test public void test_setClipData_nonImageUri() { + initController(); ClipData clipData = new ClipData("", new String[]{"resource/png"}, new ClipData.Item(Uri.parse(""))); @@ -235,6 +263,7 @@ public class ClipboardOverlayControllerTest extends SysuiTestCase { @Test public void test_setClipData_textData() { + initController(); mOverlayController.setClipData(mSampleClipData, "abc"); verify(mClipboardOverlayView, times(1)).showTextPreview("Test Item", false); @@ -245,6 +274,7 @@ public class ClipboardOverlayControllerTest extends SysuiTestCase { @Test public void test_setClipData_sensitiveTextData() { + initController(); ClipDescription description = mSampleClipData.getDescription(); PersistableBundle b = new PersistableBundle(); b.putBoolean(ClipDescription.EXTRA_IS_SENSITIVE, true); @@ -259,6 +289,7 @@ public class ClipboardOverlayControllerTest extends SysuiTestCase { @Test public void test_setClipData_repeatedCalls() { + initController(); when(mAnimator.isRunning()).thenReturn(true); mOverlayController.setClipData(mSampleClipData, ""); @@ -268,7 +299,9 @@ public class ClipboardOverlayControllerTest extends SysuiTestCase { } @Test - public void test_viewCallbacks_onShareTapped() { + public void test_viewCallbacks_onShareTapped_sharedTransitionsOff() { + mFeatureFlags.set(CLIPBOARD_SHARED_TRANSITIONS, false); + initController(); mOverlayController.setClipData(mSampleClipData, ""); mCallbacks.onShareButtonTapped(); @@ -278,7 +311,22 @@ public class ClipboardOverlayControllerTest extends SysuiTestCase { } @Test - public void test_viewCallbacks_onDismissTapped() { + public void test_viewCallbacks_onShareTapped() { + initController(); + mOverlayController.setClipData(mSampleClipData, ""); + + mCallbacks.onShareButtonTapped(); + verify(mAnimator).addListener(mAnimatorListenerCaptor.capture()); + mAnimatorListenerCaptor.getValue().onAnimationEnd(mAnimator); + + verify(mUiEventLogger, times(1)).log(CLIPBOARD_OVERLAY_SHARE_TAPPED, 0, ""); + verify(mClipboardOverlayView, times(1)).getFadeOutAnimation(); + } + + @Test + public void test_viewCallbacks_onDismissTapped_sharedTransitionsOff() { + mFeatureFlags.set(CLIPBOARD_SHARED_TRANSITIONS, false); + initController(); mOverlayController.setClipData(mSampleClipData, ""); mCallbacks.onDismissButtonTapped(); @@ -288,7 +336,35 @@ public class ClipboardOverlayControllerTest extends SysuiTestCase { } @Test + public void test_viewCallbacks_onDismissTapped() { + initController(); + + mCallbacks.onDismissButtonTapped(); + verify(mAnimator).addListener(mAnimatorListenerCaptor.capture()); + mAnimatorListenerCaptor.getValue().onAnimationEnd(mAnimator); + + // package name is null since we haven't actually set a source for this test + verify(mUiEventLogger, times(1)).log(CLIPBOARD_OVERLAY_DISMISS_TAPPED, 0, null); + verify(mClipboardOverlayView, times(1)).getExitAnimation(); + } + + @Test + public void test_multipleDismissals_dismissesOnce_sharedTransitionsOff() { + mFeatureFlags.set(CLIPBOARD_SHARED_TRANSITIONS, false); + initController(); + mCallbacks.onSwipeDismissInitiated(mAnimator); + mCallbacks.onDismissButtonTapped(); + mCallbacks.onSwipeDismissInitiated(mAnimator); + mCallbacks.onDismissButtonTapped(); + + verify(mUiEventLogger, times(1)).log(CLIPBOARD_OVERLAY_SWIPE_DISMISSED, 0, null); + verify(mUiEventLogger, never()).log(CLIPBOARD_OVERLAY_DISMISS_TAPPED); + } + + @Test public void test_multipleDismissals_dismissesOnce() { + initController(); + mCallbacks.onSwipeDismissInitiated(mAnimator); mCallbacks.onDismissButtonTapped(); mCallbacks.onSwipeDismissInitiated(mAnimator); @@ -300,6 +376,7 @@ public class ClipboardOverlayControllerTest extends SysuiTestCase { @Test public void test_remoteCopy_withFlagOn() { + initController(); when(mClipboardUtils.isRemoteCopy(any(), any(), any())).thenReturn(true); mOverlayController.setClipData(mSampleClipData, ""); @@ -309,6 +386,7 @@ public class ClipboardOverlayControllerTest extends SysuiTestCase { @Test public void test_nonRemoteCopy() { + initController(); when(mClipboardUtils.isRemoteCopy(any(), any(), any())).thenReturn(false); mOverlayController.setClipData(mSampleClipData, ""); @@ -318,13 +396,16 @@ public class ClipboardOverlayControllerTest extends SysuiTestCase { @Test public void test_logsUseLastClipSource() { + initController(); + mOverlayController.setClipData(mSampleClipData, "first.package"); - mCallbacks.onDismissButtonTapped(); + mCallbacks.onShareButtonTapped(); + mOverlayController.setClipData(mSampleClipData, "second.package"); - mCallbacks.onDismissButtonTapped(); + mCallbacks.onShareButtonTapped(); - verify(mUiEventLogger).log(CLIPBOARD_OVERLAY_DISMISS_TAPPED, 0, "first.package"); - verify(mUiEventLogger).log(CLIPBOARD_OVERLAY_DISMISS_TAPPED, 0, "second.package"); + verify(mUiEventLogger).log(CLIPBOARD_OVERLAY_SHARE_TAPPED, 0, "first.package"); + verify(mUiEventLogger).log(CLIPBOARD_OVERLAY_SHARE_TAPPED, 0, "second.package"); verify(mUiEventLogger).log(CLIPBOARD_OVERLAY_SHOWN_EXPANDED, 0, "first.package"); verify(mUiEventLogger).log(CLIPBOARD_OVERLAY_SHOWN_EXPANDED, 0, "second.package"); verifyNoMoreInteractions(mUiEventLogger); @@ -332,6 +413,7 @@ public class ClipboardOverlayControllerTest extends SysuiTestCase { @Test public void test_logOnClipboardActionsShown() { + initController(); ClipData.Item item = mSampleClipData.getItemAt(0); item.setTextLinks(Mockito.mock(TextLinks.class)); when(mClipboardUtils.isRemoteCopy(any(Context.class), any(ClipData.class), anyString())) @@ -357,6 +439,7 @@ public class ClipboardOverlayControllerTest extends SysuiTestCase { @Test public void test_noInsets_showsExpanded() { + initController(); mOverlayController.setClipData(mSampleClipData, ""); verify(mClipboardOverlayView, never()).setMinimized(true); @@ -366,6 +449,7 @@ public class ClipboardOverlayControllerTest extends SysuiTestCase { @Test public void test_insets_showsMinimized() { + initController(); when(mClipboardOverlayWindow.getWindowInsets()).thenReturn( getImeInsets(new Rect(0, 0, 0, 1))); mOverlayController.setClipData(mSampleClipData, "abc"); @@ -389,6 +473,7 @@ public class ClipboardOverlayControllerTest extends SysuiTestCase { @Test public void test_insetsChanged_minimizes() { + initController(); mOverlayController.setClipData(mSampleClipData, ""); verify(mClipboardOverlayView, never()).setMinimized(true); diff --git a/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationCollectionLiveDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationCollectionLiveDataTest.java index ca6282c66a17..461ec653d819 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationCollectionLiveDataTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/complication/ComplicationCollectionLiveDataTest.java @@ -28,6 +28,7 @@ import androidx.lifecycle.Observer; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; +import com.android.systemui.dreams.DreamLogger; import com.android.systemui.dreams.DreamOverlayStateController; import com.android.systemui.flags.FakeFeatureFlags; import com.android.systemui.flags.Flags; @@ -56,6 +57,8 @@ public class ComplicationCollectionLiveDataTest extends SysuiTestCase { private FakeFeatureFlags mFeatureFlags; @Mock private Observer mObserver; + @Mock + private DreamLogger mLogger; @Before public void setUp() { @@ -66,7 +69,8 @@ public class ComplicationCollectionLiveDataTest extends SysuiTestCase { mStateController = new DreamOverlayStateController( mExecutor, /* overlayEnabled= */ true, - mFeatureFlags); + mFeatureFlags, + mLogger); mLiveData = new ComplicationCollectionLiveData(mStateController); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsBindingControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsBindingControllerImplTest.kt index 26cbd7703075..724c9d1dfc42 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsBindingControllerImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsBindingControllerImplTest.kt @@ -116,11 +116,7 @@ class ControlsBindingControllerImplTest : SysuiTestCase() { @Test fun testBindAndLoad_cancel() { - val callback = object : ControlsBindingController.LoadCallback { - override fun error(message: String) {} - - override fun accept(t: List<Control>) {} - } + val callback = mock(ControlsBindingController.LoadCallback::class.java) val subscription = mock(IControlsSubscription::class.java) val canceller = controller.bindAndLoad(TEST_COMPONENT_NAME_1, callback) @@ -130,6 +126,7 @@ class ControlsBindingControllerImplTest : SysuiTestCase() { canceller.run() verify(providers[0]).cancelSubscription(subscription) + verify(callback).error(any()) } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerDecorProviderFactoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerDecorProviderFactoryTest.kt index fcc358982e6c..8f0b19307000 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerDecorProviderFactoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerDecorProviderFactoryTest.kt @@ -39,7 +39,7 @@ class RoundedCornerDecorProviderFactoryTest : SysuiTestCase() { @Before fun setUp() { - roundedCornerResDelegate = spy(RoundedCornerResDelegate(mContext.resources, null)) + roundedCornerResDelegate = spy(RoundedCornerResDelegateImpl(mContext.resources, null)) } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerResDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerResDelegateTest.kt index 93a1868b72f5..4feba7bfd359 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerResDelegateTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerResDelegateTest.kt @@ -48,7 +48,7 @@ class RoundedCornerResDelegateTest : SysuiTestCase() { @Test fun testTopAndBottomRoundedCornerExist() { setupResources(radius = 5) - roundedCornerResDelegate = RoundedCornerResDelegate(mContext.resources, null) + roundedCornerResDelegate = RoundedCornerResDelegateImpl(mContext.resources, null) assertEquals(true, roundedCornerResDelegate.hasTop) assertEquals(true, roundedCornerResDelegate.hasBottom) } @@ -56,7 +56,7 @@ class RoundedCornerResDelegateTest : SysuiTestCase() { @Test fun testTopRoundedCornerExist() { setupResources(radiusTop = 10) - roundedCornerResDelegate = RoundedCornerResDelegate(mContext.resources, null) + roundedCornerResDelegate = RoundedCornerResDelegateImpl(mContext.resources, null) assertEquals(true, roundedCornerResDelegate.hasTop) assertEquals(false, roundedCornerResDelegate.hasBottom) } @@ -64,7 +64,7 @@ class RoundedCornerResDelegateTest : SysuiTestCase() { @Test fun testBottomRoundedCornerExist() { setupResources(radiusBottom = 15) - roundedCornerResDelegate = RoundedCornerResDelegate(mContext.resources, null) + roundedCornerResDelegate = RoundedCornerResDelegateImpl(mContext.resources, null) assertEquals(false, roundedCornerResDelegate.hasTop) assertEquals(true, roundedCornerResDelegate.hasBottom) } @@ -75,7 +75,7 @@ class RoundedCornerResDelegateTest : SysuiTestCase() { roundedTopDrawable = getTestsDrawable(R.drawable.rounded3px), roundedBottomDrawable = getTestsDrawable(R.drawable.rounded4px)) - roundedCornerResDelegate = RoundedCornerResDelegate(mContext.resources, null) + roundedCornerResDelegate = RoundedCornerResDelegateImpl(mContext.resources, null) assertEquals(Size(3, 3), roundedCornerResDelegate.topRoundedSize) assertEquals(Size(4, 4), roundedCornerResDelegate.bottomRoundedSize) @@ -96,7 +96,7 @@ class RoundedCornerResDelegateTest : SysuiTestCase() { roundedTopDrawable = getTestsDrawable(R.drawable.rounded3px), roundedBottomDrawable = getTestsDrawable(R.drawable.rounded4px)) - roundedCornerResDelegate = RoundedCornerResDelegate(mContext.resources, null) + roundedCornerResDelegate = RoundedCornerResDelegateImpl(mContext.resources, null) assertEquals(Size(3, 3), roundedCornerResDelegate.topRoundedSize) assertEquals(Size(4, 4), roundedCornerResDelegate.bottomRoundedSize) @@ -109,33 +109,12 @@ class RoundedCornerResDelegateTest : SysuiTestCase() { } @Test - fun testUpdateTuningSizeFactor() { - setupResources(radius = 100, - roundedTopDrawable = getTestsDrawable(R.drawable.rounded3px), - roundedBottomDrawable = getTestsDrawable(R.drawable.rounded4px)) - - roundedCornerResDelegate = RoundedCornerResDelegate(mContext.resources, null) - - val factor = 5 - roundedCornerResDelegate.tuningSizeFactor = factor - val length = (factor * mContext.resources.displayMetrics.density).toInt() - - assertEquals(Size(length, length), roundedCornerResDelegate.topRoundedSize) - assertEquals(Size(length, length), roundedCornerResDelegate.bottomRoundedSize) - - roundedCornerResDelegate.tuningSizeFactor = null - - assertEquals(Size(3, 3), roundedCornerResDelegate.topRoundedSize) - assertEquals(Size(4, 4), roundedCornerResDelegate.bottomRoundedSize) - } - - @Test fun testPhysicalPixelDisplaySizeChanged() { setupResources( roundedTopDrawable = getTestsDrawable(R.drawable.rounded4px), roundedBottomDrawable = getTestsDrawable(R.drawable.rounded4px)) - roundedCornerResDelegate = RoundedCornerResDelegate(mContext.resources, null) + roundedCornerResDelegate = RoundedCornerResDelegateImpl(mContext.resources, null) assertEquals(Size(4, 4), roundedCornerResDelegate.topRoundedSize) assertEquals(Size(4, 4), roundedCornerResDelegate.bottomRoundedSize) diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java index 47b7d49ac7af..8786520ccf0e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java @@ -41,8 +41,8 @@ import com.android.keyguard.BouncerPanelExpansionCalculator; import com.android.systemui.SysuiTestCase; import com.android.systemui.complication.ComplicationHostViewController; import com.android.systemui.dreams.touch.scrim.BouncerlessScrimController; -import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerCallbackInteractor; -import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerCallbackInteractor.PrimaryBouncerExpansionCallback; +import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInteractor; +import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInteractor.PrimaryBouncerExpansionCallback; import com.android.systemui.statusbar.BlurUtils; import org.junit.Before; diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java index d0d348d53405..d99f0da494fd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java @@ -16,6 +16,8 @@ package com.android.systemui.dreams; +import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS; + import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; @@ -507,4 +509,23 @@ public class DreamOverlayServiceTest extends SysuiTestCase { mService.onWakeUp(); verify(mDreamOverlayContainerViewController, never()).wakeUp(); } + + @Test + public void testSystemFlagShowForAllUsersSetOnWindow() throws RemoteException { + final IDreamOverlayClient client = getClient(); + + // Inform the overlay service of dream starting. Do not show dream complications. + client.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT, + false /*shouldShowComplication*/); + mMainExecutor.runAllReady(); + + final ArgumentCaptor<WindowManager.LayoutParams> paramsCaptor = + ArgumentCaptor.forClass(WindowManager.LayoutParams.class); + + // Verify that a new window is added. + verify(mWindowManager).addView(any(), paramsCaptor.capture()); + + assertThat((paramsCaptor.getValue().privateFlags & SYSTEM_FLAG_SHOW_FOR_ALL_USERS) + == SYSTEM_FLAG_SHOW_FOR_ALL_USERS).isTrue(); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java index 7b4160551c82..2c1ebe4121af 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java @@ -58,6 +58,9 @@ public class DreamOverlayStateControllerTest extends SysuiTestCase { @Mock private FeatureFlags mFeatureFlags; + @Mock + private DreamLogger mLogger; + final FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock()); @Before @@ -405,6 +408,6 @@ public class DreamOverlayStateControllerTest extends SysuiTestCase { } private DreamOverlayStateController getDreamOverlayStateController(boolean overlayEnabled) { - return new DreamOverlayStateController(mExecutor, overlayEnabled, mFeatureFlags); + return new DreamOverlayStateController(mExecutor, overlayEnabled, mFeatureFlags, mLogger); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java index d16b7570e2c3..5dc0e55632fd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java @@ -113,6 +113,8 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase { DreamOverlayStateController mDreamOverlayStateController; @Mock UserTracker mUserTracker; + @Mock + DreamLogger mLogger; @Captor private ArgumentCaptor<DreamOverlayStateController.Callback> mCallbackCaptor; @@ -146,7 +148,8 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase { mStatusBarWindowStateController, mDreamOverlayStatusBarItemsProvider, mDreamOverlayStateController, - mUserTracker); + mUserTracker, + mLogger); } @Test @@ -289,7 +292,8 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase { mStatusBarWindowStateController, mDreamOverlayStatusBarItemsProvider, mDreamOverlayStateController, - mUserTracker); + mUserTracker, + mLogger); controller.onViewAttached(); verify(mView, never()).showIcon( eq(DreamOverlayStatusBarView.STATUS_ICON_NOTIFICATIONS), eq(true), any()); diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java index 1a89076741ef..3f9b198ee17d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java @@ -28,6 +28,7 @@ import static org.mockito.Mockito.when; import android.animation.AnimatorListenerAdapter; import android.animation.ValueAnimator; +import android.content.pm.UserInfo; import android.graphics.Rect; import android.graphics.Region; import android.testing.AndroidTestingRunner; @@ -39,10 +40,12 @@ import android.view.VelocityTracker; import androidx.test.filters.SmallTest; import com.android.internal.logging.UiEventLogger; +import com.android.internal.widget.LockPatternUtils; import com.android.systemui.SysuiTestCase; +import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants; import com.android.systemui.dreams.touch.scrim.ScrimController; import com.android.systemui.dreams.touch.scrim.ScrimManager; -import com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants; +import com.android.systemui.settings.FakeUserTracker; import com.android.systemui.shade.ShadeExpansionChangeEvent; import com.android.systemui.shared.system.InputChannelCompat; import com.android.systemui.statusbar.NotificationShadeWindowController; @@ -57,6 +60,7 @@ import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; +import java.util.Collections; import java.util.Optional; @SmallTest @@ -100,21 +104,34 @@ public class BouncerSwipeTouchHandlerTest extends SysuiTestCase { @Mock UiEventLogger mUiEventLogger; + @Mock + LockPatternUtils mLockPatternUtils; + + FakeUserTracker mUserTracker; + private static final float TOUCH_REGION = .3f; private static final int SCREEN_WIDTH_PX = 1024; private static final int SCREEN_HEIGHT_PX = 100; private static final Rect SCREEN_BOUNDS = new Rect(0, 0, 1024, 100); + private static final UserInfo CURRENT_USER_INFO = new UserInfo( + 10, + /* name= */ "user10", + /* flags= */ 0 + ); @Before public void setup() { MockitoAnnotations.initMocks(this); + mUserTracker = new FakeUserTracker(); mTouchHandler = new BouncerSwipeTouchHandler( mScrimManager, Optional.of(mCentralSurfaces), mNotificationShadeWindowController, mValueAnimatorCreator, mVelocityTrackerFactory, + mLockPatternUtils, + mUserTracker, mFlingAnimationUtils, mFlingAnimationUtilsClosing, TOUCH_REGION, @@ -126,6 +143,9 @@ public class BouncerSwipeTouchHandlerTest extends SysuiTestCase { when(mVelocityTrackerFactory.obtain()).thenReturn(mVelocityTracker); when(mFlingAnimationUtils.getMinVelocityPxPerSecond()).thenReturn(Float.MAX_VALUE); when(mTouchSession.getBounds()).thenReturn(SCREEN_BOUNDS); + when(mLockPatternUtils.isSecure(CURRENT_USER_INFO.id)).thenReturn(true); + + mUserTracker.set(Collections.singletonList(CURRENT_USER_INFO), 0); } /** @@ -265,6 +285,32 @@ public class BouncerSwipeTouchHandlerTest extends SysuiTestCase { verifyScroll(.7f, Direction.DOWN, true, gestureListener); } + /** + * Makes sure the expansion amount is proportional to (1 - scroll). + */ + @Test + public void testSwipeUp_keyguardNotSecure_doesNotExpand() { + when(mLockPatternUtils.isSecure(CURRENT_USER_INFO.id)).thenReturn(false); + mTouchHandler.onSessionStart(mTouchSession); + ArgumentCaptor<GestureDetector.OnGestureListener> gestureListenerCaptor = + ArgumentCaptor.forClass(GestureDetector.OnGestureListener.class); + verify(mTouchSession).registerGestureListener(gestureListenerCaptor.capture()); + + final OnGestureListener gestureListener = gestureListenerCaptor.getValue(); + + final float distanceY = SCREEN_HEIGHT_PX * 0.3f; + final MotionEvent event1 = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, + 0, SCREEN_HEIGHT_PX, 0); + final MotionEvent event2 = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, + 0, SCREEN_HEIGHT_PX - distanceY, 0); + + reset(mScrimController); + assertThat(gestureListener.onScroll(event1, event2, 0, distanceY)) + .isTrue(); + // We should not expand since the keyguard is not secure + verify(mScrimController, never()).expand(any()); + } + private void verifyScroll(float percent, Direction direction, boolean isBouncerInitiallyShowing, GestureDetector.OnGestureListener gestureListener) { final float distanceY = SCREEN_HEIGHT_PX * percent; diff --git a/packages/SystemUI/tests/src/com/android/systemui/dump/DumpHandlerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/dump/DumpHandlerTest.kt index e8cbdf3db327..2830476874ed 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/dump/DumpHandlerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/dump/DumpHandlerTest.kt @@ -22,7 +22,7 @@ import com.android.systemui.Dumpable import com.android.systemui.ProtoDumpable import com.android.systemui.SysuiTestCase import com.android.systemui.log.LogBuffer -import com.android.systemui.shared.system.UncaughtExceptionPreHandlerManager +import com.android.systemui.log.table.TableLogBuffer import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.eq import com.google.common.truth.Truth.assertThat @@ -45,8 +45,6 @@ class DumpHandlerTest : SysuiTestCase() { @Mock private lateinit var logBufferEulogizer: LogBufferEulogizer - @Mock - private lateinit var exceptionHandlerManager: UncaughtExceptionPreHandlerManager @Mock private lateinit var pw: PrintWriter @@ -70,6 +68,11 @@ class DumpHandlerTest : SysuiTestCase() { @Mock private lateinit var buffer2: LogBuffer + @Mock + private lateinit var table1: TableLogBuffer + @Mock + private lateinit var table2: TableLogBuffer + private val dumpManager = DumpManager() @Before @@ -83,21 +86,22 @@ class DumpHandlerTest : SysuiTestCase() { mutableMapOf( EmptyCoreStartable::class.java to Provider { EmptyCoreStartable() } ), - exceptionHandlerManager ) } @Test fun testDumpablesCanBeDumpedSelectively() { // GIVEN a variety of registered dumpables and buffers - dumpManager.registerDumpable("dumpable1", dumpable1) - dumpManager.registerDumpable("dumpable2", dumpable2) - dumpManager.registerDumpable("dumpable3", dumpable3) + dumpManager.registerCriticalDumpable("dumpable1", dumpable1) + dumpManager.registerCriticalDumpable("dumpable2", dumpable2) + dumpManager.registerCriticalDumpable("dumpable3", dumpable3) dumpManager.registerBuffer("buffer1", buffer1) dumpManager.registerBuffer("buffer2", buffer2) + dumpManager.registerTableLogBuffer("table1", table1) + dumpManager.registerTableLogBuffer("table2", table2) // WHEN some of them are dumped explicitly - val args = arrayOf("dumpable1", "dumpable3", "buffer2") + val args = arrayOf("dumpable1", "dumpable3", "buffer2", "table2") dumpHandler.dump(fd, pw, args) // THEN only the requested ones have their dump() method called @@ -108,12 +112,14 @@ class DumpHandlerTest : SysuiTestCase() { verify(dumpable3).dump(pw, args) verify(buffer1, never()).dump(any(PrintWriter::class.java), anyInt()) verify(buffer2).dump(pw, 0) + verify(table1, never()).dump(any(), any()) + verify(table2).dump(pw, args) } @Test fun testDumpableMatchingIsBasedOnEndOfTag() { // GIVEN a dumpable registered to the manager - dumpManager.registerDumpable("com.android.foo.bar.dumpable1", dumpable1) + dumpManager.registerCriticalDumpable("com.android.foo.bar.dumpable1", dumpable1) // WHEN that module is dumped val args = arrayOf("dumpable1") @@ -131,6 +137,8 @@ class DumpHandlerTest : SysuiTestCase() { dumpManager.registerNormalDumpable("dumpable3", dumpable3) dumpManager.registerBuffer("buffer1", buffer1) dumpManager.registerBuffer("buffer2", buffer2) + dumpManager.registerTableLogBuffer("table1", table1) + dumpManager.registerTableLogBuffer("table2", table2) // WHEN a critical dump is requested val args = arrayOf("--dump-priority", "CRITICAL") @@ -144,6 +152,8 @@ class DumpHandlerTest : SysuiTestCase() { any(Array<String>::class.java)) verify(buffer1, never()).dump(any(PrintWriter::class.java), anyInt()) verify(buffer2, never()).dump(any(PrintWriter::class.java), anyInt()) + verify(table1, never()).dump(any(), any()) + verify(table2, never()).dump(any(), any()) } @Test @@ -154,6 +164,8 @@ class DumpHandlerTest : SysuiTestCase() { dumpManager.registerNormalDumpable("dumpable3", dumpable3) dumpManager.registerBuffer("buffer1", buffer1) dumpManager.registerBuffer("buffer2", buffer2) + dumpManager.registerTableLogBuffer("table1", table1) + dumpManager.registerTableLogBuffer("table2", table2) // WHEN a normal dump is requested val args = arrayOf("--dump-priority", "NORMAL") @@ -169,6 +181,8 @@ class DumpHandlerTest : SysuiTestCase() { verify(dumpable3).dump(pw, args) verify(buffer1).dump(pw, 0) verify(buffer2).dump(pw, 0) + verify(table1).dump(pw, args) + verify(table2).dump(pw, args) } @Test @@ -184,6 +198,81 @@ class DumpHandlerTest : SysuiTestCase() { } @Test + fun testDumpBuffers() { + // GIVEN a variety of registered dumpables and buffers and tables + dumpManager.registerCriticalDumpable("dumpable1", dumpable1) + dumpManager.registerCriticalDumpable("dumpable2", dumpable2) + dumpManager.registerNormalDumpable("dumpable3", dumpable3) + dumpManager.registerBuffer("buffer1", buffer1) + dumpManager.registerBuffer("buffer2", buffer2) + dumpManager.registerTableLogBuffer("table1", table1) + dumpManager.registerTableLogBuffer("table2", table2) + + // WHEN a buffer dump is requested + val args = arrayOf("buffers", "--tail", "1") + dumpHandler.dump(fd, pw, args) + + // THEN all buffers are dumped (and no dumpables or tables) + verify(dumpable1, never()).dump(any(), any()) + verify(dumpable2, never()).dump(any(), any()) + verify(dumpable3, never()).dump(any(), any()) + verify(buffer1).dump(pw, tailLength = 1) + verify(buffer2).dump(pw, tailLength = 1) + verify(table1, never()).dump(any(), any()) + verify(table2, never()).dump(any(), any()) + } + + @Test + fun testDumpDumpables() { + // GIVEN a variety of registered dumpables and buffers and tables + dumpManager.registerCriticalDumpable("dumpable1", dumpable1) + dumpManager.registerCriticalDumpable("dumpable2", dumpable2) + dumpManager.registerNormalDumpable("dumpable3", dumpable3) + dumpManager.registerBuffer("buffer1", buffer1) + dumpManager.registerBuffer("buffer2", buffer2) + dumpManager.registerTableLogBuffer("table1", table1) + dumpManager.registerTableLogBuffer("table2", table2) + + // WHEN a dumpable dump is requested + val args = arrayOf("dumpables") + dumpHandler.dump(fd, pw, args) + + // THEN all dumpables are dumped (both critical and normal) (and no dumpables) + verify(dumpable1).dump(pw, args) + verify(dumpable2).dump(pw, args) + verify(dumpable3).dump(pw, args) + verify(buffer1, never()).dump(any(), anyInt()) + verify(buffer2, never()).dump(any(), anyInt()) + verify(table1, never()).dump(any(), any()) + verify(table2, never()).dump(any(), any()) + } + + @Test + fun testDumpTables() { + // GIVEN a variety of registered dumpables and buffers and tables + dumpManager.registerCriticalDumpable("dumpable1", dumpable1) + dumpManager.registerCriticalDumpable("dumpable2", dumpable2) + dumpManager.registerNormalDumpable("dumpable3", dumpable3) + dumpManager.registerBuffer("buffer1", buffer1) + dumpManager.registerBuffer("buffer2", buffer2) + dumpManager.registerTableLogBuffer("table1", table1) + dumpManager.registerTableLogBuffer("table2", table2) + + // WHEN a dumpable dump is requested + val args = arrayOf("tables") + dumpHandler.dump(fd, pw, args) + + // THEN all dumpables are dumped (both critical and normal) (and no dumpables) + verify(dumpable1, never()).dump(any(), any()) + verify(dumpable2, never()).dump(any(), any()) + verify(dumpable3, never()).dump(any(), any()) + verify(buffer1, never()).dump(any(), anyInt()) + verify(buffer2, never()).dump(any(), anyInt()) + verify(table1).dump(pw, args) + verify(table2).dump(pw, args) + } + + @Test fun testDumpAllProtoDumpables() { dumpManager.registerDumpable("protoDumpable1", protoDumpable1) dumpManager.registerDumpable("protoDumpable2", protoDumpable2) @@ -207,6 +296,123 @@ class DumpHandlerTest : SysuiTestCase() { verify(protoDumpable2, never()).dumpProto(any(), any()) } + @Test + fun testDumpTarget_selectsShortestNamedDumpable() { + // GIVEN a variety of registered dumpables and buffers + dumpManager.registerCriticalDumpable("first-dumpable", dumpable1) + dumpManager.registerCriticalDumpable("scnd-dumpable", dumpable2) + dumpManager.registerCriticalDumpable("third-dumpable", dumpable3) + + // WHEN a dumpable is dumped by a suffix that matches multiple options + val args = arrayOf("dumpable") + dumpHandler.dump(fd, pw, args) + + // THEN the matching dumpable with the shorter name is dumped + verify(dumpable1, never()).dump(any(), any()) + verify(dumpable2).dump(pw, args) + verify(dumpable3, never()).dump(any(), any()) + } + + @Test + fun testDumpTarget_selectsShortestNamedBuffer() { + // GIVEN a variety of registered dumpables and buffers + dumpManager.registerBuffer("first-buffer", buffer1) + dumpManager.registerBuffer("scnd-buffer", buffer2) + + // WHEN a dumpable is dumped by a suffix that matches multiple options + val args = arrayOf("buffer", "--tail", "14") + dumpHandler.dump(fd, pw, args) + + // THEN the matching buffer with the shorter name is dumped + verify(buffer1, never()).dump(any(), anyInt()) + verify(buffer2).dump(pw, tailLength = 14) + } + + @Test + fun testDumpTarget_selectsShortestNamedMatch_dumpable() { + // GIVEN a variety of registered dumpables and buffers + dumpManager.registerCriticalDumpable("dumpable1", dumpable1) + dumpManager.registerCriticalDumpable("dumpable2", dumpable2) + dumpManager.registerCriticalDumpable("dumpable3", dumpable3) + dumpManager.registerBuffer("big-buffer1", buffer1) + dumpManager.registerBuffer("big-buffer2", buffer2) + + // WHEN a dumpable is dumped by a suffix that matches multiple options + val args = arrayOf("2") + dumpHandler.dump(fd, pw, args) + + // THEN the matching dumpable with the shorter name is dumped + verify(dumpable1, never()).dump(any(), any()) + verify(dumpable2).dump(pw, args) + verify(dumpable3, never()).dump(any(), any()) + verify(buffer1, never()).dump(any(), anyInt()) + verify(buffer2, never()).dump(any(), anyInt()) + } + + @Test + fun testDumpTarget_selectsShortestNamedMatch_buffer() { + // GIVEN a variety of registered dumpables and buffers + dumpManager.registerCriticalDumpable("dumpable1", dumpable1) + dumpManager.registerCriticalDumpable("dumpable2", dumpable2) + dumpManager.registerCriticalDumpable("dumpable3", dumpable3) + dumpManager.registerBuffer("buffer1", buffer1) + dumpManager.registerBuffer("buffer2", buffer2) + + // WHEN a dumpable is dumped by a suffix that matches multiple options + val args = arrayOf("2", "--tail", "14") + dumpHandler.dump(fd, pw, args) + + // THEN the matching buffer with the shorter name is dumped + verify(dumpable1, never()).dump(any(), any()) + verify(dumpable2, never()).dump(any(), any()) + verify(dumpable3, never()).dump(any(), any()) + verify(buffer1, never()).dump(any(), anyInt()) + verify(buffer2).dump(pw, tailLength = 14) + } + + @Test + fun testDumpTarget_selectsTheAlphabeticallyFirstShortestMatch_dumpable() { + // GIVEN a variety of registered dumpables and buffers + dumpManager.registerCriticalDumpable("d1x", dumpable1) + dumpManager.registerCriticalDumpable("d2x", dumpable2) + dumpManager.registerCriticalDumpable("a3x", dumpable3) + dumpManager.registerBuffer("ab1x", buffer1) + dumpManager.registerBuffer("b2x", buffer2) + + // WHEN a dumpable is dumped by a suffix that matches multiple options + val args = arrayOf("x") + dumpHandler.dump(fd, pw, args) + + // THEN the alphabetically first dumpable/buffer (of the 3 letter names) is dumped + verify(dumpable1, never()).dump(any(), any()) + verify(dumpable2, never()).dump(any(), any()) + verify(dumpable3).dump(pw, args) + verify(buffer1, never()).dump(any(), anyInt()) + verify(buffer2, never()).dump(any(), anyInt()) + } + + @Test + fun testDumpTarget_selectsTheAlphabeticallyFirstShortestMatch_buffer() { + // GIVEN a variety of registered dumpables and buffers + dumpManager.registerCriticalDumpable("d1x", dumpable1) + dumpManager.registerCriticalDumpable("d2x", dumpable2) + dumpManager.registerCriticalDumpable("az1x", dumpable3) + dumpManager.registerBuffer("b1x", buffer1) + dumpManager.registerBuffer("b2x", buffer2) + + // WHEN a dumpable is dumped by a suffix that matches multiple options + val args = arrayOf("x", "--tail", "14") + dumpHandler.dump(fd, pw, args) + + // THEN the alphabetically first dumpable/buffer (of the 3 letter names) is dumped + verify(dumpable1, never()).dump(any(), any()) + verify(dumpable2, never()).dump(any(), any()) + verify(dumpable3, never()).dump(any(), any()) + verify(buffer1).dump(pw, tailLength = 14) + verify(buffer2, never()).dump(any(), anyInt()) + } + + private class EmptyCoreStartable : CoreStartable { override fun start() {} } diff --git a/packages/SystemUI/tests/src/com/android/systemui/dump/DumpManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/dump/DumpManagerTest.kt index 02555cfa783a..6d5226f35e97 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/dump/DumpManagerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/dump/DumpManagerTest.kt @@ -20,21 +20,17 @@ import androidx.test.filters.SmallTest import com.android.systemui.Dumpable import com.android.systemui.SysuiTestCase import com.android.systemui.log.LogBuffer -import com.android.systemui.util.mockito.any -import java.io.PrintWriter +import com.android.systemui.log.table.TableLogBuffer +import com.google.common.truth.Truth.assertThat +import org.junit.Assert.assertThrows import org.junit.Before import org.junit.Test import org.mockito.Mock -import org.mockito.Mockito.anyInt -import org.mockito.Mockito.never -import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @SmallTest class DumpManagerTest : SysuiTestCase() { - @Mock private lateinit var pw: PrintWriter - @Mock private lateinit var dumpable1: Dumpable @Mock private lateinit var dumpable2: Dumpable @Mock private lateinit var dumpable3: Dumpable @@ -42,6 +38,9 @@ class DumpManagerTest : SysuiTestCase() { @Mock private lateinit var buffer1: LogBuffer @Mock private lateinit var buffer2: LogBuffer + @Mock private lateinit var table1: TableLogBuffer + @Mock private lateinit var table2: TableLogBuffer + private val dumpManager = DumpManager() @Before @@ -50,276 +49,144 @@ class DumpManagerTest : SysuiTestCase() { } @Test - fun testDumpTarget_dumpable() { - // GIVEN a variety of registered dumpables and buffers + fun testRegisterUnregister_dumpables() { + // GIVEN a variety of registered dumpables dumpManager.registerCriticalDumpable("dumpable1", dumpable1) dumpManager.registerCriticalDumpable("dumpable2", dumpable2) - dumpManager.registerCriticalDumpable("dumpable3", dumpable3) - dumpManager.registerBuffer("buffer1", buffer1) - dumpManager.registerBuffer("buffer2", buffer2) + dumpManager.registerNormalDumpable("dumpable3", dumpable3) - // WHEN a dumpable is dumped explicitly - val args = arrayOf<String>() - dumpManager.dumpTarget("dumpable2", pw, args, tailLength = 0) + // WHEN the collection is requested + var dumpables = dumpManager.getDumpables().map { it.dumpable } - // THEN only the requested one has their dump() method called - verify(dumpable1, never()).dump(any(), any()) - verify(dumpable2).dump(pw, args) - verify(dumpable3, never()).dump(any(), any()) - verify(buffer1, never()).dump(any(), anyInt()) - verify(buffer2, never()).dump(any(), anyInt()) - } + // THEN it contains the registered entries + assertThat(dumpables).containsExactly(dumpable1, dumpable2, dumpable3) - @Test - fun testDumpTarget_buffer() { - // GIVEN a variety of registered dumpables and buffers - dumpManager.registerCriticalDumpable("dumpable1", dumpable1) - dumpManager.registerCriticalDumpable("dumpable2", dumpable2) - dumpManager.registerCriticalDumpable("dumpable3", dumpable3) - dumpManager.registerBuffer("buffer1", buffer1) - dumpManager.registerBuffer("buffer2", buffer2) + // WHEN the dumpables are unregistered + dumpManager.unregisterDumpable("dumpable2") + dumpManager.unregisterDumpable("dumpable3") - // WHEN a buffer is dumped explicitly - val args = arrayOf<String>() - dumpManager.dumpTarget("buffer1", pw, args, tailLength = 14) + // WHEN the dumpable collection is requests + dumpables = dumpManager.getDumpables().map { it.dumpable } - // THEN only the requested one has their dump() method called - verify(dumpable1, never()).dump(any(), any()) - verify(dumpable2, never()).dump(any(), any()) - verify(dumpable3, never()).dump(any(), any()) - verify(buffer1).dump(pw, tailLength = 14) - verify(buffer2, never()).dump(any(), anyInt()) + // THEN it contains only the currently-registered entry + assertThat(dumpables).containsExactly(dumpable1) } @Test - fun testDumpableMatchingIsBasedOnEndOfTag() { - // GIVEN a dumpable registered to the manager - dumpManager.registerCriticalDumpable("com.android.foo.bar.dumpable1", dumpable1) + fun testRegister_buffers() { + // GIVEN a set of registered buffers + dumpManager.registerBuffer("buffer1", buffer1) + dumpManager.registerBuffer("buffer2", buffer2) - // WHEN that module is dumped - val args = arrayOf<String>() - dumpManager.dumpTarget("dumpable1", pw, arrayOf(), tailLength = 14) + // WHEN the collection is requested + val dumpables = dumpManager.getLogBuffers().map { it.buffer } - // THEN its dump() method is called - verify(dumpable1).dump(pw, args) + // THEN it contains the registered entries + assertThat(dumpables).containsExactly(buffer1, buffer2) } @Test - fun testDumpTarget_selectsShortestNamedDumpable() { - // GIVEN a variety of registered dumpables and buffers - dumpManager.registerCriticalDumpable("first-dumpable", dumpable1) - dumpManager.registerCriticalDumpable("scnd-dumpable", dumpable2) - dumpManager.registerCriticalDumpable("third-dumpable", dumpable3) - - // WHEN a dumpable is dumped by a suffix that matches multiple options - val args = arrayOf<String>() - dumpManager.dumpTarget("dumpable", pw, args, tailLength = 0) - - // THEN the matching dumpable with the shorter name is dumped - verify(dumpable1, never()).dump(any(), any()) - verify(dumpable2).dump(pw, args) - verify(dumpable3, never()).dump(any(), any()) - } + fun testRegister_tableLogBuffers() { + // GIVEN a set of registered buffers + dumpManager.registerTableLogBuffer("table1", table1) + dumpManager.registerTableLogBuffer("table2", table2) - @Test - fun testDumpTarget_selectsShortestNamedBuffer() { - // GIVEN a variety of registered dumpables and buffers - dumpManager.registerBuffer("first-buffer", buffer1) - dumpManager.registerBuffer("scnd-buffer", buffer2) - - // WHEN a dumpable is dumped by a suffix that matches multiple options - val args = arrayOf<String>() - dumpManager.dumpTarget("buffer", pw, args, tailLength = 14) - - // THEN the matching buffer with the shorter name is dumped - verify(buffer1, never()).dump(any(), anyInt()) - verify(buffer2).dump(pw, tailLength = 14) - } + // WHEN the collection is requested + val tables = dumpManager.getTableLogBuffers().map { it.table } - @Test - fun testDumpTarget_selectsShortestNamedMatch_dumpable() { - // GIVEN a variety of registered dumpables and buffers - dumpManager.registerCriticalDumpable("dumpable1", dumpable1) - dumpManager.registerCriticalDumpable("dumpable2", dumpable2) - dumpManager.registerCriticalDumpable("dumpable3", dumpable3) - dumpManager.registerBuffer("big-buffer1", buffer1) - dumpManager.registerBuffer("big-buffer2", buffer2) - - // WHEN a dumpable is dumped by a suffix that matches multiple options - val args = arrayOf<String>() - dumpManager.dumpTarget("2", pw, args, tailLength = 14) - - // THEN the matching dumpable with the shorter name is dumped - verify(dumpable1, never()).dump(any(), any()) - verify(dumpable2).dump(pw, args) - verify(dumpable3, never()).dump(any(), any()) - verify(buffer1, never()).dump(any(), anyInt()) - verify(buffer2, never()).dump(any(), anyInt()) + // THEN it contains the registered entries + assertThat(tables).containsExactly(table1, table2) } @Test - fun testDumpTarget_selectsShortestNamedMatch_buffer() { - // GIVEN a variety of registered dumpables and buffers + fun registerDumpable_throwsWhenNameCannotBeAssigned() { + // GIVEN dumpable1 and buffer1 and table1 are registered dumpManager.registerCriticalDumpable("dumpable1", dumpable1) - dumpManager.registerCriticalDumpable("dumpable2", dumpable2) - dumpManager.registerCriticalDumpable("dumpable3", dumpable3) dumpManager.registerBuffer("buffer1", buffer1) - dumpManager.registerBuffer("buffer2", buffer2) - - // WHEN a dumpable is dumped by a suffix that matches multiple options - val args = arrayOf<String>() - dumpManager.dumpTarget("2", pw, args, tailLength = 14) - - // THEN the matching buffer with the shorter name is dumped - verify(dumpable1, never()).dump(any(), any()) - verify(dumpable2, never()).dump(any(), any()) - verify(dumpable3, never()).dump(any(), any()) - verify(buffer1, never()).dump(any(), anyInt()) - verify(buffer2).dump(pw, tailLength = 14) + dumpManager.registerTableLogBuffer("table1", table1) + + // THEN an exception is thrown when trying to re-register a new dumpable under the same key + assertThrows(IllegalArgumentException::class.java) { + dumpManager.registerCriticalDumpable("dumpable1", dumpable2) + } + assertThrows(IllegalArgumentException::class.java) { + dumpManager.registerBuffer("buffer1", buffer2) + } + assertThrows(IllegalArgumentException::class.java) { + dumpManager.registerTableLogBuffer("table1", table2) + } } @Test - fun testDumpTarget_selectsTheAlphabeticallyFirstShortestMatch_dumpable() { - // GIVEN a variety of registered dumpables and buffers - dumpManager.registerCriticalDumpable("d1x", dumpable1) - dumpManager.registerCriticalDumpable("d2x", dumpable2) - dumpManager.registerCriticalDumpable("a3x", dumpable3) - dumpManager.registerBuffer("ab1x", buffer1) - dumpManager.registerBuffer("b2x", buffer2) - - // WHEN a dumpable is dumped by a suffix that matches multiple options - val args = arrayOf<String>() - dumpManager.dumpTarget("x", pw, args, tailLength = 14) - - // THEN the alphabetically first dumpable/buffer (of the 3 letter names) is dumped - verify(dumpable1, never()).dump(any(), any()) - verify(dumpable2, never()).dump(any(), any()) - verify(dumpable3).dump(pw, args) - verify(buffer1, never()).dump(any(), anyInt()) - verify(buffer2, never()).dump(any(), anyInt()) - } - - @Test - fun testDumpTarget_selectsTheAlphabeticallyFirstShortestMatch_buffer() { - // GIVEN a variety of registered dumpables and buffers - dumpManager.registerCriticalDumpable("d1x", dumpable1) - dumpManager.registerCriticalDumpable("d2x", dumpable2) - dumpManager.registerCriticalDumpable("az1x", dumpable3) - dumpManager.registerBuffer("b1x", buffer1) - dumpManager.registerBuffer("b2x", buffer2) - - // WHEN a dumpable is dumped by a suffix that matches multiple options - val args = arrayOf<String>() - dumpManager.dumpTarget("x", pw, args, tailLength = 14) - - // THEN the alphabetically first dumpable/buffer (of the 3 letter names) is dumped - verify(dumpable1, never()).dump(any(), any()) - verify(dumpable2, never()).dump(any(), any()) - verify(dumpable3, never()).dump(any(), any()) - verify(buffer1).dump(pw, tailLength = 14) - verify(buffer2, never()).dump(any(), anyInt()) - } - - @Test - fun testDumpDumpables() { - // GIVEN a variety of registered dumpables and buffers + fun registerDumpable_doesNotThrowWhenReRegistering() { + // GIVEN dumpable1 and buffer1 are registered dumpManager.registerCriticalDumpable("dumpable1", dumpable1) - dumpManager.registerCriticalDumpable("dumpable2", dumpable2) - dumpManager.registerNormalDumpable("dumpable3", dumpable3) dumpManager.registerBuffer("buffer1", buffer1) - dumpManager.registerBuffer("buffer2", buffer2) - // WHEN a dumpable dump is requested - val args = arrayOf<String>() - dumpManager.dumpDumpables(pw, args) - - // THEN all dumpables are dumped (both critical and normal) (and no dumpables) - verify(dumpable1).dump(pw, args) - verify(dumpable2).dump(pw, args) - verify(dumpable3).dump(pw, args) - verify(buffer1, never()).dump(any(), anyInt()) - verify(buffer2, never()).dump(any(), anyInt()) - } - - @Test - fun testDumpBuffers() { - // GIVEN a variety of registered dumpables and buffers + // THEN no exception is thrown when trying to re-register a new dumpable under the same key dumpManager.registerCriticalDumpable("dumpable1", dumpable1) - dumpManager.registerCriticalDumpable("dumpable2", dumpable2) - dumpManager.registerNormalDumpable("dumpable3", dumpable3) dumpManager.registerBuffer("buffer1", buffer1) - dumpManager.registerBuffer("buffer2", buffer2) - - // WHEN a buffer dump is requested - dumpManager.dumpBuffers(pw, tailLength = 1) - // THEN all buffers are dumped (and no dumpables) - verify(dumpable1, never()).dump(any(), any()) - verify(dumpable2, never()).dump(any(), any()) - verify(dumpable3, never()).dump(any(), any()) - verify(buffer1).dump(pw, tailLength = 1) - verify(buffer2).dump(pw, tailLength = 1) + // No exception thrown } @Test - fun testCriticalDump() { - // GIVEN a variety of registered dumpables and buffers + fun getDumpables_returnsSafeCollection() { + // GIVEN a variety of registered dumpables dumpManager.registerCriticalDumpable("dumpable1", dumpable1) dumpManager.registerCriticalDumpable("dumpable2", dumpable2) dumpManager.registerNormalDumpable("dumpable3", dumpable3) - dumpManager.registerBuffer("buffer1", buffer1) - dumpManager.registerBuffer("buffer2", buffer2) - // WHEN a critical dump is requested - val args = arrayOf<String>() - dumpManager.dumpCritical(pw, args) + // WHEN the collection is retrieved + val dumpables = dumpManager.getDumpables() - // THEN only critical modules are dumped (and no buffers) - verify(dumpable1).dump(pw, args) - verify(dumpable2).dump(pw, args) - verify(dumpable3, never()).dump(any(), any()) - verify(buffer1, never()).dump(any(), anyInt()) - verify(buffer2, never()).dump(any(), anyInt()) + // WHEN the collection changes from underneath + dumpManager.unregisterDumpable("dumpable1") + dumpManager.unregisterDumpable("dumpable2") + dumpManager.unregisterDumpable("dumpable3") + + // THEN new collections are empty + assertThat(dumpManager.getDumpables()).isEmpty() + + // AND the collection is still safe to use + assertThat(dumpables).hasSize(3) } @Test - fun testNormalDump() { - // GIVEN a variety of registered dumpables and buffers - dumpManager.registerCriticalDumpable("dumpable1", dumpable1) - dumpManager.registerCriticalDumpable("dumpable2", dumpable2) - dumpManager.registerNormalDumpable("dumpable3", dumpable3) + fun getBuffers_returnsSafeCollection() { + // GIVEN a set of registered buffers dumpManager.registerBuffer("buffer1", buffer1) dumpManager.registerBuffer("buffer2", buffer2) - // WHEN a normal dump is requested - val args = arrayOf<String>() - dumpManager.dumpNormal(pw, args, tailLength = 2) + // WHEN the collection is requested + val buffers = dumpManager.getLogBuffers() + + // WHEN the collection changes + dumpManager.registerBuffer("buffer3", buffer1) - // THEN the normal module and all buffers are dumped - verify(dumpable1, never()).dump(any(), any()) - verify(dumpable2, never()).dump(any(), any()) - verify(dumpable3).dump(pw, args) - verify(buffer1).dump(pw, tailLength = 2) - verify(buffer2).dump(pw, tailLength = 2) + // THEN the new entry is represented + assertThat(dumpManager.getLogBuffers()).hasSize(3) + + // AND the previous collection is unchanged + assertThat(buffers).hasSize(2) } @Test - fun testUnregister() { - // GIVEN a variety of registered dumpables and buffers - dumpManager.registerCriticalDumpable("dumpable1", dumpable1) - dumpManager.registerCriticalDumpable("dumpable2", dumpable2) - dumpManager.registerNormalDumpable("dumpable3", dumpable3) + fun getTableBuffers_returnsSafeCollection() { + // GIVEN a set of registered buffers + dumpManager.registerTableLogBuffer("table1", table1) + dumpManager.registerTableLogBuffer("table2", table2) - dumpManager.unregisterDumpable("dumpable2") - dumpManager.unregisterDumpable("dumpable3") + // WHEN the collection is requested + val tables = dumpManager.getTableLogBuffers() + + // WHEN the collection changes + dumpManager.registerTableLogBuffer("table3", table1) - // WHEN a dumpables dump is requested - val args = arrayOf<String>() - dumpManager.dumpDumpables(pw, args) + // THEN the new entry is represented + assertThat(dumpManager.getTableLogBuffers()).hasSize(3) - // THEN the unregistered dumpables (both normal and critical) are not dumped - verify(dumpable1).dump(pw, args) - verify(dumpable2, never()).dump(any(), any()) - verify(dumpable3, never()).dump(any(), any()) + // AND the previous collection is unchanged + assertThat(tables).hasSize(2) } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/dump/LogEulogizerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/dump/LogEulogizerTest.kt index cb38846a0514..3ff72028d5ca 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/dump/LogEulogizerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/dump/LogEulogizerTest.kt @@ -18,20 +18,14 @@ package com.android.systemui.dump import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase +import com.android.systemui.dump.DumpHandler.Companion.dump +import com.android.systemui.log.LogBuffer import com.android.systemui.util.io.FakeBasicFileAttributes import com.android.systemui.util.io.Files import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.eq +import com.android.systemui.util.mockito.whenever import com.android.systemui.util.time.FakeSystemClock -import org.junit.Assert.assertEquals -import org.junit.Assert.assertTrue -import org.junit.Before -import org.junit.Test -import org.mockito.Mock -import org.mockito.Mockito -import org.mockito.Mockito.never -import org.mockito.Mockito.verify -import org.mockito.MockitoAnnotations import java.io.BufferedWriter import java.io.ByteArrayOutputStream import java.io.IOException @@ -42,17 +36,29 @@ import java.nio.file.OpenOption import java.nio.file.Paths import java.nio.file.attribute.BasicFileAttributes import java.util.Arrays +import org.junit.Assert.assertEquals +import org.junit.Assert.assertTrue +import org.junit.Before +import org.junit.Test +import org.mockito.ArgumentMatchers.anyInt +import org.mockito.Mock +import org.mockito.Mockito +import org.mockito.Mockito.never +import org.mockito.Mockito.verify +import org.mockito.MockitoAnnotations @SmallTest class LogEulogizerTest : SysuiTestCase() { lateinit var eulogizer: LogBufferEulogizer - @Mock - lateinit var dumpManager: DumpManager + @Mock lateinit var dumpManager: DumpManager + @Mock lateinit var logBuffer1: LogBuffer + lateinit var logBufferEntry1: DumpsysEntry.LogBufferEntry + @Mock lateinit var logBuffer2: LogBuffer + lateinit var logBufferEntry2: DumpsysEntry.LogBufferEntry - @Mock - lateinit var files: Files + @Mock lateinit var files: Files private val clock = FakeSystemClock() @@ -67,37 +73,47 @@ class LogEulogizerTest : SysuiTestCase() { @Before fun setUp() { MockitoAnnotations.initMocks(this) + logBufferEntry1 = DumpsysEntry.LogBufferEntry(logBuffer1, "logbuffer1") + logBufferEntry2 = DumpsysEntry.LogBufferEntry(logBuffer2, "logbuffer2") - eulogizer = - LogBufferEulogizer(dumpManager, clock, files, path, MIN_WRITE_GAP, MAX_READ_AGE) + eulogizer = LogBufferEulogizer(dumpManager, clock, files, path, MIN_WRITE_GAP, MAX_READ_AGE) Mockito.`when`(files.newBufferedWriter(eq(path), any(OpenOption::class.java))) - .thenReturn(fileWriter) + .thenReturn(fileWriter) Mockito.`when`( - files.readAttributes(eq(path), - eq(BasicFileAttributes::class.java), - any(LinkOption::class.java)) - ).thenReturn(fileAttrs) + files.readAttributes( + eq(path), + eq(BasicFileAttributes::class.java), + any(LinkOption::class.java) + ) + ) + .thenReturn(fileAttrs) Mockito.`when`(files.lines(eq(path))).thenReturn(Arrays.stream(FAKE_LINES)) + + whenever(dumpManager.getLogBuffers()).thenReturn(listOf(logBufferEntry1, logBufferEntry2)) } @Test fun testFileIsCreated() { // GIVEN that the log file doesn't already exist Mockito.`when`( - files.readAttributes(eq(path), - eq(BasicFileAttributes::class.java), - any(LinkOption::class.java)) - ).thenThrow(IOException("File not found")) + files.readAttributes( + eq(path), + eq(BasicFileAttributes::class.java), + any(LinkOption::class.java) + ) + ) + .thenThrow(IOException("File not found")) // WHEN .record() is called val exception = RuntimeException("Something bad happened") assertEquals(exception, eulogizer.record(exception)) // THEN the buffers are dumped to the file - verify(dumpManager).dumpBuffers(any(PrintWriter::class.java), Mockito.anyInt()) + verify(logBuffer1).dump(any(PrintWriter::class.java), anyInt()) + verify(logBuffer2).dump(any(PrintWriter::class.java), anyInt()) assertTrue(fileStream.toString().isNotEmpty()) } @@ -111,7 +127,8 @@ class LogEulogizerTest : SysuiTestCase() { assertEquals(exception, eulogizer.record(exception)) // THEN the buffers are dumped to the file - verify(dumpManager).dumpBuffers(any(PrintWriter::class.java), Mockito.anyInt()) + verify(logBuffer1).dump(any(PrintWriter::class.java), anyInt()) + verify(logBuffer2).dump(any(PrintWriter::class.java), anyInt()) assertTrue(fileStream.toString().isNotEmpty()) } @@ -125,7 +142,8 @@ class LogEulogizerTest : SysuiTestCase() { assertEquals(exception, eulogizer.record(exception)) // THEN the file isn't written to - verify(dumpManager, never()).dumpBuffers(any(PrintWriter::class.java), Mockito.anyInt()) + verify(logBuffer1, never()).dump(any(PrintWriter::class.java), anyInt()) + verify(logBuffer2, never()).dump(any(PrintWriter::class.java), anyInt()) assertTrue(fileStream.toString().isEmpty()) } @@ -161,9 +179,4 @@ class LogEulogizerTest : SysuiTestCase() { private const val MIN_WRITE_GAP = 10L private const val MAX_READ_AGE = 100L -private val FAKE_LINES = - arrayOf( - "First line", - "Second line", - "Third line" - ) +private val FAKE_LINES = arrayOf("First line", "Second line", "Third line") diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java index c9ee1e8ef5b9..6aa5a00c36da 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java @@ -67,6 +67,7 @@ import com.android.systemui.colorextraction.SysuiColorExtractor; import com.android.systemui.plugins.GlobalActions; import com.android.systemui.settings.UserContextProvider; import com.android.systemui.settings.UserTracker; +import com.android.systemui.shade.ShadeController; import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.VibratorHelper; import com.android.systemui.statusbar.phone.CentralSurfaces; @@ -128,6 +129,7 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase { @Mock private UserContextProvider mUserContextProvider; @Mock private VibratorHelper mVibratorHelper; @Mock private CentralSurfaces mCentralSurfaces; + @Mock private ShadeController mShadeController; @Mock private KeyguardUpdateMonitor mKeyguardUpdateMonitor; @Mock private DialogLaunchAnimator mDialogLaunchAnimator; @Mock private OnBackInvokedDispatcher mOnBackInvokedDispatcher; @@ -177,6 +179,7 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase { mHandler, mPackageManager, Optional.of(mCentralSurfaces), + mShadeController, mKeyguardUpdateMonitor, mDialogLaunchAnimator); mGlobalActionsDialogLite.setZeroDialogPressDelayForTesting(); @@ -317,7 +320,7 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase { MotionEvent end = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 500, 0); gestureListener.onFling(start, end, 0, 1000); verifyLogPosted(GlobalActionsDialogLite.GlobalActionsEvent.GA_CLOSE_TAP_OUTSIDE); - verify(mCentralSurfaces).animateExpandSettingsPanel(null); + verify(mShadeController).animateExpandQs(); } @Test @@ -341,7 +344,7 @@ public class GlobalActionsDialogLiteTest extends SysuiTestCase { MotionEvent end = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 500, 0); gestureListener.onFling(start, end, 0, 1000); verifyLogPosted(GlobalActionsDialogLite.GlobalActionsEvent.GA_CLOSE_TAP_OUTSIDE); - verify(mCentralSurfaces).animateExpandNotificationsPanel(); + verify(mShadeController).animateExpandShade(); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt index cdc99af3cbfe..3383516fa366 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt @@ -34,6 +34,8 @@ import com.android.systemui.R import com.android.systemui.SystemUIAppComponentFactoryBase import com.android.systemui.SysuiTestCase import com.android.systemui.animation.DialogLaunchAnimator +import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository +import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository import com.android.systemui.dock.DockManagerFake import com.android.systemui.flags.FakeFeatureFlags import com.android.systemui.flags.Flags @@ -43,7 +45,6 @@ import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanc import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceLocalUserSelectionManager import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceRemoteUserSelectionManager import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository -import com.android.systemui.keyguard.data.repository.FakeKeyguardBouncerRepository import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository import com.android.systemui.keyguard.data.repository.KeyguardQuickAffordanceRepository import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor @@ -188,6 +189,7 @@ class CustomizationProviderTest : SysuiTestCase() { commandQueue = commandQueue, featureFlags = featureFlags, bouncerRepository = FakeKeyguardBouncerRepository(), + configurationRepository = FakeConfigurationRepository(), ), registry = mock(), lockPatternUtils = lockPatternUtils, diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt index 22308414547a..9a2936e227f8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt @@ -385,8 +385,6 @@ class KeyguardUnlockAnimationControllerTest : SysuiTestCase() { // We expect that we've set the surface behind to alpha = 0f since we're not interactive. assertEquals(0f, params.alpha) assertTrue(params.matrix.isIdentity) - assertEquals("Wallpaper surface was expected to have opacity 0", - 0f, captorWp.getLastValue().alpha) verifyNoMoreInteractions(surfaceTransactionApplier) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java index c280538f2d30..60b1567c8044 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java @@ -16,12 +16,17 @@ package com.android.systemui.keyguard; +import static android.os.PowerManager.WAKE_REASON_WAKE_MOTION; +import static android.provider.Settings.Secure.LOCK_SCREEN_LOCK_AFTER_TIMEOUT; import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY; +import static android.view.WindowManagerPolicyConstants.OFF_BECAUSE_OF_TIMEOUT; import static android.view.WindowManagerPolicyConstants.OFF_BECAUSE_OF_USER; import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW; import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_NON_STRONG_BIOMETRICS_TIMEOUT; import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN; +import static com.android.systemui.keyguard.KeyguardViewMediator.DELAYED_KEYGUARD_ACTION; +import static com.android.systemui.keyguard.KeyguardViewMediator.KEYGUARD_LOCK_AFTER_DELAY_DEFAULT; import static com.android.systemui.keyguard.KeyguardViewMediator.REBOOT_MAINLINE_UPDATE; import static com.android.systemui.keyguard.KeyguardViewMediator.SYS_BOOT_REASON_PROP; @@ -38,9 +43,12 @@ import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.app.AlarmManager; import android.app.IActivityManager; +import android.app.PendingIntent; import android.app.admin.DevicePolicyManager; import android.app.trust.TrustManager; +import android.content.Context; import android.os.PowerManager; import android.os.PowerManager.WakeLock; import android.telephony.TelephonyManager; @@ -99,9 +107,12 @@ import com.android.systemui.statusbar.policy.UserSwitcherController; import com.android.systemui.util.DeviceConfigProxy; import com.android.systemui.util.DeviceConfigProxyFake; import com.android.systemui.util.concurrency.FakeExecutor; +import com.android.systemui.util.settings.SecureSettings; +import com.android.systemui.util.settings.SystemSettings; import com.android.systemui.util.time.FakeSystemClock; import com.android.wm.shell.keyguard.KeyguardTransitions; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -166,21 +177,28 @@ public class KeyguardViewMediatorTest extends SysuiTestCase { private @Mock CentralSurfaces mCentralSurfaces; private @Mock UiEventLogger mUiEventLogger; private @Mock SessionTracker mSessionTracker; - private @Mock SystemPropertiesHelper mSystemPropertiesHelper; + private @Mock SystemSettings mSystemSettings; + private @Mock SecureSettings mSecureSettings; + private @Mock AlarmManager mAlarmManager; + private FakeSystemClock mSystemClock; + private @Mock CoroutineDispatcher mDispatcher; private @Mock DreamingToLockscreenTransitionViewModel mDreamingToLockscreenTransitionViewModel; + private @Mock SystemPropertiesHelper mSystemPropertiesHelper; private FakeFeatureFlags mFeatureFlags; + private int mInitialUserId; @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); mFalsingCollector = new FalsingCollectorFake(); - + mSystemClock = new FakeSystemClock(); when(mLockPatternUtils.getDevicePolicyManager()).thenReturn(mDevicePolicyManager); when(mPowerManager.newWakeLock(anyInt(), any())).thenReturn(mock(WakeLock.class)); when(mInteractionJankMonitor.begin(any(), anyInt())).thenReturn(true); when(mInteractionJankMonitor.end(anyInt())).thenReturn(true); + mContext.addMockSystemService(Context.ALARM_SERVICE, mAlarmManager); final ViewRootImpl testViewRoot = mock(ViewRootImpl.class); when(testViewRoot.getView()).thenReturn(mock(View.class)); when(mStatusBarKeyguardViewManager.getViewRootImpl()).thenReturn(testViewRoot); @@ -198,6 +216,12 @@ public class KeyguardViewMediatorTest extends SysuiTestCase { DejankUtils.setImmediate(true); createAndStartViewMediator(); + mInitialUserId = KeyguardUpdateMonitor.getCurrentUser(); + } + + @After + public void teardown() { + KeyguardUpdateMonitor.setCurrentUser(mInitialUserId); } @Test @@ -438,6 +462,49 @@ public class KeyguardViewMediatorTest extends SysuiTestCase { } @Test + public void lockAfterScreenTimeoutUsesValueFromSettings() { + int currentUserId = 99; + int userSpecificTimeout = 5999; + KeyguardUpdateMonitor.setCurrentUser(currentUserId); + + when(mKeyguardStateController.isKeyguardGoingAway()).thenReturn(false); + when(mDevicePolicyManager.getMaximumTimeToLock(null, currentUserId)).thenReturn(0L); + when(mSecureSettings.getIntForUser(LOCK_SCREEN_LOCK_AFTER_TIMEOUT, + KEYGUARD_LOCK_AFTER_DELAY_DEFAULT, currentUserId)).thenReturn(userSpecificTimeout); + mSystemClock.setElapsedRealtime(0L); + ArgumentCaptor<PendingIntent> pendingIntent = ArgumentCaptor.forClass(PendingIntent.class); + + mViewMediator.onStartedGoingToSleep(OFF_BECAUSE_OF_TIMEOUT); + + verify(mAlarmManager).setExactAndAllowWhileIdle(eq(AlarmManager.ELAPSED_REALTIME_WAKEUP), + eq(Long.valueOf(userSpecificTimeout)), pendingIntent.capture()); + assertEquals(DELAYED_KEYGUARD_ACTION, pendingIntent.getValue().getIntent().getAction()); + } + + @Test + public void lockAfterSpecifiedAfterDreamStarted() { + int currentUserId = 99; + int userSpecificTimeout = 5999; + KeyguardUpdateMonitor.setCurrentUser(currentUserId); + + // set mDeviceInteractive to true + mViewMediator.onStartedWakingUp(WAKE_REASON_WAKE_MOTION, false); + mFeatureFlags.set(Flags.LOCKSCREEN_WITHOUT_SECURE_LOCK_WHEN_DREAMING, false); + when(mLockPatternUtils.isSecure(currentUserId)).thenReturn(true); + when(mDevicePolicyManager.getMaximumTimeToLock(null, currentUserId)).thenReturn(0L); + when(mSecureSettings.getIntForUser(LOCK_SCREEN_LOCK_AFTER_TIMEOUT, + KEYGUARD_LOCK_AFTER_DELAY_DEFAULT, currentUserId)).thenReturn(userSpecificTimeout); + mSystemClock.setElapsedRealtime(0L); + ArgumentCaptor<PendingIntent> pendingIntent = ArgumentCaptor.forClass(PendingIntent.class); + + mViewMediator.onDreamingStarted(); + + verify(mAlarmManager).setExactAndAllowWhileIdle(eq(AlarmManager.ELAPSED_REALTIME_WAKEUP), + eq(Long.valueOf(userSpecificTimeout)), pendingIntent.capture()); + assertEquals(DELAYED_KEYGUARD_ACTION, pendingIntent.getValue().getIntent().getAction()); + } + + @Test public void testHideSurfaceBehindKeyguardMarksKeyguardNotGoingAway() { mViewMediator.hideSurfaceBehindKeyguard(); @@ -726,6 +793,9 @@ public class KeyguardViewMediatorTest extends SysuiTestCase { () -> mActivityLaunchAnimator, () -> mScrimController, mFeatureFlags, + mSecureSettings, + mSystemSettings, + mSystemClock, mDispatcher, () -> mDreamingToLockscreenTransitionViewModel, mSystemPropertiesHelper); diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ResourceTrimmerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ResourceTrimmerTest.kt index 644736899b6c..b4bd473b8b8c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ResourceTrimmerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ResourceTrimmerTest.kt @@ -7,11 +7,9 @@ import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.flags.FakeFeatureFlags import com.android.systemui.flags.Flags -import com.android.systemui.keyguard.data.repository.FakeCommandQueue -import com.android.systemui.keyguard.data.repository.FakeKeyguardBouncerRepository import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository -import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor +import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.TransitionStep @@ -60,13 +58,12 @@ class ResourceTrimmerTest : SysuiTestCase() { keyguardRepository.setDozeAmount(0f) keyguardRepository.setKeyguardGoingAway(false) - val keyguardInteractor = - KeyguardInteractor( - keyguardRepository, - FakeCommandQueue(), - featureFlags, - FakeKeyguardBouncerRepository() + val withDeps = + KeyguardInteractorFactory.create( + repository = keyguardRepository, + featureFlags = featureFlags, ) + val keyguardInteractor = withDeps.keyguardInteractor resourceTrimmer = ResourceTrimmer( keyguardInteractor, diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt index 1090c15d8625..92ec9a1cfabc 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt @@ -39,14 +39,16 @@ import com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUN import com.android.systemui.R import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase +import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository +import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor import com.android.systemui.coroutines.FlowValue import com.android.systemui.coroutines.collectLastValue import com.android.systemui.dump.DumpManager import com.android.systemui.dump.logcatLogBuffer import com.android.systemui.flags.FakeFeatureFlags import com.android.systemui.flags.Flags.FACE_AUTH_REFACTOR -import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor +import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor import com.android.systemui.keyguard.shared.model.AuthenticationStatus import com.android.systemui.keyguard.shared.model.DetectionStatus @@ -159,17 +161,16 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() { biometricSettingsRepository = FakeBiometricSettingsRepository() deviceEntryFingerprintAuthRepository = FakeDeviceEntryFingerprintAuthRepository() trustRepository = FakeTrustRepository() - keyguardRepository = FakeKeyguardRepository() - bouncerRepository = FakeKeyguardBouncerRepository() featureFlags = FakeFeatureFlags().apply { set(FACE_AUTH_REFACTOR, true) } - fakeCommandQueue = FakeCommandQueue() - keyguardInteractor = - KeyguardInteractor( - keyguardRepository, - fakeCommandQueue, - featureFlags, - bouncerRepository + val withDeps = + KeyguardInteractorFactory.create( + featureFlags = featureFlags, ) + keyguardInteractor = withDeps.keyguardInteractor + keyguardRepository = withDeps.repository + bouncerRepository = withDeps.bouncerRepository + fakeCommandQueue = withDeps.commandQueue + alternateBouncerInteractor = AlternateBouncerInteractor( bouncerRepository = bouncerRepository, diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt index 4b797cb1ba46..953d61844596 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt @@ -97,7 +97,8 @@ class KeyguardRepositoryImplTest : SysuiTestCase() { dozeParameters, authController, dreamOverlayCallbackController, - mainDispatcher + mainDispatcher, + testScope.backgroundScope, ) } @@ -343,8 +344,6 @@ class KeyguardRepositoryImplTest : SysuiTestCase() { ) job.cancel() - runCurrent() - verify(wakefulnessLifecycle).removeObserver(captor.value) } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt index 71e73cbd768f..80700e59a2d4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt @@ -24,16 +24,19 @@ import com.android.keyguard.FaceAuthUiEvent import com.android.keyguard.KeyguardSecurityModel import com.android.keyguard.KeyguardUpdateMonitor import com.android.systemui.SysuiTestCase +import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository +import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor +import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInteractor +import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor +import com.android.systemui.bouncer.ui.BouncerView import com.android.systemui.classifier.FalsingCollector import com.android.systemui.dump.logcatLogBuffer import com.android.systemui.flags.FakeFeatureFlags import com.android.systemui.flags.Flags import com.android.systemui.keyguard.DismissCallbackRegistry -import com.android.systemui.keyguard.data.BouncerView import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFaceAuthRepository import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository -import com.android.systemui.keyguard.data.repository.FakeKeyguardBouncerRepository import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository import com.android.systemui.keyguard.data.repository.FakeTrustRepository import com.android.systemui.keyguard.shared.model.KeyguardState diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt index 0d695aa231cc..4b094687cc61 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt @@ -22,11 +22,12 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase +import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository +import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository import com.android.systemui.coroutines.collectLastValue import com.android.systemui.flags.FakeFeatureFlags import com.android.systemui.flags.Flags.FACE_AUTH_REFACTOR import com.android.systemui.keyguard.data.repository.FakeCommandQueue -import com.android.systemui.keyguard.data.repository.FakeKeyguardBouncerRepository import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository import com.android.systemui.keyguard.shared.model.CameraLaunchSourceModel import com.google.common.truth.Truth.assertThat @@ -50,6 +51,7 @@ class KeyguardInteractorTest : SysuiTestCase() { private lateinit var underTest: KeyguardInteractor private lateinit var repository: FakeKeyguardRepository private lateinit var bouncerRepository: FakeKeyguardBouncerRepository + private lateinit var configurationRepository: FakeConfigurationRepository @Before fun setUp() { @@ -59,12 +61,14 @@ class KeyguardInteractorTest : SysuiTestCase() { testScope = TestScope() repository = FakeKeyguardRepository() bouncerRepository = FakeKeyguardBouncerRepository() + configurationRepository = FakeConfigurationRepository() underTest = KeyguardInteractor( repository, commandQueue, featureFlags, bouncerRepository, + configurationRepository, ) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt index 2c90d530194d..8540bf7f6b17 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt @@ -39,8 +39,6 @@ import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanc import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceLocalUserSelectionManager import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceRemoteUserSelectionManager import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository -import com.android.systemui.keyguard.data.repository.FakeKeyguardBouncerRepository -import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository import com.android.systemui.keyguard.data.repository.KeyguardQuickAffordanceRepository import com.android.systemui.keyguard.domain.quickaffordance.FakeKeyguardQuickAffordanceRegistry import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancePosition @@ -49,7 +47,6 @@ import com.android.systemui.plugins.ActivityStarter import com.android.systemui.settings.FakeUserTracker import com.android.systemui.settings.UserFileManager import com.android.systemui.settings.UserTracker -import com.android.systemui.statusbar.CommandQueue import com.android.systemui.statusbar.policy.KeyguardStateController import com.android.systemui.util.FakeSharedPreferences import com.android.systemui.util.mockito.any @@ -226,7 +223,6 @@ class KeyguardQuickAffordanceInteractorParameterizedTest : SysuiTestCase() { @Mock private lateinit var animationController: ActivityLaunchAnimator.Controller @Mock private lateinit var expandable: Expandable @Mock private lateinit var launchAnimator: DialogLaunchAnimator - @Mock private lateinit var commandQueue: CommandQueue @Mock private lateinit var devicePolicyManager: DevicePolicyManager @Mock private lateinit var logger: KeyguardQuickAffordancesMetricsLogger @@ -312,12 +308,10 @@ class KeyguardQuickAffordanceInteractorParameterizedTest : SysuiTestCase() { underTest = KeyguardQuickAffordanceInteractor( keyguardInteractor = - KeyguardInteractor( - repository = FakeKeyguardRepository(), - commandQueue = commandQueue, - featureFlags = featureFlags, - bouncerRepository = FakeKeyguardBouncerRepository(), - ), + KeyguardInteractorFactory.create( + featureFlags = featureFlags, + ) + .keyguardInteractor, registry = FakeKeyguardQuickAffordanceRegistry( mapOf( @@ -372,11 +366,11 @@ class KeyguardQuickAffordanceInteractorParameterizedTest : SysuiTestCase() { KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled } - underTest.onQuickAffordanceTriggered( - configKey = BuiltInKeyguardQuickAffordanceKeys.HOME_CONTROLS, - expandable = expandable, - slotId = "", - ) + underTest.onQuickAffordanceTriggered( + configKey = BuiltInKeyguardQuickAffordanceKeys.HOME_CONTROLS, + expandable = expandable, + slotId = "", + ) if (startActivity) { if (needsToUnlockFirst) { diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt index 7cab680ef3e6..a0c5a75ecd9e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt @@ -41,7 +41,6 @@ import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanc import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceLocalUserSelectionManager import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceRemoteUserSelectionManager import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository -import com.android.systemui.keyguard.data.repository.FakeKeyguardBouncerRepository import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository import com.android.systemui.keyguard.data.repository.KeyguardQuickAffordanceRepository import com.android.systemui.keyguard.domain.model.KeyguardQuickAffordanceModel @@ -54,7 +53,6 @@ import com.android.systemui.plugins.ActivityStarter import com.android.systemui.settings.UserFileManager import com.android.systemui.settings.UserTracker import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots -import com.android.systemui.statusbar.CommandQueue import com.android.systemui.statusbar.policy.KeyguardStateController import com.android.systemui.util.FakeSharedPreferences import com.android.systemui.util.mockito.mock @@ -85,7 +83,6 @@ class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() { @Mock private lateinit var userTracker: UserTracker @Mock private lateinit var activityStarter: ActivityStarter @Mock private lateinit var launchAnimator: DialogLaunchAnimator - @Mock private lateinit var commandQueue: CommandQueue @Mock private lateinit var devicePolicyManager: DevicePolicyManager @Mock private lateinit var logger: KeyguardQuickAffordancesMetricsLogger @@ -171,15 +168,14 @@ class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() { set(Flags.FACE_AUTH_REFACTOR, true) } + val withDeps = + KeyguardInteractorFactory.create( + featureFlags = featureFlags, + repository = repository, + ) underTest = KeyguardQuickAffordanceInteractor( - keyguardInteractor = - KeyguardInteractor( - repository = repository, - commandQueue = commandQueue, - featureFlags = featureFlags, - bouncerRepository = FakeKeyguardBouncerRepository(), - ), + keyguardInteractor = withDeps.keyguardInteractor, registry = FakeKeyguardQuickAffordanceRegistry( mapOf( diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt index 30dba23ba394..50075b5ae5d2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt @@ -20,10 +20,9 @@ import androidx.test.filters.SmallTest import com.android.keyguard.KeyguardSecurityModel import com.android.keyguard.KeyguardSecurityModel.SecurityMode.PIN import com.android.systemui.SysuiTestCase +import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository import com.android.systemui.flags.FakeFeatureFlags -import com.android.systemui.flags.FeatureFlags import com.android.systemui.flags.Flags -import com.android.systemui.keyguard.data.repository.FakeKeyguardBouncerRepository import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository @@ -39,7 +38,6 @@ import com.android.systemui.keyguard.shared.model.WakefulnessModel import com.android.systemui.keyguard.shared.model.WakefulnessState import com.android.systemui.shade.data.repository.FakeShadeRepository import com.android.systemui.shade.data.repository.ShadeRepository -import com.android.systemui.statusbar.CommandQueue import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.whenever import com.android.systemui.util.mockito.withArgCaptor @@ -74,10 +72,10 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { private lateinit var bouncerRepository: FakeKeyguardBouncerRepository private lateinit var shadeRepository: ShadeRepository private lateinit var transitionRepository: FakeKeyguardTransitionRepository + private lateinit var featureFlags: FakeFeatureFlags // Used to verify transition requests for test output @Mock private lateinit var mockTransitionRepository: KeyguardTransitionRepository - @Mock private lateinit var commandQueue: CommandQueue @Mock private lateinit var keyguardSecurityModel: KeyguardSecurityModel private lateinit var fromLockscreenTransitionInteractor: FromLockscreenTransitionInteractor @@ -103,11 +101,11 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { whenever(keyguardSecurityModel.getSecurityMode(anyInt())).thenReturn(PIN) - val featureFlags = FakeFeatureFlags().apply { set(Flags.FACE_AUTH_REFACTOR, true) } + featureFlags = FakeFeatureFlags().apply { set(Flags.FACE_AUTH_REFACTOR, true) } fromLockscreenTransitionInteractor = FromLockscreenTransitionInteractor( scope = testScope, - keyguardInteractor = createKeyguardInteractor(featureFlags), + keyguardInteractor = createKeyguardInteractor(), shadeRepository = shadeRepository, keyguardTransitionRepository = mockTransitionRepository, keyguardTransitionInteractor = @@ -118,7 +116,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { fromDreamingTransitionInteractor = FromDreamingTransitionInteractor( scope = testScope, - keyguardInteractor = createKeyguardInteractor(featureFlags), + keyguardInteractor = createKeyguardInteractor(), keyguardTransitionRepository = mockTransitionRepository, keyguardTransitionInteractor = KeyguardTransitionInteractor(transitionRepository, testScope.backgroundScope), @@ -128,7 +126,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { fromAodTransitionInteractor = FromAodTransitionInteractor( scope = testScope, - keyguardInteractor = createKeyguardInteractor(featureFlags), + keyguardInteractor = createKeyguardInteractor(), keyguardTransitionRepository = mockTransitionRepository, keyguardTransitionInteractor = KeyguardTransitionInteractor(transitionRepository, testScope.backgroundScope), @@ -138,7 +136,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { fromGoneTransitionInteractor = FromGoneTransitionInteractor( scope = testScope, - keyguardInteractor = createKeyguardInteractor(featureFlags), + keyguardInteractor = createKeyguardInteractor(), keyguardTransitionRepository = mockTransitionRepository, keyguardTransitionInteractor = KeyguardTransitionInteractor(transitionRepository, testScope.backgroundScope), @@ -148,7 +146,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { fromDozingTransitionInteractor = FromDozingTransitionInteractor( scope = testScope, - keyguardInteractor = createKeyguardInteractor(featureFlags), + keyguardInteractor = createKeyguardInteractor(), keyguardTransitionRepository = mockTransitionRepository, keyguardTransitionInteractor = KeyguardTransitionInteractor(transitionRepository, testScope.backgroundScope), @@ -158,7 +156,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { fromOccludedTransitionInteractor = FromOccludedTransitionInteractor( scope = testScope, - keyguardInteractor = createKeyguardInteractor(featureFlags), + keyguardInteractor = createKeyguardInteractor(), keyguardTransitionRepository = mockTransitionRepository, keyguardTransitionInteractor = KeyguardTransitionInteractor(transitionRepository, testScope.backgroundScope), @@ -168,7 +166,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { fromAlternateBouncerTransitionInteractor = FromAlternateBouncerTransitionInteractor( scope = testScope, - keyguardInteractor = createKeyguardInteractor(featureFlags), + keyguardInteractor = createKeyguardInteractor(), keyguardTransitionRepository = mockTransitionRepository, keyguardTransitionInteractor = KeyguardTransitionInteractor(transitionRepository, testScope.backgroundScope), @@ -178,7 +176,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { fromPrimaryBouncerTransitionInteractor = FromPrimaryBouncerTransitionInteractor( scope = testScope, - keyguardInteractor = createKeyguardInteractor(featureFlags), + keyguardInteractor = createKeyguardInteractor(), keyguardTransitionRepository = mockTransitionRepository, keyguardTransitionInteractor = KeyguardTransitionInteractor(transitionRepository, testScope.backgroundScope), @@ -855,13 +853,13 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() { WakeSleepReason.OTHER ) - private fun createKeyguardInteractor(featureFlags: FeatureFlags): KeyguardInteractor { - return KeyguardInteractor( - keyguardRepository, - commandQueue, - featureFlags, - bouncerRepository, - ) + private fun createKeyguardInteractor(): KeyguardInteractor { + return KeyguardInteractorFactory.create( + featureFlags = featureFlags, + repository = keyguardRepository, + bouncerRepository = bouncerRepository, + ) + .keyguardInteractor } private suspend fun TestScope.runTransition(from: KeyguardState, to: KeyguardState) { diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/LockscreenSceneInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/LockscreenSceneInteractorTest.kt index d622f1c30816..65781c497944 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/LockscreenSceneInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/LockscreenSceneInteractorTest.kt @@ -95,7 +95,7 @@ class LockscreenSceneInteractorTest : SysuiTestCase() { testScope.runTest { val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_1)) authenticationInteractor.lockDevice() - authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.PIN(1234)) + authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234)) assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Lockscreen)) underTest.dismissLockscreen() @@ -108,7 +108,7 @@ class LockscreenSceneInteractorTest : SysuiTestCase() { testScope.runTest { val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_1)) authenticationInteractor.unlockDevice() - authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.PIN(1234)) + authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234)) assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Lockscreen)) underTest.dismissLockscreen() @@ -195,7 +195,7 @@ class LockscreenSceneInteractorTest : SysuiTestCase() { testScope.runTest { val isUnlocked by collectLastValue(authenticationInteractor.isUnlocked) sceneInteractor.setCurrentScene(CONTAINER_1, SceneModel(SceneKey.Lockscreen)) - authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.PIN(1234)) + authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234)) assertThat(isUnlocked).isFalse() sceneInteractor.setCurrentScene(CONTAINER_1, SceneModel(SceneKey.Gone)) diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt index b6368cf57f9e..29886d5481b9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt @@ -40,12 +40,11 @@ import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanc import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceLocalUserSelectionManager import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceRemoteUserSelectionManager import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository -import com.android.systemui.keyguard.data.repository.FakeKeyguardBouncerRepository import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository import com.android.systemui.keyguard.data.repository.KeyguardQuickAffordanceRepository import com.android.systemui.keyguard.domain.interactor.KeyguardBottomAreaInteractor -import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor +import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory import com.android.systemui.keyguard.domain.interactor.KeyguardLongPressInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardQuickAffordanceInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor @@ -57,7 +56,6 @@ import com.android.systemui.plugins.ActivityStarter import com.android.systemui.settings.UserFileManager import com.android.systemui.settings.UserTracker import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots -import com.android.systemui.statusbar.CommandQueue import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper import com.android.systemui.statusbar.policy.KeyguardStateController import com.android.systemui.util.FakeSharedPreferences @@ -96,7 +94,6 @@ class KeyguardBottomAreaViewModelTest : SysuiTestCase() { @Mock private lateinit var userTracker: UserTracker @Mock private lateinit var activityStarter: ActivityStarter @Mock private lateinit var launchAnimator: DialogLaunchAnimator - @Mock private lateinit var commandQueue: CommandQueue @Mock private lateinit var devicePolicyManager: DevicePolicyManager @Mock private lateinit var logger: KeyguardQuickAffordancesMetricsLogger @Mock private lateinit var broadcastDispatcher: BroadcastDispatcher @@ -143,7 +140,6 @@ class KeyguardBottomAreaViewModelTest : SysuiTestCase() { ), ), ) - repository = FakeKeyguardRepository() val featureFlags = FakeFeatureFlags().apply { set(Flags.CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES, false) @@ -152,13 +148,10 @@ class KeyguardBottomAreaViewModelTest : SysuiTestCase() { set(Flags.LOCK_SCREEN_LONG_PRESS_DIRECT_TO_WPP, false) } - val keyguardInteractor = - KeyguardInteractor( - repository = repository, - commandQueue = commandQueue, - featureFlags = featureFlags, - bouncerRepository = FakeKeyguardBouncerRepository(), - ) + val withDeps = KeyguardInteractorFactory.create(featureFlags = featureFlags) + val keyguardInteractor = withDeps.keyguardInteractor + repository = withDeps.repository + whenever(userTracker.userHandle).thenReturn(mock()) whenever(userTracker.userId).thenReturn(10) whenever(lockPatternUtils.getStrongAuthForUser(anyInt())) @@ -555,91 +548,6 @@ class KeyguardBottomAreaViewModelTest : SysuiTestCase() { } @Test - fun isIndicationAreaPadded() = - testScope.runTest { - repository.setKeyguardShowing(true) - val value = collectLastValue(underTest.isIndicationAreaPadded) - - assertThat(value()).isFalse() - setUpQuickAffordanceModel( - position = KeyguardQuickAffordancePosition.BOTTOM_START, - testConfig = - TestConfig( - isVisible = true, - isClickable = true, - icon = mock(), - canShowWhileLocked = true, - slotId = KeyguardQuickAffordancePosition.BOTTOM_START.toSlotId(), - ) - ) - assertThat(value()).isTrue() - setUpQuickAffordanceModel( - position = KeyguardQuickAffordancePosition.BOTTOM_END, - testConfig = - TestConfig( - isVisible = true, - isClickable = true, - icon = mock(), - canShowWhileLocked = false, - slotId = KeyguardQuickAffordancePosition.BOTTOM_END.toSlotId(), - ) - ) - assertThat(value()).isTrue() - setUpQuickAffordanceModel( - position = KeyguardQuickAffordancePosition.BOTTOM_START, - testConfig = - TestConfig( - isVisible = false, - slotId = KeyguardQuickAffordancePosition.BOTTOM_START.toSlotId(), - ) - ) - assertThat(value()).isTrue() - setUpQuickAffordanceModel( - position = KeyguardQuickAffordancePosition.BOTTOM_END, - testConfig = - TestConfig( - isVisible = false, - slotId = KeyguardQuickAffordancePosition.BOTTOM_END.toSlotId(), - ) - ) - assertThat(value()).isFalse() - } - - @Test - fun indicationAreaTranslationX() = - testScope.runTest { - val value = collectLastValue(underTest.indicationAreaTranslationX) - - assertThat(value()).isEqualTo(0f) - repository.setClockPosition(100, 100) - assertThat(value()).isEqualTo(100f) - repository.setClockPosition(200, 100) - assertThat(value()).isEqualTo(200f) - repository.setClockPosition(200, 200) - assertThat(value()).isEqualTo(200f) - repository.setClockPosition(300, 100) - assertThat(value()).isEqualTo(300f) - } - - @Test - fun indicationAreaTranslationY() = - testScope.runTest { - val value = - collectLastValue(underTest.indicationAreaTranslationY(DEFAULT_BURN_IN_OFFSET)) - - // Negative 0 - apparently there's a difference in floating point arithmetic - FML - assertThat(value()).isEqualTo(-0f) - val expected1 = setDozeAmountAndCalculateExpectedTranslationY(0.1f) - assertThat(value()).isEqualTo(expected1) - val expected2 = setDozeAmountAndCalculateExpectedTranslationY(0.2f) - assertThat(value()).isEqualTo(expected2) - val expected3 = setDozeAmountAndCalculateExpectedTranslationY(0.5f) - assertThat(value()).isEqualTo(expected3) - val expected4 = setDozeAmountAndCalculateExpectedTranslationY(1f) - assertThat(value()).isEqualTo(expected4) - } - - @Test fun isClickable_trueWhenAlphaAtThreshold() = testScope.runTest { repository.setKeyguardShowing(true) @@ -762,11 +670,6 @@ class KeyguardBottomAreaViewModelTest : SysuiTestCase() { ) } - private fun setDozeAmountAndCalculateExpectedTranslationY(dozeAmount: Float): Float { - repository.setDozeAmount(dozeAmount) - return dozeAmount * (RETURNED_BURN_IN_OFFSET - DEFAULT_BURN_IN_OFFSET) - } - private suspend fun setUpQuickAffordanceModel( position: KeyguardQuickAffordancePosition, testConfig: TestConfig, diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModelTest.kt new file mode 100644 index 000000000000..dff0f2909126 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModelTest.kt @@ -0,0 +1,160 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.keyguard.ui.viewmodel + +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.doze.util.BurnInHelperWrapper +import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository +import com.android.systemui.keyguard.domain.interactor.KeyguardBottomAreaInteractor +import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory +import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancePosition +import com.android.systemui.util.mockito.any +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 +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 +import org.mockito.ArgumentMatchers.anyInt +import org.mockito.Mock +import org.mockito.MockitoAnnotations + +@OptIn(ExperimentalCoroutinesApi::class) +@SmallTest +@RunWith(JUnit4::class) +class KeyguardIndicationAreaViewModelTest : SysuiTestCase() { + + @Mock private lateinit var burnInHelperWrapper: BurnInHelperWrapper + + private lateinit var underTest: KeyguardIndicationAreaViewModel + private lateinit var repository: FakeKeyguardRepository + + private val startButtonFlow = + MutableStateFlow<KeyguardQuickAffordanceViewModel>( + KeyguardQuickAffordanceViewModel( + slotId = KeyguardQuickAffordancePosition.BOTTOM_START.toSlotId() + ) + ) + private val endButtonFlow = + MutableStateFlow<KeyguardQuickAffordanceViewModel>( + KeyguardQuickAffordanceViewModel( + slotId = KeyguardQuickAffordancePosition.BOTTOM_END.toSlotId() + ) + ) + private val alphaFlow = MutableStateFlow<Float>(1f) + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + whenever(burnInHelperWrapper.burnInOffset(anyInt(), any())) + .thenReturn(RETURNED_BURN_IN_OFFSET) + + val withDeps = KeyguardInteractorFactory.create() + val keyguardInteractor = withDeps.keyguardInteractor + repository = withDeps.repository + + val bottomAreaViewModel: KeyguardBottomAreaViewModel = mock() + whenever(bottomAreaViewModel.startButton).thenReturn(startButtonFlow) + whenever(bottomAreaViewModel.endButton).thenReturn(endButtonFlow) + whenever(bottomAreaViewModel.alpha).thenReturn(alphaFlow) + underTest = + KeyguardIndicationAreaViewModel( + keyguardInteractor = keyguardInteractor, + bottomAreaInteractor = KeyguardBottomAreaInteractor(repository = repository), + keyguardBottomAreaViewModel = bottomAreaViewModel, + burnInHelperWrapper = burnInHelperWrapper, + ) + } + + @Test + fun alpha() = runTest { + val value = collectLastValue(underTest.alpha) + + assertThat(value()).isEqualTo(1f) + alphaFlow.value = 0.1f + assertThat(value()).isEqualTo(0.1f) + alphaFlow.value = 0.5f + assertThat(value()).isEqualTo(0.5f) + alphaFlow.value = 0.2f + assertThat(value()).isEqualTo(0.2f) + alphaFlow.value = 0f + assertThat(value()).isEqualTo(0f) + } + + @Test + fun isIndicationAreaPadded() = runTest { + repository.setKeyguardShowing(true) + val value = collectLastValue(underTest.isIndicationAreaPadded) + + assertThat(value()).isFalse() + startButtonFlow.value = startButtonFlow.value.copy(isVisible = true) + assertThat(value()).isTrue() + endButtonFlow.value = endButtonFlow.value.copy(isVisible = true) + assertThat(value()).isTrue() + startButtonFlow.value = startButtonFlow.value.copy(isVisible = false) + assertThat(value()).isTrue() + endButtonFlow.value = endButtonFlow.value.copy(isVisible = false) + assertThat(value()).isFalse() + } + + @Test + fun indicationAreaTranslationX() = runTest { + val value = collectLastValue(underTest.indicationAreaTranslationX) + + assertThat(value()).isEqualTo(0f) + repository.setClockPosition(100, 100) + assertThat(value()).isEqualTo(100f) + repository.setClockPosition(200, 100) + assertThat(value()).isEqualTo(200f) + repository.setClockPosition(200, 200) + assertThat(value()).isEqualTo(200f) + repository.setClockPosition(300, 100) + assertThat(value()).isEqualTo(300f) + } + + @Test + fun indicationAreaTranslationY() = runTest { + val value = collectLastValue(underTest.indicationAreaTranslationY(DEFAULT_BURN_IN_OFFSET)) + + // Negative 0 - apparently there's a difference in floating point arithmetic - FML + assertThat(value()).isEqualTo(-0f) + val expected1 = setDozeAmountAndCalculateExpectedTranslationY(0.1f) + assertThat(value()).isEqualTo(expected1) + val expected2 = setDozeAmountAndCalculateExpectedTranslationY(0.2f) + assertThat(value()).isEqualTo(expected2) + val expected3 = setDozeAmountAndCalculateExpectedTranslationY(0.5f) + assertThat(value()).isEqualTo(expected3) + val expected4 = setDozeAmountAndCalculateExpectedTranslationY(1f) + assertThat(value()).isEqualTo(expected4) + } + + private fun setDozeAmountAndCalculateExpectedTranslationY(dozeAmount: Float): Float { + repository.setDozeAmount(dozeAmount) + return dozeAmount * (RETURNED_BURN_IN_OFFSET - DEFAULT_BURN_IN_OFFSET) + } + + companion object { + private const val DEFAULT_BURN_IN_OFFSET = 5 + private const val RETURNED_BURN_IN_OFFSET = 3 + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt index 8ba3f0f57eed..f0ea0077596a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt @@ -109,7 +109,7 @@ class LockscreenSceneViewModelTest : SysuiTestCase() { fun upTransitionSceneKey_swipeToUnlockedNotEnabled_bouncer() = testScope.runTest { val upTransitionSceneKey by collectLastValue(underTest.upDestinationSceneKey) - authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.PIN(1234)) + authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234)) authenticationInteractor.lockDevice() assertThat(upTransitionSceneKey).isEqualTo(SceneKey.Bouncer) @@ -119,7 +119,7 @@ class LockscreenSceneViewModelTest : SysuiTestCase() { fun onLockButtonClicked_deviceLockedSecurely_switchesToBouncer() = testScope.runTest { val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_1)) - authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.PIN(1234)) + authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234)) authenticationInteractor.lockDevice() runCurrent() @@ -132,7 +132,7 @@ class LockscreenSceneViewModelTest : SysuiTestCase() { fun onContentClicked_deviceUnlocked_switchesToGone() = testScope.runTest { val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_1)) - authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.PIN(1234)) + authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234)) authenticationInteractor.unlockDevice() runCurrent() @@ -145,7 +145,7 @@ class LockscreenSceneViewModelTest : SysuiTestCase() { fun onContentClicked_deviceLockedSecurely_switchesToBouncer() = testScope.runTest { val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_1)) - authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.PIN(1234)) + authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234)) authenticationInteractor.lockDevice() runCurrent() @@ -158,7 +158,7 @@ class LockscreenSceneViewModelTest : SysuiTestCase() { fun onLockButtonClicked_deviceUnlocked_switchesToGone() = testScope.runTest { val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_1)) - authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.PIN(1234)) + authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234)) authenticationInteractor.unlockDevice() runCurrent() diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt index 65b2ef767afe..f88b71d469cf 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt @@ -20,9 +20,9 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.RoboPilotTest import com.android.systemui.SysuiTestCase +import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor -import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerInteractor import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.ScrimAlpha import com.android.systemui.keyguard.shared.model.TransitionState diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavBarHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavBarHelperTest.java index 9b8605dccd09..8d306cceeaa7 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavBarHelperTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavBarHelperTest.java @@ -52,6 +52,7 @@ import com.android.systemui.recents.OverviewProxyService; import com.android.systemui.settings.DisplayTracker; import com.android.systemui.settings.UserTracker; import com.android.systemui.statusbar.CommandQueue; +import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.phone.CentralSurfaces; import com.android.systemui.statusbar.policy.KeyguardStateController; @@ -112,6 +113,9 @@ public class NavBarHelperTest extends SysuiTestCase { EdgeBackGestureHandler mEdgeBackGestureHandler; @Mock EdgeBackGestureHandler.Factory mEdgeBackGestureHandlerFactory; + @Mock + NotificationShadeWindowController mNotificationShadeWindowController; + private AccessibilityManager.AccessibilityServicesStateChangeListener mAccessibilityServicesStateChangeListener; @@ -136,7 +140,7 @@ public class NavBarHelperTest extends SysuiTestCase { mSystemActions, mOverviewProxyService, mAssistManagerLazy, () -> Optional.of(mock(CentralSurfaces.class)), mock(KeyguardStateController.class), mNavigationModeController, mEdgeBackGestureHandlerFactory, mWm, mUserTracker, - mDisplayTracker, mDumpManager, mCommandQueue); + mDisplayTracker, mNotificationShadeWindowController, mDumpManager, mCommandQueue); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java index f062ec732b21..697d1a3b775c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java @@ -20,7 +20,6 @@ import static android.app.StatusBarManager.NAVIGATION_HINT_BACK_ALT; import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SHOWN; import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SWITCHER_SHOWN; import static android.inputmethodservice.InputMethodService.BACK_DISPOSITION_DEFAULT; -import static android.inputmethodservice.InputMethodService.IME_INVISIBLE; import static android.inputmethodservice.InputMethodService.IME_VISIBLE; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS; @@ -99,6 +98,7 @@ import com.android.systemui.shared.system.TaskStackChangeListeners; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.NotificationRemoteInputManager; import com.android.systemui.statusbar.NotificationShadeDepthController; +import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.phone.AutoHideController; import com.android.systemui.statusbar.phone.CentralSurfaces; import com.android.systemui.statusbar.phone.LightBarController; @@ -209,6 +209,8 @@ public class NavigationBarTest extends SysuiTestCase { private EdgeBackGestureHandler.Factory mEdgeBackGestureHandlerFactory; @Mock private EdgeBackGestureHandler mEdgeBackGestureHandler; + @Mock + private NotificationShadeWindowController mNotificationShadeWindowController; private FakeExecutor mFakeExecutor = new FakeExecutor(new FakeSystemClock()); private DeviceConfigProxyFake mDeviceConfigProxyFake = new DeviceConfigProxyFake(); private TaskStackChangeListeners mTaskStackChangeListeners = @@ -256,7 +258,8 @@ public class NavigationBarTest extends SysuiTestCase { () -> mock(AssistManager.class), () -> Optional.of(mCentralSurfaces), mKeyguardStateController, mock(NavigationModeController.class), mEdgeBackGestureHandlerFactory, mock(IWindowManager.class), - mock(UserTracker.class), mock(DisplayTracker.class), mock(DumpManager.class), + mock(UserTracker.class), mock(DisplayTracker.class), + mNotificationShadeWindowController, mock(DumpManager.class), mock(CommandQueue.class))); mNavigationBar = createNavBar(mContext); mExternalDisplayNavigationBar = createNavBar(mSysuiTestableContextExternal); @@ -339,7 +342,6 @@ public class NavigationBarTest extends SysuiTestCase { NotificationShadeWindowView mockShadeWindowView = mock(NotificationShadeWindowView.class); WindowInsets windowInsets = new WindowInsets.Builder().setVisible(ime(), false).build(); doReturn(windowInsets).when(mockShadeWindowView).getRootWindowInsets(); - doReturn(mockShadeWindowView).when(mCentralSurfaces).getNotificationShadeWindowView(); doReturn(true).when(mockShadeWindowView).isAttachedToWindow(); doNothing().when(defaultNavBar).checkNavBarModes(); doNothing().when(externalNavBar).checkNavBarModes(); @@ -361,7 +363,7 @@ public class NavigationBarTest extends SysuiTestCase { externalNavBar.setImeWindowStatus(EXTERNAL_DISPLAY_ID, null, IME_VISIBLE, BACK_DISPOSITION_DEFAULT, true); defaultNavBar.setImeWindowStatus( - DEFAULT_DISPLAY, null, IME_INVISIBLE, BACK_DISPOSITION_DEFAULT, false); + DEFAULT_DISPLAY, null, 0 /* vis */, BACK_DISPOSITION_DEFAULT, false); // Verify IME window state will be updated in external NavBar & default NavBar state reset. assertEquals(NAVIGATION_HINT_BACK_ALT | NAVIGATION_HINT_IME_SHOWN | NAVIGATION_HINT_IME_SWITCHER_SHOWN, @@ -375,7 +377,7 @@ public class NavigationBarTest extends SysuiTestCase { @Test public void testSetImeWindowStatusWhenKeyguardLockingAndImeInsetsChange() { NotificationShadeWindowView mockShadeWindowView = mock(NotificationShadeWindowView.class); - doReturn(mockShadeWindowView).when(mCentralSurfaces).getNotificationShadeWindowView(); + doReturn(mockShadeWindowView).when(mNotificationShadeWindowController).getWindowRootView(); doReturn(true).when(mockShadeWindowView).isAttachedToWindow(); doNothing().when(mNavigationBar).checkNavBarModes(); mNavigationBar.init(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/FakeNoteTaskBubbleController.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/FakeNoteTaskBubbleController.kt new file mode 100644 index 000000000000..450aadd70171 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/FakeNoteTaskBubbleController.kt @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.notetask + +import android.content.Context +import android.content.Intent +import android.graphics.drawable.Icon +import android.os.UserHandle +import com.android.wm.shell.bubbles.Bubbles +import java.util.Optional +import kotlinx.coroutines.CoroutineDispatcher + +/** + * Fake for [NoteTaskBubblesController] as mocking suspending functions is not supported in the + * Android tree's version of mockito. Ideally the [NoteTaskBubblesController] should be implemented + * using an interface for effectively providing multiple implementations but as this fake primarily + * for dealing with old version of mockito there isn't any benefit in adding complexity. + */ +class FakeNoteTaskBubbleController( + unUsed1: Context, + unsUsed2: CoroutineDispatcher, + private val optionalBubbles: Optional<Bubbles> +) : NoteTaskBubblesController(unUsed1, unsUsed2) { + override suspend fun areBubblesAvailable() = optionalBubbles.isPresent + + override suspend fun showOrHideAppBubble(intent: Intent, userHandle: UserHandle, icon: Icon) { + optionalBubbles.ifPresentOrElse( + { bubbles -> bubbles.showOrHideAppBubble(intent, userHandle, icon) }, + { throw IllegalAccessException() } + ) + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskBubblesServiceTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskBubblesServiceTest.kt new file mode 100644 index 000000000000..baac9e020280 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskBubblesServiceTest.kt @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.notetask + +import android.content.Intent +import android.graphics.drawable.Icon +import android.os.UserHandle +import androidx.test.filters.SmallTest +import androidx.test.runner.AndroidJUnit4 +import com.android.systemui.R +import com.android.systemui.SysuiTestCase +import com.android.systemui.notetask.NoteTaskBubblesController.NoteTaskBubblesService +import com.android.wm.shell.bubbles.Bubbles +import com.google.common.truth.Truth.assertThat +import java.util.Optional +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.Mockito.verify +import org.mockito.MockitoAnnotations + +/** atest SystemUITests:NoteTaskBubblesServiceTest */ +@SmallTest +@RunWith(AndroidJUnit4::class) +internal class NoteTaskBubblesServiceTest : SysuiTestCase() { + + @Mock private lateinit var bubbles: Bubbles + + private fun createServiceBinder(bubbles: Bubbles? = this.bubbles) = + NoteTaskBubblesService(Optional.ofNullable(bubbles)).onBind(Intent()) + as INoteTaskBubblesService + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + } + + @Test + fun areBubblesAvailable_bubblesNotNull_shouldReturnTrue() { + assertThat(createServiceBinder().areBubblesAvailable()).isTrue() + } + + @Test + fun areBubblesAvailable_bubblesNull_shouldReturnFalse() { + assertThat(createServiceBinder(bubbles = null).areBubblesAvailable()).isFalse() + } + + @Test + fun showOrHideAppBubble() { + val intent = Intent() + val user = UserHandle.SYSTEM + val icon = Icon.createWithResource(context, R.drawable.ic_note_task_shortcut_widget) + + createServiceBinder().showOrHideAppBubble(intent, user, icon) + + verify(bubbles).showOrHideAppBubble(intent, user, icon) + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt index 0954f6f0ffaf..a76af8e83248 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt @@ -36,12 +36,12 @@ import android.content.Intent.FLAG_ACTIVITY_NEW_TASK import android.content.pm.PackageManager import android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED import android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED -import android.content.pm.ShortcutInfo import android.content.pm.ShortcutManager import android.content.pm.UserInfo import android.graphics.drawable.Icon import android.os.UserHandle import android.os.UserManager +import android.provider.Settings import androidx.test.ext.truth.content.IntentSubject.assertThat import androidx.test.filters.SmallTest import androidx.test.runner.AndroidJUnit4 @@ -56,17 +56,22 @@ import com.android.systemui.notetask.NoteTaskEntryPoint.TAIL_BUTTON import com.android.systemui.notetask.NoteTaskEntryPoint.WIDGET_PICKER_SHORTCUT import com.android.systemui.notetask.shortcut.CreateNoteTaskShortcutActivity import com.android.systemui.notetask.shortcut.LaunchNoteTaskActivity -import com.android.systemui.notetask.shortcut.LaunchNoteTaskManagedProfileProxyActivity import com.android.systemui.settings.FakeUserTracker import com.android.systemui.util.mockito.any 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.android.systemui.util.mockito.withArgCaptor +import com.android.systemui.util.settings.SecureSettings import com.android.wm.shell.bubbles.Bubble import com.android.wm.shell.bubbles.Bubbles import com.google.common.truth.Truth.assertThat import java.util.Optional +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.UnconfinedTestDispatcher import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @@ -81,6 +86,7 @@ import org.mockito.Mockito.verifyZeroInteractions import org.mockito.MockitoAnnotations /** atest SystemUITests:NoteTaskControllerTest */ +@OptIn(ExperimentalCoroutinesApi::class) @SmallTest @RunWith(AndroidJUnit4::class) internal class NoteTaskControllerTest : SysuiTestCase() { @@ -98,7 +104,10 @@ internal class NoteTaskControllerTest : SysuiTestCase() { @Mock private lateinit var shortcutManager: ShortcutManager @Mock private lateinit var activityManager: ActivityManager @Mock private lateinit var devicePolicyManager: DevicePolicyManager + @Mock private lateinit var secureSettings: SecureSettings private val userTracker = FakeUserTracker() + private val testDispatcher = UnconfinedTestDispatcher() + private val testScope = TestScope(testDispatcher) @Before fun setUp() { @@ -106,7 +115,11 @@ internal class NoteTaskControllerTest : SysuiTestCase() { whenever(context.getString(R.string.note_task_button_label)) .thenReturn(NOTE_TASK_SHORT_LABEL) + whenever(context.getString(eq(R.string.note_task_shortcut_long_label), any())) + .thenReturn(NOTE_TASK_LONG_LABEL) whenever(context.packageManager).thenReturn(packageManager) + whenever(packageManager.getApplicationInfo(any(), any<Int>())).thenReturn(mock()) + whenever(packageManager.getApplicationLabel(any())).thenReturn(NOTE_TASK_LONG_LABEL) whenever(resolver.resolveInfo(any(), any(), any())).thenReturn(NOTE_TASK_INFO) whenever(userManager.isUserUnlocked).thenReturn(true) whenever(userManager.isUserUnlocked(any<Int>())).thenReturn(true) @@ -123,6 +136,7 @@ internal class NoteTaskControllerTest : SysuiTestCase() { whenever(activityManager.getRunningTasks(anyInt())).thenReturn(emptyList()) whenever(userManager.isManagedProfile(workUserInfo.id)).thenReturn(true) whenever(context.resources).thenReturn(getContext().resources) + whenever(secureSettings.userTracker).thenReturn(userTracker) } private fun createNoteTaskController( @@ -133,7 +147,6 @@ internal class NoteTaskControllerTest : SysuiTestCase() { context = context, resolver = resolver, eventLogger = eventLogger, - optionalBubbles = Optional.ofNullable(bubbles), userManager = userManager, keyguardManager = keyguardManager, isEnabled = isEnabled, @@ -142,6 +155,10 @@ internal class NoteTaskControllerTest : SysuiTestCase() { roleManager = roleManager, shortcutManager = shortcutManager, activityManager = activityManager, + secureSettings = secureSettings, + noteTaskBubblesController = + FakeNoteTaskBubbleController(context, testDispatcher, Optional.ofNullable(bubbles)), + applicationScope = testScope, ) // region onBubbleExpandChanged @@ -157,7 +174,7 @@ internal class NoteTaskControllerTest : SysuiTestCase() { ) verify(eventLogger).logNoteTaskOpened(expectedInfo) - verifyZeroInteractions(context, bubbles, keyguardManager, userManager) + verifyZeroInteractions(bubbles, keyguardManager, userManager) } @Test @@ -172,7 +189,7 @@ internal class NoteTaskControllerTest : SysuiTestCase() { ) verify(eventLogger).logNoteTaskClosed(expectedInfo) - verifyZeroInteractions(context, bubbles, keyguardManager, userManager) + verifyZeroInteractions(bubbles, keyguardManager, userManager) } @Test @@ -186,7 +203,7 @@ internal class NoteTaskControllerTest : SysuiTestCase() { key = Bubble.getAppBubbleKeyForApp(expectedInfo.packageName, expectedInfo.user), ) - verifyZeroInteractions(context, bubbles, keyguardManager, userManager, eventLogger) + verifyZeroInteractions(bubbles, keyguardManager, userManager, eventLogger) } @Test @@ -200,7 +217,7 @@ internal class NoteTaskControllerTest : SysuiTestCase() { key = Bubble.getAppBubbleKeyForApp(expectedInfo.packageName, expectedInfo.user), ) - verifyZeroInteractions(context, bubbles, keyguardManager, userManager, eventLogger) + verifyZeroInteractions(bubbles, keyguardManager, userManager, eventLogger) } @Test @@ -211,7 +228,7 @@ internal class NoteTaskControllerTest : SysuiTestCase() { key = "any other key", ) - verifyZeroInteractions(context, bubbles, keyguardManager, userManager, eventLogger) + verifyZeroInteractions(bubbles, keyguardManager, userManager, eventLogger) } @Test @@ -222,16 +239,19 @@ internal class NoteTaskControllerTest : SysuiTestCase() { key = Bubble.getAppBubbleKeyForApp(NOTE_TASK_INFO.packageName, NOTE_TASK_INFO.user), ) - verifyZeroInteractions(context, bubbles, keyguardManager, userManager, eventLogger) + verifyZeroInteractions(bubbles, keyguardManager, userManager, eventLogger) } // endregion // region showNoteTask - @Test fun showNoteTaskAsUser_keyguardIsLocked_shouldStartActivityWithExpectedUserAndLogUiEvent() { val user10 = UserHandle.of(/* userId= */ 10) val expectedInfo = - NOTE_TASK_INFO.copy(entryPoint = TAIL_BUTTON, isKeyguardLocked = true, user = user10) + NOTE_TASK_INFO.copy( + entryPoint = TAIL_BUTTON, + isKeyguardLocked = true, + user = user10, + ) whenever(keyguardManager.isKeyguardLocked).thenReturn(expectedInfo.isKeyguardLocked) whenever(resolver.resolveInfo(any(), any(), any())).thenReturn(expectedInfo) @@ -332,10 +352,48 @@ internal class NoteTaskControllerTest : SysuiTestCase() { } @Test + fun showNoteTask_defaultUserSet_shouldStartActivityWithExpectedUserAndLogUiEvent() { + whenever(secureSettings.getInt(eq(Settings.Secure.DEFAULT_NOTE_TASK_PROFILE), any())) + .thenReturn(10) + val user10 = UserHandle.of(/* userId= */ 10) + + val expectedInfo = + NOTE_TASK_INFO.copy( + entryPoint = NoteTaskEntryPoint.TAIL_BUTTON, + isKeyguardLocked = true, + user = user10, + ) + whenever(keyguardManager.isKeyguardLocked).thenReturn(expectedInfo.isKeyguardLocked) + whenever(resolver.resolveInfo(any(), any(), any())).thenReturn(expectedInfo) + + createNoteTaskController() + .showNoteTask( + entryPoint = expectedInfo.entryPoint!!, + ) + + val intentCaptor = argumentCaptor<Intent>() + val userCaptor = argumentCaptor<UserHandle>() + verify(context).startActivityAsUser(capture(intentCaptor), capture(userCaptor)) + intentCaptor.value.let { intent -> + assertThat(intent.action).isEqualTo(Intent.ACTION_CREATE_NOTE) + assertThat(intent.`package`).isEqualTo(NOTE_TASK_PACKAGE_NAME) + assertThat(intent.flags and FLAG_ACTIVITY_NEW_TASK).isEqualTo(FLAG_ACTIVITY_NEW_TASK) + assertThat(intent.flags and FLAG_ACTIVITY_MULTIPLE_TASK) + .isEqualTo(FLAG_ACTIVITY_MULTIPLE_TASK) + assertThat(intent.flags and FLAG_ACTIVITY_NEW_DOCUMENT) + .isEqualTo(FLAG_ACTIVITY_NEW_DOCUMENT) + assertThat(intent.getBooleanExtra(Intent.EXTRA_USE_STYLUS_MODE, false)).isTrue() + } + assertThat(userCaptor.value).isEqualTo(user10) + verify(eventLogger).logNoteTaskOpened(expectedInfo) + verifyZeroInteractions(bubbles) + } + + @Test fun showNoteTask_bubblesIsNull_shouldDoNothing() { createNoteTaskController(bubbles = null).showNoteTask(entryPoint = TAIL_BUTTON) - verifyZeroInteractions(context, bubbles, eventLogger) + verifyZeroInteractions(bubbles, eventLogger) } @Test @@ -347,14 +405,14 @@ internal class NoteTaskControllerTest : SysuiTestCase() { noteTaskController.showNoteTask(entryPoint = TAIL_BUTTON) verify(noteTaskController).showNoDefaultNotesAppToast() - verifyZeroInteractions(context, bubbles, eventLogger) + verifyZeroInteractions(bubbles, eventLogger) } @Test fun showNoteTask_flagDisabled_shouldDoNothing() { createNoteTaskController(isEnabled = false).showNoteTask(entryPoint = TAIL_BUTTON) - verifyZeroInteractions(context, bubbles, eventLogger) + verifyZeroInteractions(bubbles, eventLogger) } @Test @@ -363,7 +421,7 @@ internal class NoteTaskControllerTest : SysuiTestCase() { createNoteTaskController().showNoteTask(entryPoint = TAIL_BUTTON) - verifyZeroInteractions(context, bubbles, eventLogger) + verifyZeroInteractions(bubbles, eventLogger) } @Test @@ -483,7 +541,7 @@ internal class NoteTaskControllerTest : SysuiTestCase() { createNoteTaskController().showNoteTask(entryPoint = QUICK_AFFORDANCE) - verifyZeroInteractions(context, bubbles, eventLogger) + verifyZeroInteractions(bubbles, eventLogger) } @Test @@ -499,7 +557,7 @@ internal class NoteTaskControllerTest : SysuiTestCase() { createNoteTaskController().showNoteTask(entryPoint = QUICK_AFFORDANCE) - verifyZeroInteractions(context, bubbles, eventLogger) + verifyZeroInteractions(bubbles, eventLogger) } @Test @@ -611,11 +669,11 @@ internal class NoteTaskControllerTest : SysuiTestCase() { createNoteTaskController(isEnabled = true).onRoleHoldersChanged("NOT_NOTES", user) - verifyZeroInteractions(context) + verify(context, never()).startActivityAsUser(any(), any()) } @Test - fun onRoleHoldersChanged_notesRole_sameUser_shouldUpdateShortcuts() { + fun onRoleHoldersChanged_notesRole_shouldUpdateShortcuts() { val user = userTracker.userHandle val controller = spy(createNoteTaskController()) doNothing().whenever(controller).updateNoteTaskAsUser(any()) @@ -624,22 +682,41 @@ internal class NoteTaskControllerTest : SysuiTestCase() { verify(controller).updateNoteTaskAsUser(user) } + // endregion + // region updateNoteTaskAsUser @Test - fun onRoleHoldersChanged_notesRole_differentUser_shouldUpdateShortcutsInUserProcess() { + fun updateNoteTaskAsUser_sameUser_shouldUpdateShortcuts() { + val user = userTracker.userHandle + val controller = spy(createNoteTaskController()) + doNothing().whenever(controller).updateNoteTaskAsUserInternal(any()) + + controller.updateNoteTaskAsUser(user) + + verify(controller).updateNoteTaskAsUserInternal(user) + verify(context, never()).startServiceAsUser(any(), any()) + } + + @Test + fun updateNoteTaskAsUser_differentUser_shouldUpdateShortcutsInUserProcess() { // FakeUserTracker will default to UserHandle.SYSTEM. val user = UserHandle.CURRENT + val controller = spy(createNoteTaskController(isEnabled = true)) + doNothing().whenever(controller).updateNoteTaskAsUserInternal(any()) - createNoteTaskController(isEnabled = true).onRoleHoldersChanged(ROLE_NOTES, user) + controller.updateNoteTaskAsUser(user) - verify(context).startServiceAsUser(any(), eq(user)) + verify(controller, never()).updateNoteTaskAsUserInternal(any()) + val intent = withArgCaptor { verify(context).startServiceAsUser(capture(), eq(user)) } + assertThat(intent).hasComponentClass(NoteTaskControllerUpdateService::class.java) } // endregion - // region updateNoteTaskAsUser + // region internalUpdateNoteTaskAsUser @Test - fun updateNoteTaskAsUser_withNotesRole_withShortcuts_shouldUpdateShortcuts() { - createNoteTaskController(isEnabled = true).updateNoteTaskAsUser(userTracker.userHandle) + fun updateNoteTaskAsUserInternal_withNotesRole_withShortcuts_shouldUpdateShortcuts() { + createNoteTaskController(isEnabled = true) + .updateNoteTaskAsUserInternal(userTracker.userHandle) val actualComponent = argumentCaptor<ComponentName>() verify(context.packageManager) @@ -652,27 +729,29 @@ internal class NoteTaskControllerTest : SysuiTestCase() { .isEqualTo(CreateNoteTaskShortcutActivity::class.java.name) verify(shortcutManager, never()).disableShortcuts(any()) verify(shortcutManager).enableShortcuts(listOf(SHORTCUT_ID)) - val actualShortcuts = argumentCaptor<List<ShortcutInfo>>() - verify(shortcutManager).updateShortcuts(actualShortcuts.capture()) - val actualShortcut = actualShortcuts.value.first() - assertThat(actualShortcut.id).isEqualTo(SHORTCUT_ID) - assertThat(actualShortcut.intent).run { - hasComponentClass(LaunchNoteTaskActivity::class.java) - hasAction(ACTION_CREATE_NOTE) + val shortcutInfo = withArgCaptor { verify(shortcutManager).updateShortcuts(capture()) } + with(shortcutInfo.first()) { + assertThat(id).isEqualTo(SHORTCUT_ID) + assertThat(intent).run { + hasComponentClass(LaunchNoteTaskActivity::class.java) + hasAction(ACTION_CREATE_NOTE) + } + assertThat(shortLabel).isEqualTo(NOTE_TASK_SHORT_LABEL) + assertThat(longLabel).isEqualTo(NOTE_TASK_LONG_LABEL) + assertThat(isLongLived).isEqualTo(true) + assertThat(icon.resId).isEqualTo(R.drawable.ic_note_task_shortcut_widget) + assertThat(extras?.getString(EXTRA_SHORTCUT_BADGE_OVERRIDE_PACKAGE)) + .isEqualTo(NOTE_TASK_PACKAGE_NAME) } - assertThat(actualShortcut.shortLabel).isEqualTo(NOTE_TASK_SHORT_LABEL) - assertThat(actualShortcut.isLongLived).isEqualTo(true) - assertThat(actualShortcut.icon.resId).isEqualTo(R.drawable.ic_note_task_shortcut_widget) - assertThat(actualShortcut.extras?.getString(EXTRA_SHORTCUT_BADGE_OVERRIDE_PACKAGE)) - .isEqualTo(NOTE_TASK_PACKAGE_NAME) } @Test - fun updateNoteTaskAsUser_noNotesRole_shouldDisableShortcuts() { + fun updateNoteTaskAsUserInternal_noNotesRole_shouldDisableShortcuts() { whenever(roleManager.getRoleHoldersAsUser(ROLE_NOTES, userTracker.userHandle)) .thenReturn(emptyList()) - createNoteTaskController(isEnabled = true).updateNoteTaskAsUser(userTracker.userHandle) + createNoteTaskController(isEnabled = true) + .updateNoteTaskAsUserInternal(userTracker.userHandle) val argument = argumentCaptor<ComponentName>() verify(context.packageManager) @@ -689,8 +768,9 @@ internal class NoteTaskControllerTest : SysuiTestCase() { } @Test - fun updateNoteTaskAsUser_flagDisabled_shouldDisableShortcuts() { - createNoteTaskController(isEnabled = false).updateNoteTaskAsUser(userTracker.userHandle) + fun updateNoteTaskAsUserInternal_flagDisabled_shouldDisableShortcuts() { + createNoteTaskController(isEnabled = false) + .updateNoteTaskAsUserInternal(userTracker.userHandle) val argument = argumentCaptor<ComponentName>() verify(context.packageManager) @@ -707,18 +787,17 @@ internal class NoteTaskControllerTest : SysuiTestCase() { } // endregion - // startregion startNoteTaskProxyActivityForUser + // startregion updateNoteTaskForAllUsers @Test - fun startNoteTaskProxyActivityForUser_shouldStartLaunchNoteTaskProxyActivityWithExpectedUser() { - val user0 = UserHandle.of(0) - createNoteTaskController().startNoteTaskProxyActivityForUser(user0) + fun updateNoteTaskForAllUsers_shouldRunUpdateForCurrentUserAndProfiles() { + userTracker.set(mainAndWorkProfileUsers, mainAndWorkProfileUsers.indexOf(mainUserInfo)) + val controller = spy(createNoteTaskController()) + doNothing().whenever(controller).updateNoteTaskAsUser(any()) - val intentCaptor = argumentCaptor<Intent>() - verify(context).startActivityAsUser(intentCaptor.capture(), eq(user0)) - assertThat(intentCaptor.value).run { - hasComponentClass(LaunchNoteTaskManagedProfileProxyActivity::class.java) - hasFlags(FLAG_ACTIVITY_NEW_TASK) - } + controller.updateNoteTaskForCurrentUserAndManagedProfiles() + + verify(controller).updateNoteTaskAsUser(mainUserInfo.userHandle) + verify(controller).updateNoteTaskAsUser(workUserInfo.userHandle) } // endregion @@ -838,7 +917,8 @@ internal class NoteTaskControllerTest : SysuiTestCase() { // endregion private companion object { - const val NOTE_TASK_SHORT_LABEL = "Notetaking" + const val NOTE_TASK_SHORT_LABEL = "Note-taking" + const val NOTE_TASK_LONG_LABEL = "Note-taking, App" const val NOTE_TASK_ACTIVITY_NAME = "NoteTaskActivity" const val NOTE_TASK_PACKAGE_NAME = "com.android.note.app" const val NOTE_TASK_UID = 123456 diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInitializerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInitializerTest.kt index 4e85b6c555ef..95bb3e0a4538 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInitializerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInitializerTest.kt @@ -32,9 +32,12 @@ import com.android.systemui.util.concurrency.FakeExecutor import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.argumentCaptor import com.android.systemui.util.mockito.eq +import com.android.systemui.util.mockito.mock import com.android.systemui.util.mockito.whenever +import com.android.systemui.util.mockito.withArgCaptor import com.android.systemui.util.time.FakeSystemClock import com.android.wm.shell.bubbles.Bubbles +import com.google.common.truth.Truth.assertThat import java.util.Optional import kotlinx.coroutines.ExperimentalCoroutinesApi import org.junit.Before @@ -71,19 +74,19 @@ internal class NoteTaskInitializerTest : SysuiTestCase() { } private fun createUnderTest( - isEnabled: Boolean, - bubbles: Bubbles?, + isEnabled: Boolean, + bubbles: Bubbles?, ): NoteTaskInitializer = - NoteTaskInitializer( - controller = controller, - commandQueue = commandQueue, - optionalBubbles = Optional.ofNullable(bubbles), - isEnabled = isEnabled, - roleManager = roleManager, - userTracker = userTracker, - keyguardUpdateMonitor = keyguardMonitor, - backgroundExecutor = executor, - ) + NoteTaskInitializer( + controller = controller, + commandQueue = commandQueue, + optionalBubbles = Optional.ofNullable(bubbles), + isEnabled = isEnabled, + roleManager = roleManager, + userTracker = userTracker, + keyguardUpdateMonitor = keyguardMonitor, + backgroundExecutor = executor, + ) @Test fun initialize_withUserUnlocked() { @@ -93,8 +96,8 @@ internal class NoteTaskInitializerTest : SysuiTestCase() { verify(commandQueue).addCallback(any()) verify(roleManager).addOnRoleHoldersChangedListenerAsUser(any(), any(), any()) - verify(controller).setNoteTaskShortcutEnabled(any(), any()) - verify(keyguardMonitor, never()).registerCallback(any()) + verify(controller).updateNoteTaskForCurrentUserAndManagedProfiles() + verify(keyguardMonitor).registerCallback(any()) } @Test @@ -107,6 +110,7 @@ internal class NoteTaskInitializerTest : SysuiTestCase() { verify(roleManager).addOnRoleHoldersChangedListenerAsUser(any(), any(), any()) verify(controller, never()).setNoteTaskShortcutEnabled(any(), any()) verify(keyguardMonitor).registerCallback(any()) + assertThat(userTracker.callbacks).isNotEmpty() } @Test @@ -116,12 +120,12 @@ internal class NoteTaskInitializerTest : SysuiTestCase() { underTest.initialize() verifyZeroInteractions( - commandQueue, - bubbles, - controller, - roleManager, - userManager, - keyguardMonitor, + commandQueue, + bubbles, + controller, + roleManager, + userManager, + keyguardMonitor, ) } @@ -132,12 +136,12 @@ internal class NoteTaskInitializerTest : SysuiTestCase() { underTest.initialize() verifyZeroInteractions( - commandQueue, - bubbles, - controller, - roleManager, - userManager, - keyguardMonitor, + commandQueue, + bubbles, + controller, + roleManager, + userManager, + keyguardMonitor, ) } @@ -146,7 +150,7 @@ internal class NoteTaskInitializerTest : SysuiTestCase() { val expectedKeyEvent = KeyEvent(ACTION_DOWN, KEYCODE_STYLUS_BUTTON_TAIL) val underTest = createUnderTest(isEnabled = true, bubbles = bubbles) underTest.initialize() - val callback = captureArgument { verify(commandQueue).addCallback(capture()) } + val callback = withArgCaptor { verify(commandQueue).addCallback(capture()) } callback.handleSystemKey(expectedKeyEvent) @@ -154,31 +158,49 @@ internal class NoteTaskInitializerTest : SysuiTestCase() { } @Test - fun initialize_userUnlocked() { + fun initialize_userUnlocked_shouldUpdateNoteTask() { whenever(keyguardMonitor.isUserUnlocked(userTracker.userId)).thenReturn(false) val underTest = createUnderTest(isEnabled = true, bubbles = bubbles) underTest.initialize() - val callback = captureArgument { verify(keyguardMonitor).registerCallback(capture()) } + val callback = withArgCaptor { verify(keyguardMonitor).registerCallback(capture()) } whenever(keyguardMonitor.isUserUnlocked(userTracker.userId)).thenReturn(true) callback.onUserUnlocked() - verify(controller).setNoteTaskShortcutEnabled(any(), any()) + + verify(controller).updateNoteTaskForCurrentUserAndManagedProfiles() } @Test - fun initialize_onRoleHoldersChanged() { + fun initialize_onRoleHoldersChanged_shouldRunOnRoleHoldersChanged() { val underTest = createUnderTest(isEnabled = true, bubbles = bubbles) underTest.initialize() - val callback = captureArgument { + val callback = withArgCaptor { verify(roleManager) - .addOnRoleHoldersChangedListenerAsUser(any(), capture(), eq(UserHandle.ALL)) + .addOnRoleHoldersChangedListenerAsUser(any(), capture(), eq(UserHandle.ALL)) } callback.onRoleHoldersChanged(ROLE_NOTES, userTracker.userHandle) verify(controller).onRoleHoldersChanged(ROLE_NOTES, userTracker.userHandle) } -} -private inline fun <reified T : Any> captureArgument(block: ArgumentCaptor<T>.() -> Unit) = - argumentCaptor<T>().apply(block).value + @Test + fun initialize_onProfilesChanged_shouldUpdateNoteTask() { + val underTest = createUnderTest(isEnabled = true, bubbles = bubbles) + underTest.initialize() + + userTracker.callbacks.first().onProfilesChanged(emptyList()) + + verify(controller, times(2)).updateNoteTaskForCurrentUserAndManagedProfiles() + } + + @Test + fun initialize_onUserChanged_shouldUpdateNoteTask() { + val underTest = createUnderTest(isEnabled = true, bubbles = bubbles) + underTest.initialize() + + userTracker.callbacks.first().onUserChanged(0, mock()) + + verify(controller, times(2)).updateNoteTaskForCurrentUserAndManagedProfiles() + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/shortcut/LaunchNoteTaskActivityTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/shortcut/LaunchNoteTaskActivityTest.kt index a0c376ff1a1c..627c4a80e1ec 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/notetask/shortcut/LaunchNoteTaskActivityTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/shortcut/LaunchNoteTaskActivityTest.kt @@ -17,9 +17,6 @@ package com.android.systemui.notetask.shortcut import android.content.Intent -import android.content.pm.UserInfo -import android.os.UserHandle -import android.os.UserManager import android.testing.AndroidTestingRunner import android.testing.TestableLooper import androidx.test.filters.SmallTest @@ -29,17 +26,14 @@ import com.android.dx.mockito.inline.extended.ExtendedMockito.verify import com.android.systemui.SysuiTestCase import com.android.systemui.notetask.NoteTaskController import com.android.systemui.notetask.NoteTaskEntryPoint -import com.android.systemui.settings.FakeUserTracker import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.eq -import com.android.systemui.util.mockito.whenever import org.junit.After import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock -import org.mockito.Mockito.never import org.mockito.MockitoAnnotations @RunWith(AndroidTestingRunner::class) @@ -48,8 +42,6 @@ import org.mockito.MockitoAnnotations class LaunchNoteTaskActivityTest : SysuiTestCase() { @Mock lateinit var noteTaskController: NoteTaskController - @Mock lateinit var userManager: UserManager - private val userTracker: FakeUserTracker = FakeUserTracker() @Rule @JvmField @@ -60,8 +52,6 @@ class LaunchNoteTaskActivityTest : SysuiTestCase() { override fun create(intent: Intent?) = LaunchNoteTaskActivity( controller = noteTaskController, - userManager = userManager, - userTracker = userTracker ) }, /* initialTouchMode= */ false, @@ -71,7 +61,6 @@ class LaunchNoteTaskActivityTest : SysuiTestCase() { @Before fun setUp() { MockitoAnnotations.initMocks(this) - whenever(userManager.isManagedProfile(eq(workProfileUser.id))).thenReturn(true) } @After @@ -83,36 +72,7 @@ class LaunchNoteTaskActivityTest : SysuiTestCase() { fun startActivityOnNonWorkProfileUser_shouldLaunchNoteTask() { activityRule.launchActivity(/* startIntent= */ null) - verify(noteTaskController).showNoteTask(eq(NoteTaskEntryPoint.WIDGET_PICKER_SHORTCUT)) - } - - @Test - fun startActivityOnWorkProfileUser_shouldLaunchProxyActivity() { - val mainUserHandle: UserHandle = mainUser.userHandle - userTracker.set(listOf(mainUser, workProfileUser), selectedUserIndex = 1) - whenever(userManager.isManagedProfile).thenReturn(true) - whenever(userManager.mainUser).thenReturn(mainUserHandle) - - activityRule.launchActivity(/* startIntent= */ null) - - verify(noteTaskController).startNoteTaskProxyActivityForUser(eq(mainUserHandle)) - } - - @Test - fun startActivityOnWorkProfileUser_noMainUser_shouldNotLaunch() { - userTracker.set(listOf(mainUser, workProfileUser), selectedUserIndex = 1) - whenever(userManager.isManagedProfile).thenReturn(true) - whenever(userManager.mainUser).thenReturn(null) - - activityRule.launchActivity(/* startIntent= */ null) - - verify(noteTaskController, never()).showNoteTask(any()) - verify(noteTaskController, never()).startNoteTaskProxyActivityForUser(any()) - } - - private companion object { - val mainUser = UserInfo(/* id= */ 0, /* name= */ "primary", /* flags= */ UserInfo.FLAG_MAIN) - val workProfileUser = - UserInfo(/* id= */ 10, /* name= */ "work", /* flags= */ UserInfo.FLAG_PROFILE) + verify(noteTaskController) + .showNoteTaskAsUser(eq(NoteTaskEntryPoint.WIDGET_PICKER_SHORTCUT), any()) } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/shortcut/LaunchNoteTaskManagedProfileProxyActivityTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/shortcut/LaunchNoteTaskManagedProfileProxyActivityTest.kt deleted file mode 100644 index 6347c3404348..000000000000 --- a/packages/SystemUI/tests/src/com/android/systemui/notetask/shortcut/LaunchNoteTaskManagedProfileProxyActivityTest.kt +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.notetask.shortcut - -import android.content.Intent -import android.content.pm.UserInfo -import android.os.UserHandle -import android.os.UserManager -import android.testing.AndroidTestingRunner -import android.testing.TestableLooper -import androidx.test.filters.SmallTest -import androidx.test.rule.ActivityTestRule -import androidx.test.runner.intercepting.SingleActivityFactory -import com.android.dx.mockito.inline.extended.ExtendedMockito.never -import com.android.dx.mockito.inline.extended.ExtendedMockito.verify -import com.android.systemui.SysuiTestCase -import com.android.systemui.notetask.NoteTaskController -import com.android.systemui.notetask.NoteTaskEntryPoint -import com.android.systemui.settings.FakeUserTracker -import com.android.systemui.util.mockito.any -import com.android.systemui.util.mockito.eq -import com.android.systemui.util.mockito.whenever -import org.junit.After -import org.junit.Before -import org.junit.Rule -import org.junit.Test -import org.junit.runner.RunWith -import org.mockito.Mock -import org.mockito.MockitoAnnotations - -@RunWith(AndroidTestingRunner::class) -@SmallTest -@TestableLooper.RunWithLooper -class LaunchNoteTaskManagedProfileProxyActivityTest : SysuiTestCase() { - - @Mock lateinit var noteTaskController: NoteTaskController - @Mock lateinit var userManager: UserManager - private val userTracker = FakeUserTracker() - - @Rule - @JvmField - val activityRule = - ActivityTestRule<LaunchNoteTaskManagedProfileProxyActivity>( - /* activityFactory= */ object : - SingleActivityFactory<LaunchNoteTaskManagedProfileProxyActivity>( - LaunchNoteTaskManagedProfileProxyActivity::class.java - ) { - override fun create(intent: Intent?) = - LaunchNoteTaskManagedProfileProxyActivity( - controller = noteTaskController, - userManager = userManager, - userTracker = userTracker - ) - }, - /* initialTouchMode= */ false, - /* launchActivity= */ false, - ) - - @Before - fun setUp() { - MockitoAnnotations.initMocks(this) - whenever(userManager.isManagedProfile(eq(workProfileUser.id))).thenReturn(true) - } - - @After - fun tearDown() { - activityRule.finishActivity() - } - - @Test - fun startActivity_noWorkProfileUser_shouldNotLaunchNoteTask() { - userTracker.set(listOf(mainUser), selectedUserIndex = 0) - activityRule.launchActivity(/* startIntent= */ null) - - verify(noteTaskController, never()).showNoteTaskAsUser(any(), any()) - } - - @Test - fun startActivity_hasWorkProfileUser_shouldLaunchNoteTaskOnTheWorkProfileUser() { - userTracker.set(mainAndWorkProfileUsers, mainAndWorkProfileUsers.indexOf(mainUser)) - activityRule.launchActivity(/* startIntent= */ null) - - val workProfileUserHandle: UserHandle = workProfileUser.userHandle - verify(noteTaskController) - .showNoteTaskAsUser( - eq(NoteTaskEntryPoint.WIDGET_PICKER_SHORTCUT), - eq(workProfileUserHandle) - ) - } - - private companion object { - val mainUser = UserInfo(/* id= */ 0, /* name= */ "primary", /* flags= */ UserInfo.FLAG_MAIN) - val workProfileUser = - UserInfo(/* id= */ 10, /* name= */ "work", /* flags= */ UserInfo.FLAG_PROFILE) - val mainAndWorkProfileUsers = listOf(mainUser, workProfileUser) - } -} diff --git a/packages/SystemUI/tests/src/com/android/systemui/power/data/repository/PowerRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/power/data/repository/PowerRepositoryImplTest.kt index bb3b3f709a7b..a01394f027cf 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/power/data/repository/PowerRepositoryImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/power/data/repository/PowerRepositoryImplTest.kt @@ -24,7 +24,11 @@ import android.os.PowerManager import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.broadcast.BroadcastDispatcher +import com.android.systemui.util.mockito.any +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.time.FakeSystemClock import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.launchIn @@ -47,6 +51,8 @@ import org.mockito.MockitoAnnotations @RunWith(JUnit4::class) class PowerRepositoryImplTest : SysuiTestCase() { + private val systemClock = FakeSystemClock() + @Mock private lateinit var manager: PowerManager @Mock private lateinit var dispatcher: BroadcastDispatcher @Captor private lateinit var receiverCaptor: ArgumentCaptor<BroadcastReceiver> @@ -62,7 +68,13 @@ class PowerRepositoryImplTest : SysuiTestCase() { isInteractive = true whenever(manager.isInteractive).then { isInteractive } - underTest = PowerRepositoryImpl(manager = manager, dispatcher = dispatcher) + underTest = + PowerRepositoryImpl( + manager, + context.applicationContext, + systemClock, + dispatcher, + ) } @Test @@ -160,6 +172,27 @@ class PowerRepositoryImplTest : SysuiTestCase() { job.cancel() } + @Test + fun wakeUp_notifiesPowerManager() { + systemClock.setUptimeMillis(345000) + + underTest.wakeUp("fakeWhy", PowerManager.WAKE_REASON_GESTURE) + + val reasonCaptor = argumentCaptor<String>() + verify(manager) + .wakeUp(eq(345000L), eq(PowerManager.WAKE_REASON_GESTURE), capture(reasonCaptor)) + assertThat(reasonCaptor.value).contains("fakeWhy") + } + + @Test + fun wakeUp_usesApplicationPackageName() { + underTest.wakeUp("fakeWhy", PowerManager.WAKE_REASON_GESTURE) + + val reasonCaptor = argumentCaptor<String>() + verify(manager).wakeUp(any(), any(), capture(reasonCaptor)) + assertThat(reasonCaptor.value).contains(context.applicationContext.packageName) + } + private fun verifyRegistered() { // We must verify with all arguments, even those that are optional because they have default // values because Mockito is forcing us to. Once we can use mockito-kotlin, we should be diff --git a/packages/SystemUI/tests/src/com/android/systemui/power/domain/interactor/PowerInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/power/domain/interactor/PowerInteractorTest.kt index 31d451227a9b..023ed061c642 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/power/domain/interactor/PowerInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/power/domain/interactor/PowerInteractorTest.kt @@ -17,9 +17,14 @@ package com.android.systemui.power.domain.interactor +import android.os.PowerManager import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase +import com.android.systemui.classifier.FalsingCollector +import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.power.data.repository.FakePowerRepository +import com.android.systemui.statusbar.phone.ScreenOffAnimationController +import com.android.systemui.util.mockito.whenever import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.launchIn @@ -29,6 +34,9 @@ import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.JUnit4 +import org.mockito.Mock +import org.mockito.Mockito.verify +import org.mockito.MockitoAnnotations @SmallTest @RunWith(JUnit4::class) @@ -36,14 +44,25 @@ class PowerInteractorTest : SysuiTestCase() { private lateinit var underTest: PowerInteractor private lateinit var repository: FakePowerRepository + @Mock private lateinit var falsingCollector: FalsingCollector + @Mock private lateinit var screenOffAnimationController: ScreenOffAnimationController + @Mock private lateinit var statusBarStateController: StatusBarStateController @Before fun setUp() { + MockitoAnnotations.initMocks(this) + repository = FakePowerRepository( initialInteractive = true, ) - underTest = PowerInteractor(repository = repository) + underTest = + PowerInteractor( + repository, + falsingCollector, + screenOffAnimationController, + statusBarStateController, + ) } @Test @@ -72,6 +91,40 @@ class PowerInteractorTest : SysuiTestCase() { job.cancel() } + @Test + fun wakeUpIfDozing_notDozing_notWoken() { + whenever(statusBarStateController.isDozing).thenReturn(false) + whenever(screenOffAnimationController.allowWakeUpIfDozing()).thenReturn(true) + + underTest.wakeUpIfDozing("why", PowerManager.WAKE_REASON_TAP) + + assertThat(repository.lastWakeWhy).isNull() + assertThat(repository.lastWakeReason).isNull() + } + + @Test + fun wakeUpIfDozing_notAllowed_notWoken() { + whenever(screenOffAnimationController.allowWakeUpIfDozing()).thenReturn(false) + whenever(statusBarStateController.isDozing).thenReturn(true) + + underTest.wakeUpIfDozing("why", PowerManager.WAKE_REASON_TAP) + + assertThat(repository.lastWakeWhy).isNull() + assertThat(repository.lastWakeReason).isNull() + } + + @Test + fun wakeUpIfDozing_dozingAndAllowed_wokenAndFalsingNotified() { + whenever(statusBarStateController.isDozing).thenReturn(true) + whenever(screenOffAnimationController.allowWakeUpIfDozing()).thenReturn(true) + + underTest.wakeUpIfDozing("testReason", PowerManager.WAKE_REASON_GESTURE) + + assertThat(repository.lastWakeWhy).isEqualTo("testReason") + assertThat(repository.lastWakeReason).isEqualTo(PowerManager.WAKE_REASON_GESTURE) + verify(falsingCollector).onScreenOnFromTouch() + } + companion object { private val IMMEDIATE = Dispatchers.Main.immediate } diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentDisableFlagsLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentDisableFlagsLoggerTest.kt index aacc695ef301..93f316e9a619 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentDisableFlagsLoggerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentDisableFlagsLoggerTest.kt @@ -49,7 +49,7 @@ class QSFragmentDisableFlagsLoggerTest : SysuiTestCase() { buffer.dump(PrintWriter(stringWriter), tailLength = 0) val actualString = stringWriter.toString() val expectedLogString = disableFlagsLogger.getDisableFlagsString( - old = null, new = state, newAfterLocalModification = state + new = state, newAfterLocalModification = state ) assertThat(actualString).contains(expectedLogString) diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java index 87ca9dfdae11..fcda5f50cef2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java @@ -50,10 +50,8 @@ import androidx.test.filters.SmallTest; import com.android.keyguard.BouncerPanelExpansionCalculator; import com.android.systemui.R; import com.android.systemui.SysuiBaseFragmentTest; -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.MediaHost; import com.android.systemui.qs.customize.QSCustomizerController; import com.android.systemui.qs.dagger.QSFragmentComponent; @@ -169,9 +167,7 @@ public class QSFragmentTest extends SysuiBaseFragmentTest { } @Test - public void transitionToFullShade_largeScreen_flagEnabled_alphaLargeScreenShadeInterpolator() { - when(mFeatureFlags.isEnabled(Flags.LARGE_SHADE_GRANULAR_ALPHA_INTERPOLATION)) - .thenReturn(true); + public void transitionToFullShade_largeScreen_alphaLargeScreenShadeInterpolator() { QSFragment fragment = resumeAndGetFragment(); setIsLargeScreen(); setStatusBarCurrentAndUpcomingState(StatusBarState.SHADE); @@ -188,25 +184,6 @@ public class QSFragmentTest extends SysuiBaseFragmentTest { } @Test - public void transitionToFullShade_largeScreen_flagDisabled_alphaStandardInterpolator() { - when(mFeatureFlags.isEnabled(Flags.LARGE_SHADE_GRANULAR_ALPHA_INTERPOLATION)) - .thenReturn(false); - QSFragment fragment = resumeAndGetFragment(); - setIsLargeScreen(); - setStatusBarCurrentAndUpcomingState(StatusBarState.SHADE); - boolean isTransitioningToFullShade = true; - float transitionProgress = 0.5f; - float squishinessFraction = 0.5f; - when(mLargeScreenShadeInterpolator.getQsAlpha(transitionProgress)).thenReturn(123f); - - fragment.setTransitionToFullShadeProgress(isTransitioningToFullShade, transitionProgress, - squishinessFraction); - - assertThat(mQsFragmentView.getAlpha()) - .isEqualTo(ShadeInterpolation.getContentAlpha(transitionProgress)); - } - - @Test public void transitionToFullShade_onKeyguard_noBouncer_setsAlphaUsingLinearInterpolator() { QSFragment fragment = resumeAndGetFragment(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java index 810ab344e7d8..d98bcee1e01a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java @@ -67,8 +67,8 @@ import com.android.systemui.qs.logging.QSLogger; import com.android.systemui.qs.tileimpl.QSTileImpl; import com.android.systemui.settings.UserFileManager; import com.android.systemui.settings.UserTracker; +import com.android.systemui.shade.ShadeController; import com.android.systemui.statusbar.phone.AutoTileManager; -import com.android.systemui.statusbar.phone.CentralSurfaces; import com.android.systemui.tuner.TunerService; import com.android.systemui.util.FakeSharedPreferences; import com.android.systemui.util.concurrency.FakeExecutor; @@ -86,7 +86,6 @@ import org.mockito.stubbing.Answer; import java.io.PrintWriter; import java.io.StringWriter; import java.util.List; -import java.util.Optional; import java.util.concurrent.Executor; import javax.inject.Provider; @@ -110,7 +109,7 @@ public class QSTileHostTest extends SysuiTestCase { @Mock private Provider<AutoTileManager> mAutoTiles; @Mock - private CentralSurfaces mCentralSurfaces; + private ShadeController mShadeController; @Mock private QSLogger mQSLogger; @Mock @@ -161,7 +160,7 @@ public class QSTileHostTest extends SysuiTestCase { mSecureSettings = new FakeSettings(); saveSetting(""); mQSTileHost = new TestQSTileHost(mContext, mDefaultFactory, mMainExecutor, - mPluginManager, mTunerService, mAutoTiles, mCentralSurfaces, + mPluginManager, mTunerService, mAutoTiles, mShadeController, mQSLogger, mUserTracker, mSecureSettings, mCustomTileStatePersister, mTileLifecycleManagerFactory, mUserFileManager, mFeatureFlags); @@ -682,13 +681,13 @@ public class QSTileHostTest extends SysuiTestCase { QSFactory defaultFactory, Executor mainExecutor, PluginManager pluginManager, TunerService tunerService, Provider<AutoTileManager> autoTiles, - CentralSurfaces centralSurfaces, QSLogger qsLogger, + ShadeController shadeController, QSLogger qsLogger, UserTracker userTracker, SecureSettings secureSettings, CustomTileStatePersister customTileStatePersister, TileLifecycleManager.Factory tileLifecycleManagerFactory, UserFileManager userFileManager, FeatureFlags featureFlags) { super(context, defaultFactory, mainExecutor, pluginManager, - tunerService, autoTiles, Optional.of(centralSurfaces), qsLogger, + tunerService, autoTiles, shadeController, qsLogger, userTracker, secureSettings, customTileStatePersister, tileLifecycleManagerFactory, userFileManager, featureFlags); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt index 198ed4ac08fd..41240e51c9fc 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt @@ -35,7 +35,7 @@ import android.view.View import com.android.internal.logging.MetricsLogger import com.android.systemui.SysuiTestCase import com.android.systemui.animation.ActivityLaunchAnimator -import com.android.systemui.animation.LaunchableFrameLayout +import com.android.systemui.animation.view.LaunchableFrameLayout import com.android.systemui.classifier.FalsingManagerFake import com.android.systemui.plugins.ActivityStarter import com.android.systemui.plugins.qs.QSTile diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/PanelInteractorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/PanelInteractorImplTest.kt index 45783abe9ee4..6556cfd22901 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/PanelInteractorImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/PanelInteractorImplTest.kt @@ -18,8 +18,7 @@ package com.android.systemui.qs.pipeline.domain.interactor import android.testing.AndroidTestingRunner import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase -import com.android.systemui.statusbar.phone.CentralSurfaces -import java.util.Optional +import com.android.systemui.shade.ShadeController import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @@ -31,7 +30,7 @@ import org.mockito.MockitoAnnotations @SmallTest class PanelInteractorImplTest : SysuiTestCase() { - @Mock private lateinit var centralSurfaces: CentralSurfaces + @Mock private lateinit var shadeController: ShadeController @Before fun setup() { @@ -40,37 +39,28 @@ class PanelInteractorImplTest : SysuiTestCase() { @Test fun openPanels_callsCentralSurfaces() { - val underTest = PanelInteractorImpl(Optional.of(centralSurfaces)) + val underTest = PanelInteractorImpl(shadeController) underTest.openPanels() - verify(centralSurfaces).postAnimateOpenPanels() + verify(shadeController).postAnimateExpandQs() } @Test fun collapsePanels_callsCentralSurfaces() { - val underTest = PanelInteractorImpl(Optional.of(centralSurfaces)) + val underTest = PanelInteractorImpl(shadeController) underTest.collapsePanels() - verify(centralSurfaces).postAnimateCollapsePanels() + verify(shadeController).postAnimateCollapseShade() } @Test fun forceCollapsePanels_callsCentralSurfaces() { - val underTest = PanelInteractorImpl(Optional.of(centralSurfaces)) + val underTest = PanelInteractorImpl(shadeController) underTest.forceCollapsePanels() - verify(centralSurfaces).postAnimateForceCollapsePanels() - } - - @Test - fun whenOptionalEmpty_doesnThrow() { - val underTest = PanelInteractorImpl(Optional.empty()) - - underTest.openPanels() - underTest.collapsePanels() - underTest.forceCollapsePanels() + verify(shadeController).postAnimateForceCollapseShade() } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest.java index f55d262cfc0d..180fed904d01 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest.java @@ -155,6 +155,28 @@ public class QSIconViewImplTest extends SysuiTestCase { inOrder.verify(d).stop(); } + @Test + public void testAnimatorCallbackRemovedOnOldDrawable() { + ImageView iv = new ImageView(mContext); + AnimatedVectorDrawable d1 = mock(AnimatedVectorDrawable.class); + when(d1.getConstantState()).thenReturn(fakeConstantState(d1)); + AnimatedVectorDrawable d2 = mock(AnimatedVectorDrawable.class); + when(d2.getConstantState()).thenReturn(fakeConstantState(d2)); + State s = new State(); + s.isTransient = true; + + // When set Animatable2 d1 + s.icon = new QSTileImpl.DrawableIcon(d1); + mIconView.updateIcon(iv, s, true); + + // And then set Animatable2 d2 + s.icon = new QSTileImpl.DrawableIcon(d2); + mIconView.updateIcon(iv, s, true); + + // Then d1 has its callback cleared + verify(d1).clearAnimationCallbacks(); + } + private static Drawable.ConstantState fakeConstantState(Drawable otherDrawable) { return new Drawable.ConstantState() { @Override diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/FontScalingTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/FontScalingTileTest.kt index ddbfca57e688..cbc355380568 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/FontScalingTileTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/FontScalingTileTest.kt @@ -33,6 +33,8 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.qs.QSHost import com.android.systemui.qs.QsEventLogger import com.android.systemui.qs.logging.QSLogger +import com.android.systemui.settings.UserTracker +import com.android.systemui.statusbar.policy.KeyguardStateController import com.android.systemui.util.concurrency.FakeExecutor import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.eq @@ -44,8 +46,11 @@ import org.junit.After import org.junit.Before import org.junit.Test import org.junit.runner.RunWith +import org.mockito.ArgumentCaptor +import org.mockito.Captor import org.mockito.Mock import org.mockito.Mockito.anyBoolean +import org.mockito.Mockito.never import org.mockito.Mockito.verify import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations @@ -61,6 +66,8 @@ class FontScalingTileTest : SysuiTestCase() { @Mock private lateinit var qsLogger: QSLogger @Mock private lateinit var dialogLaunchAnimator: DialogLaunchAnimator @Mock private lateinit var uiEventLogger: QsEventLogger + @Mock private lateinit var userTracker: UserTracker + @Mock private lateinit var keyguardStateController: KeyguardStateController private lateinit var testableLooper: TestableLooper private lateinit var systemClock: FakeSystemClock @@ -69,6 +76,8 @@ class FontScalingTileTest : SysuiTestCase() { val featureFlags = FakeFeatureFlags() + @Captor private lateinit var argumentCaptor: ArgumentCaptor<Runnable> + @Before fun setUp() { MockitoAnnotations.initMocks(this) @@ -88,11 +97,13 @@ class FontScalingTileTest : SysuiTestCase() { statusBarStateController, activityStarter, qsLogger, + keyguardStateController, dialogLaunchAnimator, FakeSettings(), FakeSettings(), FakeSystemClock(), featureFlags, + userTracker, backgroundDelayableExecutor, ) fontScalingTile.initialize() @@ -124,15 +135,45 @@ class FontScalingTileTest : SysuiTestCase() { } @Test - fun clickTile_showDialog() { + fun clickTile_screenUnlocked_showDialogAnimationFromView() { + `when`(keyguardStateController.isShowing).thenReturn(false) val view = View(context) fontScalingTile.click(view) testableLooper.processAllMessages() + verify(activityStarter) + .executeRunnableDismissingKeyguard( + argumentCaptor.capture(), + eq(null), + eq(true), + eq(true), + eq(false) + ) + argumentCaptor.value.run() verify(dialogLaunchAnimator).showFromView(any(), eq(view), nullable(), anyBoolean()) } @Test + fun clickTile_onLockScreen_neverShowDialogAnimationFromView() { + `when`(keyguardStateController.isShowing).thenReturn(true) + val view = View(context) + fontScalingTile.click(view) + testableLooper.processAllMessages() + + verify(activityStarter) + .executeRunnableDismissingKeyguard( + argumentCaptor.capture(), + eq(null), + eq(true), + eq(true), + eq(false) + ) + argumentCaptor.value.run() + verify(dialogLaunchAnimator, never()) + .showFromView(any(), eq(view), nullable(), anyBoolean()) + } + + @Test fun getLongClickIntent_getExpectedIntent() { val intent: Intent? = fontScalingTile.getLongClickIntent() diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java index 66143923132b..5b3068744df0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java @@ -968,6 +968,7 @@ public class InternetDialogControllerTest extends SysuiTestCase { assertThat(mInternetDialogController.mSubIdTelephonyManagerMap.get(SUB_ID)).isEqualTo( mTelephonyManager); assertThat(mInternetDialogController.mSubIdTelephonyCallbackMap.get(SUB_ID)).isNotNull(); + assertThat(mInternetDialogController.mCallback).isNotNull(); mInternetDialogController.onStop(); @@ -980,6 +981,7 @@ public class InternetDialogControllerTest extends SysuiTestCase { verify(mAccessPointController).removeAccessPointCallback(mInternetDialogController); verify(mConnectivityManager).unregisterNetworkCallback( any(ConnectivityManager.NetworkCallback.class)); + assertThat(mInternetDialogController.mCallback).isNull(); } private String getResourcesString(String name) { diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt index 105387d49bd4..05a16994e021 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt @@ -70,7 +70,7 @@ class QuickSettingsSceneViewModelTest : SysuiTestCase() { fun onContentClicked_deviceUnlocked_switchesToGone() = testScope.runTest { val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_1)) - authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.PIN(1234)) + authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234)) authenticationInteractor.unlockDevice() runCurrent() @@ -83,7 +83,7 @@ class QuickSettingsSceneViewModelTest : SysuiTestCase() { fun onContentClicked_deviceLockedSecurely_switchesToBouncer() = testScope.runTest { val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_1)) - authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.PIN(1234)) + authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234)) authenticationInteractor.lockDevice() runCurrent() diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java index 8744aa346f8d..59b595393749 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java @@ -16,9 +16,11 @@ package com.android.systemui.screenrecord; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThrows; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doThrow; @@ -32,6 +34,7 @@ import android.content.Intent; import android.os.Binder; import android.os.Handler; import android.os.RemoteException; +import android.os.UserHandle; import android.testing.AndroidTestingRunner; import androidx.test.filters.SmallTest; @@ -41,7 +44,9 @@ import com.android.systemui.SysuiTestCase; import com.android.systemui.media.MediaProjectionCaptureTarget; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.settings.UserContextProvider; +import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.phone.KeyguardDismissUtil; +import com.android.systemui.statusbar.policy.KeyguardStateController; import org.junit.Before; import org.junit.Test; @@ -77,20 +82,38 @@ public class RecordingServiceTest extends SysuiTestCase { private UserContextProvider mUserContextTracker; @Captor private ArgumentCaptor<Runnable> mRunnableCaptor; - private KeyguardDismissUtil mKeyguardDismissUtil = new KeyguardDismissUtil() { - public void executeWhenUnlocked(ActivityStarter.OnDismissAction action, - boolean requiresShadeOpen) { - action.onDismiss(); - } - }; + @Mock + private KeyguardStateController mKeyguardStateController; + @Mock + private SysuiStatusBarStateController mStatusBarStateController; + @Mock + private ActivityStarter mActivityStarter; + + private static final String PERMISSION_SELF = "com.android.systemui.permission.SELF"; + + private KeyguardDismissUtil mKeyguardDismissUtil = new KeyguardDismissUtil( + mKeyguardStateController, mStatusBarStateController, mActivityStarter); private RecordingService mRecordingService; + private class RecordingServiceTestable extends RecordingService { + RecordingServiceTestable( + RecordingController controller, Executor executor, + Handler handler, UiEventLogger uiEventLogger, + NotificationManager notificationManager, + UserContextProvider userContextTracker, KeyguardDismissUtil keyguardDismissUtil) { + super(controller, executor, handler, + uiEventLogger, notificationManager, userContextTracker, keyguardDismissUtil); + attachBaseContext(mContext); + } + } + @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); - mRecordingService = Mockito.spy(new RecordingService(mController, mExecutor, mHandler, - mUiEventLogger, mNotificationManager, mUserContextTracker, mKeyguardDismissUtil)); + mRecordingService = Mockito.spy(new RecordingServiceTestable(mController, mExecutor, + mHandler, mUiEventLogger, mNotificationManager, + mUserContextTracker, mKeyguardDismissUtil)); // Return actual context info doReturn(mContext).when(mRecordingService).getApplicationContext(); @@ -160,8 +183,7 @@ public class RecordingServiceTest extends SysuiTestCase { Intent startIntent = RecordingService.getStartIntent(mContext, 0, 0, false, null); mRecordingService.onStartCommand(startIntent, 0, 0); - // Then the state is set to not recording - verify(mController).updateState(false); + assertUpdateState(false); } @Test @@ -179,7 +201,7 @@ public class RecordingServiceTest extends SysuiTestCase { mRecordingService.onStopped(); - verify(mController).updateState(false); + assertUpdateState(false); } @Test @@ -235,8 +257,21 @@ public class RecordingServiceTest extends SysuiTestCase { verify(mExecutor).execute(mRunnableCaptor.capture()); mRunnableCaptor.getValue().run(); - // Then the state is set to not recording and we cancel the notification - verify(mController).updateState(false); + assertUpdateState(false); verify(mNotificationManager).cancelAsUser(any(), anyInt(), any()); } + + private void assertUpdateState(boolean state) { + // Then the state is set to not recording, and we cancel the notification + // non SYSTEM user doesn't have the reference to the correct controller, + // so a broadcast is sent in case of non SYSTEM user. + if (UserHandle.USER_SYSTEM == mContext.getUserId()) { + verify(mController).updateState(state); + } else { + ArgumentCaptor<Intent> argumentCaptor = ArgumentCaptor.forClass(Intent.class); + verify(mRecordingService).sendBroadcast(argumentCaptor.capture(), eq(PERMISSION_SELF)); + assertEquals(RecordingController.INTENT_UPDATE_STATE, + argumentCaptor.getValue().getAction()); + } + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsServiceTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsServiceTest.java index 67b1099c1e0a..d8897e9048c3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsServiceTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsServiceTest.java @@ -16,14 +16,17 @@ package com.android.systemui.screenshot.appclips; +import static android.content.Intent.CAPTURE_CONTENT_FOR_NOTE_BLOCKED_BY_ADMIN; +import static android.content.Intent.CAPTURE_CONTENT_FOR_NOTE_FAILED; +import static android.content.Intent.CAPTURE_CONTENT_FOR_NOTE_SUCCESS; +import static android.content.Intent.CAPTURE_CONTENT_FOR_NOTE_WINDOW_MODE_UNSUPPORTED; + import static com.android.systemui.flags.Flags.SCREENSHOT_APP_CLIPS; 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.verify; import static org.mockito.Mockito.when; import android.app.admin.DevicePolicyManager; @@ -31,8 +34,6 @@ import android.content.Context; import android.content.Intent; import android.os.IBinder; import android.os.RemoteException; -import android.os.UserHandle; -import android.os.UserManager; import androidx.test.runner.AndroidJUnit4; @@ -46,7 +47,6 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; -import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import java.util.Optional; @@ -63,7 +63,6 @@ public final class AppClipsServiceTest extends SysuiTestCase { @Mock private Optional<Bubbles> mOptionalBubbles; @Mock private Bubbles mBubbles; @Mock private DevicePolicyManager mDevicePolicyManager; - @Mock private UserManager mUserManager; private AppClipsService mAppClipsService; @@ -81,51 +80,84 @@ public final class AppClipsServiceTest extends SysuiTestCase { } @Test + public void flagOff_internal_shouldReturnFailed() throws RemoteException { + when(mFeatureFlags.isEnabled(SCREENSHOT_APP_CLIPS)).thenReturn(false); + + assertThat(getInterfaceWithRealContext() + .canLaunchCaptureContentActivityForNoteInternal(FAKE_TASK_ID)) + .isEqualTo(CAPTURE_CONTENT_FOR_NOTE_FAILED); + } + + @Test public void emptyBubbles_shouldReturnFalse() throws RemoteException { - when(mFeatureFlags.isEnabled(SCREENSHOT_APP_CLIPS)).thenReturn(true); - when(mOptionalBubbles.isEmpty()).thenReturn(true); + mockForEmptyBubbles(); assertThat(getInterfaceWithRealContext() .canLaunchCaptureContentActivityForNote(FAKE_TASK_ID)).isFalse(); } @Test + public void emptyBubbles_internal_shouldReturnFailed() throws RemoteException { + mockForEmptyBubbles(); + + assertThat(getInterfaceWithRealContext() + .canLaunchCaptureContentActivityForNoteInternal(FAKE_TASK_ID)) + .isEqualTo(CAPTURE_CONTENT_FOR_NOTE_FAILED); + } + + @Test public void taskIdNotAppBubble_shouldReturnFalse() throws RemoteException { - when(mFeatureFlags.isEnabled(SCREENSHOT_APP_CLIPS)).thenReturn(true); - when(mOptionalBubbles.isEmpty()).thenReturn(false); - when(mOptionalBubbles.get()).thenReturn(mBubbles); - when(mBubbles.isAppBubbleTaskId(eq((FAKE_TASK_ID)))).thenReturn(false); + mockForTaskIdNotAppBubble(); assertThat(getInterfaceWithRealContext() .canLaunchCaptureContentActivityForNote(FAKE_TASK_ID)).isFalse(); } @Test + public void taskIdNotAppBubble_internal_shouldReturnWindowUnsupported() throws RemoteException { + mockForTaskIdNotAppBubble(); + + assertThat(getInterfaceWithRealContext() + .canLaunchCaptureContentActivityForNoteInternal(FAKE_TASK_ID)) + .isEqualTo(CAPTURE_CONTENT_FOR_NOTE_WINDOW_MODE_UNSUPPORTED); + } + + @Test public void dpmScreenshotBlocked_shouldReturnFalse() throws RemoteException { - when(mFeatureFlags.isEnabled(SCREENSHOT_APP_CLIPS)).thenReturn(true); - when(mOptionalBubbles.isEmpty()).thenReturn(false); - when(mOptionalBubbles.get()).thenReturn(mBubbles); - when(mBubbles.isAppBubbleTaskId(eq((FAKE_TASK_ID)))).thenReturn(true); - when(mDevicePolicyManager.getScreenCaptureDisabled(eq(null))).thenReturn(true); + mockForScreenshotBlocked(); assertThat(getInterfaceWithRealContext() .canLaunchCaptureContentActivityForNote(FAKE_TASK_ID)).isFalse(); } @Test + public void dpmScreenshotBlocked_internal_shouldReturnBlockedByAdmin() throws RemoteException { + mockForScreenshotBlocked(); + + assertThat(getInterfaceWithRealContext() + .canLaunchCaptureContentActivityForNoteInternal(FAKE_TASK_ID)) + .isEqualTo(CAPTURE_CONTENT_FOR_NOTE_BLOCKED_BY_ADMIN); + } + + @Test public void configComponentNameNotValid_shouldReturnFalse() throws RemoteException { - when(mMockContext.getString(anyInt())).thenReturn(EMPTY); - when(mFeatureFlags.isEnabled(SCREENSHOT_APP_CLIPS)).thenReturn(true); - when(mOptionalBubbles.isEmpty()).thenReturn(false); - when(mOptionalBubbles.get()).thenReturn(mBubbles); - when(mBubbles.isAppBubbleTaskId(eq((FAKE_TASK_ID)))).thenReturn(true); - when(mDevicePolicyManager.getScreenCaptureDisabled(eq(null))).thenReturn(false); + mockForInvalidConfigComponentName(); assertThat(getInterfaceWithMockContext() .canLaunchCaptureContentActivityForNote(FAKE_TASK_ID)).isFalse(); } @Test + public void configComponentNameNotValid_internal_shouldReturnFailed() throws RemoteException { + mockForInvalidConfigComponentName(); + + assertThat(getInterfaceWithMockContext() + .canLaunchCaptureContentActivityForNoteInternal(FAKE_TASK_ID)) + .isEqualTo(CAPTURE_CONTENT_FOR_NOTE_FAILED); + } + + + @Test public void allPrerequisitesSatisfy_shouldReturnTrue() throws RemoteException { mockToSatisfyAllPrerequisites(); @@ -134,28 +166,44 @@ public final class AppClipsServiceTest extends SysuiTestCase { } @Test - public void isManagedProfile_shouldUseProxyConnection() throws RemoteException { - when(mUserManager.isManagedProfile()).thenReturn(true); - when(mUserManager.getMainUser()).thenReturn(UserHandle.SYSTEM); - IAppClipsService service = getInterfaceWithRealContext(); - mAppClipsService.mProxyConnectorToMainProfile = - Mockito.spy(mAppClipsService.mProxyConnectorToMainProfile); + public void allPrerequisitesSatisfy_internal_shouldReturnSuccess() throws RemoteException { + mockToSatisfyAllPrerequisites(); - service.canLaunchCaptureContentActivityForNote(FAKE_TASK_ID); + assertThat(getInterfaceWithRealContext() + .canLaunchCaptureContentActivityForNoteInternal(FAKE_TASK_ID)) + .isEqualTo(CAPTURE_CONTENT_FOR_NOTE_SUCCESS); + } - verify(mAppClipsService.mProxyConnectorToMainProfile).postForResult(any()); + private void mockForEmptyBubbles() { + when(mFeatureFlags.isEnabled(SCREENSHOT_APP_CLIPS)).thenReturn(true); + when(mOptionalBubbles.isEmpty()).thenReturn(true); } - @Test - public void isManagedProfile_noMainUser_shouldReturnFalse() { - when(mUserManager.isManagedProfile()).thenReturn(true); - when(mUserManager.getMainUser()).thenReturn(null); + private void mockForTaskIdNotAppBubble() { + when(mFeatureFlags.isEnabled(SCREENSHOT_APP_CLIPS)).thenReturn(true); + when(mOptionalBubbles.isEmpty()).thenReturn(false); + when(mOptionalBubbles.get()).thenReturn(mBubbles); + when(mBubbles.isAppBubbleTaskId(eq((FAKE_TASK_ID)))).thenReturn(false); + } - getInterfaceWithRealContext(); + private void mockForScreenshotBlocked() { + when(mFeatureFlags.isEnabled(SCREENSHOT_APP_CLIPS)).thenReturn(true); + when(mOptionalBubbles.isEmpty()).thenReturn(false); + when(mOptionalBubbles.get()).thenReturn(mBubbles); + when(mBubbles.isAppBubbleTaskId(eq((FAKE_TASK_ID)))).thenReturn(true); + when(mDevicePolicyManager.getScreenCaptureDisabled(eq(null))).thenReturn(true); + } - assertThat(mAppClipsService.mProxyConnectorToMainProfile).isNull(); + private void mockForInvalidConfigComponentName() { + when(mMockContext.getString(anyInt())).thenReturn(EMPTY); + when(mFeatureFlags.isEnabled(SCREENSHOT_APP_CLIPS)).thenReturn(true); + when(mOptionalBubbles.isEmpty()).thenReturn(false); + when(mOptionalBubbles.get()).thenReturn(mBubbles); + when(mBubbles.isAppBubbleTaskId(eq((FAKE_TASK_ID)))).thenReturn(true); + when(mDevicePolicyManager.getScreenCaptureDisabled(eq(null))).thenReturn(false); } + private void mockToSatisfyAllPrerequisites() { when(mFeatureFlags.isEnabled(SCREENSHOT_APP_CLIPS)).thenReturn(true); when(mOptionalBubbles.isEmpty()).thenReturn(false); @@ -166,13 +214,13 @@ public final class AppClipsServiceTest extends SysuiTestCase { private IAppClipsService getInterfaceWithRealContext() { mAppClipsService = new AppClipsService(getContext(), mFeatureFlags, - mOptionalBubbles, mDevicePolicyManager, mUserManager); + mOptionalBubbles, mDevicePolicyManager); return getInterfaceFromService(mAppClipsService); } private IAppClipsService getInterfaceWithMockContext() { mAppClipsService = new AppClipsService(mMockContext, mFeatureFlags, - mOptionalBubbles, mDevicePolicyManager, mUserManager); + mOptionalBubbles, mDevicePolicyManager); return getInterfaceFromService(mAppClipsService); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsTrampolineActivityTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsTrampolineActivityTest.java index e9007ff84f13..7fad972d83fc 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsTrampolineActivityTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/appclips/AppClipsTrampolineActivityTest.java @@ -24,22 +24,19 @@ import static android.content.Intent.CAPTURE_CONTENT_FOR_NOTE_USER_CANCELED; import static android.content.Intent.CAPTURE_CONTENT_FOR_NOTE_WINDOW_MODE_UNSUPPORTED; import static android.content.Intent.EXTRA_CAPTURE_CONTENT_FOR_NOTE_STATUS_CODE; -import static com.android.systemui.flags.Flags.SCREENSHOT_APP_CLIPS; +import static com.android.internal.infra.AndroidFuture.completedFuture; import static com.android.systemui.screenshot.appclips.AppClipsEvent.SCREENSHOT_FOR_NOTE_TRIGGERED; import static com.android.systemui.screenshot.appclips.AppClipsTrampolineActivity.EXTRA_SCREENSHOT_URI; -import static com.android.systemui.screenshot.appclips.AppClipsTrampolineActivity.EXTRA_USE_WP_USER; import static com.google.common.truth.Truth.assertThat; import static org.junit.Assume.assumeFalse; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.Activity; -import android.app.admin.DevicePolicyManager; import android.content.ComponentName; import android.content.Intent; import android.content.pm.ActivityInfo; @@ -52,20 +49,22 @@ import android.net.Uri; import android.os.Bundle; import android.os.Handler; import android.os.UserHandle; -import android.os.UserManager; import android.testing.AndroidTestingRunner; import androidx.test.rule.ActivityTestRule; import androidx.test.runner.intercepting.SingleActivityFactory; +import com.android.internal.infra.ServiceConnector; import com.android.internal.logging.UiEventLogger; +import com.android.internal.statusbar.IAppClipsService; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; +import com.android.systemui.broadcast.BroadcastSender; +import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; -import com.android.systemui.flags.FeatureFlags; import com.android.systemui.notetask.NoteTaskController; -import com.android.systemui.settings.UserTracker; -import com.android.wm.shell.bubbles.Bubbles; + +import com.google.common.util.concurrent.MoreExecutors; import org.junit.After; import org.junit.Before; @@ -75,8 +74,7 @@ import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -import java.util.List; -import java.util.Optional; +import java.util.concurrent.Executor; @RunWith(AndroidTestingRunner.class) public final class AppClipsTrampolineActivityTest extends SysuiTestCase { @@ -86,25 +84,19 @@ public final class AppClipsTrampolineActivityTest extends SysuiTestCase { private static final int TEST_UID = 42; private static final String TEST_CALLING_PACKAGE = "test-calling-package"; - @Mock - private DevicePolicyManager mDevicePolicyManager; - @Mock - private FeatureFlags mFeatureFlags; - @Mock - private Optional<Bubbles> mOptionalBubbles; - @Mock - private Bubbles mBubbles; + @Mock private ServiceConnector<IAppClipsService> mServiceConnector; @Mock private NoteTaskController mNoteTaskController; @Mock private PackageManager mPackageManager; @Mock - private UserTracker mUserTracker; - @Mock private UiEventLogger mUiEventLogger; @Mock - private UserManager mUserManager; - + private BroadcastSender mBroadcastSender; + @Background + private Executor mBgExecutor; + @Main + private Executor mMainExecutor; @Main private Handler mMainHandler; @@ -114,9 +106,9 @@ public final class AppClipsTrampolineActivityTest extends SysuiTestCase { new SingleActivityFactory<>(AppClipsTrampolineActivityTestable.class) { @Override protected AppClipsTrampolineActivityTestable create(Intent unUsed) { - return new AppClipsTrampolineActivityTestable(mDevicePolicyManager, - mFeatureFlags, mOptionalBubbles, mNoteTaskController, mPackageManager, - mUserTracker, mUiEventLogger, mUserManager, mMainHandler); + return new AppClipsTrampolineActivityTestable(mServiceConnector, + mNoteTaskController, mPackageManager, mUiEventLogger, mBroadcastSender, + mBgExecutor, mMainExecutor, mMainHandler); } }; @@ -133,6 +125,8 @@ public final class AppClipsTrampolineActivityTest extends SysuiTestCase { mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH)); MockitoAnnotations.initMocks(this); + mBgExecutor = MoreExecutors.directExecutor(); + mMainExecutor = MoreExecutors.directExecutor(); mMainHandler = mContext.getMainThreadHandler(); mActivityIntent = new Intent(mContext, AppClipsTrampolineActivityTestable.class); @@ -169,19 +163,9 @@ public final class AppClipsTrampolineActivityTest extends SysuiTestCase { } @Test - public void flagOff_shouldFinishWithResultCancel() { - when(mFeatureFlags.isEnabled(SCREENSHOT_APP_CLIPS)).thenReturn(false); - - mActivityRule.launchActivity(mActivityIntent); - - assertThat(mActivityRule.getActivityResult().getResultCode()) - .isEqualTo(Activity.RESULT_CANCELED); - } - - @Test - public void bubblesEmpty_shouldFinishWithFailed() { - when(mFeatureFlags.isEnabled(SCREENSHOT_APP_CLIPS)).thenReturn(true); - when(mOptionalBubbles.isEmpty()).thenReturn(true); + public void queryService_returnedFailed_shouldFinishWithFailed() { + when(mServiceConnector.postForResult(any())) + .thenReturn(completedFuture(CAPTURE_CONTENT_FOR_NOTE_FAILED)); mActivityRule.launchActivity(mActivityIntent); @@ -189,14 +173,13 @@ public final class AppClipsTrampolineActivityTest extends SysuiTestCase { assertThat(actualResult.getResultCode()).isEqualTo(Activity.RESULT_OK); assertThat(getStatusCodeExtra(actualResult.getResultData())) .isEqualTo(CAPTURE_CONTENT_FOR_NOTE_FAILED); + assertThat(mActivityRule.getActivity().isFinishing()).isTrue(); } @Test - public void taskIdNotAppBubble_shouldFinishWithWindowModeUnsupported() { - when(mFeatureFlags.isEnabled(SCREENSHOT_APP_CLIPS)).thenReturn(true); - when(mOptionalBubbles.isEmpty()).thenReturn(false); - when(mOptionalBubbles.get()).thenReturn(mBubbles); - when(mBubbles.isAppBubbleTaskId(anyInt())).thenReturn(false); + public void queryService_returnedWindowModeUnsupported_shouldFinishWithWindowModeUnsupported() { + when(mServiceConnector.postForResult(any())) + .thenReturn(completedFuture(CAPTURE_CONTENT_FOR_NOTE_WINDOW_MODE_UNSUPPORTED)); mActivityRule.launchActivity(mActivityIntent); @@ -204,15 +187,13 @@ public final class AppClipsTrampolineActivityTest extends SysuiTestCase { assertThat(actualResult.getResultCode()).isEqualTo(Activity.RESULT_OK); assertThat(getStatusCodeExtra(actualResult.getResultData())) .isEqualTo(CAPTURE_CONTENT_FOR_NOTE_WINDOW_MODE_UNSUPPORTED); + assertThat(mActivityRule.getActivity().isFinishing()).isTrue(); } @Test - public void dpmScreenshotBlocked_shouldFinishWithBlockedByAdmin() { - when(mFeatureFlags.isEnabled(SCREENSHOT_APP_CLIPS)).thenReturn(true); - when(mOptionalBubbles.isEmpty()).thenReturn(false); - when(mOptionalBubbles.get()).thenReturn(mBubbles); - when(mBubbles.isAppBubbleTaskId(anyInt())).thenReturn(true); - when(mDevicePolicyManager.getScreenCaptureDisabled(eq(null))).thenReturn(true); + public void queryService_returnedScreenshotBlocked_shouldFinishWithBlockedByAdmin() { + when(mServiceConnector.postForResult(any())) + .thenReturn(completedFuture(CAPTURE_CONTENT_FOR_NOTE_BLOCKED_BY_ADMIN)); mActivityRule.launchActivity(mActivityIntent); @@ -220,6 +201,7 @@ public final class AppClipsTrampolineActivityTest extends SysuiTestCase { assertThat(actualResult.getResultCode()).isEqualTo(Activity.RESULT_OK); assertThat(getStatusCodeExtra(actualResult.getResultData())) .isEqualTo(CAPTURE_CONTENT_FOR_NOTE_BLOCKED_BY_ADMIN); + assertThat(mActivityRule.getActivity().isFinishing()).isTrue(); } @Test @@ -240,6 +222,7 @@ public final class AppClipsTrampolineActivityTest extends SysuiTestCase { assertThat(actualResult.getResultCode()).isEqualTo(Activity.RESULT_OK); assertThat(getStatusCodeExtra(actualResult.getResultData())) .isEqualTo(CAPTURE_CONTENT_FOR_NOTE_USER_CANCELED); + assertThat(mActivityRule.getActivity().isFinishing()).isTrue(); } @Test @@ -261,6 +244,7 @@ public final class AppClipsTrampolineActivityTest extends SysuiTestCase { assertThat(getStatusCodeExtra(actualResult.getResultData())) .isEqualTo(CAPTURE_CONTENT_FOR_NOTE_SUCCESS); assertThat(actualResult.getResultData().getData()).isEqualTo(TEST_URI); + assertThat(mActivityRule.getActivity().isFinishing()).isTrue(); } @Test @@ -274,48 +258,9 @@ public final class AppClipsTrampolineActivityTest extends SysuiTestCase { verify(mUiEventLogger).log(SCREENSHOT_FOR_NOTE_TRIGGERED, TEST_UID, TEST_CALLING_PACKAGE); } - @Test - public void startAppClipsActivity_throughWPUser_shouldStartMainUserActivity() - throws NameNotFoundException { - when(mUserManager.isManagedProfile()).thenReturn(true); - when(mUserManager.getMainUser()).thenReturn(UserHandle.SYSTEM); - mockToSatisfyAllPrerequisites(); - - AppClipsTrampolineActivityTestable activity = mActivityRule.launchActivity(mActivityIntent); - waitForIdleSync(); - - Intent actualIntent = activity.mStartedIntent; - assertThat(actualIntent.getComponent()).isEqualTo( - new ComponentName(mContext, AppClipsTrampolineActivity.class)); - assertThat(actualIntent.getFlags()).isEqualTo(Intent.FLAG_ACTIVITY_FORWARD_RESULT); - assertThat(actualIntent.getBooleanExtra(EXTRA_USE_WP_USER, false)).isTrue(); - assertThat(activity.mStartingUser).isEqualTo(UserHandle.SYSTEM); - } - - @Test - public void startAppClipsActivity_throughWPUser_noMainUser_shouldFinishWithFailed() - throws NameNotFoundException { - when(mUserManager.isManagedProfile()).thenReturn(true); - when(mUserManager.getMainUser()).thenReturn(null); - - mockToSatisfyAllPrerequisites(); - - mActivityRule.launchActivity(mActivityIntent); - waitForIdleSync(); - - ActivityResult actualResult = mActivityRule.getActivityResult(); - assertThat(actualResult.getResultCode()).isEqualTo(Activity.RESULT_OK); - assertThat(getStatusCodeExtra(actualResult.getResultData())) - .isEqualTo(CAPTURE_CONTENT_FOR_NOTE_FAILED); - } - private void mockToSatisfyAllPrerequisites() throws NameNotFoundException { - when(mFeatureFlags.isEnabled(SCREENSHOT_APP_CLIPS)).thenReturn(true); - when(mOptionalBubbles.isEmpty()).thenReturn(false); - when(mOptionalBubbles.get()).thenReturn(mBubbles); - when(mBubbles.isAppBubbleTaskId(anyInt())).thenReturn(true); - when(mDevicePolicyManager.getScreenCaptureDisabled(eq(null))).thenReturn(false); - when(mUserTracker.getUserProfiles()).thenReturn(List.of()); + when(mServiceConnector.postForResult(any())) + .thenReturn(completedFuture(CAPTURE_CONTENT_FOR_NOTE_SUCCESS)); ApplicationInfo testApplicationInfo = new ApplicationInfo(); testApplicationInfo.uid = TEST_UID; @@ -330,17 +275,14 @@ public final class AppClipsTrampolineActivityTest extends SysuiTestCase { Intent mStartedIntent; UserHandle mStartingUser; - public AppClipsTrampolineActivityTestable(DevicePolicyManager devicePolicyManager, - FeatureFlags flags, - Optional<Bubbles> optionalBubbles, - NoteTaskController noteTaskController, - PackageManager packageManager, - UserTracker userTracker, - UiEventLogger uiEventLogger, - UserManager userManager, + public AppClipsTrampolineActivityTestable( + ServiceConnector<IAppClipsService> serviceServiceConnector, + NoteTaskController noteTaskController, PackageManager packageManager, + UiEventLogger uiEventLogger, BroadcastSender broadcastSender, + @Background Executor bgExecutor, @Main Executor mainExecutor, @Main Handler mainHandler) { - super(devicePolicyManager, flags, optionalBubbles, noteTaskController, packageManager, - userTracker, uiEventLogger, userManager, mainHandler); + super(serviceServiceConnector, noteTaskController, packageManager, uiEventLogger, + broadcastSender, bgExecutor, mainExecutor, mainHandler); } @Override diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/CombinedShadeHeaderConstraintsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/CombinedShadeHeaderConstraintsTest.kt index 3706859a5b74..0a1eca69f1ce 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/CombinedShadeHeaderConstraintsTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/CombinedShadeHeaderConstraintsTest.kt @@ -62,9 +62,9 @@ class CombinedShadeHeaderConstraintsTest : SysuiTestCase() { assertThat(getConstraint(R.id.clock).layout.startToStart).isEqualTo(R.id.begin_guide) assertThat(getConstraint(R.id.clock).layout.horizontalBias).isEqualTo(0f) - assertThat(getConstraint(R.id.batteryRemainingIcon).layout.endToEnd) + assertThat(getConstraint(R.id.shade_header_system_icons).layout.endToEnd) .isEqualTo(R.id.end_guide) - assertThat(getConstraint(R.id.batteryRemainingIcon).layout.horizontalBias) + assertThat(getConstraint(R.id.shade_header_system_icons).layout.horizontalBias) .isEqualTo(1f) assertThat(getConstraint(R.id.privacy_container).layout.endToEnd) @@ -95,9 +95,9 @@ class CombinedShadeHeaderConstraintsTest : SysuiTestCase() { assertThat(getConstraint(R.id.date).layout.startToStart).isEqualTo(PARENT_ID) assertThat(getConstraint(R.id.date).layout.horizontalBias).isEqualTo(0.5f) - assertThat(getConstraint(R.id.batteryRemainingIcon).layout.endToEnd) + assertThat(getConstraint(R.id.shade_header_system_icons).layout.endToEnd) .isEqualTo(PARENT_ID) - assertThat(getConstraint(R.id.batteryRemainingIcon).layout.horizontalBias) + assertThat(getConstraint(R.id.shade_header_system_icons).layout.horizontalBias) .isEqualTo(0.5f) assertThat(getConstraint(R.id.privacy_container).layout.endToEnd) @@ -133,18 +133,15 @@ class CombinedShadeHeaderConstraintsTest : SysuiTestCase() { changes() with(qqsConstraint) { - assertThat(getConstraint(R.id.statusIcons).propertySet.alpha).isEqualTo(1f) - assertThat(getConstraint(R.id.batteryRemainingIcon).propertySet.alpha).isEqualTo(1f) + assertThat(systemIconsAlphaConstraint).isEqualTo(1f) } with(qsConstraint) { - assertThat(getConstraint(R.id.statusIcons).propertySet.alpha).isEqualTo(1f) - assertThat(getConstraint(R.id.batteryRemainingIcon).propertySet.alpha).isEqualTo(1f) + assertThat(systemIconsAlphaConstraint).isEqualTo(1f) } with(largeScreenConstraint) { - assertThat(getConstraint(R.id.statusIcons).propertySet.alpha).isEqualTo(1f) - assertThat(getConstraint(R.id.batteryRemainingIcon).propertySet.alpha).isEqualTo(1f) + assertThat(systemIconsAlphaConstraint).isEqualTo(1f) } } @@ -155,18 +152,15 @@ class CombinedShadeHeaderConstraintsTest : SysuiTestCase() { changes() with(qqsConstraint) { - assertThat(getConstraint(R.id.statusIcons).propertySet.alpha).isEqualTo(0f) - assertThat(getConstraint(R.id.batteryRemainingIcon).propertySet.alpha).isEqualTo(0f) + assertThat(systemIconsAlphaConstraint).isEqualTo(0f) } with(qsConstraint) { - assertThat(getConstraint(R.id.statusIcons).propertySet.alpha).isEqualTo(1f) - assertThat(getConstraint(R.id.batteryRemainingIcon).propertySet.alpha).isEqualTo(1f) + assertThat(systemIconsAlphaConstraint).isEqualTo(1f) } with(largeScreenConstraint) { - assertThat(getConstraint(R.id.statusIcons).propertySet.alpha).isEqualTo(1f) - assertThat(getConstraint(R.id.batteryRemainingIcon).propertySet.alpha).isEqualTo(1f) + assertThat(systemIconsAlphaConstraint).isEqualTo(1f) } } @@ -181,12 +175,13 @@ class CombinedShadeHeaderConstraintsTest : SysuiTestCase() { with(qqsConstraint) { // In this case, the date is constrained on the end by a Barrier determined by either - // privacy or statusIcons + // privacy or clickableIcons assertThat(getConstraint(R.id.date).layout.endToStart).isEqualTo(R.id.barrier) - assertThat(getConstraint(R.id.statusIcons).layout.startToEnd).isEqualTo(R.id.date) + assertThat(getConstraint(R.id.shade_header_system_icons).layout.startToEnd) + .isEqualTo(R.id.date) assertThat(getConstraint(R.id.privacy_container).layout.startToEnd).isEqualTo(R.id.date) assertThat(getConstraint(R.id.barrier).layout.mReferenceIds).asList().containsExactly( - R.id.statusIcons, + R.id.shade_header_system_icons, R.id.privacy_container ) assertThat(getConstraint(R.id.barrier).layout.mBarrierDirection).isEqualTo(START) @@ -272,7 +267,7 @@ class CombinedShadeHeaderConstraintsTest : SysuiTestCase() { assertThat(getConstraint(R.id.center_left).layout.guideBegin).isEqualTo(offsetFromEdge) assertThat(getConstraint(R.id.center_right).layout.guideEnd).isEqualTo(offsetFromEdge) assertThat(getConstraint(R.id.date).layout.endToStart).isEqualTo(R.id.center_left) - assertThat(getConstraint(R.id.statusIcons).layout.startToEnd) + assertThat(getConstraint(R.id.shade_header_system_icons).layout.startToEnd) .isEqualTo(R.id.center_right) assertThat(getConstraint(R.id.privacy_container).layout.startToEnd) .isEqualTo(R.id.center_right) @@ -285,9 +280,9 @@ class CombinedShadeHeaderConstraintsTest : SysuiTestCase() { assertThat(getConstraint(R.id.date).layout.endToStart).isNotEqualTo(R.id.center_left) assertThat(getConstraint(R.id.date).layout.endToStart).isNotEqualTo(R.id.center_right) - assertThat(getConstraint(R.id.statusIcons).layout.startToEnd) + assertThat(getConstraint(R.id.shade_header_system_icons).layout.startToEnd) .isNotEqualTo(R.id.center_left) - assertThat(getConstraint(R.id.statusIcons).layout.startToEnd) + assertThat(getConstraint(R.id.shade_header_system_icons).layout.startToEnd) .isNotEqualTo(R.id.center_right) assertThat(getConstraint(R.id.privacy_container).layout.startToEnd) @@ -311,7 +306,7 @@ class CombinedShadeHeaderConstraintsTest : SysuiTestCase() { assertThat(getConstraint(R.id.center_left).layout.guideEnd).isEqualTo(offsetFromEdge) assertThat(getConstraint(R.id.center_right).layout.guideBegin).isEqualTo(offsetFromEdge) assertThat(getConstraint(R.id.date).layout.endToStart).isEqualTo(R.id.center_right) - assertThat(getConstraint(R.id.statusIcons).layout.startToEnd) + assertThat(getConstraint(R.id.shade_header_system_icons).layout.startToEnd) .isEqualTo(R.id.center_left) assertThat(getConstraint(R.id.privacy_container).layout.startToEnd) .isEqualTo(R.id.center_left) @@ -324,9 +319,9 @@ class CombinedShadeHeaderConstraintsTest : SysuiTestCase() { assertThat(getConstraint(R.id.date).layout.endToStart).isNotEqualTo(R.id.center_left) assertThat(getConstraint(R.id.date).layout.endToStart).isNotEqualTo(R.id.center_right) - assertThat(getConstraint(R.id.statusIcons).layout.startToEnd) + assertThat(getConstraint(R.id.shade_header_system_icons).layout.startToEnd) .isNotEqualTo(R.id.center_left) - assertThat(getConstraint(R.id.statusIcons).layout.startToEnd) + assertThat(getConstraint(R.id.shade_header_system_icons).layout.startToEnd) .isNotEqualTo(R.id.center_right) assertThat(getConstraint(R.id.privacy_container).layout.startToEnd) @@ -382,7 +377,8 @@ class CombinedShadeHeaderConstraintsTest : SysuiTestCase() { CombinedShadeHeadersConstraintManagerImpl.emptyCutoutConstraints()() assertThat(qqsConstraint.getConstraint(R.id.date).layout.constrainedWidth).isTrue() - assertThat(qqsConstraint.getConstraint(R.id.statusIcons).layout.constrainedWidth).isTrue() + val shadeHeaderConstraint = qqsConstraint.getConstraint(R.id.shade_header_system_icons) + assertThat(shadeHeaderConstraint.layout.constrainedWidth).isTrue() } @Test @@ -390,9 +386,13 @@ class CombinedShadeHeaderConstraintsTest : SysuiTestCase() { CombinedShadeHeadersConstraintManagerImpl.centerCutoutConstraints(false, 10)() assertThat(qqsConstraint.getConstraint(R.id.date).layout.constrainedWidth).isTrue() - assertThat(qqsConstraint.getConstraint(R.id.statusIcons).layout.constrainedWidth).isTrue() + val shadeHeaderConstraint = qqsConstraint.getConstraint(R.id.shade_header_system_icons) + assertThat(shadeHeaderConstraint.layout.constrainedWidth).isTrue() } + private val ConstraintSet.systemIconsAlphaConstraint + get() = getConstraint(R.id.shade_header_system_icons).propertySet.alpha + private operator fun ConstraintsChanges.invoke() { qqsConstraintsChanges?.invoke(qqsConstraint) qsConstraintsChanges?.invoke(qsConstraint) diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java index fe89a143e880..46ced827cafd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java @@ -89,12 +89,13 @@ import com.android.systemui.flags.FeatureFlags; import com.android.systemui.fragments.FragmentHostManager; import com.android.systemui.fragments.FragmentService; import com.android.systemui.keyguard.KeyguardUnlockAnimationController; -import com.android.systemui.keyguard.data.repository.FakeKeyguardBouncerRepository; +import com.android.systemui.keyguard.KeyguardViewConfigurator; import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository; -import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor; +import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor; import com.android.systemui.keyguard.domain.interactor.KeyguardBottomAreaInteractor; import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor; import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor; +import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory; import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor; import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel; import com.android.systemui.keyguard.ui.viewmodel.GoneToDreamingTransitionViewModel; @@ -253,6 +254,7 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase { @Mock protected UserManager mUserManager; @Mock protected UiEventLogger mUiEventLogger; @Mock protected LockIconViewController mLockIconViewController; + @Mock protected KeyguardViewConfigurator mKeyguardViewConfigurator; @Mock protected KeyguardMediaController mKeyguardMediaController; @Mock protected NavigationModeController mNavigationModeController; @Mock protected NavigationBarController mNavigationBarController; @@ -313,6 +315,7 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase { protected final int mMaxUdfpsBurnInOffsetY = 5; protected KeyguardBottomAreaInteractor mKeyguardBottomAreaInteractor; + protected FakeKeyguardRepository mFakeKeyguardRepository; protected KeyguardInteractor mKeyguardInteractor; protected NotificationPanelViewController.TouchHandler mTouchHandler; protected ConfigurationController mConfigurationController; @@ -340,10 +343,12 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase { public void setup() { MockitoAnnotations.initMocks(this); mMainDispatcher = getMainDispatcher(); - mKeyguardBottomAreaInteractor = new KeyguardBottomAreaInteractor( - new FakeKeyguardRepository()); - mKeyguardInteractor = new KeyguardInteractor(new FakeKeyguardRepository(), mCommandQueue, - mFeatureFlags, new FakeKeyguardBouncerRepository()); + KeyguardInteractorFactory.WithDependencies keyguardInteractorDeps = + KeyguardInteractorFactory.create(); + mFakeKeyguardRepository = keyguardInteractorDeps.getRepository(); + mKeyguardBottomAreaInteractor = new KeyguardBottomAreaInteractor(mFakeKeyguardRepository); + mKeyguardInteractor = keyguardInteractorDeps.getKeyguardInteractor(); + SystemClock systemClock = new FakeSystemClock(); mStatusBarStateController = new StatusBarStateControllerImpl(mUiEventLogger, mDumpManager, mInteractionJankMonitor, mShadeExpansionStateManager); @@ -611,6 +616,7 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase { mKeyuardLongPressViewModel, mKeyguardInteractor, mActivityStarter, + mKeyguardViewConfigurator, mKeyguardFaceAuthInteractor); mNotificationPanelViewController.initDependencies( mCentralSurfaces, diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java index 8f2ee91d6a6a..5802eb3d9618 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java @@ -34,6 +34,7 @@ import static org.mockito.ArgumentMatchers.anyFloat; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.mock; @@ -58,6 +59,9 @@ import androidx.test.filters.SmallTest; import com.android.keyguard.FaceAuthApiRequestReason; import com.android.systemui.DejankUtils; import com.android.systemui.R; +import com.android.systemui.keyguard.shared.model.WakeSleepReason; +import com.android.systemui.keyguard.shared.model.WakefulnessModel; +import com.android.systemui.keyguard.shared.model.WakefulnessState; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.notification.row.ExpandableView; import com.android.systemui.statusbar.notification.row.ExpandableView.OnHeightChangedListener; @@ -730,6 +734,47 @@ public class NotificationPanelViewControllerTest extends NotificationPanelViewCo } @Test + public void onQsSetExpansionHeightCalled_qsFullyExpandedOnKeyguard_showNSSL() { + // GIVEN + mStatusBarStateController.setState(KEYGUARD); + when(mKeyguardBypassController.getBypassEnabled()).thenReturn(false); + when(mQsController.getFullyExpanded()).thenReturn(true); + when(mQsController.getExpanded()).thenReturn(true); + + // WHEN + int transitionDistance = mNotificationPanelViewController.getMaxPanelTransitionDistance(); + mNotificationPanelViewController.setExpandedHeight(transitionDistance); + + // THEN + // We are interested in the last value of the stack alpha. + ArgumentCaptor<Float> alphaCaptor = ArgumentCaptor.forClass(Float.class); + verify(mNotificationStackScrollLayoutController, atLeastOnce()) + .setAlpha(alphaCaptor.capture()); + assertThat(alphaCaptor.getValue()).isEqualTo(1.0f); + } + + @Test + public void onQsSetExpansionHeightCalled_qsFullyExpandedOnKeyguard_hideNSSL() { + // GIVEN + mStatusBarStateController.setState(KEYGUARD); + when(mKeyguardBypassController.getBypassEnabled()).thenReturn(false); + when(mQsController.getFullyExpanded()).thenReturn(false); + when(mQsController.getExpanded()).thenReturn(true); + + // WHEN + int transitionDistance = mNotificationPanelViewController + .getMaxPanelTransitionDistance() / 2; + mNotificationPanelViewController.setExpandedHeight(transitionDistance); + + // THEN + // We are interested in the last value of the stack alpha. + ArgumentCaptor<Float> alphaCaptor = ArgumentCaptor.forClass(Float.class); + verify(mNotificationStackScrollLayoutController, atLeastOnce()) + .setAlpha(alphaCaptor.capture()); + assertThat(alphaCaptor.getValue()).isEqualTo(0.0f); + } + + @Test public void testSwitchesToBigClockInSplitShadeOnAodAnimateDisabled() { when(mScreenOffAnimationController.shouldAnimateClockChange()).thenReturn(false); mStatusBarStateController.setState(KEYGUARD); @@ -1169,4 +1214,43 @@ public class NotificationPanelViewControllerTest extends NotificationPanelViewCo when(mUnlockedScreenOffAnimationController.isAnimationPlaying()).thenReturn(true); assertThat(mNotificationPanelViewController.isExpanded()).isTrue(); } + + @Test + public void getFalsingThreshold_deviceNotInteractive_isQsThreshold() { + mFakeKeyguardRepository.setWakefulnessModel( + new WakefulnessModel( + WakefulnessState.ASLEEP, + /* lastWakeReason= */ WakeSleepReason.TAP, + /* lastSleepReason= */ WakeSleepReason.POWER_BUTTON) + ); + when(mQsController.getFalsingThreshold()).thenReturn(14); + + assertThat(mNotificationPanelViewController.getFalsingThreshold()).isEqualTo(14); + } + + @Test + public void getFalsingThreshold_lastWakeNotDueToTouch_isQsThreshold() { + mFakeKeyguardRepository.setWakefulnessModel( + new WakefulnessModel( + WakefulnessState.AWAKE, + /* lastWakeReason= */ WakeSleepReason.POWER_BUTTON, + /* lastSleepReason= */ WakeSleepReason.POWER_BUTTON) + ); + when(mQsController.getFalsingThreshold()).thenReturn(14); + + assertThat(mNotificationPanelViewController.getFalsingThreshold()).isEqualTo(14); + } + + @Test + public void getFalsingThreshold_lastWakeDueToTouch_greaterThanQsThreshold() { + mFakeKeyguardRepository.setWakefulnessModel( + new WakefulnessModel( + WakefulnessState.AWAKE, + /* lastWakeReason= */ WakeSleepReason.TAP, + /* lastSleepReason= */ WakeSleepReason.POWER_BUTTON) + ); + when(mQsController.getFalsingThreshold()).thenReturn(14); + + assertThat(mNotificationPanelViewController.getFalsingThreshold()).isGreaterThan(14); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java index 526dc8d150fe..cde6ac08d5fd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java @@ -126,7 +126,7 @@ public class NotificationShadeWindowControllerImplTest extends SysuiTestCase { } }; mNotificationShadeWindowController.setScrimsVisibilityListener((visibility) -> {}); - mNotificationShadeWindowController.setNotificationShadeView(mNotificationShadeWindowView); + mNotificationShadeWindowController.setWindowRootView(mNotificationShadeWindowView); mNotificationShadeWindowController.attach(); verify(mWindowManager).addView(eq(mNotificationShadeWindowView), any()); diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt index 3995e71d24d4..c737a18ea8a2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt @@ -21,22 +21,30 @@ import android.testing.TestableLooper.RunWithLooper import android.view.MotionEvent import android.view.ViewGroup import androidx.test.filters.SmallTest +import com.android.keyguard.KeyguardMessageAreaController import com.android.keyguard.KeyguardSecurityContainerController import com.android.keyguard.LockIconViewController import com.android.keyguard.dagger.KeyguardBouncerComponent import com.android.systemui.R import com.android.systemui.SysuiTestCase +import com.android.systemui.back.domain.interactor.BackActionInteractor import com.android.systemui.classifier.FalsingCollectorFake import com.android.systemui.classifier.FalsingManagerFake import com.android.systemui.dock.DockManager +import com.android.systemui.dump.logcatLogBuffer import com.android.systemui.flags.FakeFeatureFlags import com.android.systemui.flags.Flags import com.android.systemui.keyguard.KeyguardUnlockAnimationController +import com.android.systemui.bouncer.data.factory.BouncerMessageFactory +import com.android.systemui.bouncer.domain.interactor.BouncerMessageInteractor +import com.android.systemui.bouncer.domain.interactor.CountDownTimerUtil +import com.android.systemui.bouncer.data.repository.FakeBouncerMessageRepository import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor import com.android.systemui.keyguard.shared.model.TransitionStep -import com.android.systemui.keyguard.ui.viewmodel.KeyguardBouncerViewModel +import com.android.systemui.bouncer.ui.viewmodel.KeyguardBouncerViewModel import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel +import com.android.systemui.log.BouncerLogger import com.android.systemui.multishade.data.remoteproxy.MultiShadeInputProxy import com.android.systemui.multishade.data.repository.MultiShadeRepository import com.android.systemui.multishade.domain.interactor.MultiShadeInteractor @@ -54,6 +62,7 @@ import com.android.systemui.statusbar.phone.PhoneStatusBarViewController import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager import com.android.systemui.statusbar.window.StatusBarWindowStateController import com.android.systemui.unfold.UnfoldTransitionProgressProvider +import com.android.systemui.user.data.repository.FakeUserRepository import com.android.systemui.util.mockito.any import com.android.systemui.util.time.FakeSystemClock import com.google.common.truth.Truth.assertThat @@ -83,6 +92,7 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() { @Mock private lateinit var view: NotificationShadeWindowView @Mock private lateinit var sysuiStatusBarStateController: SysuiStatusBarStateController @Mock private lateinit var centralSurfaces: CentralSurfaces + @Mock private lateinit var backActionInteractor: BackActionInteractor @Mock private lateinit var dockManager: DockManager @Mock private lateinit var notificationPanelViewController: NotificationPanelViewController @Mock private lateinit var notificationShadeDepthController: NotificationShadeDepthController @@ -134,6 +144,7 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() { featureFlags.set(Flags.TRACKPAD_GESTURE_FEATURES, false) featureFlags.set(Flags.DUAL_SHADE, false) featureFlags.set(Flags.SPLIT_SHADE_SUBPIXEL_OPTIMIZATION, true) + featureFlags.set(Flags.REVAMPED_BOUNCER_MESSAGES, true) val inputProxy = MultiShadeInputProxy() testScope = TestScope() @@ -162,6 +173,7 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() { statusBarWindowStateController, lockIconViewController, centralSurfaces, + backActionInteractor, notificationShadeWindowController, unfoldTransitionProgressProvider, keyguardUnlockAnimationController, @@ -170,6 +182,7 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() { pulsingGestureListener, keyguardBouncerViewModel, keyguardBouncerComponentFactory, + mock(KeyguardMessageAreaController.Factory::class.java), keyguardTransitionInteractor, primaryBouncerToGoneTransitionViewModel, featureFlags, @@ -190,6 +203,10 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() { shadeController = shadeController, ) }, + BouncerMessageInteractor(FakeBouncerMessageRepository(), + mock(BouncerMessageFactory::class.java), + FakeUserRepository(), CountDownTimerUtil(), featureFlags), + BouncerLogger(logcatLogBuffer("BouncerLog")) ) underTest.setupExpandedStatusBar() diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt index fe9574251ddb..1740284a26dd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt @@ -21,21 +21,29 @@ import android.testing.TestableLooper.RunWithLooper import android.view.MotionEvent import android.widget.FrameLayout import androidx.test.filters.SmallTest +import com.android.keyguard.KeyguardMessageAreaController import com.android.keyguard.KeyguardSecurityContainerController import com.android.keyguard.LockIconViewController import com.android.keyguard.dagger.KeyguardBouncerComponent import com.android.systemui.R import com.android.systemui.SysuiTestCase +import com.android.systemui.back.domain.interactor.BackActionInteractor +import com.android.systemui.bouncer.data.factory.BouncerMessageFactory +import com.android.systemui.bouncer.data.repository.FakeBouncerMessageRepository +import com.android.systemui.bouncer.domain.interactor.BouncerMessageInteractor +import com.android.systemui.bouncer.domain.interactor.CountDownTimerUtil +import com.android.systemui.bouncer.ui.viewmodel.KeyguardBouncerViewModel import com.android.systemui.classifier.FalsingCollectorFake import com.android.systemui.classifier.FalsingManagerFake import com.android.systemui.dock.DockManager +import com.android.systemui.dump.logcatLogBuffer import com.android.systemui.flags.FakeFeatureFlags import com.android.systemui.flags.Flags import com.android.systemui.keyguard.KeyguardUnlockAnimationController import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor -import com.android.systemui.keyguard.ui.viewmodel.KeyguardBouncerViewModel import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel +import com.android.systemui.log.BouncerLogger import com.android.systemui.multishade.data.remoteproxy.MultiShadeInputProxy import com.android.systemui.multishade.data.repository.MultiShadeRepository import com.android.systemui.multishade.domain.interactor.MultiShadeInteractor @@ -54,6 +62,7 @@ import com.android.systemui.statusbar.phone.CentralSurfaces import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager import com.android.systemui.statusbar.window.StatusBarWindowStateController import com.android.systemui.unfold.UnfoldTransitionProgressProvider +import com.android.systemui.user.data.repository.FakeUserRepository import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.mock import com.android.systemui.util.mockito.whenever @@ -70,6 +79,7 @@ import org.junit.runner.RunWith import org.mockito.ArgumentCaptor import org.mockito.Captor import org.mockito.Mock +import org.mockito.Mockito import org.mockito.Mockito.spy import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @@ -84,6 +94,7 @@ class NotificationShadeWindowViewTest : SysuiTestCase() { @Mock private lateinit var statusBarStateController: SysuiStatusBarStateController @Mock private lateinit var shadeController: ShadeController @Mock private lateinit var centralSurfaces: CentralSurfaces + @Mock private lateinit var backActionInteractor: BackActionInteractor @Mock private lateinit var dockManager: DockManager @Mock private lateinit var notificationPanelViewController: NotificationPanelViewController @Mock private lateinit var notificationStackScrollLayout: NotificationStackScrollLayout @@ -147,6 +158,7 @@ class NotificationShadeWindowViewTest : SysuiTestCase() { featureFlags.set(Flags.TRACKPAD_GESTURE_FEATURES, false) featureFlags.set(Flags.DUAL_SHADE, false) featureFlags.set(Flags.SPLIT_SHADE_SUBPIXEL_OPTIMIZATION, true) + featureFlags.set(Flags.REVAMPED_BOUNCER_MESSAGES, true) val inputProxy = MultiShadeInputProxy() testScope = TestScope() val multiShadeInteractor = @@ -174,6 +186,7 @@ class NotificationShadeWindowViewTest : SysuiTestCase() { statusBarWindowStateController, lockIconViewController, centralSurfaces, + backActionInteractor, notificationShadeWindowController, unfoldTransitionProgressProvider, keyguardUnlockAnimationController, @@ -182,6 +195,7 @@ class NotificationShadeWindowViewTest : SysuiTestCase() { pulsingGestureListener, keyguardBouncerViewModel, keyguardBouncerComponentFactory, + Mockito.mock(KeyguardMessageAreaController.Factory::class.java), keyguardTransitionInteractor, primaryBouncerToGoneTransitionViewModel, featureFlags, @@ -202,6 +216,14 @@ class NotificationShadeWindowViewTest : SysuiTestCase() { shadeController = shadeController, ) }, + BouncerMessageInteractor( + FakeBouncerMessageRepository(), + Mockito.mock(BouncerMessageFactory::class.java), + FakeUserRepository(), + CountDownTimerUtil(), + featureFlags + ), + BouncerLogger(logcatLogBuffer("BouncerLog")) ) controller.setupExpandedStatusBar() diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/PulsingGestureListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/PulsingGestureListenerTest.kt index d7c06a76bf0f..a4fab1dbac57 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/PulsingGestureListenerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/PulsingGestureListenerTest.kt @@ -25,22 +25,24 @@ import android.testing.TestableLooper.RunWithLooper import android.view.MotionEvent import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase +import com.android.systemui.classifier.FalsingCollector import com.android.systemui.dock.DockManager import com.android.systemui.dump.DumpManager import com.android.systemui.plugins.FalsingManager import com.android.systemui.plugins.statusbar.StatusBarStateController +import com.android.systemui.power.data.repository.FakePowerRepository +import com.android.systemui.power.domain.interactor.PowerInteractor import com.android.systemui.settings.UserTracker -import com.android.systemui.statusbar.phone.CentralSurfaces +import com.android.systemui.statusbar.phone.ScreenOffAnimationController import com.android.systemui.tuner.TunerService import com.android.systemui.tuner.TunerService.Tunable import com.android.systemui.util.mockito.eq +import com.google.common.truth.Truth.assertThat import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentCaptor import org.mockito.ArgumentMatchers.anyInt -import org.mockito.ArgumentMatchers.anyLong -import org.mockito.ArgumentMatchers.anyString import org.mockito.Mock import org.mockito.Mockito.never import org.mockito.Mockito.verify @@ -52,14 +54,12 @@ import org.mockito.Mockito.`when` as whenever @SmallTest class PulsingGestureListenerTest : SysuiTestCase() { @Mock - private lateinit var view: NotificationShadeWindowView - @Mock - private lateinit var centralSurfaces: CentralSurfaces - @Mock private lateinit var dockManager: DockManager @Mock private lateinit var falsingManager: FalsingManager @Mock + private lateinit var falsingCollector: FalsingCollector + @Mock private lateinit var ambientDisplayConfiguration: AmbientDisplayConfiguration @Mock private lateinit var tunerService: TunerService @@ -71,7 +71,10 @@ class PulsingGestureListenerTest : SysuiTestCase() { private lateinit var shadeLogger: ShadeLogger @Mock private lateinit var userTracker: UserTracker + @Mock + private lateinit var screenOffAnimationController: ScreenOffAnimationController + private lateinit var powerRepository: FakePowerRepository private lateinit var tunableCaptor: ArgumentCaptor<Tunable> private lateinit var underTest: PulsingGestureListener @@ -79,11 +82,17 @@ class PulsingGestureListenerTest : SysuiTestCase() { fun setUp() { MockitoAnnotations.initMocks(this) + powerRepository = FakePowerRepository() + underTest = PulsingGestureListener( - view, falsingManager, dockManager, - centralSurfaces, + PowerInteractor( + powerRepository, + falsingCollector, + screenOffAnimationController, + statusBarStateController, + ), ambientDisplayConfiguration, statusBarStateController, shadeLogger, @@ -92,6 +101,7 @@ class PulsingGestureListenerTest : SysuiTestCase() { dumpManager ) whenever(dockManager.isDocked).thenReturn(false) + whenever(screenOffAnimationController.allowWakeUpIfDozing()).thenReturn(true) } @Test @@ -110,8 +120,8 @@ class PulsingGestureListenerTest : SysuiTestCase() { underTest.onSingleTapUp(upEv) // THEN wake up device if dozing - verify(centralSurfaces).wakeUpIfDozing( - anyLong(), anyString(), eq(PowerManager.WAKE_REASON_TAP)) + assertThat(powerRepository.lastWakeWhy).isNotNull() + assertThat(powerRepository.lastWakeReason).isEqualTo(PowerManager.WAKE_REASON_TAP) } @Test @@ -130,8 +140,8 @@ class PulsingGestureListenerTest : SysuiTestCase() { underTest.onDoubleTapEvent(upEv) // THEN wake up device if dozing - verify(centralSurfaces).wakeUpIfDozing( - anyLong(), anyString(), eq(PowerManager.WAKE_REASON_TAP)) + assertThat(powerRepository.lastWakeWhy).isNotNull() + assertThat(powerRepository.lastWakeReason).isEqualTo(PowerManager.WAKE_REASON_TAP) } @Test @@ -162,8 +172,8 @@ class PulsingGestureListenerTest : SysuiTestCase() { underTest.onSingleTapUp(upEv) // THEN the device doesn't wake up - verify(centralSurfaces, never()).wakeUpIfDozing( - anyLong(), anyString(), anyInt()) + assertThat(powerRepository.lastWakeWhy).isNull() + assertThat(powerRepository.lastWakeReason).isNull() } @Test @@ -210,8 +220,8 @@ class PulsingGestureListenerTest : SysuiTestCase() { underTest.onDoubleTapEvent(upEv) // THEN the device doesn't wake up - verify(centralSurfaces, never()).wakeUpIfDozing( - anyLong(), anyString(), anyInt()) + assertThat(powerRepository.lastWakeWhy).isNull() + assertThat(powerRepository.lastWakeReason).isNull() } @Test @@ -230,8 +240,8 @@ class PulsingGestureListenerTest : SysuiTestCase() { underTest.onSingleTapUp(upEv) // THEN the device doesn't wake up - verify(centralSurfaces, never()).wakeUpIfDozing( - anyLong(), anyString(), anyInt()) + assertThat(powerRepository.lastWakeWhy).isNull() + assertThat(powerRepository.lastWakeReason).isNull() } @Test @@ -250,8 +260,8 @@ class PulsingGestureListenerTest : SysuiTestCase() { underTest.onDoubleTapEvent(upEv) // THEN the device doesn't wake up - verify(centralSurfaces, never()).wakeUpIfDozing( - anyLong(), anyString(), anyInt()) + assertThat(powerRepository.lastWakeWhy).isNull() + assertThat(powerRepository.lastWakeReason).isNull() } fun updateSettings() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeControllerImplTest.kt new file mode 100644 index 000000000000..00a056708f07 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeControllerImplTest.kt @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.shade + +import android.testing.AndroidTestingRunner +import android.view.Display +import android.view.WindowManager +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.assist.AssistManager +import com.android.systemui.plugins.statusbar.StatusBarStateController +import com.android.systemui.statusbar.CommandQueue +import com.android.systemui.statusbar.NotificationShadeWindowController +import com.android.systemui.statusbar.notification.row.NotificationGutsManager +import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager +import com.android.systemui.statusbar.policy.DeviceProvisionedController +import com.android.systemui.statusbar.policy.KeyguardStateController +import com.android.systemui.statusbar.window.StatusBarWindowController +import com.android.systemui.util.concurrency.FakeExecutor +import com.android.systemui.util.mockito.whenever +import com.android.systemui.util.time.FakeSystemClock +import dagger.Lazy +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.ArgumentMatchers +import org.mockito.Mock +import org.mockito.Mockito.never +import org.mockito.Mockito.verify +import org.mockito.MockitoAnnotations + +@RunWith(AndroidTestingRunner::class) +@SmallTest +class ShadeControllerImplTest : SysuiTestCase() { + @Mock private lateinit var commandQueue: CommandQueue + @Mock private lateinit var keyguardStateController: KeyguardStateController + @Mock private lateinit var statusBarStateController: StatusBarStateController + @Mock private lateinit var statusBarKeyguardViewManager: StatusBarKeyguardViewManager + @Mock private lateinit var statusBarWindowController: StatusBarWindowController + @Mock private lateinit var deviceProvisionedController: DeviceProvisionedController + @Mock private lateinit var notificationShadeWindowController: NotificationShadeWindowController + @Mock private lateinit var windowManager: WindowManager + @Mock private lateinit var assistManager: AssistManager + @Mock private lateinit var gutsManager: NotificationGutsManager + @Mock private lateinit var notificationPanelViewController: NotificationPanelViewController + @Mock private lateinit var nswvc: NotificationShadeWindowViewController + @Mock private lateinit var display: Display + + private lateinit var shadeController: ShadeControllerImpl + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + whenever(windowManager.defaultDisplay).thenReturn(display) + whenever(deviceProvisionedController.isCurrentUserSetup).thenReturn(true) + shadeController = + ShadeControllerImpl( + commandQueue, + FakeExecutor(FakeSystemClock()), + keyguardStateController, + statusBarStateController, + statusBarKeyguardViewManager, + statusBarWindowController, + deviceProvisionedController, + notificationShadeWindowController, + windowManager, + Lazy { assistManager }, + Lazy { gutsManager }, + ) + shadeController.setNotificationShadeWindowViewController(nswvc) + shadeController.setNotificationPanelViewController(notificationPanelViewController) + } + + @Test + fun testDisableNotificationShade() { + whenever(commandQueue.panelsEnabled()).thenReturn(false) + + // Trying to open it does nothing. + shadeController.animateExpandShade() + verify(notificationPanelViewController, never()).expandToNotifications() + shadeController.animateExpandQs() + verify(notificationPanelViewController, never()).expand(ArgumentMatchers.anyBoolean()) + } + + @Test + fun testEnableNotificationShade() { + whenever(commandQueue.panelsEnabled()).thenReturn(true) + + // Can now be opened. + shadeController.animateExpandShade() + verify(notificationPanelViewController).expandToNotifications() + shadeController.animateExpandQs() + verify(notificationPanelViewController).expandToQs() + } + + @Test + fun cancelExpansionAndCollapseShade_callsCancelCurrentTouch() { + // GIVEN the shade is tracking a touch + whenever(notificationPanelViewController.isTracking).thenReturn(true) + + // WHEN cancelExpansionAndCollapseShade is called + shadeController.cancelExpansionAndCollapseShade() + + // VERIFY that cancelCurrentTouch is called + verify(nswvc).cancelCurrentTouch() + } + + @Test + fun cancelExpansionAndCollapseShade_doesNotCallAnimateCollapseShade_whenCollapsed() { + // GIVEN the shade is tracking a touch + whenever(notificationPanelViewController.isTracking).thenReturn(false) + + // WHEN cancelExpansionAndCollapseShade is called + shadeController.cancelExpansionAndCollapseShade() + + // VERIFY that cancelCurrentTouch is NOT called + verify(nswvc, never()).cancelCurrentTouch() + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/transition/ScrimShadeTransitionControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/transition/ScrimShadeTransitionControllerTest.kt index cbf54854759b..a5bd2aea2d95 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/transition/ScrimShadeTransitionControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/transition/ScrimShadeTransitionControllerTest.kt @@ -2,26 +2,16 @@ package com.android.systemui.shade.transition import android.testing.AndroidTestingRunner import androidx.test.filters.SmallTest -import com.android.systemui.R import com.android.systemui.SysuiTestCase import com.android.systemui.dump.DumpManager -import com.android.systemui.flags.FeatureFlags -import com.android.systemui.flags.Flags -import com.android.systemui.shade.STATE_CLOSED -import com.android.systemui.shade.STATE_OPEN import com.android.systemui.shade.STATE_OPENING import com.android.systemui.shade.ShadeExpansionChangeEvent -import com.android.systemui.statusbar.StatusBarState -import com.android.systemui.statusbar.SysuiStatusBarStateController import com.android.systemui.statusbar.phone.ScrimController -import com.android.systemui.statusbar.policy.FakeConfigurationController -import com.android.systemui.statusbar.policy.HeadsUpManager import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock import org.mockito.Mockito.verify -import org.mockito.Mockito.`when` as whenever import org.mockito.MockitoAnnotations @RunWith(AndroidTestingRunner::class) @@ -30,10 +20,6 @@ class ScrimShadeTransitionControllerTest : SysuiTestCase() { @Mock private lateinit var scrimController: ScrimController @Mock private lateinit var dumpManager: DumpManager - @Mock private lateinit var statusBarStateController: SysuiStatusBarStateController - @Mock private lateinit var headsUpManager: HeadsUpManager - @Mock private lateinit var featureFlags: FeatureFlags - private val configurationController = FakeConfigurationController() private lateinit var controller: ScrimShadeTransitionController @@ -41,131 +27,25 @@ class ScrimShadeTransitionControllerTest : SysuiTestCase() { fun setUp() { MockitoAnnotations.initMocks(this) context.ensureTestableResources() - controller = - ScrimShadeTransitionController( - configurationController, - dumpManager, - scrimController, - context.resources, - statusBarStateController, - headsUpManager, - featureFlags) + controller = ScrimShadeTransitionController(dumpManager, scrimController) controller.onPanelStateChanged(STATE_OPENING) } @Test - fun onPanelExpansionChanged_inSingleShade_setsFractionEqualToEventFraction() { - setSplitShadeEnabled(false) - - controller.onPanelExpansionChanged(EXPANSION_EVENT) - - verify(scrimController).setRawPanelExpansionFraction(EXPANSION_EVENT.fraction) - } - - @Test - fun onPanelExpansionChanged_inSplitShade_unlockedShade_setsFractionBasedOnDragDownAmount() { - whenever(statusBarStateController.currentOrUpcomingState).thenReturn(StatusBarState.SHADE) - val scrimShadeTransitionDistance = - context.resources.getDimensionPixelSize(R.dimen.split_shade_scrim_transition_distance) - setSplitShadeEnabled(true) - - controller.onPanelExpansionChanged(EXPANSION_EVENT) - - val expectedFraction = EXPANSION_EVENT.dragDownPxAmount / scrimShadeTransitionDistance - verify(scrimController).setRawPanelExpansionFraction(expectedFraction) - } - - @Test - fun onPanelExpansionChanged_inSplitShade_largeDragDownAmount_fractionIsNotGreaterThan1() { - whenever(statusBarStateController.currentOrUpcomingState).thenReturn(StatusBarState.SHADE) - val scrimShadeTransitionDistance = - context.resources.getDimensionPixelSize(R.dimen.split_shade_scrim_transition_distance) - setSplitShadeEnabled(true) - - controller.onPanelExpansionChanged( - EXPANSION_EVENT.copy(dragDownPxAmount = 100f * scrimShadeTransitionDistance)) - - verify(scrimController).setRawPanelExpansionFraction(1f) - } - - @Test - fun onPanelExpansionChanged_inSplitShade_negativeDragDownAmount_fractionIsNotLessThan0() { - whenever(statusBarStateController.currentOrUpcomingState).thenReturn(StatusBarState.SHADE) - setSplitShadeEnabled(true) - - controller.onPanelExpansionChanged(EXPANSION_EVENT.copy(dragDownPxAmount = -100f)) - - verify(scrimController).setRawPanelExpansionFraction(0f) - } - - @Test - fun onPanelExpansionChanged_inSplitShade_onLockedShade_setsFractionEqualToEventFraction() { - whenever(statusBarStateController.currentOrUpcomingState) - .thenReturn(StatusBarState.SHADE_LOCKED) - setSplitShadeEnabled(true) - - controller.onPanelExpansionChanged(EXPANSION_EVENT) - - verify(scrimController).setRawPanelExpansionFraction(EXPANSION_EVENT.fraction) - } - - @Test - fun onPanelExpansionChanged_inSplitShade_flagTrue_setsFractionEqualToEventFraction() { - whenever(featureFlags.isEnabled(Flags.LARGE_SHADE_GRANULAR_ALPHA_INTERPOLATION)) - .thenReturn(true) - whenever(statusBarStateController.currentOrUpcomingState) - .thenReturn(StatusBarState.SHADE) - setSplitShadeEnabled(true) - - controller.onPanelExpansionChanged(EXPANSION_EVENT) - - verify(scrimController).setRawPanelExpansionFraction(EXPANSION_EVENT.fraction) - } - - @Test - fun onPanelExpansionChanged_inSplitShade_onKeyguard_setsFractionEqualToEventFraction() { - whenever(statusBarStateController.currentOrUpcomingState) - .thenReturn(StatusBarState.KEYGUARD) - setSplitShadeEnabled(true) - + fun onPanelExpansionChanged_setsFractionEqualToEventFraction() { controller.onPanelExpansionChanged(EXPANSION_EVENT) verify(scrimController).setRawPanelExpansionFraction(EXPANSION_EVENT.fraction) } - @Test - fun onPanelExpansionChanged_inSplitShade_panelOpen_setsFractionEqualToEventFraction() { - controller.onPanelStateChanged(STATE_OPEN) - whenever(statusBarStateController.currentOrUpcomingState) - .thenReturn(StatusBarState.KEYGUARD) - setSplitShadeEnabled(true) - - controller.onPanelExpansionChanged(EXPANSION_EVENT) - - verify(scrimController).setRawPanelExpansionFraction(EXPANSION_EVENT.fraction) - } - - @Test - fun onPanelExpansionChanged_inSplitShade_panelClosed_setsFractionEqualToEventFraction() { - controller.onPanelStateChanged(STATE_CLOSED) - whenever(statusBarStateController.currentOrUpcomingState) - .thenReturn(StatusBarState.KEYGUARD) - setSplitShadeEnabled(true) - - controller.onPanelExpansionChanged(EXPANSION_EVENT) - - verify(scrimController).setRawPanelExpansionFraction(EXPANSION_EVENT.fraction) - } - - private fun setSplitShadeEnabled(enabled: Boolean) { - overrideResource(R.bool.config_use_split_notification_shade, enabled) - configurationController.notifyConfigurationChanged() - } - companion object { val EXPANSION_EVENT = ShadeExpansionChangeEvent( - fraction = 0.5f, expanded = true, tracking = true, dragDownPxAmount = 10f) + fraction = 0.5f, + expanded = true, + tracking = true, + dragDownPxAmount = 10f + ) } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt index 69d03d9b0e4c..f8e1a9d12657 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt @@ -71,7 +71,7 @@ class ShadeSceneViewModelTest : SysuiTestCase() { fun upTransitionSceneKey_deviceLocked_lockScreen() = testScope.runTest { val upTransitionSceneKey by collectLastValue(underTest.upDestinationSceneKey) - authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.PIN(1234)) + authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234)) authenticationInteractor.lockDevice() assertThat(upTransitionSceneKey).isEqualTo(SceneKey.Lockscreen) @@ -81,7 +81,7 @@ class ShadeSceneViewModelTest : SysuiTestCase() { fun upTransitionSceneKey_deviceUnlocked_gone() = testScope.runTest { val upTransitionSceneKey by collectLastValue(underTest.upDestinationSceneKey) - authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.PIN(1234)) + authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234)) authenticationInteractor.unlockDevice() assertThat(upTransitionSceneKey).isEqualTo(SceneKey.Gone) @@ -91,7 +91,7 @@ class ShadeSceneViewModelTest : SysuiTestCase() { fun onContentClicked_deviceUnlocked_switchesToGone() = testScope.runTest { val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_1)) - authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.PIN(1234)) + authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234)) authenticationInteractor.unlockDevice() runCurrent() @@ -104,7 +104,7 @@ class ShadeSceneViewModelTest : SysuiTestCase() { fun onContentClicked_deviceLockedSecurely_switchesToBouncer() = testScope.runTest { val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_1)) - authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.PIN(1234)) + authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.Pin(1234)) authenticationInteractor.lockDevice() runCurrent() diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/condition/ConditionMonitorTest.java b/packages/SystemUI/tests/src/com/android/systemui/shared/condition/ConditionMonitorTest.java index b7241759ca24..0b1753ff72ff 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shared/condition/ConditionMonitorTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shared/condition/ConditionMonitorTest.java @@ -555,60 +555,6 @@ public class ConditionMonitorTest extends SysuiTestCase { } /** - * Ensures a subscription is predicated on its precondition. - */ - @Test - public void testPrecondition() { - mCondition1.fakeUpdateCondition(false); - final Monitor.Callback callback = - mock(Monitor.Callback.class); - - mCondition2.fakeUpdateCondition(false); - - // Create a nested condition - mConditionMonitor.addSubscription(new Monitor.Subscription.Builder(callback) - .addPrecondition(mCondition1) - .addCondition(mCondition2) - .build()); - - mExecutor.runAllReady(); - - // Ensure the nested condition callback is not called at all. - verify(callback, never()).onActiveChanged(anyBoolean()); - verify(callback, never()).onConditionsChanged(anyBoolean()); - - // Update the condition to true and ensure that the nested condition is not triggered. - mCondition2.fakeUpdateCondition(true); - verify(callback, never()).onConditionsChanged(anyBoolean()); - mCondition2.fakeUpdateCondition(false); - - // Set precondition and make sure the inner condition becomes active and reports that - // conditions aren't met - mCondition1.fakeUpdateCondition(true); - mExecutor.runAllReady(); - - verify(callback).onActiveChanged(eq(true)); - verify(callback).onConditionsChanged(eq(false)); - - Mockito.clearInvocations(callback); - - // Update the condition and make sure the callback is updated. - mCondition2.fakeUpdateCondition(true); - mExecutor.runAllReady(); - - verify(callback).onConditionsChanged(true); - - Mockito.clearInvocations(callback); - // Invalidate precondition and make sure callback is informed, but the last state is - // not affected. - mCondition1.fakeUpdateCondition(false); - mExecutor.runAllReady(); - - verify(callback).onActiveChanged(eq(false)); - verify(callback, never()).onConditionsChanged(anyBoolean()); - } - - /** * Ensure preconditions are applied to every subscription added to a monitor. */ @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java index 1643e174ee13..385d556092a6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java @@ -16,7 +16,6 @@ package com.android.systemui.statusbar; import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE; import static android.inputmethodservice.InputMethodService.BACK_DISPOSITION_DEFAULT; -import static android.inputmethodservice.InputMethodService.IME_INVISIBLE; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.WindowInsetsController.BEHAVIOR_DEFAULT; @@ -201,7 +200,7 @@ public class CommandQueueTest extends SysuiTestCase { mCommandQueue.setImeWindowStatus(SECONDARY_DISPLAY, null, 1, 2, true); waitForIdleSync(); - verify(mCallbacks).setImeWindowStatus(eq(DEFAULT_DISPLAY), eq(null), eq(IME_INVISIBLE), + verify(mCallbacks).setImeWindowStatus(eq(DEFAULT_DISPLAY), eq(null), eq(0), eq(BACK_DISPOSITION_DEFAULT), eq(false)); verify(mCallbacks).setImeWindowStatus( eq(SECONDARY_DISPLAY), eq(null), eq(1), eq(2), eq(true)); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyboardShortcutListSearchTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyboardShortcutListSearchTest.java index 109f185c625e..22c9e45d48af 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyboardShortcutListSearchTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyboardShortcutListSearchTest.java @@ -76,5 +76,6 @@ public class KeyboardShortcutListSearchTest extends SysuiTestCase { mKeyboardShortcutListSearch.toggle(mContext, DEVICE_ID); verify(mWindowManager).requestAppKeyboardShortcuts(any(), anyInt()); + verify(mWindowManager).requestImeKeyboardShortcuts(any(), anyInt()); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyboardShortcutsTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyboardShortcutsTest.java index ea822aa00429..a3ecde0fe976 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyboardShortcutsTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyboardShortcutsTest.java @@ -75,5 +75,6 @@ public class KeyboardShortcutsTest extends SysuiTestCase { mKeyboardShortcuts.toggle(mContext, DEVICE_ID); verify(mWindowManager).requestAppKeyboardShortcuts(any(), anyInt()); + verify(mWindowManager).requestImeKeyboardShortcuts(any(), anyInt()); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java index cd0c135264ae..48c3e2d85574 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java @@ -104,7 +104,8 @@ import com.android.systemui.flags.FakeFeatureFlags; import com.android.systemui.keyguard.KeyguardIndication; import com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController; import com.android.systemui.keyguard.ScreenLifecycle; -import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor; +import com.android.systemui.bouncer.domain.interactor.BouncerMessageInteractor; +import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.settings.UserTracker; @@ -302,6 +303,7 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase { mAlternateBouncerInteractor, mAlarmManager, mUserTracker, + mock(BouncerMessageInteractor.class), flags ); mController.init(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt index 2351f7600c65..21e0f68cff2d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt @@ -8,12 +8,15 @@ import com.android.systemui.ExpandHelper import com.android.systemui.R import com.android.systemui.SysuiTestCase import com.android.systemui.classifier.FalsingCollector +import com.android.systemui.classifier.FalsingCollectorFake import com.android.systemui.dump.DumpManager import com.android.systemui.keyguard.WakefulnessLifecycle import com.android.systemui.media.controls.ui.MediaHierarchyManager import com.android.systemui.plugins.ActivityStarter import com.android.systemui.plugins.FalsingManager import com.android.systemui.plugins.qs.QS +import com.android.systemui.power.data.repository.FakePowerRepository +import com.android.systemui.power.domain.interactor.PowerInteractor import com.android.systemui.shade.ShadeViewController import com.android.systemui.shade.data.repository.FakeShadeRepository import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow @@ -26,6 +29,7 @@ import com.android.systemui.statusbar.phone.KeyguardBypassController import com.android.systemui.statusbar.phone.LSShadeTransitionLogger import com.android.systemui.statusbar.phone.ScrimController import com.android.systemui.statusbar.policy.FakeConfigurationController +import com.android.systemui.util.mockito.mock import org.junit.After import org.junit.Assert.assertFalse import org.junit.Assert.assertNotNull @@ -83,6 +87,12 @@ class LockscreenShadeTransitionControllerTest : SysuiTestCase() { @Mock lateinit var qsTransitionController: LockscreenShadeQsTransitionController @Mock lateinit var activityStarter: ActivityStarter @Mock lateinit var transitionControllerCallback: LockscreenShadeTransitionController.Callback + private val powerInteractor = PowerInteractor( + FakePowerRepository(), + FalsingCollectorFake(), + screenOffAnimationController = mock(), + statusBarStateController = mock(), + ) @JvmField @Rule val mockito = MockitoJUnit.rule() private val configurationController = FakeConfigurationController() @@ -129,6 +139,7 @@ class LockscreenShadeTransitionControllerTest : SysuiTestCase() { qsTransitionControllerFactory = { qsTransitionController }, activityStarter = activityStarter, shadeRepository = FakeShadeRepository(), + powerInteractor = powerInteractor, ) transitionController.addCallback(transitionControllerCallback) whenever(nsslController.view).thenReturn(stackscroller) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/disableflags/DisableFlagsLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/disableflags/DisableFlagsLoggerTest.kt index 4b5d0a37b023..f1c7956a1bc7 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/disableflags/DisableFlagsLoggerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/disableflags/DisableFlagsLoggerTest.kt @@ -37,73 +37,8 @@ class DisableFlagsLoggerTest : SysuiTestCase() { private val disableFlagsLogger = DisableFlagsLogger(disable1Flags, disable2Flags) @Test - fun getDisableFlagsString_oldAndNewSame_newAndUnchangedLoggedOldNotLogged() { - val state = DisableFlagsLogger.DisableState( - 0b111, // ABC - 0b01 // mN - ) - - val result = disableFlagsLogger.getDisableFlagsString(state, state) - - assertThat(result).doesNotContain("Old") - assertThat(result).contains("ABC.mN") - assertThat(result).contains("(unchanged)") - } - - @Test - fun getDisableFlagsString_oldAndNewDifferent_statesAndDiffLogged() { - val result = disableFlagsLogger.getDisableFlagsString( - DisableFlagsLogger.DisableState( - 0b111, // ABC - 0b01, // mN - ), - DisableFlagsLogger.DisableState( - 0b001, // abC - 0b10 // Mn - ) - ) - - assertThat(result).contains("Old: ABC.mN") - assertThat(result).contains("New: abC.Mn") - assertThat(result).contains("(changed: ab.Mn)") - } - - @Test - fun getDisableFlagsString_onlyDisable2Different_diffLoggedCorrectly() { - val result = disableFlagsLogger.getDisableFlagsString( - DisableFlagsLogger.DisableState( - 0b001, // abC - 0b01, // mN - ), - DisableFlagsLogger.DisableState( - 0b001, // abC - 0b00 // mn - ) - ) - - assertThat(result).contains("(changed: .n)") - } - - @Test - fun getDisableFlagsString_nullOld_onlyNewStateLogged() { - val result = disableFlagsLogger.getDisableFlagsString( - old = null, - new = DisableFlagsLogger.DisableState( - 0b001, // abC - 0b01, // mN - ), - ) - - assertThat(result).doesNotContain("Old") - assertThat(result).contains("abC.mN") - assertThat(result).doesNotContain("(") - assertThat(result).doesNotContain(")") - } - - @Test fun getDisableFlagsString_nullLocalModification_localModNotLogged() { val result = disableFlagsLogger.getDisableFlagsString( - DisableFlagsLogger.DisableState(0, 0), DisableFlagsLogger.DisableState(1, 1), newAfterLocalModification = null ) @@ -118,9 +53,7 @@ class DisableFlagsLoggerTest : SysuiTestCase() { 0b10 // mn ) - val result = disableFlagsLogger.getDisableFlagsString( - DisableFlagsLogger.DisableState(0, 0), newState, newState - ) + val result = disableFlagsLogger.getDisableFlagsString(newState, newState) assertThat(result).doesNotContain("local modification") } @@ -128,7 +61,6 @@ class DisableFlagsLoggerTest : SysuiTestCase() { @Test fun getDisableFlagsString_newAfterLocalModificationDifferent_localModAndDiffLogged() { val result = disableFlagsLogger.getDisableFlagsString( - old = DisableFlagsLogger.DisableState(0, 0), new = DisableFlagsLogger.DisableState( 0b000, // abc 0b00 // mn @@ -143,6 +75,22 @@ class DisableFlagsLoggerTest : SysuiTestCase() { } @Test + fun getDisableFlagsString_onlyDisable2Different_diffLoggedCorrectly() { + val result = disableFlagsLogger.getDisableFlagsString( + DisableFlagsLogger.DisableState( + 0b001, // abC + 0b01, // mN + ), + DisableFlagsLogger.DisableState( + 0b001, // abC + 0b00 // mn + ) + ) + + assertThat(result).contains("(changed: .n)") + } + + @Test fun constructor_defaultDisableFlags_noException() { // Just creating the logger with the default params will trigger the exception if there // is one. diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/disableflags/data/repository/DisableFlagsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/disableflags/data/repository/DisableFlagsRepositoryTest.kt new file mode 100644 index 000000000000..4a71c37a4616 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/disableflags/data/repository/DisableFlagsRepositoryTest.kt @@ -0,0 +1,153 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package com.android.systemui.statusbar.disableflags.data.repository + +import android.app.StatusBarManager.DISABLE2_NONE +import android.app.StatusBarManager.DISABLE_CLOCK +import android.app.StatusBarManager.DISABLE_NONE +import android.app.StatusBarManager.DISABLE_NOTIFICATION_ALERTS +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.dump.DumpManager +import com.android.systemui.log.LogBufferFactory +import com.android.systemui.statusbar.CommandQueue +import com.android.systemui.statusbar.disableflags.DisableFlagsLogger +import com.android.systemui.statusbar.disableflags.data.model.DisableFlagsModel +import com.android.systemui.util.mockito.any +import com.android.systemui.util.mockito.argumentCaptor +import com.android.systemui.util.mockito.mock +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.UnconfinedTestDispatcher +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.Test +import org.mockito.Mockito.verify + +@SmallTest +@OptIn(ExperimentalCoroutinesApi::class) +class DisableFlagsRepositoryTest : SysuiTestCase() { + + private lateinit var underTest: DisableFlagsRepository + + private val testScope = TestScope(UnconfinedTestDispatcher()) + private val commandQueue: CommandQueue = mock() + private val logBuffer = LogBufferFactory(DumpManager(), mock()).create("buffer", 10) + private val disableFlagsLogger = DisableFlagsLogger() + + @Before + fun setUp() { + underTest = + DisableFlagsRepository( + commandQueue, + DISPLAY_ID, + testScope.backgroundScope, + logBuffer, + disableFlagsLogger, + ) + } + + @Test + fun disableFlags_initialValue_none() { + assertThat(underTest.disableFlags.value) + .isEqualTo(DisableFlagsModel(DISABLE_NONE, DISABLE2_NONE)) + } + + @Test + fun disableFlags_noSubscribers_callbackStillRegistered() = + testScope.runTest { verify(commandQueue).addCallback(any()) } + + @Test + fun disableFlags_notifAlertsNotDisabled_notifAlertsEnabledTrue() = + testScope.runTest { + getCommandQueueCallback() + .disable(DISPLAY_ID, DISABLE_NONE, DISABLE2_NONE, /* animate= */ false) + + assertThat(underTest.disableFlags.value.areNotificationAlertsEnabled()).isTrue() + } + + @Test + fun disableFlags_notifAlertsDisabled_notifAlertsEnabledFalse() = + testScope.runTest { + getCommandQueueCallback() + .disable( + DISPLAY_ID, + DISABLE_NOTIFICATION_ALERTS, + DISABLE2_NONE, + /* animate= */ false, + ) + + assertThat(underTest.disableFlags.value.areNotificationAlertsEnabled()).isFalse() + } + + @Test + fun disableFlags_notifAlertsDisabled_differentDisplay_notifAlertsEnabledTrue() = + testScope.runTest { + val wrongDisplayId = DISPLAY_ID + 10 + + getCommandQueueCallback() + .disable( + wrongDisplayId, + DISABLE_NOTIFICATION_ALERTS, + DISABLE2_NONE, + /* animate= */ false, + ) + + // THEN our repo reports them as still enabled + assertThat(underTest.disableFlags.value.areNotificationAlertsEnabled()).isTrue() + } + + @Test + fun disableFlags_reactsToChanges() = + testScope.runTest { + getCommandQueueCallback() + .disable( + DISPLAY_ID, + DISABLE_NOTIFICATION_ALERTS, + DISABLE2_NONE, + /* animate= */ false, + ) + assertThat(underTest.disableFlags.value.areNotificationAlertsEnabled()).isFalse() + + getCommandQueueCallback() + .disable( + DISPLAY_ID, + DISABLE_CLOCK, // Unrelated to notifications + DISABLE2_NONE, + /* animate= */ false, + ) + assertThat(underTest.disableFlags.value.areNotificationAlertsEnabled()).isTrue() + + getCommandQueueCallback() + .disable( + DISPLAY_ID, + DISABLE_NOTIFICATION_ALERTS, + DISABLE2_NONE, + /* animate= */ false, + ) + assertThat(underTest.disableFlags.value.areNotificationAlertsEnabled()).isFalse() + } + + private fun getCommandQueueCallback(): CommandQueue.Callbacks { + val callbackCaptor = argumentCaptor<CommandQueue.Callbacks>() + verify(commandQueue).addCallback(callbackCaptor.capture()) + return callbackCaptor.value + } + + private companion object { + const val DISPLAY_ID = 1 + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsInteractorTest.kt new file mode 100644 index 000000000000..4427a8d220f8 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsInteractorTest.kt @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package com.android.systemui.statusbar.notification.domain.interactor + +import android.app.StatusBarManager +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.dump.DumpManager +import com.android.systemui.log.LogBufferFactory +import com.android.systemui.statusbar.CommandQueue +import com.android.systemui.statusbar.disableflags.DisableFlagsLogger +import com.android.systemui.statusbar.disableflags.data.repository.DisableFlagsRepository +import com.android.systemui.util.mockito.argumentCaptor +import com.android.systemui.util.mockito.mock +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.UnconfinedTestDispatcher +import org.junit.Before +import org.junit.Test +import org.mockito.Mockito.verify + +@SmallTest +@OptIn(ExperimentalCoroutinesApi::class) +class NotificationsInteractorTest : SysuiTestCase() { + + private lateinit var underTest: NotificationsInteractor + + private val testScope = TestScope(UnconfinedTestDispatcher()) + private val commandQueue: CommandQueue = mock() + private val logBuffer = LogBufferFactory(DumpManager(), mock()).create("buffer", 10) + private val disableFlagsLogger = DisableFlagsLogger() + private lateinit var disableFlagsRepository: DisableFlagsRepository + + @Before + fun setUp() { + disableFlagsRepository = + DisableFlagsRepository( + commandQueue, + DISPLAY_ID, + testScope.backgroundScope, + logBuffer, + disableFlagsLogger, + ) + underTest = NotificationsInteractor(disableFlagsRepository) + } + + @Test + fun disableFlags_notifAlertsNotDisabled_notifAlertsEnabledTrue() { + val callback = getCommandQueueCallback() + + callback.disable( + DISPLAY_ID, + StatusBarManager.DISABLE_NONE, + StatusBarManager.DISABLE2_NONE, + /* animate= */ false + ) + + assertThat(underTest.areNotificationAlertsEnabled()).isTrue() + } + + @Test + fun disableFlags_notifAlertsDisabled_notifAlertsEnabledFalse() { + val callback = getCommandQueueCallback() + + callback.disable( + DISPLAY_ID, + StatusBarManager.DISABLE_NOTIFICATION_ALERTS, + StatusBarManager.DISABLE2_NONE, + /* animate= */ false + ) + + assertThat(underTest.areNotificationAlertsEnabled()).isFalse() + } + + private fun getCommandQueueCallback(): CommandQueue.Callbacks { + val callbackCaptor = argumentCaptor<CommandQueue.Callbacks>() + verify(commandQueue).addCallback(callbackCaptor.capture()) + return callbackCaptor.value + } + + private companion object { + const val DISPLAY_ID = 1 + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragControllerTest.java index 915924f13197..bdc8135707bc 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowDragControllerTest.java @@ -108,7 +108,7 @@ public class ExpandableNotificationRowDragControllerTest extends SysuiTestCase { mRow.doDragCallback(0, 0); verify(controller).startDragAndDrop(mRow); - verify(mShadeController).animateCollapsePanels(eq(0), eq(true), + verify(mShadeController).animateCollapseShade(eq(0), eq(true), eq(false), anyFloat()); verify(mNotificationPanelLogger, times(1)).logNotificationDrag(any()); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java index 4c83194783ab..608778e05dad 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java @@ -733,6 +733,36 @@ public class ExpandableNotificationRowTest extends SysuiTestCase { verify(lowPriVectorDrawable, times(1)).start(); } + @Test + public void isExpanded_hideSensitive_sensitiveNotExpanded() + throws Exception { + // GIVEN + final ExpandableNotificationRow row = mNotificationTestHelper.createRow(); + row.setUserExpanded(true); + row.setOnKeyguard(false); + row.setSensitive(/* sensitive= */true, /* hideSensitive= */false); + row.setHideSensitive(/* hideSensitive= */true, /* animated= */false, + /* delay= */0L, /* duration= */0L); + + // THEN + assertThat(row.isExpanded()).isFalse(); + } + + @Test + public void isExpanded_hideSensitive_nonSensitiveExpanded() + throws Exception { + // GIVEN + final ExpandableNotificationRow row = mNotificationTestHelper.createRow(); + row.setUserExpanded(true); + row.setOnKeyguard(false); + row.setSensitive(/* sensitive= */true, /* hideSensitive= */false); + row.setHideSensitive(/* hideSensitive= */false, /* animated= */false, + /* delay= */0L, /* duration= */0L); + + // THEN + assertThat(row.isExpanded()).isTrue(); + } + private void setDrawableIconsInImageView(CachingIconView icon, Drawable iconDrawable, Drawable rightIconDrawable) { ImageView iconView = mock(ImageView.class); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractorTest.kt index 944eb2d8aadf..a87dd2d3d670 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractorTest.kt @@ -22,21 +22,23 @@ import android.os.PowerManager import android.testing.AndroidTestingRunner import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase +import com.android.systemui.classifier.FalsingCollectorFake import com.android.systemui.coroutines.collectLastValue import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFaceAuthRepository import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository +import com.android.systemui.plugins.statusbar.StatusBarStateController +import com.android.systemui.power.data.repository.FakePowerRepository +import com.android.systemui.power.domain.interactor.PowerInteractor import com.android.systemui.statusbar.LockscreenShadeTransitionController -import com.android.systemui.statusbar.phone.CentralSurfaces -import com.android.systemui.util.mockito.any +import com.android.systemui.statusbar.phone.ScreenOffAnimationController import com.android.systemui.util.mockito.eq import com.android.systemui.util.mockito.mock -import com.android.systemui.util.time.FakeSystemClock +import com.android.systemui.util.mockito.whenever import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import org.junit.Test import org.junit.runner.RunWith -import org.mockito.ArgumentMatchers.anyLong import org.mockito.Mockito.isNull import org.mockito.Mockito.verify @@ -46,15 +48,27 @@ class NotificationShelfInteractorTest : SysuiTestCase() { private val keyguardRepository = FakeKeyguardRepository() private val deviceEntryFaceAuthRepository = FakeDeviceEntryFaceAuthRepository() - private val centralSurfaces: CentralSurfaces = mock() - private val systemClock = FakeSystemClock() + + private val screenOffAnimationController = + mock<ScreenOffAnimationController>().also { + whenever(it.allowWakeUpIfDozing()).thenReturn(true) + } + private val statusBarStateController: StatusBarStateController = mock() + private val powerRepository = FakePowerRepository() + private val powerInteractor = + PowerInteractor( + powerRepository, + FalsingCollectorFake(), + screenOffAnimationController, + statusBarStateController, + ) + private val keyguardTransitionController: LockscreenShadeTransitionController = mock() private val underTest = NotificationShelfInteractor( keyguardRepository, deviceEntryFaceAuthRepository, - centralSurfaces, - systemClock, + powerInteractor, keyguardTransitionController, ) @@ -107,10 +121,12 @@ class NotificationShelfInteractorTest : SysuiTestCase() { @Test fun goToLockedShadeFromShelf_wakesUpFromDoze() { + whenever(statusBarStateController.isDozing).thenReturn(true) + underTest.goToLockedShadeFromShelf() - verify(centralSurfaces) - .wakeUpIfDozing(anyLong(), any(), eq(PowerManager.WAKE_REASON_GESTURE)) + assertThat(powerRepository.lastWakeReason).isNotNull() + assertThat(powerRepository.lastWakeReason).isEqualTo(PowerManager.WAKE_REASON_GESTURE) } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModelTest.kt index e9a8f3f0d60c..7ae150231b98 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModelTest.kt @@ -24,23 +24,26 @@ import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.accessibility.data.repository.FakeAccessibilityRepository import com.android.systemui.accessibility.domain.interactor.AccessibilityInteractor +import com.android.systemui.classifier.FalsingCollectorFake import com.android.systemui.coroutines.collectLastValue import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFaceAuthRepository import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository +import com.android.systemui.plugins.statusbar.StatusBarStateController +import com.android.systemui.power.data.repository.FakePowerRepository +import com.android.systemui.power.domain.interactor.PowerInteractor import com.android.systemui.statusbar.LockscreenShadeTransitionController import com.android.systemui.statusbar.notification.row.ui.viewmodel.ActivatableNotificationViewModel import com.android.systemui.statusbar.notification.shelf.domain.interactor.NotificationShelfInteractor -import com.android.systemui.statusbar.phone.CentralSurfaces -import com.android.systemui.util.mockito.any +import com.android.systemui.statusbar.phone.ScreenOffAnimationController import com.android.systemui.util.mockito.eq -import com.android.systemui.util.time.FakeSystemClock +import com.android.systemui.util.mockito.whenever import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest +import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith -import org.mockito.ArgumentMatchers import org.mockito.Mock import org.mockito.Mockito import org.mockito.Mockito.verify @@ -54,14 +57,23 @@ class NotificationShelfViewModelTest : SysuiTestCase() { @Rule @JvmField val mockitoRule: MockitoRule = MockitoJUnit.rule() // mocks - @Mock private lateinit var centralSurfaces: CentralSurfaces @Mock private lateinit var keyguardTransitionController: LockscreenShadeTransitionController + @Mock private lateinit var screenOffAnimationController: ScreenOffAnimationController + @Mock private lateinit var statusBarStateController: StatusBarStateController // fakes private val keyguardRepository = FakeKeyguardRepository() private val deviceEntryFaceAuthRepository = FakeDeviceEntryFaceAuthRepository() - private val systemClock = FakeSystemClock() private val a11yRepo = FakeAccessibilityRepository() + private val powerRepository = FakePowerRepository() + private val powerInteractor by lazy { + PowerInteractor( + powerRepository, + FalsingCollectorFake(), + screenOffAnimationController, + statusBarStateController, + ) + } // real impls private val a11yInteractor = AccessibilityInteractor(a11yRepo) @@ -70,13 +82,17 @@ class NotificationShelfViewModelTest : SysuiTestCase() { NotificationShelfInteractor( keyguardRepository, deviceEntryFaceAuthRepository, - centralSurfaces, - systemClock, + powerInteractor, keyguardTransitionController, ) } private val underTest by lazy { NotificationShelfViewModel(interactor, activatableViewModel) } + @Before + fun setUp() { + whenever(screenOffAnimationController.allowWakeUpIfDozing()).thenReturn(true) + } + @Test fun canModifyColorOfNotifications_whenKeyguardNotShowing() = runTest { val canModifyNotifColor by collectLastValue(underTest.canModifyColorOfNotifications) @@ -126,10 +142,12 @@ class NotificationShelfViewModelTest : SysuiTestCase() { @Test fun onClicked_goesToLockedShade() { + whenever(statusBarStateController.isDozing).thenReturn(true) + underTest.onShelfClicked() - verify(centralSurfaces) - .wakeUpIfDozing(ArgumentMatchers.anyLong(), any(), eq(PowerManager.WAKE_REASON_GESTURE)) + assertThat(powerRepository.lastWakeReason).isNotNull() + assertThat(powerRepository.lastWakeReason).isEqualTo(PowerManager.WAKE_REASON_GESTURE) verify(keyguardTransitionController).goToLockedShade(Mockito.isNull(), eq(true)) } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt index 05b70ebfbb3e..8d751e3b2808 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt @@ -1,20 +1,20 @@ package com.android.systemui.statusbar.notification.stack import android.testing.AndroidTestingRunner -import android.testing.TestableLooper import android.testing.TestableLooper.RunWithLooper +import android.view.LayoutInflater +import android.widget.FrameLayout import androidx.test.filters.SmallTest import com.android.keyguard.BouncerPanelExpansionCalculator.aboutToShowBouncerProgress +import com.android.systemui.R import com.android.systemui.SysuiTestCase import com.android.systemui.animation.ShadeInterpolation import com.android.systemui.flags.FeatureFlags -import com.android.systemui.flags.Flags import com.android.systemui.shade.transition.LargeScreenShadeInterpolator import com.android.systemui.statusbar.NotificationShelf import com.android.systemui.statusbar.StatusBarIconView import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow import com.android.systemui.statusbar.notification.row.ExpandableView -import com.android.systemui.statusbar.notification.row.NotificationTestHelper import com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm.StackScrollAlgorithmState import com.android.systemui.util.mockito.mock import junit.framework.Assert.assertEquals @@ -36,33 +36,32 @@ import org.mockito.Mockito.`when` as whenever @RunWithLooper class NotificationShelfTest : SysuiTestCase() { - @Mock private lateinit var largeScreenShadeInterpolator: LargeScreenShadeInterpolator - @Mock private lateinit var flags: FeatureFlags - - private val shelf = NotificationShelf( - context, - /* attrs */ null, - /* showNotificationShelf */true - ) - private val shelfState = shelf.viewState as NotificationShelf.ShelfState - private val ambientState = mock(AmbientState::class.java) - private val hostLayoutController: NotificationStackScrollLayoutController = mock() - private val notificationTestHelper by lazy { - allowTestableLooperAsMainThread() - NotificationTestHelper( - mContext, - mDependency, - TestableLooper.get(this)) - } + @Mock + private lateinit var largeScreenShadeInterpolator: LargeScreenShadeInterpolator + @Mock + private lateinit var flags: FeatureFlags + @Mock + private lateinit var ambientState: AmbientState + @Mock + private lateinit var hostLayoutController: NotificationStackScrollLayoutController + + private lateinit var shelf: NotificationShelf @Before fun setUp() { MockitoAnnotations.initMocks(this) + val root = FrameLayout(context) + shelf = LayoutInflater.from(root.context) + .inflate(/* resource = */ R.layout.status_bar_notification_shelf, + /* root = */root, + /* attachToRoot = */false) as NotificationShelf + whenever(ambientState.largeScreenShadeInterpolator).thenReturn(largeScreenShadeInterpolator) whenever(ambientState.featureFlags).thenReturn(flags) + whenever(ambientState.isSmallScreen).thenReturn(true) + shelf.bind(ambientState, /* hostLayoutController */ hostLayoutController) shelf.layout(/* left */ 0, /* top */ 0, /* right */ 30, /* bottom */5) - whenever(ambientState.isSmallScreen).thenReturn(true) } @Test @@ -311,9 +310,8 @@ class NotificationShelfTest : SysuiTestCase() { } @Test - fun updateState_flagTrue_largeScreen_expansionChanging_shelfAlphaUpdated_largeScreenValue() { + fun updateState_largeScreen_expansionChanging_shelfAlphaUpdated_largeScreenValue() { val expansionFraction = 0.6f - whenever(flags.isEnabled(Flags.LARGE_SHADE_GRANULAR_ALPHA_INTERPOLATION)).thenReturn(true) whenever(ambientState.isSmallScreen).thenReturn(false) whenever(largeScreenShadeInterpolator.getNotificationContentAlpha(expansionFraction)) .thenReturn(0.123f) @@ -325,20 +323,6 @@ class NotificationShelfTest : SysuiTestCase() { } @Test - fun updateState_flagFalse_largeScreen_expansionChanging_shelfAlphaUpdated_standardValue() { - val expansionFraction = 0.6f - whenever(flags.isEnabled(Flags.LARGE_SHADE_GRANULAR_ALPHA_INTERPOLATION)).thenReturn(false) - whenever(ambientState.isSmallScreen).thenReturn(false) - whenever(largeScreenShadeInterpolator.getNotificationContentAlpha(expansionFraction)) - .thenReturn(0.123f) - - updateState_expansionChanging_shelfAlphaUpdated( - expansionFraction = expansionFraction, - expectedAlpha = ShadeInterpolation.getContentAlpha(expansionFraction) - ) - } - - @Test fun updateState_expansionChangingWhileBouncerInTransit_shelfAlphaUpdated() { whenever(ambientState.isBouncerInTransit).thenReturn(true) @@ -358,6 +342,127 @@ class NotificationShelfTest : SysuiTestCase() { ) } + @Test + fun updateState_withNullLastVisibleBackgroundChild_hideShelf() { + // GIVEN + shelf.setSensitiveRevealAnimEnabled(true) + whenever(ambientState.stackY).thenReturn(100f) + whenever(ambientState.stackHeight).thenReturn(100f) + val paddingBetweenElements = + context.resources.getDimensionPixelSize(R.dimen.notification_divider_height) + val endOfStack = 200f + paddingBetweenElements + whenever(ambientState.isShadeExpanded).thenReturn(true) + val lastVisibleBackgroundChild = mock<ExpandableView>() + val expandableViewState = ExpandableViewState() + whenever(lastVisibleBackgroundChild.viewState).thenReturn(expandableViewState) + val stackScrollAlgorithmState = StackScrollAlgorithmState() + stackScrollAlgorithmState.firstViewInShelf = mock() + + whenever(ambientState.lastVisibleBackgroundChild).thenReturn(null) + + // WHEN + shelf.updateState(stackScrollAlgorithmState, ambientState) + + // THEN + val shelfState = shelf.viewState as NotificationShelf.ShelfState + assertEquals(true, shelfState.hidden) + assertEquals(endOfStack, shelfState.yTranslation) + } + + @Test + fun updateState_withNullFirstViewInShelf_hideShelf() { + // GIVEN + shelf.setSensitiveRevealAnimEnabled(true) + whenever(ambientState.stackY).thenReturn(100f) + whenever(ambientState.stackHeight).thenReturn(100f) + val paddingBetweenElements = + context.resources.getDimensionPixelSize(R.dimen.notification_divider_height) + val endOfStack = 200f + paddingBetweenElements + whenever(ambientState.isShadeExpanded).thenReturn(true) + val lastVisibleBackgroundChild = mock<ExpandableView>() + val expandableViewState = ExpandableViewState() + whenever(lastVisibleBackgroundChild.viewState).thenReturn(expandableViewState) + whenever(ambientState.lastVisibleBackgroundChild).thenReturn(lastVisibleBackgroundChild) + val stackScrollAlgorithmState = StackScrollAlgorithmState() + + stackScrollAlgorithmState.firstViewInShelf = null + + // WHEN + shelf.updateState(stackScrollAlgorithmState, ambientState) + + // THEN + val shelfState = shelf.viewState as NotificationShelf.ShelfState + assertEquals(true, shelfState.hidden) + assertEquals(endOfStack, shelfState.yTranslation) + } + + @Test + fun updateState_withCollapsedShade_hideShelf() { + // GIVEN + shelf.setSensitiveRevealAnimEnabled(true) + whenever(ambientState.stackY).thenReturn(100f) + whenever(ambientState.stackHeight).thenReturn(100f) + val paddingBetweenElements = + context.resources.getDimensionPixelSize(R.dimen.notification_divider_height) + val endOfStack = 200f + paddingBetweenElements + val lastVisibleBackgroundChild = mock<ExpandableView>() + val expandableViewState = ExpandableViewState() + whenever(lastVisibleBackgroundChild.viewState).thenReturn(expandableViewState) + whenever(ambientState.lastVisibleBackgroundChild).thenReturn(lastVisibleBackgroundChild) + val stackScrollAlgorithmState = StackScrollAlgorithmState() + stackScrollAlgorithmState.firstViewInShelf = mock() + + whenever(ambientState.isShadeExpanded).thenReturn(false) + + // WHEN + shelf.updateState(stackScrollAlgorithmState, ambientState) + + // THEN + val shelfState = shelf.viewState as NotificationShelf.ShelfState + assertEquals(true, shelfState.hidden) + assertEquals(endOfStack, shelfState.yTranslation) + } + + @Test + fun updateState_withHiddenSectionBeforeShelf_hideShelf() { + // GIVEN + shelf.setSensitiveRevealAnimEnabled(true) + whenever(ambientState.stackY).thenReturn(100f) + whenever(ambientState.stackHeight).thenReturn(100f) + val paddingBetweenElements = + context.resources.getDimensionPixelSize(R.dimen.notification_divider_height) + val endOfStack = 200f + paddingBetweenElements + whenever(ambientState.isShadeExpanded).thenReturn(true) + val lastVisibleBackgroundChild = mock<ExpandableView>() + val expandableViewState = ExpandableViewState() + whenever(lastVisibleBackgroundChild.viewState).thenReturn(expandableViewState) + val stackScrollAlgorithmState = StackScrollAlgorithmState() + whenever(ambientState.lastVisibleBackgroundChild).thenReturn(lastVisibleBackgroundChild) + + val ssaVisibleChild = mock<ExpandableView>() + val ssaVisibleChildState = ExpandableViewState() + ssaVisibleChildState.hidden = true + whenever(ssaVisibleChild.viewState).thenReturn(ssaVisibleChildState) + + val ssaVisibleChild1 = mock<ExpandableView>() + val ssaVisibleChildState1 = ExpandableViewState() + ssaVisibleChildState1.hidden = true + whenever(ssaVisibleChild1.viewState).thenReturn(ssaVisibleChildState1) + + stackScrollAlgorithmState.visibleChildren.add(ssaVisibleChild) + stackScrollAlgorithmState.visibleChildren.add(ssaVisibleChild1) + whenever(ambientState.isExpansionChanging).thenReturn(true) + stackScrollAlgorithmState.firstViewInShelf = ssaVisibleChild1 + + // WHEN + shelf.updateState(stackScrollAlgorithmState, ambientState) + + // THEN + val shelfState = shelf.viewState as NotificationShelf.ShelfState + assertEquals(true, shelfState.hidden) + assertEquals(endOfStack, shelfState.yTranslation) + } + private fun setFractionToShade(fraction: Float) { whenever(ambientState.fractionToShade).thenReturn(fraction) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java index 02666e40b98d..bb9937bc743d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java @@ -44,10 +44,13 @@ import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.UiEventLogger; import com.android.internal.logging.nano.MetricsProto; import com.android.systemui.SysuiTestCase; +import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor; import com.android.systemui.classifier.FalsingCollectorFake; import com.android.systemui.classifier.FalsingManagerFake; import com.android.systemui.dump.DumpManager; -import com.android.systemui.flags.FeatureFlags; +import com.android.systemui.flags.FakeFeatureFlags; +import com.android.systemui.flags.Flags; +import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor; import com.android.systemui.media.controls.ui.KeyguardMediaController; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; @@ -71,11 +74,11 @@ import com.android.systemui.statusbar.notification.collection.render.GroupExpans import com.android.systemui.statusbar.notification.collection.render.NotifStats; import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider; import com.android.systemui.statusbar.notification.collection.render.SectionHeaderController; +import com.android.systemui.statusbar.notification.init.NotificationsController; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.NotificationGutsManager; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController.NotificationPanelEvent; import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationListViewModel; -import com.android.systemui.statusbar.phone.CentralSurfaces; import com.android.systemui.statusbar.phone.HeadsUpManagerPhone; import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.phone.NotificationIconAreaController; @@ -106,6 +109,7 @@ import java.util.Optional; public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase { @Mock private NotificationGutsManager mNotificationGutsManager; + @Mock private NotificationsController mNotificationsController; @Mock private NotificationVisibilityProvider mVisibilityProvider; @Mock private HeadsUpManagerPhone mHeadsUpManager; @Mock private NotificationRoundnessManager mNotificationRoundnessManager; @@ -118,6 +122,8 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase { @Mock private KeyguardMediaController mKeyguardMediaController; @Mock private SysuiStatusBarStateController mSysuiStatusBarStateController; @Mock private KeyguardBypassController mKeyguardBypassController; + @Mock private KeyguardInteractor mKeyguardInteractor; + @Mock private PrimaryBouncerInteractor mPrimaryBouncerInteractor; @Mock private NotificationLockscreenUserManager mNotificationLockscreenUserManager; @Mock private MetricsLogger mMetricsLogger; @Mock private DumpManager mDumpManager; @@ -125,7 +131,6 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase { @Mock(answer = Answers.RETURNS_SELF) private NotificationSwipeHelper.Builder mNotificationSwipeHelperBuilder; @Mock private NotificationSwipeHelper mNotificationSwipeHelper; - @Mock private CentralSurfaces mCentralSurfaces; @Mock private ScrimController mScrimController; @Mock private GroupExpansionManager mGroupExpansionManager; @Mock private SectionHeaderController mSilentHeaderController; @@ -141,7 +146,7 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase { @Mock private StackStateLogger mStackLogger; @Mock private NotificationStackScrollLogger mLogger; @Mock private NotificationStackSizeCalculator mNotificationStackSizeCalculator; - @Mock private FeatureFlags mFeatureFlags; + private FakeFeatureFlags mFeatureFlags = new FakeFeatureFlags(); @Mock private NotificationTargetsHelper mNotificationTargetsHelper; @Mock private SecureSettings mSecureSettings; @Mock private NotificationIconAreaController mIconAreaController; @@ -159,6 +164,8 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase { public void setUp() { MockitoAnnotations.initMocks(this); + mFeatureFlags.set(Flags.USE_REPOS_FOR_BOUNCER_SHOWING, false); + when(mNotificationSwipeHelperBuilder.build()).thenReturn(mNotificationSwipeHelper); } @@ -258,28 +265,84 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase { } @Test - public void testUpdateEmptyShadeView_bouncerShowing_hideEmptyView() { + public void testUpdateEmptyShadeView_bouncerShowing_flagOff_hideEmptyView() { when(mZenModeController.areNotificationsHiddenInShade()).thenReturn(false); initController(/* viewIsAttached= */ true); - when(mCentralSurfaces.isBouncerShowing()).thenReturn(true); + // WHEN the flag is off and *only* CentralSurfaces has bouncer as showing + mFeatureFlags.set(Flags.USE_REPOS_FOR_BOUNCER_SHOWING, false); + mController.setBouncerShowingFromCentralSurfaces(true); + when(mPrimaryBouncerInteractor.isBouncerShowing()).thenReturn(false); + setupShowEmptyShadeViewState(true); reset(mNotificationStackScrollLayout); mController.updateShowEmptyShadeView(); + + // THEN the CentralSurfaces value is used. Since the bouncer is showing, we hide the empty + // view. verify(mNotificationStackScrollLayout).updateEmptyShadeView( /* visible= */ false, /* areNotificationsHiddenInShade= */ false); } @Test - public void testUpdateEmptyShadeView_bouncerNotShowing_showEmptyView() { + public void testUpdateEmptyShadeView_bouncerShowing_flagOn_hideEmptyView() { when(mZenModeController.areNotificationsHiddenInShade()).thenReturn(false); initController(/* viewIsAttached= */ true); - when(mCentralSurfaces.isBouncerShowing()).thenReturn(false); + // WHEN the flag is on and *only* PrimaryBouncerInteractor has bouncer as showing + mFeatureFlags.set(Flags.USE_REPOS_FOR_BOUNCER_SHOWING, true); + when(mPrimaryBouncerInteractor.isBouncerShowing()).thenReturn(true); + mController.setBouncerShowingFromCentralSurfaces(false); + setupShowEmptyShadeViewState(true); reset(mNotificationStackScrollLayout); mController.updateShowEmptyShadeView(); + + // THEN the PrimaryBouncerInteractor value is used. Since the bouncer is showing, we + // hide the empty view. + verify(mNotificationStackScrollLayout).updateEmptyShadeView( + /* visible= */ false, + /* areNotificationsHiddenInShade= */ false); + } + + @Test + public void testUpdateEmptyShadeView_bouncerNotShowing_flagOff_showEmptyView() { + when(mZenModeController.areNotificationsHiddenInShade()).thenReturn(false); + initController(/* viewIsAttached= */ true); + + // WHEN the flag is off and *only* CentralSurfaces has bouncer as not showing + mFeatureFlags.set(Flags.USE_REPOS_FOR_BOUNCER_SHOWING, false); + mController.setBouncerShowingFromCentralSurfaces(false); + when(mPrimaryBouncerInteractor.isBouncerShowing()).thenReturn(true); + + setupShowEmptyShadeViewState(true); + reset(mNotificationStackScrollLayout); + mController.updateShowEmptyShadeView(); + + // THEN the CentralSurfaces value is used. Since the bouncer isn't showing, we can show the + // empty view. + verify(mNotificationStackScrollLayout).updateEmptyShadeView( + /* visible= */ true, + /* areNotificationsHiddenInShade= */ false); + } + + @Test + public void testUpdateEmptyShadeView_bouncerNotShowing_flagOn_showEmptyView() { + when(mZenModeController.areNotificationsHiddenInShade()).thenReturn(false); + initController(/* viewIsAttached= */ true); + + // WHEN the flag is on and *only* PrimaryBouncerInteractor has bouncer as not showing + mFeatureFlags.set(Flags.USE_REPOS_FOR_BOUNCER_SHOWING, true); + when(mPrimaryBouncerInteractor.isBouncerShowing()).thenReturn(false); + mController.setBouncerShowingFromCentralSurfaces(true); + + setupShowEmptyShadeViewState(true); + reset(mNotificationStackScrollLayout); + mController.updateShowEmptyShadeView(); + + // THEN the PrimaryBouncerInteractor value is used. Since the bouncer isn't showing, we + // can show the empty view. verify(mNotificationStackScrollLayout).updateEmptyShadeView( /* visible= */ true, /* areNotificationsHiddenInShade= */ false); @@ -532,6 +595,7 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase { mNotificationStackScrollLayout, true, mNotificationGutsManager, + mNotificationsController, mVisibilityProvider, mHeadsUpManager, mNotificationRoundnessManager, @@ -542,6 +606,8 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase { mSysuiStatusBarStateController, mKeyguardMediaController, mKeyguardBypassController, + mKeyguardInteractor, + mPrimaryBouncerInteractor, mZenModeController, mNotificationLockscreenUserManager, Optional.<NotificationListViewModel>empty(), @@ -551,7 +617,6 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase { new FalsingManagerFake(), mResources, mNotificationSwipeHelperBuilder, - mCentralSurfaces, mScrimController, mGroupExpansionManager, mSilentHeaderController, 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 40e7e8d61b5e..8ad271bef2e4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java @@ -79,9 +79,9 @@ import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager; import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager; +import com.android.systemui.statusbar.notification.init.NotificationsController; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.FooterView; -import com.android.systemui.statusbar.phone.CentralSurfaces; import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.phone.ScreenOffAnimationController; import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; @@ -111,7 +111,7 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { private AmbientState mAmbientState; private TestableResources mTestableResources; @Rule public MockitoRule mockito = MockitoJUnit.rule(); - @Mock private CentralSurfaces mCentralSurfaces; + @Mock private NotificationsController mNotificationsController; @Mock private SysuiStatusBarStateController mBarState; @Mock private GroupMembershipManager mGroupMembershipManger; @Mock private GroupExpansionManager mGroupExpansionManager; @@ -177,7 +177,7 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase { mNotificationStackSizeCalculator); mStackScroller = spy(mStackScrollerInternal); mStackScroller.setShelfController(notificationShelfController); - mStackScroller.setCentralSurfaces(mCentralSurfaces); + mStackScroller.setNotificationsController(mNotificationsController); mStackScroller.setEmptyShadeView(mEmptyShadeView); when(mStackScrollLayoutController.isHistoryEnabled()).thenReturn(true); when(mStackScrollLayoutController.getNotificationRoundnessManager()) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt index 88b1eb3d565c..4c97d20c5da8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt @@ -9,7 +9,6 @@ import com.android.systemui.SysuiTestCase import com.android.systemui.animation.ShadeInterpolation.getContentAlpha import com.android.systemui.dump.DumpManager import com.android.systemui.flags.FeatureFlags -import com.android.systemui.flags.Flags import com.android.systemui.shade.transition.LargeScreenShadeInterpolator import com.android.systemui.statusbar.EmptyShadeView import com.android.systemui.statusbar.NotificationShelf @@ -191,12 +190,10 @@ class StackScrollAlgorithmTest : SysuiTestCase() { } @Test - fun resetViewStates_flagTrue_largeScreen_expansionChanging_alphaUpdated_largeScreenValue() { + fun resetViewStates_largeScreen_expansionChanging_alphaUpdated_largeScreenValue() { val expansionFraction = 0.6f val surfaceAlpha = 123f ambientState.isSmallScreen = false - whenever(featureFlags.isEnabled(Flags.LARGE_SHADE_GRANULAR_ALPHA_INTERPOLATION)) - .thenReturn(true) whenever(mStatusBarKeyguardViewManager.isPrimaryBouncerInTransit).thenReturn(false) whenever(largeScreenShadeInterpolator.getNotificationContentAlpha(expansionFraction)) .thenReturn(surfaceAlpha) @@ -208,23 +205,6 @@ class StackScrollAlgorithmTest : SysuiTestCase() { } @Test - fun resetViewStates_flagFalse_largeScreen_expansionChanging_alphaUpdated_standardValue() { - val expansionFraction = 0.6f - val surfaceAlpha = 123f - ambientState.isSmallScreen = false - whenever(featureFlags.isEnabled(Flags.LARGE_SHADE_GRANULAR_ALPHA_INTERPOLATION)) - .thenReturn(false) - whenever(mStatusBarKeyguardViewManager.isPrimaryBouncerInTransit).thenReturn(false) - whenever(largeScreenShadeInterpolator.getNotificationContentAlpha(expansionFraction)) - .thenReturn(surfaceAlpha) - - resetViewStates_expansionChanging_notificationAlphaUpdated( - expansionFraction = expansionFraction, - expectedAlpha = getContentAlpha(expansionFraction), - ) - } - - @Test fun expansionChanging_largeScreen_bouncerInTransit_alphaUpdated_bouncerValues() { ambientState.isSmallScreen = false whenever(mStatusBarKeyguardViewManager.isPrimaryBouncerInTransit).thenReturn(true) @@ -835,6 +815,74 @@ class StackScrollAlgorithmTest : SysuiTestCase() { ) } + // region shouldPinHunToBottomOfExpandedQs + @Test + fun shouldHunBeVisibleWhenScrolled_mustStayOnScreenFalse_false() { + assertThat(stackScrollAlgorithm.shouldHunBeVisibleWhenScrolled( + /* mustStayOnScreen= */false, + /* headsUpIsVisible= */false, + /* showingPulsing= */false, + /* isOnKeyguard=*/false, + /*headsUpOnKeyguard=*/false + )).isFalse() + } + + @Test + fun shouldPinHunToBottomOfExpandedQs_headsUpIsVisible_false() { + assertThat(stackScrollAlgorithm.shouldHunBeVisibleWhenScrolled( + /* mustStayOnScreen= */true, + /* headsUpIsVisible= */true, + /* showingPulsing= */false, + /* isOnKeyguard=*/false, + /*headsUpOnKeyguard=*/false + )).isFalse() + } + + @Test + fun shouldHunBeVisibleWhenScrolled_showingPulsing_false() { + assertThat(stackScrollAlgorithm.shouldHunBeVisibleWhenScrolled( + /* mustStayOnScreen= */true, + /* headsUpIsVisible= */false, + /* showingPulsing= */true, + /* isOnKeyguard=*/false, + /* headsUpOnKeyguard= */false + )).isFalse() + } + + @Test + fun shouldHunBeVisibleWhenScrolled_isOnKeyguard_false() { + assertThat(stackScrollAlgorithm.shouldHunBeVisibleWhenScrolled( + /* mustStayOnScreen= */true, + /* headsUpIsVisible= */false, + /* showingPulsing= */false, + /* isOnKeyguard=*/true, + /* headsUpOnKeyguard= */false + )).isFalse() + } + + @Test + fun shouldHunBeVisibleWhenScrolled_isNotOnKeyguard_true() { + assertThat(stackScrollAlgorithm.shouldHunBeVisibleWhenScrolled( + /* mustStayOnScreen= */true, + /* headsUpIsVisible= */false, + /* showingPulsing= */false, + /* isOnKeyguard=*/false, + /* headsUpOnKeyguard= */false + )).isTrue() + } + + @Test + fun shouldHunBeVisibleWhenScrolled_headsUpOnKeyguard_true() { + assertThat(stackScrollAlgorithm.shouldHunBeVisibleWhenScrolled( + /* mustStayOnScreen= */true, + /* headsUpIsVisible= */false, + /* showingPulsing= */false, + /* isOnKeyguard=*/true, + /* headsUpOnKeyguard= */true + )).isTrue() + } + // endregion + private fun createHunViewMock( isShadeOpen: Boolean, fullyVisible: Boolean, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ActivityStarterImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ActivityStarterImplTest.kt index 4a3080050a37..442ba0977cf6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ActivityStarterImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ActivityStarterImplTest.kt @@ -31,6 +31,7 @@ import com.android.systemui.plugins.ActivityStarter.OnDismissAction import com.android.systemui.settings.UserTracker import com.android.systemui.shade.ShadeController import com.android.systemui.statusbar.NotificationLockscreenUserManager +import com.android.systemui.statusbar.NotificationShadeWindowController import com.android.systemui.statusbar.SysuiStatusBarStateController import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow import com.android.systemui.statusbar.policy.DeviceProvisionedController @@ -68,6 +69,7 @@ class ActivityStarterImplTest : SysuiTestCase() { @Mock private lateinit var activityLaunchAnimator: ActivityLaunchAnimator @Mock private lateinit var lockScreenUserManager: NotificationLockscreenUserManager @Mock private lateinit var statusBarWindowController: StatusBarWindowController + @Mock private lateinit var notifShadeWindowController: NotificationShadeWindowController @Mock private lateinit var wakefulnessLifecycle: WakefulnessLifecycle @Mock private lateinit var keyguardStateController: KeyguardStateController @Mock private lateinit var statusBarStateController: SysuiStatusBarStateController @@ -90,6 +92,7 @@ class ActivityStarterImplTest : SysuiTestCase() { Lazy { keyguardViewMediator }, Lazy { shadeController }, Lazy { statusBarKeyguardViewManager }, + Lazy { notifShadeWindowController }, activityLaunchAnimator, context, lockScreenUserManager, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java index 3870d996d2ae..cb71fb8f703a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java @@ -18,7 +18,6 @@ package com.android.systemui.statusbar.phone; import static android.view.Display.DEFAULT_DISPLAY; -import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; @@ -153,12 +152,6 @@ public class CentralSurfacesCommandQueueCallbacksTest extends SysuiTestCase { verify(mCentralSurfaces).updateQsExpansionEnabled(); verify(mShadeController).animateCollapseShade(); - - // Trying to open it does nothing. - mSbcqCallbacks.animateExpandNotificationsPanel(); - verify(mShadeViewController, never()).expandToNotifications(); - mSbcqCallbacks.animateExpandSettingsPanel(null); - verify(mShadeViewController, never()).expand(anyBoolean()); } @Test @@ -171,12 +164,6 @@ public class CentralSurfacesCommandQueueCallbacksTest extends SysuiTestCase { StatusBarManager.DISABLE2_NONE, false); verify(mCentralSurfaces).updateQsExpansionEnabled(); verify(mShadeController, never()).animateCollapseShade(); - - // Can now be opened. - mSbcqCallbacks.animateExpandNotificationsPanel(); - verify(mShadeViewController).expandToNotifications(); - mSbcqCallbacks.animateExpandSettingsPanel(null); - verify(mShadeViewController).expandToQs(); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java index 77026308ad38..cc8324b22027 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java @@ -98,7 +98,9 @@ import com.android.systemui.SysuiTestCase; import com.android.systemui.accessibility.floatingmenu.AccessibilityFloatingMenuController; import com.android.systemui.animation.ActivityLaunchAnimator; import com.android.systemui.assist.AssistManager; +import com.android.systemui.back.domain.interactor.BackActionInteractor; import com.android.systemui.biometrics.AuthRippleController; +import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.charging.WiredChargingRippleController; import com.android.systemui.classifier.FalsingCollectorFake; @@ -113,7 +115,6 @@ import com.android.systemui.keyguard.KeyguardUnlockAnimationController; import com.android.systemui.keyguard.KeyguardViewMediator; import com.android.systemui.keyguard.ScreenLifecycle; import com.android.systemui.keyguard.WakefulnessLifecycle; -import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor; import com.android.systemui.keyguard.ui.viewmodel.LightRevealScrimViewModel; import com.android.systemui.navigationbar.NavigationBarController; import com.android.systemui.notetask.NoteTaskController; @@ -122,6 +123,7 @@ import com.android.systemui.plugins.ActivityStarter.OnDismissAction; import com.android.systemui.plugins.PluginDependencyProvider; import com.android.systemui.plugins.PluginManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.power.domain.interactor.PowerInteractor; import com.android.systemui.recents.ScreenPinningRequest; import com.android.systemui.settings.UserTracker; import com.android.systemui.settings.brightness.BrightnessSliderController; @@ -145,6 +147,7 @@ import com.android.systemui.statusbar.NotificationMediaManager; import com.android.systemui.statusbar.NotificationRemoteInputManager; import com.android.systemui.statusbar.NotificationShadeDepthController; import com.android.systemui.statusbar.NotificationShadeWindowController; +import com.android.systemui.statusbar.NotificationShelfController; import com.android.systemui.statusbar.OperatorNameViewController; import com.android.systemui.statusbar.PulseExpansionHandler; import com.android.systemui.statusbar.StatusBarState; @@ -259,6 +262,7 @@ public class CentralSurfacesImplTest extends SysuiTestCase { @Mock private AccessibilityFloatingMenuController mAccessibilityFloatingMenuController; @Mock private SysuiColorExtractor mColorExtractor; private WakefulnessLifecycle mWakefulnessLifecycle; + @Mock private PowerInteractor mPowerInteractor; @Mock private ColorExtractor.GradientColors mGradientColors; @Mock private PulseExpansionHandler mPulseExpansionHandler; @Mock private NotificationWakeUpCoordinator mNotificationWakeUpCoordinator; @@ -273,10 +277,12 @@ public class CentralSurfacesImplTest extends SysuiTestCase { @Mock private NotificationShadeWindowController mNotificationShadeWindowController; @Mock private NotificationIconAreaController mNotificationIconAreaController; @Mock private NotificationShadeWindowViewController mNotificationShadeWindowViewController; + @Mock private NotificationShelfController mNotificationShelfController; @Mock private DozeParameters mDozeParameters; @Mock private Lazy<LockscreenWallpaper> mLockscreenWallpaperLazy; @Mock private LockscreenWallpaper mLockscreenWallpaper; @Mock private DozeServiceHost mDozeServiceHost; + @Mock private BackActionInteractor mBackActionInteractor; @Mock private ViewMediatorCallback mKeyguardVieMediatorCallback; @Mock private VolumeComponent mVolumeComponent; @Mock private CommandQueue mCommandQueue; @@ -287,7 +293,6 @@ public class CentralSurfacesImplTest extends SysuiTestCase { @Mock private StatusBarTouchableRegionManager mStatusBarTouchableRegionManager; @Mock private ScreenPinningRequest mScreenPinningRequest; @Mock private PluginDependencyProvider mPluginDependencyProvider; - @Mock private KeyguardDismissUtil mKeyguardDismissUtil; @Mock private ExtensionController mExtensionController; @Mock private UserInfoControllerImpl mUserInfoControllerImpl; @Mock private PhoneStatusBarPolicy mPhoneStatusBarPolicy; @@ -435,10 +440,12 @@ public class CentralSurfacesImplTest extends SysuiTestCase { mShadeController = spy(new ShadeControllerImpl( mCommandQueue, + mMainExecutor, mKeyguardStateController, mStatusBarStateController, mStatusBarKeyguardViewManager, mStatusBarWindowController, + mDeviceProvisionedController, mNotificationShadeWindowController, mContext.getSystemService(WindowManager.class), () -> mAssistManager, @@ -492,6 +499,7 @@ public class CentralSurfacesImplTest extends SysuiTestCase { mColorExtractor, mScreenLifecycle, mWakefulnessLifecycle, + mPowerInteractor, mStatusBarStateController, Optional.of(mBubbles), () -> mNoteTaskController, @@ -501,13 +509,16 @@ public class CentralSurfacesImplTest extends SysuiTestCase { () -> mAssistManager, configurationController, mNotificationShadeWindowController, + mNotificationShelfController, mDozeParameters, mScrimController, mLockscreenWallpaperLazy, mBiometricUnlockControllerLazy, mAuthRippleController, mDozeServiceHost, - mPowerManager, mScreenPinningRequest, + mBackActionInteractor, + mPowerManager, + mScreenPinningRequest, mDozeScrimController, mVolumeComponent, mCommandQueue, @@ -519,7 +530,6 @@ public class CentralSurfacesImplTest extends SysuiTestCase { mInitController, new Handler(TestableLooper.get(this).getLooper()), mPluginDependencyProvider, - mKeyguardDismissUtil, mExtensionController, mUserInfoControllerImpl, mPhoneStatusBarPolicy, @@ -583,6 +593,7 @@ public class CentralSurfacesImplTest extends SysuiTestCase { mCentralSurfaces.mPresenter = mNotificationPresenter; mCentralSurfaces.mKeyguardIndicationController = mKeyguardIndicationController; mCentralSurfaces.mBarService = mBarService; + mCentralSurfaces.mStackScrollerController = mStackScrollerController; mCentralSurfaces.mStackScroller = mStackScroller; mCentralSurfaces.mGestureWakeLock = mPowerManager.newWakeLock( PowerManager.SCREEN_BRIGHT_WAKE_LOCK, "sysui:GestureWakeLock"); @@ -821,9 +832,9 @@ public class CentralSurfacesImplTest extends SysuiTestCase { eq(OnBackInvokedDispatcher.PRIORITY_DEFAULT), mOnBackInvokedCallback.capture()); - when(mNotificationPanelViewController.canBeCollapsed()).thenReturn(true); + when(mBackActionInteractor.shouldBackBeHandled()).thenReturn(true); mOnBackInvokedCallback.getValue().onBackInvoked(); - verify(mShadeController).animateCollapseShade(); + verify(mBackActionInteractor).onBackRequested(); } /** @@ -841,6 +852,7 @@ public class CentralSurfacesImplTest extends SysuiTestCase { OnBackAnimationCallback onBackAnimationCallback = (OnBackAnimationCallback) (mOnBackInvokedCallback.getValue()); + when(mBackActionInteractor.shouldBackBeHandled()).thenReturn(true); when(mNotificationPanelViewController.canBeCollapsed()).thenReturn(true); BackEvent fakeSwipeInFromLeftEdge = new BackEvent(20.0f, 100.0f, 1.0f, BackEvent.EDGE_LEFT); @@ -863,6 +875,7 @@ public class CentralSurfacesImplTest extends SysuiTestCase { OnBackAnimationCallback onBackAnimationCallback = (OnBackAnimationCallback) (mOnBackInvokedCallback.getValue()); + when(mBackActionInteractor.shouldBackBeHandled()).thenReturn(true); when(mNotificationPanelViewController.canBeCollapsed()).thenReturn(true); BackEvent fakeSwipeInFromLeftEdge = new BackEvent(20.0f, 10.0f, 0.0f, BackEvent.EDGE_LEFT); @@ -1062,7 +1075,7 @@ public class CentralSurfacesImplTest extends SysuiTestCase { // GIVEN device occluded and panel is NOT expanded mCentralSurfaces.setBarStateForTest(SHADE); // occluding on LS has StatusBarState = SHADE when(mKeyguardStateController.isOccluded()).thenReturn(true); - mCentralSurfaces.mPanelExpanded = false; + when(mNotificationPanelViewController.isPanelExpanded()).thenReturn(false); mCentralSurfaces.updateScrimController(); @@ -1076,7 +1089,7 @@ public class CentralSurfacesImplTest extends SysuiTestCase { // GIVEN device occluded and qs IS expanded mCentralSurfaces.setBarStateForTest(SHADE); // occluding on LS has StatusBarState = SHADE when(mKeyguardStateController.isOccluded()).thenReturn(true); - mCentralSurfaces.mPanelExpanded = true; + when(mNotificationPanelViewController.isPanelExpanded()).thenReturn(true); mCentralSurfaces.updateScrimController(); @@ -1145,32 +1158,6 @@ public class CentralSurfacesImplTest extends SysuiTestCase { } @Test - public void collapseShade_callsanimateCollapseShade_whenExpanded() { - // GIVEN the shade is expanded - mCentralSurfaces.onShadeExpansionFullyChanged(true); - mCentralSurfaces.setBarStateForTest(SHADE); - - // WHEN collapseShade is called - mCentralSurfaces.collapseShade(); - - // VERIFY that animateCollapseShade is called - verify(mShadeController).animateCollapseShade(); - } - - @Test - public void collapseShade_doesNotCallanimateCollapseShade_whenCollapsed() { - // GIVEN the shade is collapsed - mCentralSurfaces.onShadeExpansionFullyChanged(false); - mCentralSurfaces.setBarStateForTest(SHADE); - - // WHEN collapseShade is called - mCentralSurfaces.collapseShade(); - - // VERIFY that animateCollapseShade is NOT called - verify(mShadeController, never()).animateCollapseShade(); - } - - @Test public void deviceStateChange_unfolded_shadeOpen_setsLeaveOpenOnKeyguardHide() { setFoldedStates(FOLD_STATE_FOLDED); setGoToSleepStates(FOLD_STATE_FOLDED); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardDismissUtilTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardDismissUtilTest.java new file mode 100644 index 000000000000..b0aa2d3934cc --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardDismissUtilTest.java @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.phone; + +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import android.testing.AndroidTestingRunner; + +import androidx.test.filters.SmallTest; + +import com.android.systemui.SysuiTestCase; +import com.android.systemui.plugins.ActivityStarter; +import com.android.systemui.statusbar.SysuiStatusBarStateController; +import com.android.systemui.statusbar.policy.KeyguardStateController; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +@RunWith(AndroidTestingRunner.class) +@SmallTest +public class KeyguardDismissUtilTest extends SysuiTestCase { + @Mock + private KeyguardStateController mKeyguardStateController; + @Mock + private SysuiStatusBarStateController mStatusBarStateController; + @Mock + private ActivityStarter mActivityStarter; + @Mock + private ActivityStarter.OnDismissAction mAction; + + private KeyguardDismissUtil mKeyguardDismissUtil; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + mKeyguardDismissUtil = new KeyguardDismissUtil( + mKeyguardStateController, mStatusBarStateController, mActivityStarter); + } + + @Test + public void testSetLeaveOpenOnKeyguardHideWhenKeyGuardStateControllerIsShowing() { + doReturn(true).when(mKeyguardStateController).isShowing(); + + mKeyguardDismissUtil.executeWhenUnlocked(mAction, true /* requiresShadeOpen */, + true /* afterKeyguardGone */); + + verify(mStatusBarStateController).setLeaveOpenOnKeyguardHide(true); + + verify(mActivityStarter).dismissKeyguardThenExecute(mAction, null, true); + + } + + @Test + public void testSetLeaveOpenOnKeyguardHideWhenKeyGuardStateControllerIsNotShowing() { + doReturn(false).when(mKeyguardStateController).isShowing(); + + mKeyguardDismissUtil.executeWhenUnlocked(mAction, true /* requiresShadeOpen */, + true /* afterKeyguardGone */); + + //no interaction with mStatusBarStateController + verify(mStatusBarStateController, times(0)).setLeaveOpenOnKeyguardHide(true); + + verify(mActivityStarter).dismissKeyguardThenExecute(mAction, null, true); + + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java index a9ed17531926..ab801588575d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java @@ -61,10 +61,9 @@ import com.android.systemui.SysuiTestCase; import com.android.systemui.animation.ShadeInterpolation; import com.android.systemui.dock.DockManager; import com.android.systemui.flags.FeatureFlags; -import com.android.systemui.flags.Flags; import com.android.systemui.keyguard.KeyguardUnlockAnimationController; import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor; -import com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants; +import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants; import com.android.systemui.keyguard.shared.model.KeyguardState; import com.android.systemui.keyguard.shared.model.TransitionState; import com.android.systemui.keyguard.shared.model.TransitionStep; @@ -697,9 +696,7 @@ public class ScrimControllerTest extends SysuiTestCase { } @Test - public void transitionToUnlocked_nonClippedQs_flagTrue_followsLargeScreensInterpolator() { - when(mFeatureFlags.isEnabled(Flags.LARGE_SHADE_GRANULAR_ALPHA_INTERPOLATION)) - .thenReturn(true); + public void transitionToUnlocked_nonClippedQs_followsLargeScreensInterpolator() { mScrimController.setClipsQsScrim(false); mScrimController.setRawPanelExpansionFraction(0f); mScrimController.transitionTo(ScrimState.UNLOCKED); @@ -737,48 +734,6 @@ public class ScrimControllerTest extends SysuiTestCase { mScrimBehind, OPAQUE)); } - - @Test - public void transitionToUnlocked_nonClippedQs_flagFalse() { - when(mFeatureFlags.isEnabled(Flags.LARGE_SHADE_GRANULAR_ALPHA_INTERPOLATION)) - .thenReturn(false); - mScrimController.setClipsQsScrim(false); - mScrimController.setRawPanelExpansionFraction(0f); - mScrimController.transitionTo(ScrimState.UNLOCKED); - finishAnimationsImmediately(); - assertScrimAlpha(Map.of( - mScrimInFront, TRANSPARENT, - mNotificationsScrim, TRANSPARENT, - mScrimBehind, TRANSPARENT)); - - assertScrimTinted(Map.of( - mNotificationsScrim, false, - mScrimInFront, false, - mScrimBehind, true - )); - - // Back scrim should be visible after start dragging - mScrimController.setRawPanelExpansionFraction(0.29f); - assertScrimAlpha(Map.of( - mScrimInFront, TRANSPARENT, - mNotificationsScrim, TRANSPARENT, - mScrimBehind, SEMI_TRANSPARENT)); - - // Back scrim should be opaque at 30% - mScrimController.setRawPanelExpansionFraction(0.3f); - assertScrimAlpha(Map.of( - mScrimInFront, TRANSPARENT, - mNotificationsScrim, TRANSPARENT, - mScrimBehind, OPAQUE)); - - // Then, notification scrim should fade in - mScrimController.setRawPanelExpansionFraction(0.31f); - assertScrimAlpha(Map.of( - mScrimInFront, TRANSPARENT, - mNotificationsScrim, SEMI_TRANSPARENT, - mScrimBehind, OPAQUE)); - } - @Test public void scrimStateCallback() { mScrimController.transitionTo(ScrimState.UNLOCKED); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java index 3eea93c6ec2c..548e1b501d45 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java @@ -16,8 +16,8 @@ package com.android.systemui.statusbar.phone; -import static com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants.EXPANSION_HIDDEN; -import static com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants.EXPANSION_VISIBLE; +import static com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants.EXPANSION_HIDDEN; +import static com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants.EXPANSION_VISIBLE; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; @@ -60,16 +60,16 @@ import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.ViewMediatorCallback; import com.android.systemui.SysuiTestCase; import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor; +import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor; +import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInteractor; +import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInteractor.PrimaryBouncerExpansionCallback; +import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor; +import com.android.systemui.bouncer.ui.BouncerView; +import com.android.systemui.bouncer.ui.BouncerViewDelegate; import com.android.systemui.dock.DockManager; import com.android.systemui.dreams.DreamOverlayStateController; import com.android.systemui.flags.FeatureFlags; import com.android.systemui.flags.Flags; -import com.android.systemui.keyguard.data.BouncerView; -import com.android.systemui.keyguard.data.BouncerViewDelegate; -import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor; -import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerCallbackInteractor; -import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerCallbackInteractor.PrimaryBouncerExpansionCallback; -import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerInteractor; import com.android.systemui.navigationbar.NavigationModeController; import com.android.systemui.navigationbar.TaskbarDelegate; import com.android.systemui.plugins.ActivityStarter; @@ -135,6 +135,7 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { @Mock private BouncerView mBouncerView; @Mock private BouncerViewDelegate mBouncerViewDelegate; @Mock private OnBackAnimationCallback mBouncerViewDelegateBackCallback; + @Mock private NotificationShadeWindowController mNotificationShadeWindowController; @Mock private NotificationShadeWindowView mNotificationShadeWindowView; @Mock private WindowInsetsController mWindowInsetsController; @Mock private TaskbarDelegate mTaskbarDelegate; @@ -168,7 +169,7 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { .isEnabled(Flags.WM_ENABLE_PREDICTIVE_BACK_BOUNCER_ANIM)) .thenReturn(true); - when(mCentralSurfaces.getNotificationShadeWindowView()) + when(mNotificationShadeWindowController.getWindowRootView()) .thenReturn(mNotificationShadeWindowView); when(mNotificationShadeWindowView.getWindowInsetsController()) .thenReturn(mWindowInsetsController); @@ -184,7 +185,7 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { mDreamOverlayStateController, mock(NavigationModeController.class), mock(DockManager.class), - mock(NotificationShadeWindowController.class), + mNotificationShadeWindowController, mKeyguardStateController, mock(NotificationMediaManager.class), mKeyguardMessageAreaFactory, @@ -646,21 +647,21 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { @Test public void testReportBouncerOnDreamWhenVisible() { mBouncerExpansionCallback.onVisibilityChanged(true); - verify(mCentralSurfaces).setBouncerShowingOverDream(false); + assertFalse(mStatusBarKeyguardViewManager.isBouncerShowingOverDream()); Mockito.clearInvocations(mCentralSurfaces); when(mDreamOverlayStateController.isOverlayActive()).thenReturn(true); mBouncerExpansionCallback.onVisibilityChanged(true); - verify(mCentralSurfaces).setBouncerShowingOverDream(true); + assertTrue(mStatusBarKeyguardViewManager.isBouncerShowingOverDream()); } @Test public void testReportBouncerOnDreamWhenNotVisible() { mBouncerExpansionCallback.onVisibilityChanged(false); - verify(mCentralSurfaces).setBouncerShowingOverDream(false); + assertFalse(mStatusBarKeyguardViewManager.isBouncerShowingOverDream()); Mockito.clearInvocations(mCentralSurfaces); when(mDreamOverlayStateController.isOverlayActive()).thenReturn(true); mBouncerExpansionCallback.onVisibilityChanged(false); - verify(mCentralSurfaces).setBouncerShowingOverDream(false); + assertFalse(mStatusBarKeyguardViewManager.isBouncerShowingOverDream()); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java index d6ae2b711583..d44af885a27e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java @@ -73,6 +73,7 @@ import com.android.systemui.statusbar.NotificationClickNotifier; import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.NotificationPresenter; import com.android.systemui.statusbar.NotificationRemoteInputManager; +import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.notification.NotificationLaunchAnimatorControllerProvider; import com.android.systemui.statusbar.notification.collection.NotificationEntry; @@ -233,6 +234,7 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { mCentralSurfaces, mock(NotificationPresenter.class), mock(ShadeViewController.class), + mock(NotificationShadeWindowController.class), mActivityLaunchAnimator, notificationAnimationProvider, mock(LaunchFullScreenIntentProvider.class), diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java index fdfe028765ef..dfbe0471196e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java @@ -44,7 +44,6 @@ import com.android.systemui.shade.ShadeController; import com.android.systemui.shade.ShadeNotificationPresenter; import com.android.systemui.shade.ShadeViewController; import com.android.systemui.statusbar.CommandQueue; -import com.android.systemui.statusbar.KeyguardIndicationController; import com.android.systemui.statusbar.LockscreenShadeTransitionController; import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.NotificationMediaManager; @@ -56,6 +55,7 @@ import com.android.systemui.statusbar.notification.NotifPipelineFlags; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder; import com.android.systemui.statusbar.notification.collection.render.NotifShadeEventSource; +import com.android.systemui.statusbar.notification.domain.interactor.NotificationsInteractor; import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider; import com.android.systemui.statusbar.notification.interruption.NotificationInterruptSuppressor; import com.android.systemui.statusbar.notification.row.NotificationGutsManager; @@ -81,6 +81,8 @@ public class StatusBarNotificationPresenterTest extends SysuiTestCase { private FakeMetricsLogger mMetricsLogger; private final ShadeController mShadeController = mock(ShadeController.class); private final CentralSurfaces mCentralSurfaces = mock(CentralSurfaces.class); + private final NotificationsInteractor mNotificationsInteractor = + mock(NotificationsInteractor.class); private final KeyguardStateController mKeyguardStateController = mock(KeyguardStateController.class); private final NotifPipelineFlags mNotifPipelineFlags = mock(NotifPipelineFlags.class); @@ -122,8 +124,8 @@ public class StatusBarNotificationPresenterTest extends SysuiTestCase { mock(NotificationShadeWindowController.class), mock(DynamicPrivacyController.class), mKeyguardStateController, - mock(KeyguardIndicationController.class), mCentralSurfaces, + mNotificationsInteractor, mock(LockscreenShadeTransitionController.class), mCommandQueue, mock(NotificationLockscreenUserManager.class), @@ -235,9 +237,9 @@ public class StatusBarNotificationPresenterTest extends SysuiTestCase { .setTag("a") .setNotification(n) .build(); - when(mCentralSurfaces.areNotificationAlertsDisabled()).thenReturn(true); + when(mNotificationsInteractor.areNotificationAlertsEnabled()).thenReturn(false); - assertTrue("CentralSurfaces alerts disabled shouldn't allow interruptions", + assertTrue("When alerts aren't enabled, interruptions are suppressed", mInterruptSuppressor.suppressInterruptions(entry)); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationControllerTest.kt index ea534bbd0794..2e9a6909e402 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationControllerTest.kt @@ -28,6 +28,7 @@ import com.android.systemui.keyguard.KeyguardViewMediator import com.android.systemui.keyguard.WakefulnessLifecycle import com.android.systemui.shade.ShadeViewController import com.android.systemui.statusbar.LightRevealScrim +import com.android.systemui.statusbar.NotificationShadeWindowController import com.android.systemui.statusbar.StatusBarStateControllerImpl import com.android.systemui.statusbar.policy.KeyguardStateController import com.android.systemui.util.mockito.eq @@ -65,6 +66,8 @@ class UnlockedScreenOffAnimationControllerTest : SysuiTestCase() { @Mock private lateinit var shadeViewController: ShadeViewController @Mock + private lateinit var notifShadeWindowController: NotificationShadeWindowController + @Mock private lateinit var lightRevealScrim: LightRevealScrim @Mock private lateinit var wakefulnessLifecycle: WakefulnessLifecycle @@ -89,6 +92,7 @@ class UnlockedScreenOffAnimationControllerTest : SysuiTestCase() { keyguardStateController, dagger.Lazy<DozeParameters> { dozeParameters }, globalSettings, + dagger.Lazy<NotificationShadeWindowController> { notifShadeWindowController }, interactionJankMonitor, powerManager, handler = handler 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 9bc49ae02436..87d813c6d19f 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 @@ -50,7 +50,6 @@ class CollapsedStatusBarFragmentLoggerTest : SysuiTestCase() { val actualString = stringWriter.toString() val expectedLogString = disableFlagsLogger.getDisableFlagsString( - old = null, new = state, newAfterLocalModification = null, ) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java index 3b0d5120cca3..ae38958ecb44 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java @@ -51,9 +51,11 @@ import com.android.systemui.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.ActivityStarter.OnDismissAction; +import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.shade.ShadeController; import com.android.systemui.statusbar.NotificationRemoteInputManager; import com.android.systemui.statusbar.SmartReplyController; +import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder; import com.android.systemui.statusbar.phone.KeyguardDismissUtil; @@ -107,14 +109,15 @@ public class SmartReplyViewTest extends SysuiTestCase { private SmartReplyInflaterImpl mSmartReplyInflater; private SmartActionInflaterImpl mSmartActionInflater; + private KeyguardDismissUtil mKeyguardDismissUtil; @Mock private SmartReplyConstants mConstants; @Mock private ActivityStarter mActivityStarter; @Mock private HeadsUpManager mHeadsUpManager; @Mock private NotificationRemoteInputManager mNotificationRemoteInputManager; @Mock private SmartReplyController mSmartReplyController; - - private final KeyguardDismissUtil mKeyguardDismissUtil = new KeyguardDismissUtil(); + @Mock private KeyguardStateController mKeyguardStateController; + @Mock private SysuiStatusBarStateController mStatusBarStateController; @Before public void setUp() { @@ -122,12 +125,15 @@ public class SmartReplyViewTest extends SysuiTestCase { mReceiver = new BlockingQueueIntentReceiver(); mContext.registerReceiver(mReceiver, new IntentFilter(TEST_ACTION), Context.RECEIVER_EXPORTED_UNAUDITED); - mKeyguardDismissUtil.setDismissHandler((action, unused, afterKgGone) -> action.onDismiss()); + mDependency.injectMockDependency(KeyguardUpdateMonitor.class); mDependency.injectMockDependency(ShadeController.class); mDependency.injectMockDependency(NotificationRemoteInputManager.class); mDependency.injectTestDependency(ActivityStarter.class, mActivityStarter); mDependency.injectTestDependency(SmartReplyConstants.class, mConstants); + mDependency.injectTestDependency(KeyguardStateController.class, mKeyguardStateController); + mDependency.injectTestDependency(StatusBarStateController.class, mStatusBarStateController); + // Any number of replies are fine. when(mConstants.getMinNumSystemGeneratedReplies()).thenReturn(0); @@ -153,6 +159,13 @@ public class SmartReplyViewTest extends SysuiTestCase { mActionIcon = Icon.createWithResource(mContext, R.drawable.ic_person); + mKeyguardDismissUtil = new KeyguardDismissUtil( + mKeyguardStateController, mStatusBarStateController, mActivityStarter) { + public void executeWhenUnlocked(ActivityStarter.OnDismissAction action, + boolean requiresShadeOpen, boolean afterKeyguardGone) { + action.onDismiss(); + } + }; mSmartReplyInflater = new SmartReplyInflaterImpl( mConstants, mKeyguardDismissUtil, @@ -185,7 +198,17 @@ public class SmartReplyViewTest extends SysuiTestCase { @Test public void testSendSmartReply_keyguardCancelled() throws InterruptedException { - mKeyguardDismissUtil.setDismissHandler((action, unused, afterKgGone) -> { }); + mKeyguardDismissUtil = new KeyguardDismissUtil( + mKeyguardStateController, mStatusBarStateController, mActivityStarter) { + public void executeWhenUnlocked(ActivityStarter.OnDismissAction action, + boolean requiresShadeOpen, boolean afterKeyguardGone) { }}; + mSmartReplyInflater = new SmartReplyInflaterImpl( + mConstants, + mKeyguardDismissUtil, + mNotificationRemoteInputManager, + mSmartReplyController, + mContext); + setSmartReplies(TEST_CHOICES); mView.getChildAt(2).performClick(); @@ -196,9 +219,20 @@ public class SmartReplyViewTest extends SysuiTestCase { @Test public void testSendSmartReply_waitsForKeyguard() throws InterruptedException { AtomicReference<OnDismissAction> actionRef = new AtomicReference<>(); + mKeyguardDismissUtil = new KeyguardDismissUtil( + mKeyguardStateController, mStatusBarStateController, mActivityStarter) { + public void executeWhenUnlocked(ActivityStarter.OnDismissAction action, + boolean requiresShadeOpen, boolean afterKeyguardGone) { + actionRef.set(action); + } + }; + mSmartReplyInflater = new SmartReplyInflaterImpl( + mConstants, + mKeyguardDismissUtil, + mNotificationRemoteInputManager, + mSmartReplyController, + mContext); - mKeyguardDismissUtil.setDismissHandler((action, unused, afterKgGone) - -> actionRef.set(action)); setSmartReplies(TEST_CHOICES); mView.getChildAt(2).performClick(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldAodAnimationControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldAodAnimationControllerTest.kt index d9ee08157c84..813597a8b576 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldAodAnimationControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldAodAnimationControllerTest.kt @@ -28,12 +28,10 @@ import com.android.systemui.SysuiTestCase import com.android.systemui.flags.FakeFeatureFlags import com.android.systemui.flags.Flags.FACE_AUTH_REFACTOR import com.android.systemui.keyguard.WakefulnessLifecycle -import com.android.systemui.keyguard.data.repository.FakeKeyguardBouncerRepository import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository -import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor +import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory import com.android.systemui.shade.ShadeFoldAnimator import com.android.systemui.shade.ShadeViewController -import com.android.systemui.statusbar.CommandQueue import com.android.systemui.statusbar.LightRevealScrim import com.android.systemui.statusbar.phone.CentralSurfaces import com.android.systemui.unfold.util.FoldableDeviceStates @@ -80,8 +78,6 @@ class FoldAodAnimationControllerTest : SysuiTestCase() { @Mock lateinit var viewTreeObserver: ViewTreeObserver - @Mock private lateinit var commandQueue: CommandQueue - @Mock lateinit var shadeFoldAnimator: ShadeFoldAnimator @Captor private lateinit var foldStateListenerCaptor: ArgumentCaptor<FoldStateListener> @@ -111,15 +107,10 @@ class FoldAodAnimationControllerTest : SysuiTestCase() { onActionStarted.run() } - keyguardRepository = FakeKeyguardRepository() val featureFlags = FakeFeatureFlags().apply { set(FACE_AUTH_REFACTOR, true) } - val keyguardInteractor = - KeyguardInteractor( - repository = keyguardRepository, - commandQueue = commandQueue, - featureFlags = featureFlags, - bouncerRepository = FakeKeyguardBouncerRepository(), - ) + val withDeps = KeyguardInteractorFactory.create(featureFlags = featureFlags) + val keyguardInteractor = withDeps.keyguardInteractor + keyguardRepository = withDeps.repository // Needs to be run on the main thread runBlocking(IMMEDIATE) { diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt index ca83d49b19ca..3fbbeda8f74d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt @@ -39,12 +39,10 @@ import com.android.systemui.common.shared.model.Text import com.android.systemui.coroutines.collectLastValue import com.android.systemui.flags.FakeFeatureFlags import com.android.systemui.flags.Flags -import com.android.systemui.keyguard.data.repository.FakeKeyguardBouncerRepository import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository -import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor +import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory import com.android.systemui.plugins.ActivityStarter import com.android.systemui.qs.user.UserSwitchDialogController -import com.android.systemui.statusbar.CommandQueue import com.android.systemui.statusbar.policy.DeviceProvisionedController import com.android.systemui.telephony.data.repository.FakeTelephonyRepository import com.android.systemui.telephony.domain.interactor.TelephonyInteractor @@ -97,7 +95,6 @@ class UserInteractorTest : SysuiTestCase() { @Mock private lateinit var dialogShower: UserSwitchDialogController.DialogShower @Mock private lateinit var resumeSessionReceiver: GuestResumeSessionReceiver @Mock private lateinit var resetOrExitSessionReceiver: GuestResetOrExitSessionReceiver - @Mock private lateinit var commandQueue: CommandQueue @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor private lateinit var underTest: UserInteractor @@ -126,8 +123,9 @@ class UserInteractorTest : SysuiTestCase() { set(Flags.FULL_SCREEN_USER_SWITCHER, false) set(Flags.FACE_AUTH_REFACTOR, true) } + val reply = KeyguardInteractorFactory.create(featureFlags = featureFlags) + keyguardRepository = reply.repository userRepository = FakeUserRepository() - keyguardRepository = FakeKeyguardRepository() telephonyRepository = FakeTelephonyRepository() val testDispatcher = StandardTestDispatcher() testScope = TestScope(testDispatcher) @@ -142,13 +140,7 @@ class UserInteractorTest : SysuiTestCase() { applicationContext = context, repository = userRepository, activityStarter = activityStarter, - keyguardInteractor = - KeyguardInteractor( - repository = keyguardRepository, - commandQueue = commandQueue, - featureFlags = featureFlags, - bouncerRepository = FakeKeyguardBouncerRepository(), - ), + keyguardInteractor = reply.keyguardInteractor, manager = manager, headlessSystemUserMode = headlessSystemUserMode, applicationScope = testScope.backgroundScope, diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt index fd8c6c76720b..9cb26e05887d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt @@ -32,11 +32,8 @@ import com.android.systemui.SysuiTestCase import com.android.systemui.common.shared.model.Text import com.android.systemui.flags.FakeFeatureFlags import com.android.systemui.flags.Flags -import com.android.systemui.keyguard.data.repository.FakeKeyguardBouncerRepository -import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository -import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor +import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory import com.android.systemui.plugins.ActivityStarter -import com.android.systemui.statusbar.CommandQueue import com.android.systemui.statusbar.policy.DeviceProvisionedController import com.android.systemui.telephony.data.repository.FakeTelephonyRepository import com.android.systemui.telephony.domain.interactor.TelephonyInteractor @@ -80,13 +77,11 @@ class StatusBarUserChipViewModelTest : SysuiTestCase() { @Mock private lateinit var uiEventLogger: UiEventLogger @Mock private lateinit var resumeSessionReceiver: GuestResumeSessionReceiver @Mock private lateinit var resetOrExitSessionReceiver: GuestResetOrExitSessionReceiver - @Mock private lateinit var commandQueue: CommandQueue @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor private lateinit var underTest: StatusBarUserChipViewModel private val userRepository = FakeUserRepository() - private val keyguardRepository = FakeKeyguardRepository() private lateinit var guestUserInteractor: GuestUserInteractor private lateinit var refreshUsersScheduler: RefreshUsersScheduler @@ -250,12 +245,8 @@ class StatusBarUserChipViewModelTest : SysuiTestCase() { repository = userRepository, activityStarter = activityStarter, keyguardInteractor = - KeyguardInteractor( - repository = keyguardRepository, - commandQueue = commandQueue, - featureFlags = featureFlags, - bouncerRepository = FakeKeyguardBouncerRepository(), - ), + KeyguardInteractorFactory.create(featureFlags = featureFlags) + .keyguardInteractor, featureFlags = featureFlags, manager = manager, headlessSystemUserMode = headlessSystemUserMode, diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt index 91550844ceca..e3f9fac27815 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt @@ -30,11 +30,9 @@ import com.android.systemui.SysuiTestCase import com.android.systemui.common.shared.model.Text import com.android.systemui.flags.FakeFeatureFlags import com.android.systemui.flags.Flags -import com.android.systemui.keyguard.data.repository.FakeKeyguardBouncerRepository import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository -import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor +import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory import com.android.systemui.plugins.ActivityStarter -import com.android.systemui.statusbar.CommandQueue import com.android.systemui.statusbar.policy.DeviceProvisionedController import com.android.systemui.telephony.data.repository.FakeTelephonyRepository import com.android.systemui.telephony.domain.interactor.TelephonyInteractor @@ -79,7 +77,6 @@ class UserSwitcherViewModelTest : SysuiTestCase() { @Mock private lateinit var uiEventLogger: UiEventLogger @Mock private lateinit var resumeSessionReceiver: GuestResumeSessionReceiver @Mock private lateinit var resetOrExitSessionReceiver: GuestResetOrExitSessionReceiver - @Mock private lateinit var commandQueue: CommandQueue @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor private lateinit var underTest: UserSwitcherViewModel @@ -112,7 +109,6 @@ class UserSwitcherViewModelTest : SysuiTestCase() { ) } - keyguardRepository = FakeKeyguardRepository() val refreshUsersScheduler = RefreshUsersScheduler( applicationScope = testScope.backgroundScope, @@ -140,38 +136,35 @@ class UserSwitcherViewModelTest : SysuiTestCase() { set(Flags.FULL_SCREEN_USER_SWITCHER, false) set(Flags.FACE_AUTH_REFACTOR, true) } + val reply = KeyguardInteractorFactory.create(featureFlags = featureFlags) + keyguardRepository = reply.repository + underTest = UserSwitcherViewModel( - userInteractor = - UserInteractor( - applicationContext = context, - repository = userRepository, - activityStarter = activityStarter, - keyguardInteractor = - KeyguardInteractor( - repository = keyguardRepository, - commandQueue = commandQueue, - featureFlags = featureFlags, - bouncerRepository = FakeKeyguardBouncerRepository(), - ), - featureFlags = featureFlags, - manager = manager, - headlessSystemUserMode = headlessSystemUserMode, - applicationScope = testScope.backgroundScope, - telephonyInteractor = - TelephonyInteractor( - repository = FakeTelephonyRepository(), - ), - broadcastDispatcher = fakeBroadcastDispatcher, - keyguardUpdateMonitor = keyguardUpdateMonitor, - backgroundDispatcher = testDispatcher, - activityManager = activityManager, - refreshUsersScheduler = refreshUsersScheduler, - guestUserInteractor = guestUserInteractor, - uiEventLogger = uiEventLogger, - ), - guestUserInteractor = guestUserInteractor, - ) + userInteractor = + UserInteractor( + applicationContext = context, + repository = userRepository, + activityStarter = activityStarter, + keyguardInteractor = reply.keyguardInteractor, + featureFlags = featureFlags, + manager = manager, + headlessSystemUserMode = headlessSystemUserMode, + applicationScope = testScope.backgroundScope, + telephonyInteractor = + TelephonyInteractor( + repository = FakeTelephonyRepository(), + ), + broadcastDispatcher = fakeBroadcastDispatcher, + keyguardUpdateMonitor = keyguardUpdateMonitor, + backgroundDispatcher = testDispatcher, + activityManager = activityManager, + refreshUsersScheduler = refreshUsersScheduler, + guestUserInteractor = guestUserInteractor, + uiEventLogger = uiEventLogger, + ), + guestUserInteractor = guestUserInteractor, + ) } @Test @@ -323,7 +316,7 @@ class UserSwitcherViewModelTest : SysuiTestCase() { setUsers(count = 2) val isFinishRequested = mutableListOf<Boolean>() val job = - launch(testDispatcher) { underTest.isFinishRequested.toList(isFinishRequested) } + launch(testDispatcher) { underTest.isFinishRequested.toList(isFinishRequested) } assertThat(isFinishRequested.last()).isFalse() underTest.onCancelButtonClicked() diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java index 47a86b1fca5c..5ca936222785 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java @@ -151,7 +151,6 @@ import com.android.wm.shell.taskview.TaskViewTransitions; import org.junit.After; import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; @@ -320,7 +319,7 @@ public class BubblesTest extends SysuiTestCase { mColorExtractor, mDumpManager, mKeyguardStateController, mScreenOffAnimationController, mAuthController, mShadeExpansionStateManager, mShadeWindowLogger); - mNotificationShadeWindowController.setNotificationShadeView(mNotificationShadeWindowView); + mNotificationShadeWindowController.setWindowRootView(mNotificationShadeWindowView); mNotificationShadeWindowController.attach(); mAppBubbleIntent = new Intent(mContext, BubblesTestActivity.class); @@ -594,7 +593,6 @@ public class BubblesTest extends SysuiTestCase { } @Test - @Ignore("Currently broken.") public void testCollapseAfterChangingExpandedBubble() { // Mark it as a bubble and add it explicitly mEntryListener.onEntryAdded(mRow); 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 9179efc9f39f..e470406499b6 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiBaseFragmentTest.java +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiBaseFragmentTest.java @@ -57,8 +57,7 @@ public abstract class SysuiBaseFragmentTest extends BaseFragmentTest { @Before public void sysuiSetup() throws ExecutionException, InterruptedException { - SystemUIInitializer initializer = - SystemUIInitializerFactory.createFromConfigNoAssert(mContext); + SystemUIInitializer initializer = new SystemUIInitializerImpl(mContext); initializer.init(true); mDependency = new TestableDependency(initializer.getSysUIComponent().createDependency()); Dependency.setInstance(mDependency); diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java index 8bbd58dc8fe1..de177168e20f 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java @@ -90,8 +90,7 @@ public abstract class SysuiTestCase { if (isRobolectricTest()) { mContext = mContext.createDefaultDisplayContext(); } - SystemUIInitializer initializer = - SystemUIInitializerFactory.createFromConfigNoAssert(mContext); + SystemUIInitializer initializer = new SystemUIInitializerImpl(mContext); initializer.init(true); mDependency = new TestableDependency(initializer.getSysUIComponent().createDependency()); Dependency.setInstance(mDependency); diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeBouncerMessageRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/data/repository/FakeBouncerMessageRepository.kt index b03b4ba3687d..d9b926d80589 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeBouncerMessageRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/data/repository/FakeBouncerMessageRepository.kt @@ -14,10 +14,9 @@ * limitations under the License. */ -package com.android.systemui.keyguard.data.repository +package com.android.systemui.bouncer.data.repository -import com.android.systemui.keyguard.bouncer.data.repository.BouncerMessageRepository -import com.android.systemui.keyguard.bouncer.shared.model.BouncerMessageModel +import com.android.systemui.bouncer.shared.model.BouncerMessageModel import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardBouncerRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/data/repository/FakeKeyguardBouncerRepository.kt index 8a6d2aa7dd21..10529e68f00f 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardBouncerRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/data/repository/FakeKeyguardBouncerRepository.kt @@ -1,24 +1,7 @@ -/* - * 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.keyguard.data.repository - -import com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants.EXPANSION_HIDDEN -import com.android.systemui.keyguard.shared.model.BouncerShowMessageModel +package com.android.systemui.bouncer.data.repository + +import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants +import com.android.systemui.bouncer.shared.model.BouncerShowMessageModel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow @@ -36,7 +19,7 @@ class FakeKeyguardBouncerRepository : KeyguardBouncerRepository { _primaryBouncerDisappearAnimation.asStateFlow() private val _primaryBouncerScrimmed = MutableStateFlow(false) override val primaryBouncerScrimmed = _primaryBouncerScrimmed.asStateFlow() - private val _panelExpansionAmount = MutableStateFlow(EXPANSION_HIDDEN) + private val _panelExpansionAmount = MutableStateFlow(KeyguardBouncerConstants.EXPANSION_HIDDEN) override val panelExpansionAmount = _panelExpansionAmount.asStateFlow() private val _keyguardPosition = MutableStateFlow(0f) override val keyguardPosition = _keyguardPosition.asStateFlow() diff --git a/packages/SystemUI/tests/src/com/android/systemui/common/ui/data/repository/FakeConfigurationRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/common/ui/data/repository/FakeConfigurationRepository.kt index b2a1668df7aa..b2a1668df7aa 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/common/ui/data/repository/FakeConfigurationRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/common/ui/data/repository/FakeConfigurationRepository.kt diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt index b52a76839a99..f6cbb072495f 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt @@ -81,7 +81,7 @@ class FakeKeyguardRepository : KeyguardRepository { MutableStateFlow( WakefulnessModel(WakefulnessState.ASLEEP, WakeSleepReason.OTHER, WakeSleepReason.OTHER) ) - override val wakefulness: Flow<WakefulnessModel> = _wakefulnessModel + override val wakefulness = _wakefulnessModel private val _isUdfpsSupported = MutableStateFlow(false) diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorFactory.kt new file mode 100644 index 000000000000..f13c98ddd5e2 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorFactory.kt @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.android.systemui.keyguard.domain.interactor + +import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository +import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository +import com.android.systemui.flags.FakeFeatureFlags +import com.android.systemui.flags.Flags +import com.android.systemui.keyguard.data.repository.FakeCommandQueue +import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository + +/** + * Simply put, I got tired of adding a constructor argument and then having to tweak dozens of + * files. This should alleviate some of the burden by providing defaults for testing. + */ +object KeyguardInteractorFactory { + + @JvmOverloads + @JvmStatic + fun create( + featureFlags: FakeFeatureFlags = createFakeFeatureFlags(), + repository: FakeKeyguardRepository = FakeKeyguardRepository(), + commandQueue: FakeCommandQueue = FakeCommandQueue(), + bouncerRepository: FakeKeyguardBouncerRepository = FakeKeyguardBouncerRepository(), + configurationRepository: FakeConfigurationRepository = FakeConfigurationRepository(), + ): WithDependencies { + return WithDependencies( + repository = repository, + commandQueue = commandQueue, + featureFlags = featureFlags, + bouncerRepository = bouncerRepository, + configurationRepository = configurationRepository, + KeyguardInteractor( + repository = repository, + commandQueue = commandQueue, + featureFlags = featureFlags, + bouncerRepository = bouncerRepository, + configurationRepository = configurationRepository, + ) + ) + } + + /** Provide defaults, otherwise tests will throw an error */ + fun createFakeFeatureFlags(): FakeFeatureFlags { + return FakeFeatureFlags().apply { set(Flags.FACE_AUTH_REFACTOR, false) } + } + + data class WithDependencies( + val repository: FakeKeyguardRepository, + val commandQueue: FakeCommandQueue, + val featureFlags: FakeFeatureFlags, + val bouncerRepository: FakeKeyguardBouncerRepository, + val configurationRepository: FakeConfigurationRepository, + val keyguardInteractor: KeyguardInteractor, + ) +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/power/data/repository/FakePowerRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/power/data/repository/FakePowerRepository.kt index 15465f4d40fe..3334f3e82c59 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/power/data/repository/FakePowerRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/power/data/repository/FakePowerRepository.kt @@ -17,6 +17,7 @@ package com.android.systemui.power.data.repository +import android.os.PowerManager import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow @@ -28,7 +29,15 @@ class FakePowerRepository( private val _isInteractive = MutableStateFlow(initialInteractive) override val isInteractive: Flow<Boolean> = _isInteractive.asStateFlow() + var lastWakeWhy: String? = null + var lastWakeReason: Int? = null + fun setInteractive(value: Boolean) { _isInteractive.value = value } + + override fun wakeUp(why: String, @PowerManager.WakeReason wakeReason: Int) { + lastWakeWhy = why + lastWakeReason = wakeReason + } } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt index be3d54acd739..9c4fd9459b57 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt @@ -20,13 +20,13 @@ import com.android.systemui.SysuiTestCase import com.android.systemui.authentication.data.repository.AuthenticationRepository import com.android.systemui.authentication.data.repository.AuthenticationRepositoryImpl import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor -import com.android.systemui.bouncer.data.repo.BouncerRepository +import com.android.systemui.bouncer.data.repository.BouncerRepository import com.android.systemui.bouncer.domain.interactor.BouncerInteractor import com.android.systemui.bouncer.ui.viewmodel.BouncerViewModel import com.android.systemui.keyguard.domain.interactor.LockscreenSceneInteractor -import com.android.systemui.scene.data.model.SceneContainerConfig import com.android.systemui.scene.data.repository.SceneContainerRepository import com.android.systemui.scene.domain.interactor.SceneInteractor +import com.android.systemui.scene.shared.model.SceneContainerConfig import com.android.systemui.scene.shared.model.SceneKey import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -51,7 +51,7 @@ class SceneTestUtils( fakeSceneContainerConfig(CONTAINER_2), ) ): SceneContainerRepository { - return SceneContainerRepository(containerConfigurations) + return SceneContainerRepository(containerConfigurations.associateBy { it.name }) } fun fakeSceneKeys(): List<SceneKey> { diff --git a/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java b/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java index 308e2cf01787..3406102b28ac 100644 --- a/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java +++ b/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java @@ -359,11 +359,7 @@ public class WallpaperBackupAgent extends BackupAgent { final File infoStage = new File(filesDir, WALLPAPER_INFO_STAGE); final File imageStage = new File(filesDir, SYSTEM_WALLPAPER_STAGE); final File lockImageStage = new File(filesDir, LOCK_WALLPAPER_STAGE); - - // If we restored separate lock imagery, the system wallpaper should be - // applied as system-only; but if there's no separate lock image, make - // sure to apply the restored system wallpaper as both. - final int sysWhich = FLAG_SYSTEM | (lockImageStage.exists() ? 0 : FLAG_LOCK); + boolean lockImageStageExists = lockImageStage.exists(); try { // First parse the live component name so that we know for logging if we care about @@ -371,14 +367,31 @@ public class WallpaperBackupAgent extends BackupAgent { ComponentName wpService = parseWallpaperComponent(infoStage, "wp"); mSystemHasLiveComponent = wpService != null; + ComponentName kwpService = null; + boolean lockscreenLiveWallpaper = mWallpaperManager.isLockscreenLiveWallpaperEnabled(); + if (lockscreenLiveWallpaper) { + kwpService = parseWallpaperComponent(infoStage, "kwp"); + } + mLockHasLiveComponent = kwpService != null; + boolean separateLockWallpaper = mLockHasLiveComponent || lockImageStage.exists(); + + // if there's no separate lock wallpaper, apply the system wallpaper to both screens. + final int sysWhich = separateLockWallpaper ? FLAG_SYSTEM : FLAG_SYSTEM | FLAG_LOCK; + // It is valid for the imagery to be absent; it means that we were not permitted // to back up the original image on the source device, or there was no user-supplied // wallpaper image present. - restoreFromStage(imageStage, infoStage, "wp", sysWhich); - restoreFromStage(lockImageStage, infoStage, "kwp", FLAG_LOCK); + if (!lockscreenLiveWallpaper) restoreFromStage(imageStage, infoStage, "wp", sysWhich); + if (lockImageStageExists) { + restoreFromStage(lockImageStage, infoStage, "kwp", FLAG_LOCK); + } + if (lockscreenLiveWallpaper) restoreFromStage(imageStage, infoStage, "wp", sysWhich); // And reset to the wallpaper service we should be using - updateWallpaperComponent(wpService, !lockImageStage.exists()); + if (lockscreenLiveWallpaper && mLockHasLiveComponent) { + updateWallpaperComponent(kwpService, false, FLAG_LOCK); + } + updateWallpaperComponent(wpService, !lockImageStageExists, sysWhich); } catch (Exception e) { Slog.e(TAG, "Unable to restore wallpaper: " + e.getMessage()); mEventLogger.onRestoreException(e); @@ -397,9 +410,21 @@ public class WallpaperBackupAgent extends BackupAgent { } @VisibleForTesting - void updateWallpaperComponent(ComponentName wpService, boolean applyToLock) throws IOException { + void updateWallpaperComponent(ComponentName wpService, boolean applyToLock, int which) + throws IOException { + boolean lockscreenLiveWallpaper = mWallpaperManager.isLockscreenLiveWallpaperEnabled(); if (servicePackageExists(wpService)) { Slog.i(TAG, "Using wallpaper service " + wpService); + if (lockscreenLiveWallpaper) { + mWallpaperManager.setWallpaperComponentWithFlags(wpService, which); + if ((which & FLAG_LOCK) != 0) { + mEventLogger.onLockLiveWallpaperRestored(wpService); + } + if ((which & FLAG_SYSTEM) != 0) { + mEventLogger.onSystemLiveWallpaperRestored(wpService); + } + return; + } mWallpaperManager.setWallpaperComponent(wpService); if (applyToLock) { // We have a live wallpaper and no static lock image, @@ -414,7 +439,7 @@ public class WallpaperBackupAgent extends BackupAgent { // in reports from users if (wpService != null) { // TODO(b/268471749): Handle delayed case - applyComponentAtInstall(wpService, applyToLock); + applyComponentAtInstall(wpService, applyToLock, which); Slog.w(TAG, "Wallpaper service " + wpService + " isn't available. " + " Will try to apply later"); } @@ -437,7 +462,8 @@ public class WallpaperBackupAgent extends BackupAgent { // And log the success if ((which & FLAG_SYSTEM) > 0) { mEventLogger.onSystemImageWallpaperRestored(); - } else { + } + if ((which & FLAG_LOCK) > 0) { mEventLogger.onLockImageWallpaperRestored(); } } @@ -460,7 +486,8 @@ public class WallpaperBackupAgent extends BackupAgent { private void logRestoreError(int which, String error) { if ((which & FLAG_SYSTEM) == FLAG_SYSTEM) { mEventLogger.onSystemImageWallpaperRestoreFailed(error); - } else if ((which & FLAG_LOCK) == FLAG_LOCK) { + } + if ((which & FLAG_LOCK) == FLAG_LOCK) { mEventLogger.onLockImageWallpaperRestoreFailed(error); } } @@ -552,16 +579,21 @@ public class WallpaperBackupAgent extends BackupAgent { // Intentionally blank } - private void applyComponentAtInstall(ComponentName componentName, boolean applyToLock) { - PackageMonitor packageMonitor = getWallpaperPackageMonitor(componentName, applyToLock); + private void applyComponentAtInstall(ComponentName componentName, boolean applyToLock, + int which) { + PackageMonitor packageMonitor = getWallpaperPackageMonitor( + componentName, applyToLock, which); packageMonitor.register(getBaseContext(), null, UserHandle.ALL, true); } @VisibleForTesting - PackageMonitor getWallpaperPackageMonitor(ComponentName componentName, boolean applyToLock) { + PackageMonitor getWallpaperPackageMonitor(ComponentName componentName, boolean applyToLock, + int which) { return new PackageMonitor() { @Override public void onPackageAdded(String packageName, int uid) { + boolean lockscreenLiveWallpaper = + mWallpaperManager.isLockscreenLiveWallpaperEnabled(); if (!isDeviceInRestore()) { // We don't want to reapply the wallpaper outside a restore. unregister(); @@ -582,16 +614,29 @@ public class WallpaperBackupAgent extends BackupAgent { if (componentName.getPackageName().equals(packageName)) { Slog.d(TAG, "Applying component " + componentName); - boolean sysResult = mWallpaperManager.setWallpaperComponent(componentName); + boolean success = lockscreenLiveWallpaper + ? mWallpaperManager.setWallpaperComponentWithFlags(componentName, which) + : mWallpaperManager.setWallpaperComponent(componentName); WallpaperEventLogger logger = new WallpaperEventLogger( mBackupManager.getDelayedRestoreLogger()); - if (sysResult) { - logger.onSystemLiveWallpaperRestored(componentName); + if (success) { + if (!lockscreenLiveWallpaper || (which & FLAG_SYSTEM) != 0) { + logger.onSystemLiveWallpaperRestored(componentName); + } + if (lockscreenLiveWallpaper && (which & FLAG_LOCK) != 0) { + logger.onLockLiveWallpaperRestored(componentName); + } } else { - logger.onSystemLiveWallpaperRestoreFailed( - WallpaperEventLogger.ERROR_SET_COMPONENT_EXCEPTION); + if (!lockscreenLiveWallpaper || (which & FLAG_SYSTEM) != 0) { + logger.onSystemLiveWallpaperRestoreFailed( + WallpaperEventLogger.ERROR_SET_COMPONENT_EXCEPTION); + } + if (lockscreenLiveWallpaper && (which & FLAG_LOCK) != 0) { + logger.onLockLiveWallpaperRestoreFailed( + WallpaperEventLogger.ERROR_SET_COMPONENT_EXCEPTION); + } } - if (applyToLock) { + if (applyToLock && !lockscreenLiveWallpaper) { try { mWallpaperManager.clear(FLAG_LOCK); logger.onLockLiveWallpaperRestored(componentName); diff --git a/packages/WallpaperBackup/test/src/com/android/wallpaperbackup/WallpaperBackupAgentTest.java b/packages/WallpaperBackup/test/src/com/android/wallpaperbackup/WallpaperBackupAgentTest.java index 9b07ad41ef9a..dc1126edde41 100644 --- a/packages/WallpaperBackup/test/src/com/android/wallpaperbackup/WallpaperBackupAgentTest.java +++ b/packages/WallpaperBackup/test/src/com/android/wallpaperbackup/WallpaperBackupAgentTest.java @@ -117,6 +117,7 @@ public class WallpaperBackupAgentTest { public void setUp() { MockitoAnnotations.initMocks(this); + when(mWallpaperManager.isLockscreenLiveWallpaperEnabled()).thenReturn(true); when(mWallpaperManager.isWallpaperBackupEligible(eq(FLAG_SYSTEM))).thenReturn(true); when(mWallpaperManager.isWallpaperBackupEligible(eq(FLAG_LOCK))).thenReturn(true); @@ -364,14 +365,23 @@ public class WallpaperBackupAgentTest { mWallpaperBackupAgent.mIsDeviceInRestore = true; mWallpaperBackupAgent.updateWallpaperComponent(mWallpaperComponent, - /* applyToLock */ true); + /* applyToLock */ true, FLAG_LOCK | FLAG_SYSTEM); // Imitate wallpaper component installation. mWallpaperBackupAgent.mWallpaperPackageMonitor.onPackageAdded(TEST_WALLPAPER_PACKAGE, /* uid */0); - - verify(mWallpaperManager, times(1)).setWallpaperComponent(mWallpaperComponent); - verify(mWallpaperManager, times(1)).clear(eq(FLAG_LOCK)); + if (mWallpaperManager.isLockscreenLiveWallpaperEnabled()) { + verify(mWallpaperManager, times(1)) + .setWallpaperComponentWithFlags(mWallpaperComponent, FLAG_LOCK | FLAG_SYSTEM); + verify(mWallpaperManager, never()) + .setWallpaperComponentWithFlags(mWallpaperComponent, FLAG_SYSTEM); + verify(mWallpaperManager, never()) + .setWallpaperComponentWithFlags(mWallpaperComponent, FLAG_LOCK); + verify(mWallpaperManager, never()).clear(anyInt()); + } else { + verify(mWallpaperManager, times(1)).setWallpaperComponent(mWallpaperComponent); + verify(mWallpaperManager, times(1)).clear(eq(FLAG_LOCK)); + } } @Test @@ -380,14 +390,24 @@ public class WallpaperBackupAgentTest { mWallpaperBackupAgent.mIsDeviceInRestore = true; mWallpaperBackupAgent.updateWallpaperComponent(mWallpaperComponent, - /* applyToLock */ false); + /* applyToLock */ false, FLAG_SYSTEM); // Imitate wallpaper component installation. mWallpaperBackupAgent.mWallpaperPackageMonitor.onPackageAdded(TEST_WALLPAPER_PACKAGE, /* uid */0); - verify(mWallpaperManager, times(1)).setWallpaperComponent(mWallpaperComponent); - verify(mWallpaperManager, never()).clear(eq(FLAG_LOCK)); + if (mWallpaperManager.isLockscreenLiveWallpaperEnabled()) { + verify(mWallpaperManager, times(1)) + .setWallpaperComponentWithFlags(mWallpaperComponent, FLAG_SYSTEM); + verify(mWallpaperManager, never()) + .setWallpaperComponentWithFlags(mWallpaperComponent, FLAG_LOCK); + verify(mWallpaperManager, never()) + .setWallpaperComponentWithFlags(mWallpaperComponent, FLAG_LOCK | FLAG_SYSTEM); + verify(mWallpaperManager, never()).clear(anyInt()); + } else { + verify(mWallpaperManager, times(1)).setWallpaperComponent(mWallpaperComponent); + verify(mWallpaperManager, never()).clear(eq(FLAG_LOCK)); + } } @Test @@ -396,7 +416,7 @@ public class WallpaperBackupAgentTest { mWallpaperBackupAgent.mIsDeviceInRestore = false; mWallpaperBackupAgent.updateWallpaperComponent(mWallpaperComponent, - /* applyToLock */ true); + /* applyToLock */ true, FLAG_LOCK | FLAG_SYSTEM); // Imitate wallpaper component installation. mWallpaperBackupAgent.mWallpaperPackageMonitor.onPackageAdded(TEST_WALLPAPER_PACKAGE, @@ -412,7 +432,7 @@ public class WallpaperBackupAgentTest { mWallpaperBackupAgent.mIsDeviceInRestore = false; mWallpaperBackupAgent.updateWallpaperComponent(mWallpaperComponent, - /* applyToLock */ true); + /* applyToLock */ true, FLAG_LOCK | FLAG_SYSTEM); // Imitate "wrong" wallpaper component installation. mWallpaperBackupAgent.mWallpaperPackageMonitor.onPackageAdded(/* packageName */"", @@ -614,6 +634,13 @@ public class WallpaperBackupAgentTest { mWallpaperBackupAgent.getBackupRestoreEventLogger().getLoggingResults()); assertThat(result).isNotNull(); assertThat(result.getSuccessCount()).isEqualTo(1); + + if (mWallpaperManager.isLockscreenLiveWallpaperEnabled()) { + result = getLoggingResult(WALLPAPER_IMG_LOCK, + mWallpaperBackupAgent.getBackupRestoreEventLogger().getLoggingResults()); + assertThat(result).isNotNull(); + assertThat(result.getSuccessCount()).isEqualTo(1); + } } @Test @@ -649,19 +676,20 @@ public class WallpaperBackupAgentTest { } @Test - public void testOnRestore_lockWallpaperImgMissingAndNoLive_logsFailure() throws Exception { + public void testOnRestore_wallpaperImgMissingAndNoLive_logsFailure() throws Exception { mockStagedWallpaperFile(WALLPAPER_INFO_STAGE); - mockStagedWallpaperFile(SYSTEM_WALLPAPER_STAGE); mWallpaperBackupAgent.onCreate(USER_HANDLE, BackupAnnotations.BackupDestination.CLOUD, BackupAnnotations.OperationType.RESTORE); mWallpaperBackupAgent.onRestoreFinished(); - DataTypeResult result = getLoggingResult(WALLPAPER_IMG_LOCK, - mWallpaperBackupAgent.getBackupRestoreEventLogger().getLoggingResults()); - assertThat(result).isNotNull(); - assertThat(result.getFailCount()).isEqualTo(1); - assertThat(result.getErrors()).containsKey(ERROR_NO_WALLPAPER); + for (String wallpaper: List.of(WALLPAPER_IMG_LOCK, WALLPAPER_IMG_SYSTEM)) { + DataTypeResult result = getLoggingResult(wallpaper, + mWallpaperBackupAgent.getBackupRestoreEventLogger().getLoggingResults()); + assertThat(result).isNotNull(); + assertThat(result.getFailCount()).isEqualTo(1); + assertThat(result.getErrors()).containsKey(ERROR_NO_WALLPAPER); + } } @Test @@ -722,13 +750,15 @@ public class WallpaperBackupAgentTest { public void testUpdateWallpaperComponent_delayedRestore_logsSuccess() throws Exception { mWallpaperBackupAgent.mIsDeviceInRestore = true; when(mWallpaperManager.setWallpaperComponent(any())).thenReturn(true); + when(mWallpaperManager.setWallpaperComponentWithFlags(any(), eq(FLAG_LOCK | FLAG_SYSTEM))) + .thenReturn(true); BackupRestoreEventLogger logger = new BackupRestoreEventLogger( BackupAnnotations.OperationType.RESTORE); when(mBackupManager.getDelayedRestoreLogger()).thenReturn(logger); mWallpaperBackupAgent.setBackupManagerForTesting(mBackupManager); mWallpaperBackupAgent.updateWallpaperComponent(mWallpaperComponent, - /* applyToLock */ true); + /* applyToLock */ true, FLAG_LOCK | FLAG_SYSTEM); // Imitate wallpaper component installation. mWallpaperBackupAgent.mWallpaperPackageMonitor.onPackageAdded(TEST_WALLPAPER_PACKAGE, /* uid */0); @@ -752,7 +782,7 @@ public class WallpaperBackupAgentTest { mWallpaperBackupAgent.setBackupManagerForTesting(mBackupManager); mWallpaperBackupAgent.updateWallpaperComponent(mWallpaperComponent, - /* applyToLock */ true); + /* applyToLock */ true, FLAG_LOCK | FLAG_SYSTEM); // Imitate wallpaper component installation. mWallpaperBackupAgent.mWallpaperPackageMonitor.onPackageAdded(TEST_WALLPAPER_PACKAGE, /* uid */0); @@ -774,7 +804,7 @@ public class WallpaperBackupAgentTest { mWallpaperBackupAgent.setBackupManagerForTesting(mBackupManager); mWallpaperBackupAgent.updateWallpaperComponent(mWallpaperComponent, - /* applyToLock */ true); + /* applyToLock */ true, FLAG_LOCK | FLAG_SYSTEM); // Imitate wallpaper component installation. mWallpaperBackupAgent.mWallpaperPackageMonitor.onPackageAdded(TEST_WALLPAPER_PACKAGE, @@ -909,8 +939,9 @@ public class WallpaperBackupAgentTest { @Override PackageMonitor getWallpaperPackageMonitor(ComponentName componentName, - boolean applyToLock) { - mWallpaperPackageMonitor = super.getWallpaperPackageMonitor(componentName, applyToLock); + boolean applyToLock, int which) { + mWallpaperPackageMonitor = super.getWallpaperPackageMonitor( + componentName, applyToLock, which); return mWallpaperPackageMonitor; } diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationScaleProvider.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationScaleProvider.java index 8e1aa38be9e8..41c3dcb3e48e 100644 --- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationScaleProvider.java +++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationScaleProvider.java @@ -16,6 +16,9 @@ package com.android.server.accessibility.magnification; +import static com.android.internal.accessibility.common.MagnificationConstants.SCALE_MAX_VALUE; +import static com.android.internal.accessibility.common.MagnificationConstants.SCALE_MIN_VALUE; + import android.content.Context; import android.os.UserHandle; import android.provider.Settings; @@ -37,8 +40,9 @@ public class MagnificationScaleProvider { @VisibleForTesting protected static final float DEFAULT_MAGNIFICATION_SCALE = 2.0f; - public static final float MIN_SCALE = 1.0f; - public static final float MAX_SCALE = 8.0f; + + public static final float MIN_SCALE = SCALE_MIN_VALUE; + public static final float MAX_SCALE = SCALE_MAX_VALUE; private final Context mContext; // Stores the scale for non-default displays. @@ -134,6 +138,6 @@ public class MagnificationScaleProvider { } static float constrainScale(float scale) { - return MathUtils.constrain(scale, MIN_SCALE, MAX_SCALE); + return MathUtils.constrain(scale, SCALE_MIN_VALUE, SCALE_MAX_VALUE); } } diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java index 995e557d8176..d5aee9284116 100644 --- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java +++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java @@ -2859,8 +2859,7 @@ public class UserBackupManagerService { if (DEBUG) { Slog.d( TAG, - addUserIdToLogMessage( - mUserId, "Starting backup confirmation UI, token=" + token)); + addUserIdToLogMessage(mUserId, "Starting backup confirmation UI")); } if (!startConfirmationUi(token, FullBackup.FULL_BACKUP_INTENT_ACTION)) { Slog.e( diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java index 61fc32d5fa15..ca1ab9bbfebc 100644 --- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java +++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java @@ -50,6 +50,7 @@ import android.content.Context; import android.content.pm.ActivityPresentationInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; +import android.content.pm.ParceledListSlice; import android.content.pm.UserInfo; import android.database.ContentObserver; import android.os.Binder; @@ -69,6 +70,7 @@ import android.provider.DeviceConfig; import android.provider.DeviceConfig.Properties; import android.provider.Settings; import android.service.contentcapture.ActivityEvent.ActivityEventType; +import android.service.contentcapture.ContentCaptureServiceInfo; import android.service.contentcapture.IDataShareCallback; import android.service.contentcapture.IDataShareReadAdapter; import android.service.voice.VoiceInteractionManagerInternal; @@ -79,6 +81,7 @@ import android.util.Slog; import android.util.SparseArray; import android.util.SparseBooleanArray; import android.view.contentcapture.ContentCaptureCondition; +import android.view.contentcapture.ContentCaptureEvent; import android.view.contentcapture.ContentCaptureHelper; import android.view.contentcapture.ContentCaptureManager; import android.view.contentcapture.DataRemovalRequest; @@ -88,11 +91,15 @@ import android.view.contentcapture.IContentCaptureOptionsCallback; import android.view.contentcapture.IDataShareWriteAdapter; import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.infra.AbstractRemoteService; import com.android.internal.infra.GlobalWhitelistState; import com.android.internal.os.IResultReceiver; import com.android.internal.util.DumpUtils; import com.android.server.LocalServices; +import com.android.server.contentprotection.ContentProtectionBlocklistManager; +import com.android.server.contentprotection.ContentProtectionPackageManager; +import com.android.server.contentprotection.RemoteContentProtectionService; import com.android.server.infra.AbstractMasterSystemService; import com.android.server.infra.FrameworkResourcesServiceNameResolver; @@ -117,7 +124,7 @@ import java.util.concurrent.atomic.AtomicBoolean; * with other sources to provide contextual data in other areas of the system * such as Autofill. */ -public final class ContentCaptureManagerService extends +public class ContentCaptureManagerService extends AbstractMasterSystemService<ContentCaptureManagerService, ContentCapturePerUserService> { private static final String TAG = ContentCaptureManagerService.class.getSimpleName(); @@ -163,13 +170,35 @@ public final class ContentCaptureManagerService extends private boolean mDisabledByDeviceConfig; // Device-config settings that are cached and passed back to apps - @GuardedBy("mLock") int mDevCfgLoggingLevel; - @GuardedBy("mLock") int mDevCfgMaxBufferSize; - @GuardedBy("mLock") int mDevCfgIdleFlushingFrequencyMs; - @GuardedBy("mLock") int mDevCfgTextChangeFlushingFrequencyMs; - @GuardedBy("mLock") int mDevCfgLogHistorySize; - @GuardedBy("mLock") int mDevCfgIdleUnbindTimeoutMs; - @GuardedBy("mLock") boolean mDevCfgDisableFlushForViewTreeAppearing; + @GuardedBy("mLock") + int mDevCfgLoggingLevel; + + @GuardedBy("mLock") + int mDevCfgMaxBufferSize; + + @GuardedBy("mLock") + int mDevCfgIdleFlushingFrequencyMs; + + @GuardedBy("mLock") + int mDevCfgTextChangeFlushingFrequencyMs; + + @GuardedBy("mLock") + int mDevCfgLogHistorySize; + + @GuardedBy("mLock") + int mDevCfgIdleUnbindTimeoutMs; + + @GuardedBy("mLock") + boolean mDevCfgDisableFlushForViewTreeAppearing; + + @GuardedBy("mLock") + boolean mDevCfgEnableContentProtectionReceiver; + + @GuardedBy("mLock") + int mDevCfgContentProtectionAppsBlocklistSize; + + @GuardedBy("mLock") + int mDevCfgContentProtectionBufferSize; private final Executor mDataShareExecutor = Executors.newCachedThreadPool(); private final Handler mHandler = new Handler(Looper.getMainLooper()); @@ -183,6 +212,10 @@ public final class ContentCaptureManagerService extends final GlobalContentCaptureOptions mGlobalContentCaptureOptions = new GlobalContentCaptureOptions(); + @Nullable private final ComponentName mContentProtectionServiceComponentName; + + @Nullable private final ContentProtectionBlocklistManager mContentProtectionBlocklistManager; + public ContentCaptureManagerService(@NonNull Context context) { super(context, new FrameworkResourcesServiceNameResolver(context, com.android.internal.R.string.config_defaultContentCaptureService), @@ -220,6 +253,20 @@ public final class ContentCaptureManagerService extends mServiceNameResolver.getServiceName(userId), mServiceNameResolver.isTemporary(userId)); } + + if (getEnableContentProtectionReceiverLocked()) { + mContentProtectionServiceComponentName = getContentProtectionServiceComponentName(); + if (mContentProtectionServiceComponentName != null) { + mContentProtectionBlocklistManager = createContentProtectionBlocklistManager(); + mContentProtectionBlocklistManager.updateBlocklist( + mDevCfgContentProtectionAppsBlocklistSize); + } else { + mContentProtectionBlocklistManager = null; + } + } else { + mContentProtectionServiceComponentName = null; + mContentProtectionBlocklistManager = null; + } } @Override // from AbstractMasterSystemService @@ -362,6 +409,11 @@ public final class ContentCaptureManagerService extends case ContentCaptureManager.DEVICE_CONFIG_PROPERTY_IDLE_UNBIND_TIMEOUT: case ContentCaptureManager .DEVICE_CONFIG_PROPERTY_DISABLE_FLUSH_FOR_VIEW_TREE_APPEARING: + case ContentCaptureManager + .DEVICE_CONFIG_PROPERTY_ENABLE_CONTENT_PROTECTION_RECEIVER: + case ContentCaptureManager.DEVICE_CONFIG_PROPERTY_CONTENT_PROTECTION_BUFFER_SIZE: + case ContentCaptureManager + .DEVICE_CONFIG_PROPERTY_CONTENT_PROTECTION_APPS_BLOCKLIST_SIZE: setFineTuneParamsFromDeviceConfig(); return; default: @@ -370,41 +422,84 @@ public final class ContentCaptureManagerService extends } } - private void setFineTuneParamsFromDeviceConfig() { + /** @hide */ + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) + protected void setFineTuneParamsFromDeviceConfig() { synchronized (mLock) { - mDevCfgMaxBufferSize = DeviceConfig.getInt( - DeviceConfig.NAMESPACE_CONTENT_CAPTURE, - ContentCaptureManager.DEVICE_CONFIG_PROPERTY_MAX_BUFFER_SIZE, - ContentCaptureManager.DEFAULT_MAX_BUFFER_SIZE); - mDevCfgIdleFlushingFrequencyMs = DeviceConfig.getInt( - DeviceConfig.NAMESPACE_CONTENT_CAPTURE, - ContentCaptureManager.DEVICE_CONFIG_PROPERTY_IDLE_FLUSH_FREQUENCY, - ContentCaptureManager.DEFAULT_IDLE_FLUSHING_FREQUENCY_MS); - mDevCfgTextChangeFlushingFrequencyMs = DeviceConfig.getInt( - DeviceConfig.NAMESPACE_CONTENT_CAPTURE, - ContentCaptureManager.DEVICE_CONFIG_PROPERTY_TEXT_CHANGE_FLUSH_FREQUENCY, - ContentCaptureManager.DEFAULT_TEXT_CHANGE_FLUSHING_FREQUENCY_MS); - mDevCfgLogHistorySize = DeviceConfig.getInt( - DeviceConfig.NAMESPACE_CONTENT_CAPTURE, - ContentCaptureManager.DEVICE_CONFIG_PROPERTY_LOG_HISTORY_SIZE, 20); - mDevCfgIdleUnbindTimeoutMs = DeviceConfig.getInt( - DeviceConfig.NAMESPACE_CONTENT_CAPTURE, - ContentCaptureManager.DEVICE_CONFIG_PROPERTY_IDLE_UNBIND_TIMEOUT, - (int) AbstractRemoteService.PERMANENT_BOUND_TIMEOUT_MS); - mDevCfgDisableFlushForViewTreeAppearing = DeviceConfig.getBoolean( - DeviceConfig.NAMESPACE_CONTENT_CAPTURE, - ContentCaptureManager - .DEVICE_CONFIG_PROPERTY_DISABLE_FLUSH_FOR_VIEW_TREE_APPEARING, - false); + mDevCfgMaxBufferSize = + DeviceConfig.getInt( + DeviceConfig.NAMESPACE_CONTENT_CAPTURE, + ContentCaptureManager.DEVICE_CONFIG_PROPERTY_MAX_BUFFER_SIZE, + ContentCaptureManager.DEFAULT_MAX_BUFFER_SIZE); + mDevCfgIdleFlushingFrequencyMs = + DeviceConfig.getInt( + DeviceConfig.NAMESPACE_CONTENT_CAPTURE, + ContentCaptureManager.DEVICE_CONFIG_PROPERTY_IDLE_FLUSH_FREQUENCY, + ContentCaptureManager.DEFAULT_IDLE_FLUSHING_FREQUENCY_MS); + mDevCfgTextChangeFlushingFrequencyMs = + DeviceConfig.getInt( + DeviceConfig.NAMESPACE_CONTENT_CAPTURE, + ContentCaptureManager + .DEVICE_CONFIG_PROPERTY_TEXT_CHANGE_FLUSH_FREQUENCY, + ContentCaptureManager.DEFAULT_TEXT_CHANGE_FLUSHING_FREQUENCY_MS); + mDevCfgLogHistorySize = + DeviceConfig.getInt( + DeviceConfig.NAMESPACE_CONTENT_CAPTURE, + ContentCaptureManager.DEVICE_CONFIG_PROPERTY_LOG_HISTORY_SIZE, + 20); + mDevCfgIdleUnbindTimeoutMs = + DeviceConfig.getInt( + DeviceConfig.NAMESPACE_CONTENT_CAPTURE, + ContentCaptureManager.DEVICE_CONFIG_PROPERTY_IDLE_UNBIND_TIMEOUT, + (int) AbstractRemoteService.PERMANENT_BOUND_TIMEOUT_MS); + mDevCfgDisableFlushForViewTreeAppearing = + DeviceConfig.getBoolean( + DeviceConfig.NAMESPACE_CONTENT_CAPTURE, + ContentCaptureManager + .DEVICE_CONFIG_PROPERTY_DISABLE_FLUSH_FOR_VIEW_TREE_APPEARING, + false); + mDevCfgEnableContentProtectionReceiver = + DeviceConfig.getBoolean( + DeviceConfig.NAMESPACE_CONTENT_CAPTURE, + ContentCaptureManager + .DEVICE_CONFIG_PROPERTY_ENABLE_CONTENT_PROTECTION_RECEIVER, + ContentCaptureManager.DEFAULT_ENABLE_CONTENT_PROTECTION_RECEIVER); + mDevCfgContentProtectionAppsBlocklistSize = + DeviceConfig.getInt( + DeviceConfig.NAMESPACE_CONTENT_CAPTURE, + ContentCaptureManager + .DEVICE_CONFIG_PROPERTY_CONTENT_PROTECTION_APPS_BLOCKLIST_SIZE, + ContentCaptureManager.DEFAULT_CONTENT_PROTECTION_APPS_BLOCKLIST_SIZE); + // mContentProtectionBlocklistManager.updateBlocklist not called on purpose here to keep + // it immutable at this point + mDevCfgContentProtectionBufferSize = + DeviceConfig.getInt( + DeviceConfig.NAMESPACE_CONTENT_CAPTURE, + ContentCaptureManager + .DEVICE_CONFIG_PROPERTY_CONTENT_PROTECTION_BUFFER_SIZE, + ContentCaptureManager.DEFAULT_CONTENT_PROTECTION_BUFFER_SIZE); if (verbose) { - Slog.v(TAG, "setFineTuneParamsFromDeviceConfig(): " - + "bufferSize=" + mDevCfgMaxBufferSize - + ", idleFlush=" + mDevCfgIdleFlushingFrequencyMs - + ", textFluxh=" + mDevCfgTextChangeFlushingFrequencyMs - + ", logHistory=" + mDevCfgLogHistorySize - + ", idleUnbindTimeoutMs=" + mDevCfgIdleUnbindTimeoutMs - + ", disableFlushForViewTreeAppearing=" - + mDevCfgDisableFlushForViewTreeAppearing); + Slog.v( + TAG, + "setFineTuneParamsFromDeviceConfig(): " + + "bufferSize=" + + mDevCfgMaxBufferSize + + ", idleFlush=" + + mDevCfgIdleFlushingFrequencyMs + + ", textFluxh=" + + mDevCfgTextChangeFlushingFrequencyMs + + ", logHistory=" + + mDevCfgLogHistorySize + + ", idleUnbindTimeoutMs=" + + mDevCfgIdleUnbindTimeoutMs + + ", disableFlushForViewTreeAppearing=" + + mDevCfgDisableFlushForViewTreeAppearing + + ", enableContentProtectionReceiver=" + + mDevCfgEnableContentProtectionReceiver + + ", contentProtectionAppsBlocklistSize=" + + mDevCfgContentProtectionAppsBlocklistSize + + ", contentProtectionBufferSize=" + + mDevCfgContentProtectionBufferSize); } } } @@ -645,24 +740,137 @@ public final class ContentCaptureManagerService extends final String prefix2 = prefix + " "; - pw.print(prefix); pw.print("Users disabled by Settings: "); pw.println(mDisabledBySettings); - pw.print(prefix); pw.println("DeviceConfig Settings: "); - pw.print(prefix2); pw.print("disabled: "); pw.println(mDisabledByDeviceConfig); - pw.print(prefix2); pw.print("loggingLevel: "); pw.println(mDevCfgLoggingLevel); - pw.print(prefix2); pw.print("maxBufferSize: "); pw.println(mDevCfgMaxBufferSize); - pw.print(prefix2); pw.print("idleFlushingFrequencyMs: "); + pw.print(prefix); + pw.print("Users disabled by Settings: "); + pw.println(mDisabledBySettings); + pw.print(prefix); + pw.println("DeviceConfig Settings: "); + pw.print(prefix2); + pw.print("disabled: "); + pw.println(mDisabledByDeviceConfig); + pw.print(prefix2); + pw.print("loggingLevel: "); + pw.println(mDevCfgLoggingLevel); + pw.print(prefix2); + pw.print("maxBufferSize: "); + pw.println(mDevCfgMaxBufferSize); + pw.print(prefix2); + pw.print("idleFlushingFrequencyMs: "); pw.println(mDevCfgIdleFlushingFrequencyMs); - pw.print(prefix2); pw.print("textChangeFlushingFrequencyMs: "); + pw.print(prefix2); + pw.print("textChangeFlushingFrequencyMs: "); pw.println(mDevCfgTextChangeFlushingFrequencyMs); - pw.print(prefix2); pw.print("logHistorySize: "); pw.println(mDevCfgLogHistorySize); - pw.print(prefix2); pw.print("idleUnbindTimeoutMs: "); + pw.print(prefix2); + pw.print("logHistorySize: "); + pw.println(mDevCfgLogHistorySize); + pw.print(prefix2); + pw.print("idleUnbindTimeoutMs: "); pw.println(mDevCfgIdleUnbindTimeoutMs); - pw.print(prefix2); pw.print("disableFlushForViewTreeAppearing: "); + pw.print(prefix2); + pw.print("disableFlushForViewTreeAppearing: "); pw.println(mDevCfgDisableFlushForViewTreeAppearing); - pw.print(prefix); pw.println("Global Options:"); + pw.print(prefix2); + pw.print("enableContentProtectionReceiver: "); + pw.println(mDevCfgEnableContentProtectionReceiver); + pw.print(prefix2); + pw.print("contentProtectionAppsBlocklistSize: "); + pw.println(mDevCfgContentProtectionAppsBlocklistSize); + pw.print(prefix2); + pw.print("contentProtectionBufferSize: "); + pw.println(mDevCfgContentProtectionBufferSize); + pw.print(prefix); + pw.println("Global Options:"); mGlobalContentCaptureOptions.dump(prefix2, pw); } + /** + * Used by the constructor in order to be able to override the value in the tests. + * + * @hide + */ + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) + @GuardedBy("mLock") + protected boolean getEnableContentProtectionReceiverLocked() { + return mDevCfgEnableContentProtectionReceiver; + } + + /** @hide */ + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) + @NonNull + protected ContentProtectionBlocklistManager createContentProtectionBlocklistManager() { + return new ContentProtectionBlocklistManager( + new ContentProtectionPackageManager(getContext())); + } + + @Nullable + private ComponentName getContentProtectionServiceComponentName() { + String flatComponentName = getContentProtectionServiceFlatComponentName(); + return ComponentName.unflattenFromString(flatComponentName); + } + + /** @hide */ + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) + @Nullable + protected String getContentProtectionServiceFlatComponentName() { + return getContext() + .getString(com.android.internal.R.string.config_defaultContentProtectionService); + } + + /** + * Can also throw runtime exceptions such as {@link SecurityException}. + * + * @hide + */ + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) + @NonNull + protected ContentCaptureServiceInfo createContentProtectionServiceInfo( + @NonNull ComponentName componentName) throws PackageManager.NameNotFoundException { + return new ContentCaptureServiceInfo( + getContext(), componentName, /* isTemp= */ false, UserHandle.getCallingUserId()); + } + + @Nullable + private RemoteContentProtectionService createRemoteContentProtectionService() { + if (mContentProtectionServiceComponentName == null) { + // This case should not be possible but make sure + return null; + } + synchronized (mLock) { + if (!mDevCfgEnableContentProtectionReceiver) { + return null; + } + } + + // Check permissions by trying to construct {@link ContentCaptureServiceInfo} + try { + createContentProtectionServiceInfo(mContentProtectionServiceComponentName); + } catch (Exception ex) { + // Swallow, exception was already logged + return null; + } + + return createRemoteContentProtectionService(mContentProtectionServiceComponentName); + } + + /** @hide */ + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) + @NonNull + protected RemoteContentProtectionService createRemoteContentProtectionService( + @NonNull ComponentName componentName) { + return new RemoteContentProtectionService( + getContext(), + componentName, + UserHandle.getCallingUserId(), + isBindInstantServiceAllowed()); + } + + /** @hide */ + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) + @NonNull + protected ContentCaptureManagerServiceStub getContentCaptureManagerServiceStub() { + return mContentCaptureManagerServiceStub; + } + final class ContentCaptureManagerServiceStub extends IContentCaptureManager.Stub { @Override @@ -896,6 +1104,19 @@ public final class ContentCaptureManagerService extends public void setDefaultServiceEnabled(@UserIdInt int userId, boolean enabled) { ContentCaptureManagerService.this.setDefaultServiceEnabled(userId, enabled); } + + @Override + public void onLoginDetected(@NonNull ParceledListSlice<ContentCaptureEvent> events) { + RemoteContentProtectionService service = createRemoteContentProtectionService(); + if (service == null) { + return; + } + try { + service.onLoginDetected(events); + } catch (Exception ex) { + Slog.e(TAG, "Failed to call remote service", ex); + } + } } private final class LocalService extends ContentCaptureManagerInternal { @@ -984,14 +1205,21 @@ public final class ContentCaptureManagerService extends @GuardedBy("mGlobalWhitelistStateLock") public ContentCaptureOptions getOptions(@UserIdInt int userId, @NonNull String packageName) { - boolean packageWhitelisted; + boolean isContentCaptureReceiverEnabled; + boolean isContentProtectionReceiverEnabled; ArraySet<ComponentName> whitelistedComponents = null; + synchronized (mGlobalWhitelistStateLock) { - packageWhitelisted = isWhitelisted(userId, packageName); - if (!packageWhitelisted) { - // Full package is not allowlisted: check individual components first + isContentCaptureReceiverEnabled = + isContentCaptureReceiverEnabled(userId, packageName); + isContentProtectionReceiverEnabled = + isContentProtectionReceiverEnabled(packageName); + + if (!isContentCaptureReceiverEnabled) { + // Full package is not allowlisted: check individual components next whitelistedComponents = getWhitelistedComponents(userId, packageName); - if (whitelistedComponents == null + if (!isContentProtectionReceiverEnabled + && whitelistedComponents == null && packageName.equals(mServicePackages.get(userId))) { // No components allowlisted either, but let it go because it's the // service's own package @@ -1010,7 +1238,9 @@ public final class ContentCaptureManagerService extends } } - if (!packageWhitelisted && whitelistedComponents == null) { + if (!isContentCaptureReceiverEnabled + && !isContentProtectionReceiverEnabled + && whitelistedComponents == null) { // No can do! if (verbose) { Slog.v(TAG, "getOptionsForPackage(" + packageName + "): not whitelisted"); @@ -1019,11 +1249,19 @@ public final class ContentCaptureManagerService extends } synchronized (mLock) { - final ContentCaptureOptions options = new ContentCaptureOptions(mDevCfgLoggingLevel, - mDevCfgMaxBufferSize, mDevCfgIdleFlushingFrequencyMs, - mDevCfgTextChangeFlushingFrequencyMs, mDevCfgLogHistorySize, - mDevCfgDisableFlushForViewTreeAppearing, - whitelistedComponents); + final ContentCaptureOptions options = + new ContentCaptureOptions( + mDevCfgLoggingLevel, + mDevCfgMaxBufferSize, + mDevCfgIdleFlushingFrequencyMs, + mDevCfgTextChangeFlushingFrequencyMs, + mDevCfgLogHistorySize, + mDevCfgDisableFlushForViewTreeAppearing, + isContentCaptureReceiverEnabled || whitelistedComponents != null, + new ContentCaptureOptions.ContentProtectionOptions( + isContentProtectionReceiverEnabled, + mDevCfgContentProtectionBufferSize), + whitelistedComponents); if (verbose) Slog.v(TAG, "getOptionsForPackage(" + packageName + "): " + options); return options; } @@ -1042,6 +1280,36 @@ public final class ContentCaptureManagerService extends } } } + + @Override // from GlobalWhitelistState + public boolean isWhitelisted(@UserIdInt int userId, @NonNull String packageName) { + return isContentCaptureReceiverEnabled(userId, packageName) + || isContentProtectionReceiverEnabled(packageName); + } + + @Override // from GlobalWhitelistState + public boolean isWhitelisted(@UserIdInt int userId, @NonNull ComponentName componentName) { + return super.isWhitelisted(userId, componentName) + || isContentProtectionReceiverEnabled(componentName.getPackageName()); + } + + private boolean isContentCaptureReceiverEnabled( + @UserIdInt int userId, @NonNull String packageName) { + return super.isWhitelisted(userId, packageName); + } + + private boolean isContentProtectionReceiverEnabled(@NonNull String packageName) { + if (mContentProtectionServiceComponentName == null + || mContentProtectionBlocklistManager == null) { + return false; + } + synchronized (mLock) { + if (!mDevCfgEnableContentProtectionReceiver) { + return false; + } + } + return mContentProtectionBlocklistManager.isAllowed(packageName); + } } private static class DataShareCallbackDelegate extends IDataShareCallback.Stub { diff --git a/services/contentcapture/java/com/android/server/contentprotection/ContentProtectionBlocklistManager.java b/services/contentcapture/java/com/android/server/contentprotection/ContentProtectionBlocklistManager.java new file mode 100644 index 000000000000..a0fd28b3f279 --- /dev/null +++ b/services/contentcapture/java/com/android/server/contentprotection/ContentProtectionBlocklistManager.java @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.contentprotection; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.pm.PackageInfo; +import android.util.Slog; + +import com.android.internal.annotations.VisibleForTesting; + +import java.io.BufferedReader; +import java.io.FileReader; +import java.util.Collections; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * Manages whether the content protection is enabled for an app using a blocklist. + * + * @hide + */ +public class ContentProtectionBlocklistManager { + + private static final String TAG = "ContentProtectionBlocklistManager"; + + private static final String PACKAGE_NAME_BLOCKLIST_FILENAME = + "/product/etc/res/raw/content_protection/package_name_blocklist.txt"; + + @NonNull private final ContentProtectionPackageManager mContentProtectionPackageManager; + + @Nullable private Set<String> mPackageNameBlocklist; + + public ContentProtectionBlocklistManager( + @NonNull ContentProtectionPackageManager contentProtectionPackageManager) { + mContentProtectionPackageManager = contentProtectionPackageManager; + } + + public boolean isAllowed(@NonNull String packageName) { + if (mPackageNameBlocklist == null) { + // List not loaded or failed to load, don't run on anything + return false; + } + if (mPackageNameBlocklist.contains(packageName)) { + return false; + } + PackageInfo packageInfo = mContentProtectionPackageManager.getPackageInfo(packageName); + if (packageInfo == null) { + return false; + } + if (!mContentProtectionPackageManager.hasRequestedInternetPermissions(packageInfo)) { + return false; + } + if (mContentProtectionPackageManager.isSystemApp(packageInfo)) { + return false; + } + if (mContentProtectionPackageManager.isUpdatedSystemApp(packageInfo)) { + return false; + } + return true; + } + + public void updateBlocklist(int blocklistSize) { + Slog.i(TAG, "Blocklist size updating to: " + blocklistSize); + mPackageNameBlocklist = readPackageNameBlocklist(blocklistSize); + } + + @Nullable + private Set<String> readPackageNameBlocklist(int blocklistSize) { + if (blocklistSize <= 0) { + // Explicitly requested an empty blocklist + return Collections.emptySet(); + } + List<String> lines = readLinesFromRawFile(PACKAGE_NAME_BLOCKLIST_FILENAME); + if (lines == null) { + return null; + } + return lines.stream().limit(blocklistSize).collect(Collectors.toSet()); + } + + @VisibleForTesting + @Nullable + protected List<String> readLinesFromRawFile(@NonNull String filename) { + try (FileReader fileReader = new FileReader(filename); + BufferedReader bufferedReader = new BufferedReader(fileReader)) { + return bufferedReader + .lines() + .map(line -> line.trim()) + .filter(line -> !line.isBlank()) + .collect(Collectors.toList()); + } catch (Exception ex) { + Slog.e(TAG, "Failed to read: " + filename, ex); + return null; + } + } +} diff --git a/services/contentcapture/java/com/android/server/contentprotection/ContentProtectionPackageManager.java b/services/contentcapture/java/com/android/server/contentprotection/ContentProtectionPackageManager.java new file mode 100644 index 000000000000..4ebac07ec3ea --- /dev/null +++ b/services/contentcapture/java/com/android/server/contentprotection/ContentProtectionPackageManager.java @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.contentprotection; + +import android.Manifest; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.content.pm.PackageManager.PackageInfoFlags; +import android.util.Slog; + +import java.util.Arrays; + +/** + * Basic package manager for content protection using content capture. + * + * @hide + */ +public class ContentProtectionPackageManager { + + private static final String TAG = "ContentProtectionPackageManager"; + + private static final PackageInfoFlags PACKAGE_INFO_FLAGS = + PackageInfoFlags.of(PackageManager.GET_PERMISSIONS); + + @NonNull private final PackageManager mPackageManager; + + public ContentProtectionPackageManager(@NonNull Context context) { + mPackageManager = context.getPackageManager(); + } + + @Nullable + public PackageInfo getPackageInfo(@NonNull String packageName) { + try { + return mPackageManager.getPackageInfo(packageName, PACKAGE_INFO_FLAGS); + } catch (NameNotFoundException ex) { + Slog.w(TAG, "Package info not found for: " + packageName, ex); + return null; + } + } + + public boolean isSystemApp(@NonNull PackageInfo packageInfo) { + return packageInfo.applicationInfo != null && isSystemApp(packageInfo.applicationInfo); + } + + private boolean isSystemApp(@NonNull ApplicationInfo applicationInfo) { + return (applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0; + } + + public boolean isUpdatedSystemApp(@NonNull PackageInfo packageInfo) { + return packageInfo.applicationInfo != null + && isUpdatedSystemApp(packageInfo.applicationInfo); + } + + private boolean isUpdatedSystemApp(@NonNull ApplicationInfo applicationInfo) { + return (applicationInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0; + } + + public boolean hasRequestedInternetPermissions(@NonNull PackageInfo packageInfo) { + return packageInfo.requestedPermissions != null + && Arrays.asList(packageInfo.requestedPermissions) + .contains(Manifest.permission.INTERNET); + } +} diff --git a/services/contentcapture/java/com/android/server/contentprotection/RemoteContentProtectionService.java b/services/contentcapture/java/com/android/server/contentprotection/RemoteContentProtectionService.java new file mode 100644 index 000000000000..dd5545dcccc7 --- /dev/null +++ b/services/contentcapture/java/com/android/server/contentprotection/RemoteContentProtectionService.java @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.contentprotection; + +import android.annotation.NonNull; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.pm.ParceledListSlice; +import android.service.contentcapture.ContentCaptureService; +import android.service.contentcapture.IContentProtectionService; +import android.util.Slog; +import android.view.contentcapture.ContentCaptureEvent; + +import com.android.internal.infra.ServiceConnector; + +import java.time.Duration; + +/** + * Connector for the remote content protection service. + * + * @hide + */ +public class RemoteContentProtectionService + extends ServiceConnector.Impl<IContentProtectionService> { + + private static final String TAG = RemoteContentProtectionService.class.getSimpleName(); + + private static final Duration AUTO_DISCONNECT_TIMEOUT = Duration.ofSeconds(3); + + @NonNull private final ComponentName mComponentName; + + public RemoteContentProtectionService( + @NonNull Context context, + @NonNull ComponentName componentName, + int userId, + boolean bindAllowInstant) { + super( + context, + new Intent(ContentCaptureService.PROTECTION_SERVICE_INTERFACE) + .setComponent(componentName), + bindAllowInstant ? Context.BIND_ALLOW_INSTANT : 0, + userId, + IContentProtectionService.Stub::asInterface); + mComponentName = componentName; + } + + @Override // from ServiceConnector.Impl + protected long getAutoDisconnectTimeoutMs() { + return AUTO_DISCONNECT_TIMEOUT.toMillis(); + } + + @Override // from ServiceConnector.Impl + protected void onServiceConnectionStatusChanged( + @NonNull IContentProtectionService service, boolean isConnected) { + Slog.i( + TAG, + "Connection status for: " + + mComponentName + + " changed to: " + + (isConnected ? "connected" : "disconnected")); + } + + public void onLoginDetected(@NonNull ParceledListSlice<ContentCaptureEvent> events) { + run(service -> service.onLoginDetected(events)); + } +} diff --git a/services/core/java/com/android/server/DynamicSystemService.java b/services/core/java/com/android/server/DynamicSystemService.java index 27215b2fbf30..cbacee6ba72c 100644 --- a/services/core/java/com/android/server/DynamicSystemService.java +++ b/services/core/java/com/android/server/DynamicSystemService.java @@ -44,7 +44,7 @@ public class DynamicSystemService extends IDynamicSystemService.Stub { private static final String TAG = "DynamicSystemService"; private static final long MINIMUM_SD_MB = (30L << 10); private static final int GSID_ROUGH_TIMEOUT_MS = 8192; - private static final String PATH_DEFAULT = "/data/gsi/"; + private static final String PATH_DEFAULT = "/data/gsi/dsu/"; private Context mContext; private String mInstallPath, mDsuSlot; private volatile IGsiService mGsiService; @@ -226,9 +226,7 @@ public class DynamicSystemService extends IDynamicSystemService.Stub { IGsiService gsiService = getGsiService(); if (enable) { try { - if (mDsuSlot == null) { - mDsuSlot = gsiService.getActiveDsuSlot(); - } + getActiveDsuSlot(); GsiServiceCallback callback = new GsiServiceCallback(); synchronized (callback) { gsiService.enableGsiAsync(oneShot, mDsuSlot, callback); @@ -287,4 +285,15 @@ public class DynamicSystemService extends IDynamicSystemService.Stub { return getGsiService().suggestScratchSize(); } + + @Override + @EnforcePermission(android.Manifest.permission.MANAGE_DYNAMIC_SYSTEM) + public String getActiveDsuSlot() throws RemoteException { + super.getActiveDsuSlot_enforcePermission(); + + if (mDsuSlot == null) { + mDsuSlot = getGsiService().getActiveDsuSlot(); + } + return mDsuSlot; + } } diff --git a/services/core/java/com/android/server/WallpaperUpdateReceiver.java b/services/core/java/com/android/server/WallpaperUpdateReceiver.java index 99178920cc52..2812233815a6 100644 --- a/services/core/java/com/android/server/WallpaperUpdateReceiver.java +++ b/services/core/java/com/android/server/WallpaperUpdateReceiver.java @@ -88,7 +88,7 @@ public class WallpaperUpdateReceiver extends BroadcastReceiver { } else { //live wallpaper ComponentName currCN = info.getComponent(); - ComponentName defaultCN = WallpaperManager.getDefaultWallpaperComponent(context); + ComponentName defaultCN = WallpaperManager.getCmfDefaultWallpaperComponent(context); if (!currCN.equals(defaultCN)) { return true; } diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 476e1b4ef11e..d22528871090 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -19603,7 +19603,7 @@ public class ActivityManagerService extends IActivityManager.Stub for (Display display : allDisplays) { int displayId = display.getDisplayId(); // TODO(b/247592632): check other properties like isSecure or proper display type - if (display.isValid() + if (display.isValid() && ((display.getFlags() & Display.FLAG_PRIVATE) == 0) && (allowOnDefaultDisplay || displayId != Display.DEFAULT_DISPLAY)) { displayIds[numberValidDisplays++] = displayId; } diff --git a/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCallbackConverter.java b/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCallbackConverter.java index 2e1a363bcc68..1a682a9ffefa 100644 --- a/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCallbackConverter.java +++ b/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCallbackConverter.java @@ -170,6 +170,12 @@ public class ClientMonitorCallbackConverter { } } + public void onUdfpsOverlayShown() throws RemoteException { + if (mFingerprintServiceReceiver != null) { + mFingerprintServiceReceiver.onUdfpsOverlayShown(); + } + } + // Face-specific callbacks for FaceManager only /** diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java index 50d375c56f4a..35fc43ae5f74 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java @@ -43,7 +43,6 @@ import com.android.server.biometrics.log.BiometricLogger; import com.android.server.biometrics.log.OperationContextExt; import com.android.server.biometrics.sensors.AuthSessionCoordinator; import com.android.server.biometrics.sensors.AuthenticationClient; -import com.android.server.biometrics.sensors.BiometricNotificationUtils; import com.android.server.biometrics.sensors.ClientMonitorCallback; import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter; import com.android.server.biometrics.sensors.ClientMonitorCompositeCallback; @@ -242,9 +241,6 @@ class FaceAuthenticationClient extends AuthenticationClient<AidlSession, FaceAut vendorCode, getTargetUserId())); - if (error == BiometricConstants.BIOMETRIC_ERROR_RE_ENROLL) { - BiometricNotificationUtils.showReEnrollmentNotification(getContext()); - } super.onError(error, vendorCode); } diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java index a50164718417..33ed63ce07e0 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java @@ -149,6 +149,17 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { @NonNull String halInstanceName, @NonNull LockoutResetDispatcher lockoutResetDispatcher, @NonNull BiometricContext biometricContext) { + this(context, biometricStateCallback, props, halInstanceName, lockoutResetDispatcher, + biometricContext, null /* daemon */); + } + + @VisibleForTesting FaceProvider(@NonNull Context context, + @NonNull BiometricStateCallback biometricStateCallback, + @NonNull SensorProps[] props, + @NonNull String halInstanceName, + @NonNull LockoutResetDispatcher lockoutResetDispatcher, + @NonNull BiometricContext biometricContext, + IFace daemon) { mContext = context; mBiometricStateCallback = biometricStateCallback; mHalInstanceName = halInstanceName; @@ -160,6 +171,7 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { mTaskStackListener = new BiometricTaskStackListener(); mBiometricContext = biometricContext; mAuthSessionCoordinator = mBiometricContext.getAuthSessionCoordinator(); + mDaemon = daemon; for (SensorProps prop : props) { final int sensorId = prop.commonProps.sensorId; diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java index ece35c522ec7..3d6a156d6022 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java @@ -929,17 +929,19 @@ public class FingerprintService extends SystemService { @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL) @Override - public void onUiReady(long requestId, int sensorId) { - super.onUiReady_enforcePermission(); + public void onUdfpsUiEvent(@FingerprintManager.UdfpsUiEvent int event, long requestId, + int sensorId) { + super.onUdfpsUiEvent_enforcePermission(); final ServiceProvider provider = mRegistry.getProviderForSensor(sensorId); if (provider == null) { - Slog.w(TAG, "No matching provider for onUiReady, sensorId: " + sensorId); + Slog.w(TAG, "No matching provider for onUdfpsUiEvent, sensorId: " + sensorId); return; } - provider.onUiReady(requestId, sensorId); + provider.onUdfpsUiEvent(event, requestId, sensorId); } + @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL) @Override public void setUdfpsOverlayController(@NonNull IUdfpsOverlayController controller) { diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java index 004af2c2ad62..26701c110c39 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java @@ -130,7 +130,7 @@ public interface ServiceProvider extends void onPointerUp(long requestId, int sensorId, PointerContext pc); - void onUiReady(long requestId, int sensorId); + void onUdfpsUiEvent(@FingerprintManager.UdfpsUiEvent int event, long requestId, int sensorId); void setUdfpsOverlayController(@NonNull IUdfpsOverlayController controller); diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/Udfps.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/Udfps.java index da7163a2a678..dce0175ca593 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/Udfps.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/Udfps.java @@ -17,6 +17,7 @@ package com.android.server.biometrics.sensors.fingerprint; import android.hardware.biometrics.fingerprint.PointerContext; +import android.hardware.fingerprint.FingerprintManager; import com.android.server.biometrics.sensors.BaseClientMonitor; @@ -28,6 +29,6 @@ import com.android.server.biometrics.sensors.BaseClientMonitor; public interface Udfps { void onPointerDown(PointerContext pc); void onPointerUp(PointerContext pc); - void onUiReady(); + void onUdfpsUiEvent(@FingerprintManager.UdfpsUiEvent int event); boolean isPointerDown(); } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java index 135eccf9026a..ec1eeb138505 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/BiometricTestSessionImpl.java @@ -114,6 +114,11 @@ class BiometricTestSessionImpl extends ITestSession.Stub { public void onUdfpsPointerUp(int sensorId) { } + + @Override + public void onUdfpsOverlayShown() { + + } }; BiometricTestSessionImpl(@NonNull Context context, int sensorId, diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java index 2bfc2391b948..3fc36b6df92d 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java @@ -27,6 +27,7 @@ import android.hardware.biometrics.BiometricManager.Authenticators; import android.hardware.biometrics.common.ICancellationSignal; import android.hardware.biometrics.fingerprint.PointerContext; import android.hardware.fingerprint.FingerprintAuthenticateOptions; +import android.hardware.fingerprint.FingerprintManager; import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; import android.hardware.fingerprint.ISidefpsController; import android.hardware.fingerprint.IUdfpsOverlay; @@ -363,9 +364,11 @@ class FingerprintAuthenticationClient } @Override - public void onUiReady() { + public void onUdfpsUiEvent(@FingerprintManager.UdfpsUiEvent int event) { try { - getFreshDaemon().getSession().onUiReady(); + if (event == FingerprintManager.UDFPS_UI_READY) { + getFreshDaemon().getSession().onUiReady(); + } } catch (RemoteException e) { Slog.e(TAG, "Remote exception", e); } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java index c2ca78e91fe3..d35469c4655c 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java @@ -265,11 +265,20 @@ class FingerprintEnrollClient extends EnrollClient<AidlSession> implements Udfps } @Override - public void onUiReady() { + public void onUdfpsUiEvent(@FingerprintManager.UdfpsUiEvent int event) { try { - getFreshDaemon().getSession().onUiReady(); + switch (event) { + case FingerprintManager.UDFPS_UI_OVERLAY_SHOWN: + getListener().onUdfpsOverlayShown(); + break; + case FingerprintManager.UDFPS_UI_READY: + getFreshDaemon().getSession().onUiReady(); + break; + default: + Slog.w(TAG, "No matching event for onUdfpsUiEvent"); + } } catch (RemoteException e) { - Slog.e(TAG, "Unable to send UI ready", e); + Slog.e(TAG, "Unable to send onUdfpsUiEvent", e); } } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java index 58ece898a9fe..e682fe71b53c 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java @@ -160,6 +160,17 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi @NonNull LockoutResetDispatcher lockoutResetDispatcher, @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher, @NonNull BiometricContext biometricContext) { + this(context, biometricStateCallback, props, halInstanceName, lockoutResetDispatcher, + gestureAvailabilityDispatcher, biometricContext, null /* daemon */); + } + + @VisibleForTesting FingerprintProvider(@NonNull Context context, + @NonNull BiometricStateCallback biometricStateCallback, + @NonNull SensorProps[] props, @NonNull String halInstanceName, + @NonNull LockoutResetDispatcher lockoutResetDispatcher, + @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher, + @NonNull BiometricContext biometricContext, + IFingerprint daemon) { mContext = context; mBiometricStateCallback = biometricStateCallback; mHalInstanceName = halInstanceName; @@ -170,6 +181,7 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi mTaskStackListener = new BiometricTaskStackListener(); mBiometricContext = biometricContext; mAuthSessionCoordinator = mBiometricContext.getAuthSessionCoordinator(); + mDaemon = daemon; final List<SensorLocationInternal> workaroundLocations = getWorkaroundSensorProps(context); @@ -669,14 +681,15 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi } @Override - public void onUiReady(long requestId, int sensorId) { + public void onUdfpsUiEvent(@FingerprintManager.UdfpsUiEvent int event, long requestId, + int sensorId) { mFingerprintSensors.get(sensorId).getScheduler().getCurrentClientIfMatches( requestId, (client) -> { if (!(client instanceof Udfps)) { - Slog.e(getTag(), "onUiReady received during client: " + client); + Slog.e(getTag(), "onUdfpsUiEvent received during client: " + client); return; } - ((Udfps) client).onUiReady(); + ((Udfps) client).onUdfpsUiEvent(event); }); } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/BiometricTestSessionImpl.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/BiometricTestSessionImpl.java index 86a9f7998398..c20a9eb958c4 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/BiometricTestSessionImpl.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/BiometricTestSessionImpl.java @@ -115,6 +115,11 @@ public class BiometricTestSessionImpl extends ITestSession.Stub { public void onUdfpsPointerUp(int sensorId) { } + + @Override + public void onUdfpsOverlayShown() { + + } }; BiometricTestSessionImpl(@NonNull Context context, int sensorId, diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java index 9e6f4e4f13f3..1cbbf89e052a 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java @@ -829,13 +829,14 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider } @Override - public void onUiReady(long requestId, int sensorId) { + public void onUdfpsUiEvent(@FingerprintManager.UdfpsUiEvent int event, long requestId, + int sensorId) { mScheduler.getCurrentClientIfMatches(requestId, (client) -> { if (!(client instanceof Udfps)) { - Slog.w(TAG, "onUiReady received during client: " + client); + Slog.w(TAG, "onUdfpsUiEvent received during client: " + client); return; } - ((Udfps) client).onUiReady(); + ((Udfps) client).onUdfpsUiEvent(event); }); } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java index d22aef8b3971..2a6233824c2e 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java @@ -27,6 +27,7 @@ import android.hardware.biometrics.BiometricManager.Authenticators; import android.hardware.biometrics.fingerprint.PointerContext; import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint; import android.hardware.fingerprint.FingerprintAuthenticateOptions; +import android.hardware.fingerprint.FingerprintManager; import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; import android.hardware.fingerprint.ISidefpsController; import android.hardware.fingerprint.IUdfpsOverlay; @@ -273,7 +274,7 @@ class FingerprintAuthenticationClient } @Override - public void onUiReady() { + public void onUdfpsUiEvent(@FingerprintManager.UdfpsUiEvent int event) { // Unsupported in HIDL. } } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java index 362c820b9e8d..ed0a2015a461 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java @@ -25,6 +25,7 @@ import android.hardware.biometrics.BiometricOverlayConstants; import android.hardware.biometrics.fingerprint.PointerContext; import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint; import android.hardware.fingerprint.FingerprintAuthenticateOptions; +import android.hardware.fingerprint.FingerprintManager; import android.hardware.fingerprint.IUdfpsOverlay; import android.hardware.fingerprint.IUdfpsOverlayController; import android.os.IBinder; @@ -130,7 +131,7 @@ class FingerprintDetectClient extends AcquisitionClient<IBiometricsFingerprint> } @Override - public void onUiReady() { + public void onUdfpsUiEvent(@FingerprintManager.UdfpsUiEvent int event) { // Unsupported in HIDL. } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java index 78039ef99986..c2b7944eac35 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java @@ -186,7 +186,7 @@ public class FingerprintEnrollClient extends EnrollClient<IBiometricsFingerprint } @Override - public void onUiReady() { + public void onUdfpsUiEvent(@FingerprintManager.UdfpsUiEvent int event) { // Unsupported in HIDL. } } diff --git a/services/core/java/com/android/server/cpu/CpuInfoReader.java b/services/core/java/com/android/server/cpu/CpuInfoReader.java index 70d7bde2e53a..984ad1dd7288 100644 --- a/services/core/java/com/android/server/cpu/CpuInfoReader.java +++ b/services/core/java/com/android/server/cpu/CpuInfoReader.java @@ -203,15 +203,15 @@ public final class CpuInfoReader { continue; } if (dynamicPolicyInfo.curCpuFreqKHz == CpuInfo.MISSING_FREQUENCY - || staticPolicyInfo.maxCpuFreqKHz == CpuInfo.MISSING_FREQUENCY) { + || dynamicPolicyInfo.maxCpuFreqKHz == CpuInfo.MISSING_FREQUENCY) { Slogf.w(TAG, "Current and maximum CPU frequency information mismatch/missing for" + " policy ID %d", policyId); continue; } - if (dynamicPolicyInfo.curCpuFreqKHz > staticPolicyInfo.maxCpuFreqKHz) { + if (dynamicPolicyInfo.curCpuFreqKHz > dynamicPolicyInfo.maxCpuFreqKHz) { Slogf.w(TAG, "Current CPU frequency (%d) is greater than maximum CPU frequency" + " (%d) for policy ID (%d). Skipping CPU frequency policy", - dynamicPolicyInfo.curCpuFreqKHz, staticPolicyInfo.maxCpuFreqKHz, policyId); + dynamicPolicyInfo.curCpuFreqKHz, dynamicPolicyInfo.maxCpuFreqKHz, policyId); continue; } for (int coreIdx = 0; coreIdx < staticPolicyInfo.relatedCpuCores.size(); coreIdx++) { @@ -234,7 +234,8 @@ public final class CpuInfoReader { if (dynamicPolicyInfo.affectedCpuCores.indexOf(relatedCpuCore) < 0) { cpuInfoByCpus.append(relatedCpuCore, new CpuInfo(relatedCpuCore, cpusetCategories, /* isOnline= */false, CpuInfo.MISSING_FREQUENCY, - staticPolicyInfo.maxCpuFreqKHz, CpuInfo.MISSING_FREQUENCY, usageStats)); + dynamicPolicyInfo.maxCpuFreqKHz, CpuInfo.MISSING_FREQUENCY, + usageStats)); continue; } // If a CPU core is online, it must have the usage stats. When the usage stats is @@ -245,7 +246,7 @@ public final class CpuInfoReader { continue; } CpuInfo cpuInfo = new CpuInfo(relatedCpuCore, cpusetCategories, /* isOnline= */true, - dynamicPolicyInfo.curCpuFreqKHz, staticPolicyInfo.maxCpuFreqKHz, + dynamicPolicyInfo.curCpuFreqKHz, dynamicPolicyInfo.maxCpuFreqKHz, dynamicPolicyInfo.avgTimeInStateCpuFreqKHz, usageStats); cpuInfoByCpus.append(relatedCpuCore, cpuInfo); if (DEBUG) { @@ -423,12 +424,6 @@ public final class CpuInfoReader { for (int i = 0; i < mCpuFreqPolicyDirsById.size(); i++) { int policyId = mCpuFreqPolicyDirsById.keyAt(i); File policyDir = mCpuFreqPolicyDirsById.valueAt(i); - long maxCpuFreqKHz = readCpuFreqKHz(new File(policyDir, MAX_SCALING_FREQ_FILE)); - if (maxCpuFreqKHz == CpuInfo.MISSING_FREQUENCY) { - Slogf.w(TAG, "Missing max CPU frequency information at %s", - policyDir.getAbsolutePath()); - continue; - } File cpuCoresFile = new File(policyDir, RELATED_CPUS_FILE); IntArray relatedCpuCores = readCpuCores(cpuCoresFile); if (relatedCpuCores == null || relatedCpuCores.size() == 0) { @@ -436,8 +431,7 @@ public final class CpuInfoReader { cpuCoresFile.getAbsolutePath()); continue; } - StaticPolicyInfo staticPolicyInfo = new StaticPolicyInfo(maxCpuFreqKHz, - relatedCpuCores); + StaticPolicyInfo staticPolicyInfo = new StaticPolicyInfo(relatedCpuCores); mStaticPolicyInfoById.append(policyId, staticPolicyInfo); if (DEBUG) { Slogf.d(TAG, "Added static policy info %s for policy id %d", staticPolicyInfo, @@ -464,8 +458,14 @@ public final class CpuInfoReader { Slogf.e(TAG, "Failed to read CPU cores from %s", cpuCoresFile.getAbsolutePath()); continue; } + long maxCpuFreqKHz = readCpuFreqKHz(new File(policyDir, MAX_SCALING_FREQ_FILE)); + if (maxCpuFreqKHz == CpuInfo.MISSING_FREQUENCY) { + Slogf.w(TAG, "Missing max CPU frequency information at %s", + policyDir.getAbsolutePath()); + continue; + } DynamicPolicyInfo dynamicPolicyInfo = new DynamicPolicyInfo(curCpuFreqKHz, - avgTimeInStateCpuFreqKHz, affectedCpuCores); + maxCpuFreqKHz, avgTimeInStateCpuFreqKHz, affectedCpuCores); dynamicPolicyInfoById.append(policyId, dynamicPolicyInfo); if (DEBUG) { Slogf.d(TAG, "Read dynamic policy info %s for policy id %d", dynamicPolicyInfo, @@ -593,9 +593,12 @@ public final class CpuInfoReader { List<String> lines = Files.readAllLines(file.toPath()); IntArray cpuCores = new IntArray(0); for (int i = 0; i < lines.size(); i++) { - String line = lines.get(i); - String[] pairs = line.contains(",") ? line.trim().split(",") - : line.trim().split(" "); + String line = lines.get(i).trim(); + if (line.isEmpty()) { + continue; + } + String[] pairs = line.contains(",") ? line.split(",") + : line.split(" "); for (int j = 0; j < pairs.length; j++) { String[] minMaxPairs = pairs[j].split("-"); if (minMaxPairs.length >= 2) { @@ -615,6 +618,9 @@ public final class CpuInfoReader { } } return cpuCores; + } catch (NumberFormatException e) { + Slogf.e(TAG, e, "Failed to read CPU cores from %s due to incorrect file format", + file.getAbsolutePath()); } catch (Exception e) { Slogf.e(TAG, e, "Failed to read CPU cores from %s", file.getAbsolutePath()); } @@ -889,29 +895,28 @@ public final class CpuInfoReader { } private static final class StaticPolicyInfo { - public final long maxCpuFreqKHz; public final IntArray relatedCpuCores; - StaticPolicyInfo(long maxCpuFreqKHz, IntArray relatedCpuCores) { - this.maxCpuFreqKHz = maxCpuFreqKHz; + StaticPolicyInfo(IntArray relatedCpuCores) { this.relatedCpuCores = relatedCpuCores; } @Override public String toString() { - return "StaticPolicyInfo{maxCpuFreqKHz = " + maxCpuFreqKHz + ", relatedCpuCores = " - + relatedCpuCores + '}'; + return "StaticPolicyInfo{relatedCpuCores = " + relatedCpuCores + '}'; } } private static final class DynamicPolicyInfo { public final long curCpuFreqKHz; + public final long maxCpuFreqKHz; public final long avgTimeInStateCpuFreqKHz; public final IntArray affectedCpuCores; - DynamicPolicyInfo(long curCpuFreqKHz, long avgTimeInStateCpuFreqKHz, + DynamicPolicyInfo(long curCpuFreqKHz, long maxCpuFreqKHz, long avgTimeInStateCpuFreqKHz, IntArray affectedCpuCores) { this.curCpuFreqKHz = curCpuFreqKHz; + this.maxCpuFreqKHz = maxCpuFreqKHz; this.avgTimeInStateCpuFreqKHz = avgTimeInStateCpuFreqKHz; this.affectedCpuCores = affectedCpuCores; } @@ -919,6 +924,7 @@ public final class CpuInfoReader { @Override public String toString() { return "DynamicPolicyInfo{curCpuFreqKHz = " + curCpuFreqKHz + + ", maxCpuFreqKHz = " + maxCpuFreqKHz + ", avgTimeInStateCpuFreqKHz = " + avgTimeInStateCpuFreqKHz + ", affectedCpuCores = " + affectedCpuCores + '}'; } diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java index 75709fbb365a..d647757442e0 100644 --- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java +++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java @@ -223,11 +223,11 @@ public class AutomaticBrightnessController { private final ShortTermModel mShortTermModel; private final ShortTermModel mPausedShortTermModel; - // Controls High Brightness Mode. - private HighBrightnessModeController mHbmController; + // Controls Brightness range (including High Brightness Mode). + private final BrightnessRangeController mBrightnessRangeController; // Throttles (caps) maximum allowed brightness - private BrightnessThrottler mBrightnessThrottler; + private final BrightnessThrottler mBrightnessThrottler; private boolean mIsBrightnessThrottled; // Context-sensitive brightness configurations require keeping track of the foreground app's @@ -257,7 +257,8 @@ public class AutomaticBrightnessController { HysteresisLevels screenBrightnessThresholds, HysteresisLevels ambientBrightnessThresholdsIdle, HysteresisLevels screenBrightnessThresholdsIdle, Context context, - HighBrightnessModeController hbmController, BrightnessThrottler brightnessThrottler, + BrightnessRangeController brightnessModeController, + BrightnessThrottler brightnessThrottler, BrightnessMappingStrategy idleModeBrightnessMapper, int ambientLightHorizonShort, int ambientLightHorizonLong, float userLux, float userBrightness) { this(new Injector(), callbacks, looper, sensorManager, lightSensor, @@ -267,7 +268,7 @@ public class AutomaticBrightnessController { darkeningLightDebounceConfig, resetAmbientLuxAfterWarmUpConfig, ambientBrightnessThresholds, screenBrightnessThresholds, ambientBrightnessThresholdsIdle, screenBrightnessThresholdsIdle, context, - hbmController, brightnessThrottler, idleModeBrightnessMapper, + brightnessModeController, brightnessThrottler, idleModeBrightnessMapper, ambientLightHorizonShort, ambientLightHorizonLong, userLux, userBrightness ); } @@ -283,7 +284,8 @@ public class AutomaticBrightnessController { HysteresisLevels screenBrightnessThresholds, HysteresisLevels ambientBrightnessThresholdsIdle, HysteresisLevels screenBrightnessThresholdsIdle, Context context, - HighBrightnessModeController hbmController, BrightnessThrottler brightnessThrottler, + BrightnessRangeController brightnessModeController, + BrightnessThrottler brightnessThrottler, BrightnessMappingStrategy idleModeBrightnessMapper, int ambientLightHorizonShort, int ambientLightHorizonLong, float userLux, float userBrightness) { mInjector = injector; @@ -326,7 +328,7 @@ public class AutomaticBrightnessController { mPendingForegroundAppPackageName = null; mForegroundAppCategory = ApplicationInfo.CATEGORY_UNDEFINED; mPendingForegroundAppCategory = ApplicationInfo.CATEGORY_UNDEFINED; - mHbmController = hbmController; + mBrightnessRangeController = brightnessModeController; mBrightnessThrottler = brightnessThrottler; mInteractiveModeBrightnessMapper = interactiveModeBrightnessMapper; mIdleModeBrightnessMapper = idleModeBrightnessMapper; @@ -607,10 +609,11 @@ public class AutomaticBrightnessController { pw.println(); pw.println(" mInteractiveMapper="); - mInteractiveModeBrightnessMapper.dump(pw, mHbmController.getNormalBrightnessMax()); + mInteractiveModeBrightnessMapper.dump(pw, + mBrightnessRangeController.getNormalBrightnessMax()); if (mIdleModeBrightnessMapper != null) { pw.println(" mIdleMapper="); - mIdleModeBrightnessMapper.dump(pw, mHbmController.getNormalBrightnessMax()); + mIdleModeBrightnessMapper.dump(pw, mBrightnessRangeController.getNormalBrightnessMax()); } pw.println(); @@ -736,7 +739,7 @@ public class AutomaticBrightnessController { mAmbientDarkeningThreshold = mAmbientBrightnessThresholds.getDarkeningThreshold(lux); } - mHbmController.onAmbientLuxChange(mAmbientLux); + mBrightnessRangeController.onAmbientLuxChange(mAmbientLux); // If the short term model was invalidated and the change is drastic enough, reset it. @@ -976,9 +979,9 @@ public class AutomaticBrightnessController { // Clamps values with float range [0.0-1.0] private float clampScreenBrightness(float value) { - final float minBrightness = Math.min(mHbmController.getCurrentBrightnessMin(), + final float minBrightness = Math.min(mBrightnessRangeController.getCurrentBrightnessMin(), mBrightnessThrottler.getBrightnessCap()); - final float maxBrightness = Math.min(mHbmController.getCurrentBrightnessMax(), + final float maxBrightness = Math.min(mBrightnessRangeController.getCurrentBrightnessMax(), mBrightnessThrottler.getBrightnessCap()); return MathUtils.constrain(value, minBrightness, maxBrightness); } diff --git a/services/core/java/com/android/server/display/BrightnessRangeController.java b/services/core/java/com/android/server/display/BrightnessRangeController.java new file mode 100644 index 000000000000..47cde1517450 --- /dev/null +++ b/services/core/java/com/android/server/display/BrightnessRangeController.java @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.display; + +import android.hardware.display.BrightnessInfo; +import android.os.IBinder; + +import java.io.PrintWriter; +import java.util.function.BooleanSupplier; + +class BrightnessRangeController { + + private static final boolean NBM_FEATURE_FLAG = false; + + private final HighBrightnessModeController mHbmController; + private final NormalBrightnessModeController mNormalBrightnessModeController = + new NormalBrightnessModeController(); + + private final Runnable mModeChangeCallback; + + BrightnessRangeController(HighBrightnessModeController hbmController, + Runnable modeChangeCallback) { + mHbmController = hbmController; + mModeChangeCallback = modeChangeCallback; + } + + + void dump(PrintWriter pw) { + mHbmController.dump(pw); + } + + void onAmbientLuxChange(float ambientLux) { + applyChanges( + () -> mNormalBrightnessModeController.onAmbientLuxChange(ambientLux), + () -> mHbmController.onAmbientLuxChange(ambientLux) + ); + } + + float getNormalBrightnessMax() { + return mHbmController.getNormalBrightnessMax(); + } + + void loadFromConfig(HighBrightnessModeMetadata hbmMetadata, IBinder token, + DisplayDeviceInfo info, DisplayDeviceConfig displayDeviceConfig) { + applyChanges( + () -> mNormalBrightnessModeController.resetNbmData( + displayDeviceConfig.getLuxThrottlingData()), + () -> { + mHbmController.setHighBrightnessModeMetadata(hbmMetadata); + mHbmController.resetHbmData(info.width, info.height, token, info.uniqueId, + displayDeviceConfig.getHighBrightnessModeData(), + displayDeviceConfig::getHdrBrightnessFromSdr); + } + ); + } + + void stop() { + mHbmController.stop(); + } + + void setAutoBrightnessEnabled(int state) { + applyChanges( + () -> mNormalBrightnessModeController.setAutoBrightnessState(state), + () -> mHbmController.setAutoBrightnessEnabled(state) + ); + } + + void onBrightnessChanged(float brightness, float unthrottledBrightness, + @BrightnessInfo.BrightnessMaxReason int throttlingReason) { + mHbmController.onBrightnessChanged(brightness, unthrottledBrightness, throttlingReason); + } + + float getCurrentBrightnessMin() { + return mHbmController.getCurrentBrightnessMin(); + } + + + float getCurrentBrightnessMax() { + if (NBM_FEATURE_FLAG && mHbmController.getHighBrightnessMode() + == BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF) { + return Math.min(mHbmController.getCurrentBrightnessMax(), + mNormalBrightnessModeController.getCurrentBrightnessMax()); + } + return mHbmController.getCurrentBrightnessMax(); + } + + int getHighBrightnessMode() { + return mHbmController.getHighBrightnessMode(); + } + + float getHdrBrightnessValue() { + return mHbmController.getHdrBrightnessValue(); + } + + float getTransitionPoint() { + return mHbmController.getTransitionPoint(); + } + + private void applyChanges(BooleanSupplier nbmChangesFunc, Runnable hbmChangesFunc) { + if (NBM_FEATURE_FLAG) { + boolean nbmTransitionChanged = nbmChangesFunc.getAsBoolean(); + hbmChangesFunc.run(); + // if nbm transition changed - trigger callback + // HighBrightnessModeController handles sending changes itself + if (nbmTransitionChanged) { + mModeChangeCallback.run(); + } + } else { + hbmChangesFunc.run(); + } + } +} diff --git a/services/core/java/com/android/server/display/BrightnessSetting.java b/services/core/java/com/android/server/display/BrightnessSetting.java index de42370e6d84..651828b6b9e2 100644 --- a/services/core/java/com/android/server/display/BrightnessSetting.java +++ b/services/core/java/com/android/server/display/BrightnessSetting.java @@ -40,6 +40,7 @@ public class BrightnessSetting { private final LogicalDisplay mLogicalDisplay; + private int mUserSerial; private final Handler mHandler = new Handler(Looper.getMainLooper()) { @Override public void handleMessage(Message msg) { @@ -56,13 +57,15 @@ public class BrightnessSetting { @GuardedBy("mSyncRoot") private float mBrightness; - BrightnessSetting(@NonNull PersistentDataStore persistentDataStore, + BrightnessSetting(int userSerial, + @NonNull PersistentDataStore persistentDataStore, @NonNull LogicalDisplay logicalDisplay, DisplayManagerService.SyncRoot syncRoot) { mPersistentDataStore = persistentDataStore; mLogicalDisplay = logicalDisplay; + mUserSerial = userSerial; mBrightness = mPersistentDataStore.getBrightness( - mLogicalDisplay.getPrimaryDisplayDeviceLocked()); + mLogicalDisplay.getPrimaryDisplayDeviceLocked(), userSerial); mSyncRoot = syncRoot; } @@ -96,8 +99,13 @@ public class BrightnessSetting { mListeners.remove(l); } + /** Sets the user serial for the brightness setting */ + public void setUserSerial(int userSerial) { + mUserSerial = userSerial; + } + /** - * Sets the brigtness and broadcasts the change to the listeners. + * Sets the brightness and broadcasts the change to the listeners. * @param brightness The value to which the brightness is to be set. */ public void setBrightness(float brightness) { @@ -112,7 +120,8 @@ public class BrightnessSetting { // changed. if (brightness != mBrightness) { mPersistentDataStore.setBrightness(mLogicalDisplay.getPrimaryDisplayDeviceLocked(), - brightness); + brightness, mUserSerial + ); } mBrightness = brightness; int toSend = Float.floatToIntBits(mBrightness); diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java index 7a797dd2250c..7ccfb448cf61 100644 --- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java +++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java @@ -41,6 +41,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.display.BrightnessSynchronizer; import com.android.server.display.config.AutoBrightness; import com.android.server.display.config.BlockingZoneConfig; +import com.android.server.display.config.BrightnessLimitMap; import com.android.server.display.config.BrightnessThresholds; import com.android.server.display.config.BrightnessThrottlingMap; import com.android.server.display.config.BrightnessThrottlingPoint; @@ -51,8 +52,11 @@ import com.android.server.display.config.DisplayQuirks; import com.android.server.display.config.HbmTiming; import com.android.server.display.config.HighBrightnessMode; import com.android.server.display.config.IntegerArray; +import com.android.server.display.config.LuxThrottling; import com.android.server.display.config.NitsMap; +import com.android.server.display.config.NonNegativeFloatToFloatPoint; import com.android.server.display.config.Point; +import com.android.server.display.config.PredefinedBrightnessLimitNames; import com.android.server.display.config.RefreshRateConfigs; import com.android.server.display.config.RefreshRateRange; import com.android.server.display.config.RefreshRateThrottlingMap; @@ -219,6 +223,22 @@ import javax.xml.datatype.DatatypeConfigurationException; * <allowInLowPowerMode>false</allowInLowPowerMode> * </highBrightnessMode> * + * <luxThrottling> + * <brightnessLimitMap> + * <type>default</type> + * <map> + * <point> + * <first>5000</first> + * <second>0.3</second> + * </point> + * <point> + * <first>5000</first> + * <second>0.3</second> + * </point> + * </map> + * </brightnessPeakMap> + * </luxThrottling> + * * <quirks> * <quirk>canSetBrightnessViaHwc</quirk> * </quirks> @@ -693,6 +713,9 @@ public class DisplayDeviceConfig { private final Map<String, SparseArray<SurfaceControl.RefreshRateRange>> mRefreshRateThrottlingMap = new HashMap<>(); + private final Map<BrightnessLimitMapType, Map<Float, Float>> + mLuxThrottlingData = new HashMap<>(); + @Nullable private HostUsiVersion mHostUsiVersion; @@ -1344,6 +1367,11 @@ public class DisplayDeviceConfig { return hbmData; } + @NonNull + public Map<BrightnessLimitMapType, Map<Float, Float>> getLuxThrottlingData() { + return mLuxThrottlingData; + } + public List<RefreshRateLimitation> getRefreshRateLimitations() { return mRefreshRateLimitations; } @@ -1530,6 +1558,7 @@ public class DisplayDeviceConfig { + ", mBrightnessDefault=" + mBrightnessDefault + ", mQuirks=" + mQuirks + ", isHbmEnabled=" + mIsHighBrightnessModeEnabled + + ", mLuxThrottlingData=" + mLuxThrottlingData + ", mHbmData=" + mHbmData + ", mSdrToHdrRatioSpline=" + mSdrToHdrRatioSpline + ", mThermalBrightnessThrottlingDataMapByThrottlingId=" @@ -1676,6 +1705,7 @@ public class DisplayDeviceConfig { loadBrightnessMap(config); loadThermalThrottlingConfig(config); loadHighBrightnessModeData(config); + loadLuxThrottling(config); loadQuirks(config); loadBrightnessRamps(config); loadAmbientLightSensorFromDdc(config); @@ -2428,6 +2458,54 @@ public class DisplayDeviceConfig { } } + private void loadLuxThrottling(DisplayConfiguration config) { + LuxThrottling cfg = config.getLuxThrottling(); + if (cfg != null) { + HighBrightnessMode hbm = config.getHighBrightnessMode(); + float hbmTransitionPoint = hbm != null ? hbm.getTransitionPoint_all().floatValue() + : PowerManager.BRIGHTNESS_MAX; + List<BrightnessLimitMap> limitMaps = cfg.getBrightnessLimitMap(); + for (BrightnessLimitMap map : limitMaps) { + PredefinedBrightnessLimitNames type = map.getType(); + BrightnessLimitMapType mappedType = BrightnessLimitMapType.convert(type); + if (mappedType == null) { + Slog.wtf(TAG, "Invalid NBM config: unsupported map type=" + type); + continue; + } + if (mLuxThrottlingData.containsKey(mappedType)) { + Slog.wtf(TAG, "Invalid NBM config: duplicate map type=" + mappedType); + continue; + } + Map<Float, Float> luxToTransitionPointMap = new HashMap<>(); + + List<NonNegativeFloatToFloatPoint> points = map.getMap().getPoint(); + for (NonNegativeFloatToFloatPoint point : points) { + float lux = point.getFirst().floatValue(); + float maxBrightness = point.getSecond().floatValue(); + if (maxBrightness > hbmTransitionPoint) { + Slog.wtf(TAG, + "Invalid NBM config: maxBrightness is greater than hbm" + + ".transitionPoint. type=" + + type + "; lux=" + lux + "; maxBrightness=" + + maxBrightness); + continue; + } + if (luxToTransitionPointMap.containsKey(lux)) { + Slog.wtf(TAG, + "Invalid NBM config: duplicate lux key. type=" + type + "; lux=" + + lux); + continue; + } + luxToTransitionPointMap.put(lux, + mBacklightToBrightnessSpline.interpolate(maxBrightness)); + } + if (!luxToTransitionPointMap.isEmpty()) { + mLuxThrottlingData.put(mappedType, luxToTransitionPointMap); + } + } + } + } + private void loadBrightnessRamps(DisplayConfiguration config) { // Priority 1: Value in the display device config (float) // Priority 2: Value in the config.xml (int) @@ -3155,4 +3233,19 @@ public class DisplayDeviceConfig { } } } + + public enum BrightnessLimitMapType { + DEFAULT, ADAPTIVE; + + @Nullable + private static BrightnessLimitMapType convert(PredefinedBrightnessLimitNames type) { + switch (type) { + case _default: + return DEFAULT; + case adaptive: + return ADAPTIVE; + } + return null; + } + } } diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index da8eb23cbeae..3832e6e372c5 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -653,6 +653,12 @@ public final class DisplayManagerService extends SystemService { logicalDisplay.getPrimaryDisplayDeviceLocked().getUniqueId(), userSerial); dpc.setBrightnessConfiguration(config, /* shouldResetShortTermModel= */ true); + // change the brightness value according to the selected user. + final DisplayDevice device = logicalDisplay.getPrimaryDisplayDeviceLocked(); + if (device != null) { + dpc.setBrightness( + mPersistentDataStore.getBrightness(device, userSerial), userSerial); + } } dpc.onSwitchUser(newUserId); }); @@ -3135,8 +3141,9 @@ public final class DisplayManagerService extends SystemService { mBrightnessTracker = new BrightnessTracker(mContext, null); } - final BrightnessSetting brightnessSetting = new BrightnessSetting(mPersistentDataStore, - display, mSyncRoot); + final int userSerial = getUserManager().getUserSerialNumber(mContext.getUserId()); + final BrightnessSetting brightnessSetting = new BrightnessSetting(userSerial, + mPersistentDataStore, display, mSyncRoot); final DisplayPowerControllerInterface displayPowerController; // If display is internal and has a HighBrightnessModeMetadata mapping, use that. @@ -4621,6 +4628,22 @@ public final class DisplayManagerService extends SystemService { } @Override + public AmbientLightSensorData getAmbientLightSensorData(int displayId) { + synchronized (mSyncRoot) { + final LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(displayId); + if (display == null) { + return null; + } + final DisplayDevice device = display.getPrimaryDisplayDeviceLocked(); + if (device == null) { + return null; + } + SensorData data = device.getDisplayDeviceConfig().getAmbientLightSensor(); + return new AmbientLightSensorData(data.name, data.type); + } + } + + @Override public IntArray getDisplayGroupIds() { Set<Integer> visitedIds = new ArraySet<>(); IntArray displayGroupIds = new IntArray(); diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java index 9d31572c7d76..3fe1d9c14b01 100644 --- a/services/core/java/com/android/server/display/DisplayPowerController.java +++ b/services/core/java/com/android/server/display/DisplayPowerController.java @@ -445,7 +445,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call private final ColorDisplayServiceInternal mCdsi; private float[] mNitsRange; - private final HighBrightnessModeController mHbmController; + private final BrightnessRangeController mBrightnessRangeController; private final HighBrightnessModeMetadata mHighBrightnessModeMetadata; private final BrightnessThrottler mBrightnessThrottler; @@ -512,6 +512,9 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call // of the lead display that this DPC should follow. private float mBrightnessToFollow; + // Indicates whether we should ramp slowly to the brightness value to follow. + private boolean mBrightnessToFollowSlowChange; + // The last auto brightness adjustment that was set by the user and not temporary. Set to // Float.NaN when an auto-brightness adjustment hasn't been recorded yet. private float mAutoBrightnessAdjustment; @@ -654,8 +657,19 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call loadBrightnessRampRates(); mSkipScreenOnBrightnessRamp = resources.getBoolean( com.android.internal.R.bool.config_skipScreenOnBrightnessRamp); + Runnable modeChangeCallback = () -> { + sendUpdatePowerState(); + postBrightnessChangeRunnable(); + // TODO(b/192258832): Switch the HBMChangeCallback to a listener pattern. + if (mAutomaticBrightnessController != null) { + mAutomaticBrightnessController.update(); + } + }; + + HighBrightnessModeController hbmController = createHbmControllerLocked(modeChangeCallback); - mHbmController = createHbmControllerLocked(); + mBrightnessRangeController = new BrightnessRangeController(hbmController, + modeChangeCallback); mBrightnessThrottler = createBrightnessThrottlerLocked(); @@ -801,8 +815,9 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call } @Override - public void setBrightnessToFollow(float leadDisplayBrightness, float nits, float ambientLux) { - mHbmController.onAmbientLuxChange(ambientLux); + public void setBrightnessToFollow(float leadDisplayBrightness, float nits, float ambientLux, + boolean slowChange) { + mBrightnessRangeController.onAmbientLuxChange(ambientLux); if (nits < 0) { mBrightnessToFollow = leadDisplayBrightness; } else { @@ -814,6 +829,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mBrightnessToFollow = leadDisplayBrightness; } } + mBrightnessToFollowSlowChange = slowChange; sendUpdatePowerState(); } @@ -831,7 +847,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mDisplayBrightnessFollowers.remove(follower.getDisplayId()); mHandler.postAtTime(() -> follower.setBrightnessToFollow( PowerManager.BRIGHTNESS_INVALID_FLOAT, /* nits= */ -1, - /* ambientLux= */ 0), mClock.uptimeMillis()); + /* ambientLux= */ 0, /* slowChange= */ false), mClock.uptimeMillis()); } } @@ -841,7 +857,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call DisplayPowerControllerInterface follower = mDisplayBrightnessFollowers.valueAt(i); mHandler.postAtTime(() -> follower.setBrightnessToFollow( PowerManager.BRIGHTNESS_INVALID_FLOAT, /* nits= */ -1, - /* ambientLux= */ 0), mClock.uptimeMillis()); + /* ambientLux= */ 0, /* slowChange= */ false), mClock.uptimeMillis()); } mDisplayBrightnessFollowers.clear(); } @@ -1039,17 +1055,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mBrightnessRampIncreaseMaxTimeMillis, mBrightnessRampDecreaseMaxTimeMillis); } - mHbmController.setHighBrightnessModeMetadata(hbmMetadata); - mHbmController.resetHbmData(info.width, info.height, token, info.uniqueId, - mDisplayDeviceConfig.getHighBrightnessModeData(), - new HighBrightnessModeController.HdrBrightnessDeviceConfig() { - @Override - public float getHdrBrightnessFromSdr( - float sdrBrightness, float maxDesiredHdrSdrRatio) { - return mDisplayDeviceConfig.getHdrBrightnessFromSdr( - sdrBrightness, maxDesiredHdrSdrRatio); - } - }); + mBrightnessRangeController.loadFromConfig(hbmMetadata, token, info, mDisplayDeviceConfig); mBrightnessThrottler.loadThermalBrightnessThrottlingDataFromDisplayDeviceConfig( mDisplayDeviceConfig.getThermalBrightnessThrottlingDataMapByThrottlingId(), mThermalBrightnessThrottlingDataId, mUniqueDisplayId); @@ -1264,7 +1270,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call darkeningLightDebounce, autoBrightnessResetAmbientLuxAfterWarmUp, ambientBrightnessThresholds, screenBrightnessThresholds, ambientBrightnessThresholdsIdle, screenBrightnessThresholdsIdle, mContext, - mHbmController, mBrightnessThrottler, mIdleModeBrightnessMapper, + mBrightnessRangeController, mBrightnessThrottler, mIdleModeBrightnessMapper, mDisplayDeviceConfig.getAmbientHorizonShort(), mDisplayDeviceConfig.getAmbientHorizonLong(), userLux, userBrightness); @@ -1364,7 +1370,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call /** Clean up all resources that are accessed via the {@link #mHandler} thread. */ private void cleanupHandlerThreadAfterStop() { setProximitySensorEnabled(false); - mHbmController.stop(); + mBrightnessRangeController.stop(); mBrightnessThrottler.stop(); mHandler.removeCallbacksAndMessages(null); @@ -1558,6 +1564,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call final int oldState = mPowerState.getScreenState(); animateScreenStateChange(state, performScreenOffTransition); state = mPowerState.getScreenState(); + boolean slowChange = false; if (state == Display.STATE_OFF) { brightnessState = PowerManager.BRIGHTNESS_OFF_FLOAT; @@ -1566,6 +1573,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call if (Float.isNaN(brightnessState) && isValidBrightnessValue(mBrightnessToFollow)) { brightnessState = mBrightnessToFollow; + slowChange = mBrightnessToFollowSlowChange; mBrightnessReasonTemp.setReason(BrightnessReason.REASON_FOLLOWER); } @@ -1582,12 +1590,12 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mAllowAutoBrightnessWhileDozingConfig && Display.isDozeState(state); final boolean autoBrightnessEnabled = mUseAutoBrightness && (state == Display.STATE_ON || autoBrightnessEnabledInDoze) - && Float.isNaN(brightnessState) - && mAutomaticBrightnessController != null - && mBrightnessReasonTemp.getReason() != BrightnessReason.REASON_FOLLOWER; + && mBrightnessReasonTemp.getReason() != BrightnessReason.REASON_OVERRIDE + && mAutomaticBrightnessController != null; final boolean autoBrightnessDisabledDueToDisplayOff = mUseAutoBrightness && !(state == Display.STATE_ON || autoBrightnessEnabledInDoze); final int autoBrightnessState = autoBrightnessEnabled + && mBrightnessReasonTemp.getReason() != BrightnessReason.REASON_FOLLOWER ? AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED : autoBrightnessDisabledDueToDisplayOff ? AutomaticBrightnessController.AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE @@ -1647,9 +1655,11 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mShouldResetShortTermModel); mShouldResetShortTermModel = false; } - mHbmController.setAutoBrightnessEnabled(mUseAutoBrightness + mBrightnessRangeController.setAutoBrightnessEnabled(autoBrightnessEnabled ? AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED - : AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED); + : autoBrightnessDisabledDueToDisplayOff + ? AutomaticBrightnessController.AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE + : AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED); if (mBrightnessTracker != null) { mBrightnessTracker.setShouldCollectColorSample(mBrightnessConfiguration != null @@ -1660,7 +1670,6 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call float rawBrightnessState = brightnessState; // Apply auto-brightness. - boolean slowChange = false; if (Float.isNaN(brightnessState)) { float newAutoBrightnessAdjustment = autoBrightnessAdjustment; if (autoBrightnessEnabled) { @@ -1739,6 +1748,14 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mBrightnessReasonTemp.setReason(BrightnessReason.REASON_MANUAL); } + float ambientLux = mAutomaticBrightnessController == null ? 0 + : mAutomaticBrightnessController.getAmbientLux(); + for (int i = 0; i < displayBrightnessFollowers.size(); i++) { + DisplayPowerControllerInterface follower = displayBrightnessFollowers.valueAt(i); + follower.setBrightnessToFollow(rawBrightnessState, convertToNits(rawBrightnessState), + ambientLux, slowChange); + } + // Now that a desired brightness has been calculated, apply brightness throttling. The // dimming and low power transformations that follow can only dim brightness further. // @@ -1761,14 +1778,6 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mAppliedThrottling = false; } - float ambientLux = mAutomaticBrightnessController == null ? 0 - : mAutomaticBrightnessController.getAmbientLux(); - for (int i = 0; i < displayBrightnessFollowers.size(); i++) { - DisplayPowerControllerInterface follower = displayBrightnessFollowers.valueAt(i); - follower.setBrightnessToFollow(rawBrightnessState, convertToNits(rawBrightnessState), - ambientLux); - } - if (updateScreenBrightnessSetting) { // Tell the rest of the system about the new brightness in case we had to change it // for things like auto-brightness or high-brightness-mode. Note that we do this @@ -1820,7 +1829,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call // here instead of having HbmController listen to the brightness setting because certain // brightness sources (such as an app override) are not saved to the setting, but should be // reflected in HBM calculations. - mHbmController.onBrightnessChanged(brightnessState, unthrottledBrightnessState, + mBrightnessRangeController.onBrightnessChanged(brightnessState, unthrottledBrightnessState, mBrightnessThrottler.getBrightnessMaxReason()); // Animate the screen brightness when the screen is on or dozing. @@ -1874,13 +1883,14 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call float sdrAnimateValue = animateValue; // TODO(b/216365040): The decision to prevent HBM for HDR in low power mode should be // done in HighBrightnessModeController. - if (mHbmController.getHighBrightnessMode() == BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR + if (mBrightnessRangeController.getHighBrightnessMode() + == BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR && (mBrightnessReasonTemp.getModifier() & BrightnessReason.MODIFIER_DIMMED) == 0 && (mBrightnessReasonTemp.getModifier() & BrightnessReason.MODIFIER_LOW_POWER) == 0) { // We want to scale HDR brightness level with the SDR level, we also need to restore // SDR brightness immediately when entering dim or low power mode. - animateValue = mHbmController.getHdrBrightnessValue(); + animateValue = mBrightnessRangeController.getHdrBrightnessValue(); } final float currentBrightness = mPowerState.getScreenBrightness(); @@ -1942,8 +1952,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mTempBrightnessEvent.setBrightness(brightnessState); mTempBrightnessEvent.setPhysicalDisplayId(mUniqueDisplayId); mTempBrightnessEvent.setReason(mBrightnessReason); - mTempBrightnessEvent.setHbmMax(mHbmController.getCurrentBrightnessMax()); - mTempBrightnessEvent.setHbmMode(mHbmController.getHighBrightnessMode()); + mTempBrightnessEvent.setHbmMax(mBrightnessRangeController.getCurrentBrightnessMax()); + mTempBrightnessEvent.setHbmMode(mBrightnessRangeController.getHighBrightnessMode()); mTempBrightnessEvent.setFlags(mTempBrightnessEvent.getFlags() | (mIsRbcActive ? BrightnessEvent.FLAG_RBC : 0) | (mPowerRequest.lowPowerMode ? BrightnessEvent.FLAG_LOW_POWER_MODE : 0)); @@ -2104,9 +2114,11 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call private boolean saveBrightnessInfo(float brightness, float adjustedBrightness) { synchronized (mCachedBrightnessInfo) { - final float minBrightness = Math.min(mHbmController.getCurrentBrightnessMin(), + final float minBrightness = Math.min( + mBrightnessRangeController.getCurrentBrightnessMin(), mBrightnessThrottler.getBrightnessCap()); - final float maxBrightness = Math.min(mHbmController.getCurrentBrightnessMax(), + final float maxBrightness = Math.min( + mBrightnessRangeController.getCurrentBrightnessMax(), mBrightnessThrottler.getBrightnessCap()); boolean changed = false; @@ -2124,10 +2136,10 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call maxBrightness); changed |= mCachedBrightnessInfo.checkAndSetInt(mCachedBrightnessInfo.hbmMode, - mHbmController.getHighBrightnessMode()); + mBrightnessRangeController.getHighBrightnessMode()); changed |= mCachedBrightnessInfo.checkAndSetFloat(mCachedBrightnessInfo.hbmTransitionPoint, - mHbmController.getTransitionPoint()); + mBrightnessRangeController.getTransitionPoint()); changed |= mCachedBrightnessInfo.checkAndSetInt(mCachedBrightnessInfo.brightnessMaxReason, mBrightnessThrottler.getBrightnessMaxReason()); @@ -2137,10 +2149,13 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call } void postBrightnessChangeRunnable() { - mHandler.post(mOnBrightnessChangeRunnable); + if (!mHandler.hasCallbacks(mOnBrightnessChangeRunnable)) { + mHandler.post(mOnBrightnessChangeRunnable); + } } - private HighBrightnessModeController createHbmControllerLocked() { + private HighBrightnessModeController createHbmControllerLocked( + Runnable modeChangeCallback) { final DisplayDevice device = mLogicalDisplay.getPrimaryDisplayDeviceLocked(); final DisplayDeviceConfig ddConfig = device.getDisplayDeviceConfig(); final IBinder displayToken = @@ -2150,8 +2165,9 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call final DisplayDeviceConfig.HighBrightnessModeData hbmData = ddConfig != null ? ddConfig.getHighBrightnessModeData() : null; final DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked(); - return new HighBrightnessModeController(mHandler, info.width, info.height, displayToken, - displayUniqueId, PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX, hbmData, + return mInjector.getHighBrightnessModeController(mHandler, info.width, info.height, + displayToken, displayUniqueId, PowerManager.BRIGHTNESS_MIN, + PowerManager.BRIGHTNESS_MAX, hbmData, new HighBrightnessModeController.HdrBrightnessDeviceConfig() { @Override public float getHdrBrightnessFromSdr( @@ -2159,15 +2175,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call return mDisplayDeviceConfig.getHdrBrightnessFromSdr( sdrBrightness, maxDesiredHdrSdrRatio); } - }, - () -> { - sendUpdatePowerState(); - postBrightnessChangeRunnable(); - // TODO(b/192258832): Switch the HBMChangeCallback to a listener pattern. - if (mAutomaticBrightnessController != null) { - mAutomaticBrightnessController.update(); - } - }, mHighBrightnessModeMetadata, mContext); + }, modeChangeCallback, mHighBrightnessModeMetadata, mContext); } private BrightnessThrottler createBrightnessThrottlerLocked() { @@ -2328,8 +2336,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call if (Float.isNaN(value)) { value = PowerManager.BRIGHTNESS_MIN; } - return MathUtils.constrain(value, - mHbmController.getCurrentBrightnessMin(), mHbmController.getCurrentBrightnessMax()); + return MathUtils.constrain(value, mBrightnessRangeController.getCurrentBrightnessMin(), + mBrightnessRangeController.getCurrentBrightnessMax()); } // Checks whether the brightness is within the valid brightness range, not including off. @@ -2667,9 +2675,10 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call } @Override - public void setBrightness(float brightnessValue) { + public void setBrightness(float brightnessValue, int userSerial) { // Update the setting, which will eventually call back into DPC to have us actually update // the display with the new value. + mBrightnessSetting.setUserSerial(userSerial); mBrightnessSetting.setBrightness(brightnessValue); if (mDisplayId == Display.DEFAULT_DISPLAY && mPersistBrightnessNitsForDefaultDisplay) { float nits = convertToNits(brightnessValue); @@ -2949,6 +2958,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call + mPendingScreenBrightnessSetting); pw.println(" mTemporaryScreenBrightness=" + mTemporaryScreenBrightness); pw.println(" mBrightnessToFollow=" + mBrightnessToFollow); + pw.println(" mBrightnessToFollowSlowChange=" + mBrightnessToFollowSlowChange); pw.println(" mAutoBrightnessAdjustment=" + mAutoBrightnessAdjustment); pw.println(" mBrightnessReason=" + mBrightnessReason); pw.println(" mTemporaryAutoBrightnessAdjustment=" + mTemporaryAutoBrightnessAdjustment); @@ -3003,8 +3013,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mScreenOffBrightnessSensorController.dump(pw); } - if (mHbmController != null) { - mHbmController.dump(pw); + if (mBrightnessRangeController != null) { + mBrightnessRangeController.dump(pw); } if (mBrightnessThrottler != null) { @@ -3471,7 +3481,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call HysteresisLevels screenBrightnessThresholds, HysteresisLevels ambientBrightnessThresholdsIdle, HysteresisLevels screenBrightnessThresholdsIdle, Context context, - HighBrightnessModeController hbmController, BrightnessThrottler brightnessThrottler, + BrightnessRangeController brightnessRangeController, + BrightnessThrottler brightnessThrottler, BrightnessMappingStrategy idleModeBrightnessMapper, int ambientLightHorizonShort, int ambientLightHorizonLong, float userLux, float userBrightness) { return new AutomaticBrightnessController(callbacks, looper, sensorManager, lightSensor, @@ -3480,9 +3491,9 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call brighteningLightDebounceConfig, darkeningLightDebounceConfig, resetAmbientLuxAfterWarmUpConfig, ambientBrightnessThresholds, screenBrightnessThresholds, ambientBrightnessThresholdsIdle, - screenBrightnessThresholdsIdle, context, hbmController, brightnessThrottler, - idleModeBrightnessMapper, ambientLightHorizonShort, ambientLightHorizonLong, - userLux, userBrightness); + screenBrightnessThresholdsIdle, context, brightnessRangeController, + brightnessThrottler, idleModeBrightnessMapper, ambientLightHorizonShort, + ambientLightHorizonLong, userLux, userBrightness); } BrightnessMappingStrategy getInteractiveModeBrightnessMapper(Resources resources, @@ -3527,6 +3538,17 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call brightnessMapper ); } + + HighBrightnessModeController getHighBrightnessModeController(Handler handler, int width, + int height, IBinder displayToken, String displayUniqueId, float brightnessMin, + float brightnessMax, DisplayDeviceConfig.HighBrightnessModeData hbmData, + HighBrightnessModeController.HdrBrightnessDeviceConfig hdrBrightnessCfg, + Runnable hbmChangeCallback, HighBrightnessModeMetadata hbmMetadata, + Context context) { + return new HighBrightnessModeController(handler, width, height, displayToken, + displayUniqueId, brightnessMin, brightnessMax, hbmData, hdrBrightnessCfg, + hbmChangeCallback, hbmMetadata, context); + } } static class CachedBrightnessInfo { diff --git a/services/core/java/com/android/server/display/DisplayPowerController2.java b/services/core/java/com/android/server/display/DisplayPowerController2.java index 41e4671df1a7..e1af04208480 100644 --- a/services/core/java/com/android/server/display/DisplayPowerController2.java +++ b/services/core/java/com/android/server/display/DisplayPowerController2.java @@ -376,8 +376,7 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal private final ColorDisplayServiceInternal mCdsi; private float[] mNitsRange; - private final HighBrightnessModeController mHbmController; - private final HighBrightnessModeMetadata mHighBrightnessModeMetadata; + private final BrightnessRangeController mBrightnessRangeController; private final BrightnessThrottler mBrightnessThrottler; @@ -436,6 +435,9 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal @Nullable private BrightnessMappingStrategy mIdleModeBrightnessMapper; + // Indicates whether we should ramp slowly to the brightness value to follow. + private boolean mBrightnessToFollowSlowChange; + private boolean mIsRbcActive; // Animators. @@ -489,7 +491,6 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal mDisplayPowerProximityStateController = mInjector.getDisplayPowerProximityStateController( mWakelockController, mDisplayDeviceConfig, mHandler.getLooper(), () -> updatePowerState(), mDisplayId, mSensorManager); - mHighBrightnessModeMetadata = hbmMetadata; mDisplayStateController = new DisplayStateController(mDisplayPowerProximityStateController); mAutomaticBrightnessStrategy = new AutomaticBrightnessStrategy(context, mDisplayId); mTag = "DisplayPowerController2[" + mDisplayId + "]"; @@ -532,9 +533,22 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal mSkipScreenOnBrightnessRamp = resources.getBoolean( R.bool.config_skipScreenOnBrightnessRamp); - mHbmController = createHbmControllerLocked(); + Runnable modeChangeCallback = () -> { + sendUpdatePowerState(); + postBrightnessChangeRunnable(); + // TODO(b/192258832): Switch the HBMChangeCallback to a listener pattern. + if (mAutomaticBrightnessController != null) { + mAutomaticBrightnessController.update(); + } + }; + HighBrightnessModeController hbmController = createHbmControllerLocked(hbmMetadata, + modeChangeCallback); mBrightnessThrottler = createBrightnessThrottlerLocked(); + + mBrightnessRangeController = new BrightnessRangeController(hbmController, + modeChangeCallback); + mDisplayBrightnessController = new DisplayBrightnessController(context, null, mDisplayId, mLogicalDisplay.getDisplayInfoLocked().brightnessDefault, @@ -848,17 +862,8 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal mBrightnessRampIncreaseMaxTimeMillis, mBrightnessRampDecreaseMaxTimeMillis); } - mHbmController.setHighBrightnessModeMetadata(hbmMetadata); - mHbmController.resetHbmData(info.width, info.height, token, info.uniqueId, - mDisplayDeviceConfig.getHighBrightnessModeData(), - new HighBrightnessModeController.HdrBrightnessDeviceConfig() { - @Override - public float getHdrBrightnessFromSdr( - float sdrBrightness, float maxDesiredHdrSdrRatio) { - return mDisplayDeviceConfig.getHdrBrightnessFromSdr( - sdrBrightness, maxDesiredHdrSdrRatio); - } - }); + + mBrightnessRangeController.loadFromConfig(hbmMetadata, token, info, mDisplayDeviceConfig); mBrightnessThrottler.loadThermalBrightnessThrottlingDataFromDisplayDeviceConfig( mDisplayDeviceConfig.getThermalBrightnessThrottlingDataMapByThrottlingId(), mThermalBrightnessThrottlingDataId, mUniqueDisplayId); @@ -1076,7 +1081,7 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal darkeningLightDebounce, autoBrightnessResetAmbientLuxAfterWarmUp, ambientBrightnessThresholds, screenBrightnessThresholds, ambientBrightnessThresholdsIdle, screenBrightnessThresholdsIdle, mContext, - mHbmController, mBrightnessThrottler, mIdleModeBrightnessMapper, + mBrightnessRangeController, mBrightnessThrottler, mIdleModeBrightnessMapper, mDisplayDeviceConfig.getAmbientHorizonShort(), mDisplayDeviceConfig.getAmbientHorizonLong(), userLux, userBrightness); mDisplayBrightnessController.setAutomaticBrightnessController( @@ -1180,7 +1185,7 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal /** Clean up all resources that are accessed via the {@link #mHandler} thread. */ private void cleanupHandlerThreadAfterStop() { mDisplayPowerProximityStateController.cleanup(); - mHbmController.stop(); + mBrightnessRangeController.stop(); mBrightnessThrottler.stop(); mHandler.removeCallbacksAndMessages(null); @@ -1270,6 +1275,7 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal // actual state instead of the desired one. animateScreenStateChange(state, mDisplayStateController.shouldPerformScreenOffTransition()); state = mPowerState.getScreenState(); + boolean slowChange = false; final boolean userSetBrightnessChanged = mDisplayBrightnessController .updateUserSetScreenBrightness(); @@ -1279,13 +1285,18 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal float rawBrightnessState = displayBrightnessState.getBrightness(); mBrightnessReasonTemp.set(displayBrightnessState.getBrightnessReason()); + if (displayBrightnessState.getBrightnessReason().getReason() + == BrightnessReason.REASON_FOLLOWER) { + slowChange = mBrightnessToFollowSlowChange; + } + // Take note if the short term model was already active before applying the current // request changes. final boolean wasShortTermModelActive = mAutomaticBrightnessStrategy.isShortTermModelActive(); mAutomaticBrightnessStrategy.setAutoBrightnessState(state, mDisplayBrightnessController.isAllowAutoBrightnessWhileDozingConfig(), - brightnessState, mBrightnessReasonTemp.getReason(), mPowerRequest.policy, + mBrightnessReasonTemp.getReason(), mPowerRequest.policy, mDisplayBrightnessController.getLastUserSetScreenBrightness(), userSetBrightnessChanged); @@ -1295,15 +1306,16 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal && (mAutomaticBrightnessStrategy.getAutoBrightnessAdjustmentChanged() || userSetBrightnessChanged); - mHbmController.setAutoBrightnessEnabled(mAutomaticBrightnessStrategy - .shouldUseAutoBrightness() + mBrightnessRangeController.setAutoBrightnessEnabled( + mAutomaticBrightnessStrategy.isAutoBrightnessEnabled() ? AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED - : AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED); + : mAutomaticBrightnessStrategy.isAutoBrightnessDisabledDueToDisplayOff() + ? AutomaticBrightnessController.AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE + : AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED); boolean updateScreenBrightnessSetting = false; float currentBrightnessSetting = mDisplayBrightnessController.getCurrentBrightness(); // Apply auto-brightness. - boolean slowChange = false; int brightnessAdjustmentFlags = 0; if (Float.isNaN(brightnessState)) { if (mAutomaticBrightnessStrategy.isAutoBrightnessEnabled()) { @@ -1320,10 +1332,13 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal brightnessAdjustmentFlags = mAutomaticBrightnessStrategy.getAutoBrightnessAdjustmentReasonsFlags(); updateScreenBrightnessSetting = currentBrightnessSetting != brightnessState; + mAutomaticBrightnessStrategy.setAutoBrightnessApplied(true); mBrightnessReasonTemp.setReason(BrightnessReason.REASON_AUTOMATIC); if (mScreenOffBrightnessSensorController != null) { mScreenOffBrightnessSensorController.setLightSensorEnabled(false); } + } else { + mAutomaticBrightnessStrategy.setAutoBrightnessApplied(false); } } } else { @@ -1331,6 +1346,7 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal // to clamping so that they don't go beyond the current max as specified by HBM // Controller. brightnessState = clampScreenBrightness(brightnessState); + mAutomaticBrightnessStrategy.setAutoBrightnessApplied(false); } // Use default brightness when dozing unless overridden. @@ -1370,6 +1386,15 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal mBrightnessReasonTemp.setReason(BrightnessReason.REASON_MANUAL); } + float ambientLux = mAutomaticBrightnessController == null ? 0 + : mAutomaticBrightnessController.getAmbientLux(); + for (int i = 0; i < displayBrightnessFollowers.size(); i++) { + DisplayPowerControllerInterface follower = displayBrightnessFollowers.valueAt(i); + follower.setBrightnessToFollow(rawBrightnessState, + mDisplayBrightnessController.convertToNits(rawBrightnessState), + ambientLux, slowChange); + } + // Now that a desired brightness has been calculated, apply brightness throttling. The // dimming and low power transformations that follow can only dim brightness further. // @@ -1392,15 +1417,6 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal mAppliedThrottling = false; } - float ambientLux = mAutomaticBrightnessController == null ? 0 - : mAutomaticBrightnessController.getAmbientLux(); - for (int i = 0; i < displayBrightnessFollowers.size(); i++) { - DisplayPowerControllerInterface follower = displayBrightnessFollowers.valueAt(i); - follower.setBrightnessToFollow(rawBrightnessState, - mDisplayBrightnessController.convertToNits(rawBrightnessState), - ambientLux); - } - if (updateScreenBrightnessSetting) { // Tell the rest of the system about the new brightness in case we had to change it // for things like auto-brightness or high-brightness-mode. Note that we do this @@ -1452,7 +1468,7 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal // here instead of having HbmController listen to the brightness setting because certain // brightness sources (such as an app override) are not saved to the setting, but should be // reflected in HBM calculations. - mHbmController.onBrightnessChanged(brightnessState, unthrottledBrightnessState, + mBrightnessRangeController.onBrightnessChanged(brightnessState, unthrottledBrightnessState, mBrightnessThrottler.getBrightnessMaxReason()); // Animate the screen brightness when the screen is on or dozing. @@ -1509,13 +1525,14 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal float sdrAnimateValue = animateValue; // TODO(b/216365040): The decision to prevent HBM for HDR in low power mode should be // done in HighBrightnessModeController. - if (mHbmController.getHighBrightnessMode() == BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR + if (mBrightnessRangeController.getHighBrightnessMode() + == BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR && (mBrightnessReasonTemp.getModifier() & BrightnessReason.MODIFIER_DIMMED) == 0 && (mBrightnessReasonTemp.getModifier() & BrightnessReason.MODIFIER_LOW_POWER) == 0) { // We want to scale HDR brightness level with the SDR level, we also need to restore // SDR brightness immediately when entering dim or low power mode. - animateValue = mHbmController.getHdrBrightnessValue(); + animateValue = mBrightnessRangeController.getHdrBrightnessValue(); } final float currentBrightness = mPowerState.getScreenBrightness(); @@ -1579,8 +1596,8 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal mTempBrightnessEvent.setBrightness(brightnessState); mTempBrightnessEvent.setPhysicalDisplayId(mUniqueDisplayId); mTempBrightnessEvent.setReason(mBrightnessReason); - mTempBrightnessEvent.setHbmMax(mHbmController.getCurrentBrightnessMax()); - mTempBrightnessEvent.setHbmMode(mHbmController.getHighBrightnessMode()); + mTempBrightnessEvent.setHbmMax(mBrightnessRangeController.getCurrentBrightnessMax()); + mTempBrightnessEvent.setHbmMode(mBrightnessRangeController.getHighBrightnessMode()); mTempBrightnessEvent.setFlags(mTempBrightnessEvent.getFlags() | (mIsRbcActive ? BrightnessEvent.FLAG_RBC : 0) | (mPowerRequest.lowPowerMode ? BrightnessEvent.FLAG_LOW_POWER_MODE : 0)); @@ -1750,9 +1767,11 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal private boolean saveBrightnessInfo(float brightness, float adjustedBrightness) { synchronized (mCachedBrightnessInfo) { - final float minBrightness = Math.min(mHbmController.getCurrentBrightnessMin(), + final float minBrightness = Math.min( + mBrightnessRangeController.getCurrentBrightnessMin(), mBrightnessThrottler.getBrightnessCap()); - final float maxBrightness = Math.min(mHbmController.getCurrentBrightnessMax(), + final float maxBrightness = Math.min( + mBrightnessRangeController.getCurrentBrightnessMax(), mBrightnessThrottler.getBrightnessCap()); boolean changed = false; @@ -1770,10 +1789,10 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal maxBrightness); changed |= mCachedBrightnessInfo.checkAndSetInt(mCachedBrightnessInfo.hbmMode, - mHbmController.getHighBrightnessMode()); + mBrightnessRangeController.getHighBrightnessMode()); changed |= mCachedBrightnessInfo.checkAndSetFloat(mCachedBrightnessInfo.hbmTransitionPoint, - mHbmController.getTransitionPoint()); + mBrightnessRangeController.getTransitionPoint()); changed |= mCachedBrightnessInfo.checkAndSetInt(mCachedBrightnessInfo.brightnessMaxReason, mBrightnessThrottler.getBrightnessMaxReason()); @@ -1783,10 +1802,13 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal } void postBrightnessChangeRunnable() { - mHandler.post(mOnBrightnessChangeRunnable); + if (!mHandler.hasCallbacks(mOnBrightnessChangeRunnable)) { + mHandler.post(mOnBrightnessChangeRunnable); + } } - private HighBrightnessModeController createHbmControllerLocked() { + private HighBrightnessModeController createHbmControllerLocked( + HighBrightnessModeMetadata hbmMetadata, Runnable modeChangeCallback) { final DisplayDevice device = mLogicalDisplay.getPrimaryDisplayDeviceLocked(); final DisplayDeviceConfig ddConfig = device.getDisplayDeviceConfig(); final IBinder displayToken = @@ -1796,24 +1818,11 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal final DisplayDeviceConfig.HighBrightnessModeData hbmData = ddConfig != null ? ddConfig.getHighBrightnessModeData() : null; final DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked(); - return new HighBrightnessModeController(mHandler, info.width, info.height, displayToken, - displayUniqueId, PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX, hbmData, - new HighBrightnessModeController.HdrBrightnessDeviceConfig() { - @Override - public float getHdrBrightnessFromSdr( - float sdrBrightness, float maxDesiredHdrSdrRatio) { - return mDisplayDeviceConfig.getHdrBrightnessFromSdr( - sdrBrightness, maxDesiredHdrSdrRatio); - } - }, - () -> { - sendUpdatePowerState(); - postBrightnessChangeRunnable(); - // TODO(b/192258832): Switch the HBMChangeCallback to a listener pattern. - if (mAutomaticBrightnessController != null) { - mAutomaticBrightnessController.update(); - } - }, mHighBrightnessModeMetadata, mContext); + return mInjector.getHighBrightnessModeController(mHandler, info.width, info.height, + displayToken, displayUniqueId, PowerManager.BRIGHTNESS_MIN, + PowerManager.BRIGHTNESS_MAX, hbmData, (sdrBrightness, maxDesiredHdrSdrRatio) -> + mDisplayDeviceConfig.getHdrBrightnessFromSdr(sdrBrightness, + maxDesiredHdrSdrRatio), modeChangeCallback, hbmMetadata, mContext); } private BrightnessThrottler createBrightnessThrottlerLocked() { @@ -1960,8 +1969,8 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal if (Float.isNaN(value)) { value = PowerManager.BRIGHTNESS_MIN; } - return MathUtils.constrain(value, - mHbmController.getCurrentBrightnessMin(), mHbmController.getCurrentBrightnessMax()); + return MathUtils.constrain(value, mBrightnessRangeController.getCurrentBrightnessMin(), + mBrightnessRangeController.getCurrentBrightnessMax()); } private void animateScreenBrightness(float target, float sdrTarget, float rate) { @@ -2179,8 +2188,8 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal } @Override - public void setBrightness(float brightnessValue) { - mDisplayBrightnessController.setBrightness(brightnessValue); + public void setBrightness(float brightnessValue, int userSerial) { + mDisplayBrightnessController.setBrightness(brightnessValue, userSerial); } @Override @@ -2194,8 +2203,9 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal } @Override - public void setBrightnessToFollow(float leadDisplayBrightness, float nits, float ambientLux) { - mHbmController.onAmbientLuxChange(ambientLux); + public void setBrightnessToFollow(float leadDisplayBrightness, float nits, float ambientLux, + boolean slowChange) { + mBrightnessRangeController.onAmbientLuxChange(ambientLux); if (nits < 0) { mDisplayBrightnessController.setBrightnessToFollow(leadDisplayBrightness); } else { @@ -2207,6 +2217,7 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal mDisplayBrightnessController.setBrightnessToFollow(leadDisplayBrightness); } } + mBrightnessToFollowSlowChange = slowChange; sendUpdatePowerState(); } @@ -2266,7 +2277,7 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal mDisplayBrightnessFollowers.remove(follower.getDisplayId()); mHandler.postAtTime(() -> follower.setBrightnessToFollow( PowerManager.BRIGHTNESS_INVALID_FLOAT, /* nits= */ -1, - /* ambientLux= */ 0), mClock.uptimeMillis()); + /* ambientLux= */ 0, /* slowChange= */ false), mClock.uptimeMillis()); } } @@ -2276,7 +2287,7 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal DisplayPowerControllerInterface follower = mDisplayBrightnessFollowers.valueAt(i); mHandler.postAtTime(() -> follower.setBrightnessToFollow( PowerManager.BRIGHTNESS_INVALID_FLOAT, /* nits= */ -1, - /* ambientLux= */ 0), mClock.uptimeMillis()); + /* ambientLux= */ 0, /* slowChange= */ false), mClock.uptimeMillis()); } mDisplayBrightnessFollowers.clear(); } @@ -2346,6 +2357,7 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal pw.println(" mReportedToPolicy=" + reportedToPolicyToString(mReportedScreenStateToPolicy)); pw.println(" mIsRbcActive=" + mIsRbcActive); + pw.println(" mBrightnessToFollowSlowChange=" + mBrightnessToFollowSlowChange); IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " "); mAutomaticBrightnessStrategy.dump(ipw); @@ -2374,8 +2386,8 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal dumpRbcEvents(pw); - if (mHbmController != null) { - mHbmController.dump(pw); + if (mBrightnessRangeController != null) { + mBrightnessRangeController.dump(pw); } if (mBrightnessThrottler != null) { @@ -2840,7 +2852,8 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal HysteresisLevels screenBrightnessThresholds, HysteresisLevels ambientBrightnessThresholdsIdle, HysteresisLevels screenBrightnessThresholdsIdle, Context context, - HighBrightnessModeController hbmController, BrightnessThrottler brightnessThrottler, + BrightnessRangeController brightnessModeController, + BrightnessThrottler brightnessThrottler, BrightnessMappingStrategy idleModeBrightnessMapper, int ambientLightHorizonShort, int ambientLightHorizonLong, float userLux, float userBrightness) { return new AutomaticBrightnessController(callbacks, looper, sensorManager, lightSensor, @@ -2849,9 +2862,9 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal brighteningLightDebounceConfig, darkeningLightDebounceConfig, resetAmbientLuxAfterWarmUpConfig, ambientBrightnessThresholds, screenBrightnessThresholds, ambientBrightnessThresholdsIdle, - screenBrightnessThresholdsIdle, context, hbmController, brightnessThrottler, - idleModeBrightnessMapper, ambientLightHorizonShort, ambientLightHorizonLong, - userLux, userBrightness); + screenBrightnessThresholdsIdle, context, brightnessModeController, + brightnessThrottler, idleModeBrightnessMapper, ambientLightHorizonShort, + ambientLightHorizonLong, userLux, userBrightness); } BrightnessMappingStrategy getInteractiveModeBrightnessMapper(Resources resources, @@ -2896,6 +2909,17 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal brightnessMapper ); } + + HighBrightnessModeController getHighBrightnessModeController(Handler handler, int width, + int height, IBinder displayToken, String displayUniqueId, float brightnessMin, + float brightnessMax, DisplayDeviceConfig.HighBrightnessModeData hbmData, + HighBrightnessModeController.HdrBrightnessDeviceConfig hdrBrightnessCfg, + Runnable hbmChangeCallback, HighBrightnessModeMetadata hbmMetadata, + Context context) { + return new HighBrightnessModeController(handler, width, height, displayToken, + displayUniqueId, brightnessMin, brightnessMax, hbmData, hdrBrightnessCfg, + hbmChangeCallback, hbmMetadata, context); + } } static class CachedBrightnessInfo { diff --git a/services/core/java/com/android/server/display/DisplayPowerControllerInterface.java b/services/core/java/com/android/server/display/DisplayPowerControllerInterface.java index 73edb970ff95..e3108c955a95 100644 --- a/services/core/java/com/android/server/display/DisplayPowerControllerInterface.java +++ b/services/core/java/com/android/server/display/DisplayPowerControllerInterface.java @@ -29,7 +29,7 @@ import java.io.PrintWriter; * An interface to manage the display's power state and brightness */ public interface DisplayPowerControllerInterface { - + int DEFAULT_USER_SERIAL = -1; /** * Notified when the display is changed. * @@ -98,7 +98,17 @@ public interface DisplayPowerControllerInterface { * Set the screen brightness of the associated display * @param brightness The value to which the brightness is to be set */ - void setBrightness(float brightness); + default void setBrightness(float brightness) { + setBrightness(brightness, DEFAULT_USER_SERIAL); + } + + /** + * Set the screen brightness of the associated display + * @param brightness The value to which the brightness is to be set + * @param userSerial The user for which the brightness value is to be set. Use userSerial = -1, + * if brightness needs to be updated for the current user. + */ + void setBrightness(float brightness, int userSerial); /** * Checks if the proximity sensor is available @@ -191,8 +201,10 @@ public interface DisplayPowerControllerInterface { * @param nits The brightness value in nits if the device supports nits. Set to a negative * number otherwise. * @param ambientLux The lux value that will be passed to {@link HighBrightnessModeController} + * @param slowChange Indicates whether we should slowly animate to the given brightness value. */ - void setBrightnessToFollow(float leadDisplayBrightness, float nits, float ambientLux); + void setBrightnessToFollow(float leadDisplayBrightness, float nits, float ambientLux, + boolean slowChange); /** * Add an additional display that will copy the brightness value from this display. This is used diff --git a/services/core/java/com/android/server/display/NormalBrightnessModeController.java b/services/core/java/com/android/server/display/NormalBrightnessModeController.java new file mode 100644 index 000000000000..dbabc2441224 --- /dev/null +++ b/services/core/java/com/android/server/display/NormalBrightnessModeController.java @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.display; + +import android.annotation.NonNull; +import android.os.PowerManager; + +import com.android.server.display.DisplayDeviceConfig.BrightnessLimitMapType; + +import java.util.HashMap; +import java.util.Map; + +/** + * Limits brightness for normal-brightness mode, based on ambient lux + **/ +class NormalBrightnessModeController { + @NonNull + private Map<BrightnessLimitMapType, Map<Float, Float>> mMaxBrightnessLimits = new HashMap<>(); + private float mAmbientLux = Float.MAX_VALUE; + private boolean mAutoBrightnessEnabled = false; + + // brightness limit in normal brightness mode, based on ambient lux. + private float mMaxBrightness = PowerManager.BRIGHTNESS_MAX; + + boolean onAmbientLuxChange(float ambientLux) { + mAmbientLux = ambientLux; + return recalculateMaxBrightness(); + } + + boolean setAutoBrightnessState(int state) { + boolean isEnabled = state == AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED; + if (isEnabled != mAutoBrightnessEnabled) { + mAutoBrightnessEnabled = isEnabled; + return recalculateMaxBrightness(); + } + return false; + } + + float getCurrentBrightnessMax() { + return mMaxBrightness; + } + + boolean resetNbmData( + @NonNull Map<BrightnessLimitMapType, Map<Float, Float>> maxBrightnessLimits) { + mMaxBrightnessLimits = maxBrightnessLimits; + return recalculateMaxBrightness(); + } + + private boolean recalculateMaxBrightness() { + float foundAmbientBoundary = Float.MAX_VALUE; + float foundMaxBrightness = PowerManager.BRIGHTNESS_MAX; + + Map<Float, Float> maxBrightnessPoints = null; + + if (mAutoBrightnessEnabled) { + maxBrightnessPoints = mMaxBrightnessLimits.get(BrightnessLimitMapType.ADAPTIVE); + } + + if (maxBrightnessPoints == null) { + maxBrightnessPoints = mMaxBrightnessLimits.get(BrightnessLimitMapType.DEFAULT); + } + + if (maxBrightnessPoints != null) { + for (Map.Entry<Float, Float> brightnessPoint : maxBrightnessPoints.entrySet()) { + float ambientBoundary = brightnessPoint.getKey(); + // find ambient lux upper boundary closest to current ambient lux + if (ambientBoundary > mAmbientLux && ambientBoundary < foundAmbientBoundary) { + foundMaxBrightness = brightnessPoint.getValue(); + foundAmbientBoundary = ambientBoundary; + } + } + } + + if (mMaxBrightness != foundMaxBrightness) { + mMaxBrightness = foundMaxBrightness; + return true; + } + return false; + } +} diff --git a/services/core/java/com/android/server/display/PersistentDataStore.java b/services/core/java/com/android/server/display/PersistentDataStore.java index 6d6ed726161b..2d7792d01c53 100644 --- a/services/core/java/com/android/server/display/PersistentDataStore.java +++ b/services/core/java/com/android/server/display/PersistentDataStore.java @@ -133,6 +133,7 @@ final class PersistentDataStore { private static final String TAG_BRIGHTNESS_NITS_FOR_DEFAULT_DISPLAY = "brightness-nits-for-default-display"; + public static final int DEFAULT_USER_ID = -1; // Remembered Wifi display devices. private ArrayList<WifiDisplay> mRememberedWifiDisplays = new ArrayList<WifiDisplay>(); @@ -294,7 +295,7 @@ final class PersistentDataStore { return false; } - public float getBrightness(DisplayDevice device) { + public float getBrightness(DisplayDevice device, int userSerial) { if (device == null || !device.hasStableUniqueId()) { return Float.NaN; } @@ -302,10 +303,10 @@ final class PersistentDataStore { if (state == null) { return Float.NaN; } - return state.getBrightness(); + return state.getBrightness(userSerial); } - public boolean setBrightness(DisplayDevice displayDevice, float brightness) { + public boolean setBrightness(DisplayDevice displayDevice, float brightness, int userSerial) { if (displayDevice == null || !displayDevice.hasStableUniqueId()) { return false; } @@ -314,7 +315,7 @@ final class PersistentDataStore { return false; } final DisplayState state = getDisplayState(displayDeviceUniqueId, true); - if (state.setBrightness(brightness)) { + if (state.setBrightness(brightness, userSerial)) { setDirty(); return true; } @@ -611,6 +612,7 @@ final class PersistentDataStore { state.saveToXml(serializer); serializer.endTag(null, TAG_DISPLAY); } + serializer.endTag(null, TAG_DISPLAY_STATES); serializer.startTag(null, TAG_STABLE_DEVICE_VALUES); mStableDeviceValues.saveToXml(serializer); @@ -649,7 +651,8 @@ final class PersistentDataStore { private static final class DisplayState { private int mColorMode; - private float mBrightness = Float.NaN; + + private SparseArray<Float> mPerUserBrightness = new SparseArray<>(); private int mWidth; private int mHeight; private float mRefreshRate; @@ -670,16 +673,25 @@ final class PersistentDataStore { return mColorMode; } - public boolean setBrightness(float brightness) { - if (brightness == mBrightness) { + public boolean setBrightness(float brightness, int userSerial) { + // Remove the default user brightness, before setting a new user-specific value. + // This is a one-time operation, required to restructure the config after user-specific + // brightness was introduced. + mPerUserBrightness.remove(DEFAULT_USER_ID); + + if (getBrightness(userSerial) == brightness) { return false; } - mBrightness = brightness; + mPerUserBrightness.set(userSerial, brightness); return true; } - public float getBrightness() { - return mBrightness; + public float getBrightness(int userSerial) { + float brightness = mPerUserBrightness.get(userSerial, Float.NaN); + if (Float.isNaN(brightness)) { + brightness = mPerUserBrightness.get(DEFAULT_USER_ID, Float.NaN); + } + return brightness; } public boolean setBrightnessConfiguration(BrightnessConfiguration configuration, @@ -729,12 +741,7 @@ final class PersistentDataStore { mColorMode = Integer.parseInt(value); break; case TAG_BRIGHTNESS_VALUE: - String brightness = parser.nextText(); - try { - mBrightness = Float.parseFloat(brightness); - } catch (NumberFormatException e) { - mBrightness = Float.NaN; - } + loadBrightnessFromXml(parser); break; case TAG_BRIGHTNESS_CONFIGURATIONS: mDisplayBrightnessConfigurations.loadFromXml(parser); @@ -760,11 +767,12 @@ final class PersistentDataStore { serializer.text(Integer.toString(mColorMode)); serializer.endTag(null, TAG_COLOR_MODE); - serializer.startTag(null, TAG_BRIGHTNESS_VALUE); - if (!Float.isNaN(mBrightness)) { - serializer.text(Float.toString(mBrightness)); + for (int i = 0; i < mPerUserBrightness.size(); i++) { + serializer.startTag(null, TAG_BRIGHTNESS_VALUE); + serializer.attributeInt(null, ATTR_USER_SERIAL, mPerUserBrightness.keyAt(i)); + serializer.text(Float.toString(mPerUserBrightness.valueAt(i))); + serializer.endTag(null, TAG_BRIGHTNESS_VALUE); } - serializer.endTag(null, TAG_BRIGHTNESS_VALUE); serializer.startTag(null, TAG_BRIGHTNESS_CONFIGURATIONS); mDisplayBrightnessConfigurations.saveToXml(serializer); @@ -785,12 +793,33 @@ final class PersistentDataStore { public void dump(final PrintWriter pw, final String prefix) { pw.println(prefix + "ColorMode=" + mColorMode); - pw.println(prefix + "BrightnessValue=" + mBrightness); + pw.println(prefix + "BrightnessValues: "); + for (int i = 0; i < mPerUserBrightness.size(); i++) { + pw.println("User: " + mPerUserBrightness.keyAt(i) + + " Value: " + mPerUserBrightness.valueAt(i)); + } pw.println(prefix + "DisplayBrightnessConfigurations: "); mDisplayBrightnessConfigurations.dump(pw, prefix); pw.println(prefix + "Resolution=" + mWidth + " " + mHeight); pw.println(prefix + "RefreshRate=" + mRefreshRate); } + + private void loadBrightnessFromXml(TypedXmlPullParser parser) + throws IOException, XmlPullParserException { + int userSerial; + try { + userSerial = parser.getAttributeInt(null, ATTR_USER_SERIAL); + } catch (NumberFormatException | XmlPullParserException e) { + userSerial = DEFAULT_USER_ID; + Slog.e(TAG, "Failed to read user serial", e); + } + String brightness = parser.nextText(); + try { + mPerUserBrightness.set(userSerial, Float.parseFloat(brightness)); + } catch (NumberFormatException nfe) { + Slog.e(TAG, "Failed to read brightness", nfe); + } + } } private static final class StableDeviceValues { diff --git a/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java b/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java index 7574de841440..2f52b708dfb5 100644 --- a/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java +++ b/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java @@ -38,6 +38,8 @@ import java.io.PrintWriter; * display. Applies the chosen brightness. */ public final class DisplayBrightnessController { + private static final int DEFAULT_USER_SERIAL = -1; + // The ID of the display tied to this DisplayBrightnessController private final int mDisplayId; @@ -274,8 +276,16 @@ public final class DisplayBrightnessController { * Notifies the brightnessSetting to persist the supplied brightness value. */ public void setBrightness(float brightnessValue) { + setBrightness(brightnessValue, DEFAULT_USER_SERIAL); + } + + /** + * Notifies the brightnessSetting to persist the supplied brightness value for a user. + */ + public void setBrightness(float brightnessValue, int userSerial) { // Update the setting, which will eventually call back into DPC to have us actually // update the display with the new value. + mBrightnessSetting.setUserSerial(userSerial); mBrightnessSetting.setBrightness(brightnessValue); if (mDisplayId == Display.DEFAULT_DISPLAY && mPersistBrightnessNitsForDefaultDisplay) { float nits = convertToNits(brightnessValue); diff --git a/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy.java b/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy.java index 0e885dcd1738..bcd52598edd2 100644 --- a/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy.java +++ b/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy.java @@ -73,6 +73,9 @@ public class AutomaticBrightnessStrategy { // the user has enabled the auto-brightness from the settings, it is disabled because the // display is off private boolean mIsAutoBrightnessEnabled = false; + // Indicates if auto-brightness is disabled due to the display being off. Needed for metric + // purposes. + private boolean mAutoBrightnessDisabledDueToDisplayOff; // If the auto-brightness model for the last manual changes done by the user. private boolean mIsShortTermModelActive = false; @@ -96,24 +99,21 @@ public class AutomaticBrightnessStrategy { * AutomaticBrightnessController accounting for any manual changes made by the user. */ public void setAutoBrightnessState(int targetDisplayState, - boolean allowAutoBrightnessWhileDozingConfig, - float brightnessState, int brightnessReason, int policy, + boolean allowAutoBrightnessWhileDozingConfig, int brightnessReason, int policy, float lastUserSetScreenBrightness, boolean userSetBrightnessChanged) { final boolean autoBrightnessEnabledInDoze = allowAutoBrightnessWhileDozingConfig && Display.isDozeState(targetDisplayState); mIsAutoBrightnessEnabled = shouldUseAutoBrightness() && (targetDisplayState == Display.STATE_ON || autoBrightnessEnabledInDoze) - && (Float.isNaN(brightnessState) - || brightnessReason == BrightnessReason.REASON_TEMPORARY - || brightnessReason == BrightnessReason.REASON_BOOST) - && mAutomaticBrightnessController != null - && brightnessReason != BrightnessReason.REASON_FOLLOWER; - final boolean autoBrightnessDisabledDueToDisplayOff = shouldUseAutoBrightness() + && brightnessReason != BrightnessReason.REASON_OVERRIDE + && mAutomaticBrightnessController != null; + mAutoBrightnessDisabledDueToDisplayOff = shouldUseAutoBrightness() && !(targetDisplayState == Display.STATE_ON || autoBrightnessEnabledInDoze); final int autoBrightnessState = mIsAutoBrightnessEnabled + && brightnessReason != BrightnessReason.REASON_FOLLOWER ? AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED - : autoBrightnessDisabledDueToDisplayOff + : mAutoBrightnessDisabledDueToDisplayOff ? AutomaticBrightnessController.AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE : AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED; @@ -125,6 +125,10 @@ public class AutomaticBrightnessStrategy { return mIsAutoBrightnessEnabled; } + public boolean isAutoBrightnessDisabledDueToDisplayOff() { + return mAutoBrightnessDisabledDueToDisplayOff; + } + /** * Updates the {@link BrightnessConfiguration} that is currently being used by the associated * display. @@ -287,8 +291,6 @@ public class AutomaticBrightnessStrategy { mAutoBrightnessAdjustmentReasonsFlags = isTemporaryAutoBrightnessAdjustmentApplied() ? BrightnessReason.ADJUSTMENT_AUTO_TEMP : BrightnessReason.ADJUSTMENT_AUTO; - mAppliedAutoBrightness = BrightnessUtils.isValidBrightnessValue(brightnessState) - || brightnessState == PowerManager.BRIGHTNESS_OFF_FLOAT; float newAutoBrightnessAdjustment = (mAutomaticBrightnessController != null) ? mAutomaticBrightnessController.getAutomaticScreenBrightnessAdjustment() @@ -345,8 +347,7 @@ public class AutomaticBrightnessStrategy { /** * Sets if the auto-brightness is applied on the latest brightness change. */ - @VisibleForTesting - void setAutoBrightnessApplied(boolean autoBrightnessApplied) { + public void setAutoBrightnessApplied(boolean autoBrightnessApplied) { mAppliedAutoBrightness = autoBrightnessApplied; } diff --git a/services/core/java/com/android/server/dreams/DreamManagerService.java b/services/core/java/com/android/server/dreams/DreamManagerService.java index 41651fd5553c..68cf59f0ae1f 100644 --- a/services/core/java/com/android/server/dreams/DreamManagerService.java +++ b/services/core/java/com/android/server/dreams/DreamManagerService.java @@ -203,9 +203,7 @@ public final class DreamManagerService extends SystemService { @Override public void onChange(boolean selfChange, Uri uri) { - synchronized (mLock) { - updateWhenToDreamSettings(); - } + updateWhenToDreamSettings(); } } @@ -257,15 +255,7 @@ public final class DreamManagerService extends SystemService { if (Build.IS_DEBUGGABLE) { SystemProperties.addChangeCallback(mSystemPropertiesChanged); } - mContext.registerReceiver(new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - writePulseGestureEnabled(); - synchronized (mLock) { - stopDreamLocked(false /*immediate*/, "user switched"); - } - } - }, new IntentFilter(Intent.ACTION_USER_SWITCHED), null, mHandler); + mContext.getContentResolver().registerContentObserver( Settings.Secure.getUriFor(Settings.Secure.DOZE_DOUBLE_TAP_GESTURE), false, mDozeEnabledObserver, UserHandle.USER_ALL); @@ -303,6 +293,18 @@ public final class DreamManagerService extends SystemService { } } + @Override + public void onUserSwitching(@Nullable TargetUser from, @NonNull TargetUser to) { + updateWhenToDreamSettings(); + + mHandler.post(() -> { + writePulseGestureEnabled(); + synchronized (mLock) { + stopDreamLocked(false /*immediate*/, "user switched"); + } + }); + } + private void dumpInternal(PrintWriter pw) { synchronized (mLock) { pw.println("DREAM MANAGER (dumpsys dreams)"); @@ -318,6 +320,7 @@ public final class DreamManagerService extends SystemService { pw.println("mWhenToDream=" + mWhenToDream); pw.println("mKeepDreamingWhenUnpluggingDefault=" + mKeepDreamingWhenUnpluggingDefault); pw.println("getDozeComponent()=" + getDozeComponent()); + pw.println("mDreamOverlayServiceName=" + mDreamOverlayServiceName.flattenToString()); pw.println(); DumpUtils.dumpAsync(mHandler, (pw1, prefix) -> mController.dump(pw1), pw, "", 200); diff --git a/services/core/java/com/android/server/input/AmbientKeyboardBacklightController.java b/services/core/java/com/android/server/input/AmbientKeyboardBacklightController.java new file mode 100644 index 000000000000..ce868497b0e4 --- /dev/null +++ b/services/core/java/com/android/server/input/AmbientKeyboardBacklightController.java @@ -0,0 +1,431 @@ +/* + * Copyright 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.input; + +import android.annotation.MainThread; +import android.annotation.Nullable; +import android.content.Context; +import android.content.res.Resources; +import android.hardware.Sensor; +import android.hardware.SensorEvent; +import android.hardware.SensorEventListener; +import android.hardware.SensorManager; +import android.hardware.display.DisplayManager; +import android.hardware.display.DisplayManagerInternal; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.util.Log; +import android.util.Slog; +import android.util.TypedValue; +import android.view.Display; +import android.view.DisplayInfo; + +import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; +import com.android.server.LocalServices; +import com.android.server.display.utils.SensorUtils; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; + +/** + * A thread-safe component of {@link InputManagerService} responsible for managing the keyboard + * backlight based on ambient light sensor. + */ +final class AmbientKeyboardBacklightController implements DisplayManager.DisplayListener, + SensorEventListener { + + private static final String TAG = "KbdBacklightController"; + + // To enable these logs, run: + // 'adb shell setprop log.tag.KbdBacklightController DEBUG' (requires restart) + private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); + + // Number of light sensor responses required to overcome temporal hysteresis. + @VisibleForTesting + public static final int HYSTERESIS_THRESHOLD = 2; + + private static final int MSG_BRIGHTNESS_CALLBACK = 0; + private static final int MSG_SETUP_DISPLAY_AND_SENSOR = 1; + + private static final Object sAmbientControllerLock = new Object(); + + private final Context mContext; + private final Handler mHandler; + + @Nullable + @GuardedBy("sAmbientControllerLock") + private Sensor mLightSensor; + @GuardedBy("sAmbientControllerLock") + private String mCurrentDefaultDisplayUniqueId; + + // List of currently registered ambient backlight listeners + @GuardedBy("sAmbientControllerLock") + private final List<AmbientKeyboardBacklightListener> mAmbientKeyboardBacklightListeners = + new ArrayList<>(); + + private BrightnessStep[] mBrightnessSteps; + private int mCurrentBrightnessStepIndex; + private HysteresisState mHysteresisState; + private int mHysteresisCount = 0; + private float mSmoothingConstant; + private int mSmoothedLux; + private int mSmoothedLuxAtLastAdjustment; + + private enum HysteresisState { + // The most-recent mSmoothedLux matched mSmoothedLuxAtLastAdjustment. + STABLE, + // The most-recent mSmoothedLux was less than mSmoothedLuxAtLastAdjustment. + DECREASING, + // The most-recent mSmoothedLux was greater than mSmoothedLuxAtLastAdjustment. + INCREASING, + // The brightness should be adjusted immediately after the next sensor reading. + IMMEDIATE, + } + + AmbientKeyboardBacklightController(Context context, Looper looper) { + mContext = context; + mHandler = new Handler(looper, this::handleMessage); + initConfiguration(); + } + + public void systemRunning() { + mHandler.sendEmptyMessage(MSG_SETUP_DISPLAY_AND_SENSOR); + DisplayManager displayManager = Objects.requireNonNull( + mContext.getSystemService(DisplayManager.class)); + displayManager.registerDisplayListener(this, mHandler); + } + + public void registerAmbientBacklightListener(AmbientKeyboardBacklightListener listener) { + synchronized (sAmbientControllerLock) { + if (mAmbientKeyboardBacklightListeners.contains(listener)) { + throw new IllegalStateException( + "AmbientKeyboardBacklightListener was already registered, listener = " + + listener); + } + if (mAmbientKeyboardBacklightListeners.isEmpty()) { + // Add sensor listener when we add the first ambient backlight listener. + addSensorListener(mLightSensor); + } + mAmbientKeyboardBacklightListeners.add(listener); + } + } + + public void unregisterAmbientBacklightListener(AmbientKeyboardBacklightListener listener) { + synchronized (sAmbientControllerLock) { + if (!mAmbientKeyboardBacklightListeners.contains(listener)) { + throw new IllegalStateException( + "AmbientKeyboardBacklightListener was never registered, listener = " + + listener); + } + mAmbientKeyboardBacklightListeners.remove(listener); + if (mAmbientKeyboardBacklightListeners.isEmpty()) { + removeSensorListener(mLightSensor); + } + } + } + + private void sendBrightnessAdjustment(int brightnessValue) { + Message msg = Message.obtain(mHandler, MSG_BRIGHTNESS_CALLBACK, brightnessValue); + mHandler.sendMessage(msg); + } + + @MainThread + private void handleBrightnessCallback(int brightnessValue) { + synchronized (sAmbientControllerLock) { + for (AmbientKeyboardBacklightListener listener : mAmbientKeyboardBacklightListeners) { + listener.onKeyboardBacklightValueChanged(brightnessValue); + } + } + } + + @MainThread + private void handleAmbientLuxChange(float rawLux) { + if (rawLux < 0) { + Slog.w(TAG, "Light sensor doesn't have valid value"); + return; + } + updateSmoothedLux(rawLux); + + if (mHysteresisState != HysteresisState.IMMEDIATE + && mSmoothedLux == mSmoothedLuxAtLastAdjustment) { + mHysteresisState = HysteresisState.STABLE; + return; + } + + int newStepIndex = Math.max(0, mCurrentBrightnessStepIndex); + int numSteps = mBrightnessSteps.length; + + if (mSmoothedLux > mSmoothedLuxAtLastAdjustment) { + if (mHysteresisState != HysteresisState.IMMEDIATE + && mHysteresisState != HysteresisState.INCREASING) { + if (DEBUG) { + Slog.d(TAG, "ALS transitioned to brightness increasing state"); + } + mHysteresisState = HysteresisState.INCREASING; + mHysteresisCount = 0; + } + for (; newStepIndex < numSteps; newStepIndex++) { + if (mSmoothedLux < mBrightnessSteps[newStepIndex].mIncreaseLuxThreshold) { + break; + } + } + } else if (mSmoothedLux < mSmoothedLuxAtLastAdjustment) { + if (mHysteresisState != HysteresisState.IMMEDIATE + && mHysteresisState != HysteresisState.DECREASING) { + if (DEBUG) { + Slog.d(TAG, "ALS transitioned to brightness decreasing state"); + } + mHysteresisState = HysteresisState.DECREASING; + mHysteresisCount = 0; + } + for (; newStepIndex >= 0; newStepIndex--) { + if (mSmoothedLux > mBrightnessSteps[newStepIndex].mDecreaseLuxThreshold) { + break; + } + } + } + + if (mHysteresisState == HysteresisState.IMMEDIATE) { + mCurrentBrightnessStepIndex = newStepIndex; + mSmoothedLuxAtLastAdjustment = mSmoothedLux; + mHysteresisState = HysteresisState.STABLE; + mHysteresisCount = 0; + sendBrightnessAdjustment(mBrightnessSteps[newStepIndex].mBrightnessValue); + return; + } + + if (newStepIndex == mCurrentBrightnessStepIndex) { + return; + } + + mHysteresisCount++; + if (DEBUG) { + Slog.d(TAG, "Incremented hysteresis count to " + mHysteresisCount + " (lux went from " + + mSmoothedLuxAtLastAdjustment + " to " + mSmoothedLux + ")"); + } + if (mHysteresisCount >= HYSTERESIS_THRESHOLD) { + mCurrentBrightnessStepIndex = newStepIndex; + mSmoothedLuxAtLastAdjustment = mSmoothedLux; + mHysteresisCount = 1; + sendBrightnessAdjustment(mBrightnessSteps[newStepIndex].mBrightnessValue); + } + } + + @MainThread + private void handleDisplayChange() { + DisplayManagerInternal displayManagerInternal = LocalServices.getService( + DisplayManagerInternal.class); + DisplayInfo displayInfo = displayManagerInternal.getDisplayInfo(Display.DEFAULT_DISPLAY); + synchronized (sAmbientControllerLock) { + if (Objects.equals(mCurrentDefaultDisplayUniqueId, displayInfo.uniqueId)) { + return; + } + if (DEBUG) { + Slog.d(TAG, "Default display changed: resetting the light sensor"); + } + // Keep track of current default display + mCurrentDefaultDisplayUniqueId = displayInfo.uniqueId; + // Clear all existing sensor listeners + if (!mAmbientKeyboardBacklightListeners.isEmpty()) { + removeSensorListener(mLightSensor); + } + mLightSensor = getAmbientLightSensor( + displayManagerInternal.getAmbientLightSensorData(Display.DEFAULT_DISPLAY)); + // Re-add sensor listeners if required; + if (!mAmbientKeyboardBacklightListeners.isEmpty()) { + addSensorListener(mLightSensor); + } + } + } + + private Sensor getAmbientLightSensor( + DisplayManagerInternal.AmbientLightSensorData ambientSensor) { + SensorManager sensorManager = Objects.requireNonNull( + mContext.getSystemService(SensorManager.class)); + if (DEBUG) { + Slog.d(TAG, "Ambient Light sensor data: " + ambientSensor); + } + return SensorUtils.findSensor(sensorManager, ambientSensor.sensorType, + ambientSensor.sensorName, Sensor.TYPE_LIGHT); + } + + private void updateSmoothedLux(float rawLux) { + // For the first sensor reading, use raw lux value directly without smoothing. + if (mHysteresisState == HysteresisState.IMMEDIATE) { + mSmoothedLux = (int) rawLux; + } else { + mSmoothedLux = + (int) (mSmoothingConstant * rawLux + (1 - mSmoothingConstant) * mSmoothedLux); + } + if (DEBUG) { + Slog.d(TAG, "Current smoothed lux from ALS = " + mSmoothedLux); + } + } + + @VisibleForTesting + public void addSensorListener(@Nullable Sensor sensor) { + SensorManager sensorManager = mContext.getSystemService(SensorManager.class); + if (sensorManager == null || sensor == null) { + return; + } + // Reset values before registering listener + reset(); + sensorManager.registerListener(this, sensor, SensorManager.SENSOR_DELAY_NORMAL, mHandler); + if (DEBUG) { + Slog.d(TAG, "Registering ALS listener"); + } + } + + private void removeSensorListener(@Nullable Sensor sensor) { + SensorManager sensorManager = mContext.getSystemService(SensorManager.class); + if (sensorManager == null || sensor == null) { + return; + } + sensorManager.unregisterListener(this, sensor); + if (DEBUG) { + Slog.d(TAG, "Unregistering ALS listener"); + } + } + + private void initConfiguration() { + Resources res = mContext.getResources(); + int[] brightnessValueArray = res.getIntArray( + com.android.internal.R.array.config_autoKeyboardBacklightBrightnessValues); + int[] decreaseThresholdArray = res.getIntArray( + com.android.internal.R.array.config_autoKeyboardBacklightDecreaseLuxThreshold); + int[] increaseThresholdArray = res.getIntArray( + com.android.internal.R.array.config_autoKeyboardBacklightIncreaseLuxThreshold); + if (brightnessValueArray.length != decreaseThresholdArray.length + || decreaseThresholdArray.length != increaseThresholdArray.length) { + throw new IllegalArgumentException( + "The config files for auto keyboard backlight brightness must contain arrays " + + "of equal lengths"); + } + final int size = brightnessValueArray.length; + mBrightnessSteps = new BrightnessStep[size]; + for (int i = 0; i < size; i++) { + int increaseThreshold = + increaseThresholdArray[i] < 0 ? Integer.MAX_VALUE : increaseThresholdArray[i]; + int decreaseThreshold = + decreaseThresholdArray[i] < 0 ? Integer.MIN_VALUE : decreaseThresholdArray[i]; + mBrightnessSteps[i] = new BrightnessStep(brightnessValueArray[i], increaseThreshold, + decreaseThreshold); + } + + int numSteps = mBrightnessSteps.length; + if (numSteps == 0 || mBrightnessSteps[0].mDecreaseLuxThreshold != Integer.MIN_VALUE + || mBrightnessSteps[numSteps - 1].mIncreaseLuxThreshold != Integer.MAX_VALUE) { + throw new IllegalArgumentException( + "The config files for auto keyboard backlight brightness must contain arrays " + + "of length > 0 and have -1 or Integer.MIN_VALUE as lower bound for " + + "decrease thresholds and -1 or Integer.MAX_VALUE as upper bound for " + + "increase thresholds"); + } + + final TypedValue smoothingConstantValue = new TypedValue(); + res.getValue( + com.android.internal.R.dimen.config_autoKeyboardBrightnessSmoothingConstant, + smoothingConstantValue, + true /*resolveRefs*/); + mSmoothingConstant = smoothingConstantValue.getFloat(); + if (mSmoothingConstant <= 0.0 || mSmoothingConstant > 1.0) { + throw new IllegalArgumentException( + "The config files for auto keyboard backlight brightness must contain " + + "smoothing constant in range (0.0, 1.0]."); + } + + if (DEBUG) { + Log.d(TAG, "Brightness steps: " + Arrays.toString(mBrightnessSteps) + + " Smoothing constant = " + mSmoothingConstant); + } + } + + private void reset() { + mHysteresisState = HysteresisState.IMMEDIATE; + mSmoothedLux = 0; + mSmoothedLuxAtLastAdjustment = 0; + mCurrentBrightnessStepIndex = -1; + } + + private boolean handleMessage(Message msg) { + switch (msg.what) { + case MSG_BRIGHTNESS_CALLBACK: + handleBrightnessCallback((int) msg.obj); + return true; + case MSG_SETUP_DISPLAY_AND_SENSOR: + handleDisplayChange(); + return true; + } + return false; + } + + @Override + public void onSensorChanged(SensorEvent event) { + handleAmbientLuxChange(event.values[0]); + } + + @Override + public void onAccuracyChanged(Sensor sensor, int accuracy) { + } + + @Override + public void onDisplayAdded(int displayId) { + handleDisplayChange(); + } + + @Override + public void onDisplayRemoved(int displayId) { + handleDisplayChange(); + } + + @Override + public void onDisplayChanged(int displayId) { + handleDisplayChange(); + } + + public interface AmbientKeyboardBacklightListener { + /** + * @param value between [0, 255] to which keyboard backlight needs to be set according + * to Ambient light sensor. + */ + void onKeyboardBacklightValueChanged(int value); + } + + private static class BrightnessStep { + private final int mBrightnessValue; + private final int mIncreaseLuxThreshold; + private final int mDecreaseLuxThreshold; + + private BrightnessStep(int brightnessValue, int increaseLuxThreshold, + int decreaseLuxThreshold) { + mBrightnessValue = brightnessValue; + mIncreaseLuxThreshold = increaseLuxThreshold; + mDecreaseLuxThreshold = decreaseLuxThreshold; + } + + @Override + public String toString() { + return "BrightnessStep{" + "mBrightnessValue=" + mBrightnessValue + + ", mIncreaseThreshold=" + mIncreaseLuxThreshold + ", mDecreaseThreshold=" + + mDecreaseLuxThreshold + '}'; + } + } +} diff --git a/services/core/java/com/android/server/input/BatteryController.java b/services/core/java/com/android/server/input/BatteryController.java index ff9ce6f16075..38a0d37c5679 100644 --- a/services/core/java/com/android/server/input/BatteryController.java +++ b/services/core/java/com/android/server/input/BatteryController.java @@ -46,7 +46,6 @@ import android.view.InputDevice; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; -import com.android.server.input.BatteryController.UEventManager.UEventBatteryListener; import java.io.PrintWriter; import java.util.Arrays; @@ -102,8 +101,9 @@ final class BatteryController { @GuardedBy("mLock") private BluetoothBatteryManager.BluetoothBatteryListener mBluetoothBatteryListener; - BatteryController(Context context, NativeInputManagerService nativeService, Looper looper) { - this(context, nativeService, looper, new UEventManager() {}, + BatteryController(Context context, NativeInputManagerService nativeService, Looper looper, + UEventManager uEventManager) { + this(context, nativeService, looper, uEventManager, new LocalBluetoothBatteryManager(context, looper)); } @@ -567,7 +567,7 @@ final class BatteryController { private BluetoothAdapter.OnMetadataChangedListener mBluetoothMetadataListener; @Nullable - private UEventBatteryListener mUEventBatteryListener; + private BatteryController.UEventBatteryListener mUEventBatteryListener; DeviceMonitor(int deviceId) { mState = new State(deviceId); @@ -630,7 +630,7 @@ final class BatteryController { return; } final int deviceId = mState.deviceId; - mUEventBatteryListener = new UEventBatteryListener() { + mUEventBatteryListener = new BatteryController.UEventBatteryListener() { @Override public void onBatteryUEvent(long eventTime) { handleUEventNotification(deviceId, eventTime); @@ -898,40 +898,25 @@ final class BatteryController { } } - // An interface used to change the API of UEventObserver to a more test-friendly format. @VisibleForTesting - interface UEventManager { - - @VisibleForTesting - abstract class UEventBatteryListener { - private final UEventObserver mObserver = new UEventObserver() { - @Override - public void onUEvent(UEvent event) { - final long eventTime = SystemClock.uptimeMillis(); - if (DEBUG) { - Slog.d(TAG, - "UEventListener: Received UEvent: " - + event + " eventTime: " + eventTime); - } - if (!"CHANGE".equalsIgnoreCase(event.get("ACTION")) - || !"POWER_SUPPLY".equalsIgnoreCase(event.get("SUBSYSTEM"))) { - // Disregard any UEvents that do not correspond to battery changes. - return; - } - UEventBatteryListener.this.onBatteryUEvent(eventTime); - } - }; - - public abstract void onBatteryUEvent(long eventTime); - } - - default void addListener(UEventBatteryListener listener, String match) { - listener.mObserver.startObserving(match); + abstract static class UEventBatteryListener extends UEventManager.UEventListener { + @Override + public void onUEvent(UEventObserver.UEvent event) { + final long eventTime = SystemClock.uptimeMillis(); + if (DEBUG) { + Slog.d(TAG, + "UEventListener: Received UEvent: " + + event + " eventTime: " + eventTime); + } + if (!"CHANGE".equalsIgnoreCase(event.get("ACTION")) + || !"POWER_SUPPLY".equalsIgnoreCase(event.get("SUBSYSTEM"))) { + // Disregard any UEvents that do not correspond to battery changes. + return; + } + UEventBatteryListener.this.onBatteryUEvent(eventTime); } - default void removeListener(UEventBatteryListener listener) { - listener.mObserver.stopObserving(); - } + public abstract void onBatteryUEvent(long eventTime); } // An interface used to change the API of adding a bluetooth battery listener to a more diff --git a/services/core/java/com/android/server/input/InputFeatureFlagProvider.java b/services/core/java/com/android/server/input/InputFeatureFlagProvider.java new file mode 100644 index 000000000000..a646d1e9bcb0 --- /dev/null +++ b/services/core/java/com/android/server/input/InputFeatureFlagProvider.java @@ -0,0 +1,101 @@ +/* + * Copyright 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.input; + +import android.sysprop.InputProperties; + +import java.util.Optional; + +/** + * A component of {@link InputManagerService} responsible for managing the input sysprop flags + * + * @hide + */ +@SuppressWarnings("OptionalUsedAsFieldOrParameterType") +public final class InputFeatureFlagProvider { + + // To disable Keyboard backlight control via Framework, run: + // 'adb shell setprop persist.input.keyboard_backlight_control.enabled false' (requires restart) + private static final boolean KEYBOARD_BACKLIGHT_CONTROL_ENABLED = + InputProperties.enable_keyboard_backlight_control().orElse(true); + + // To disable Framework controlled keyboard backlight animation run: + // adb shell setprop persist.input.keyboard.backlight_animation.enabled false (requires restart) + private static final boolean KEYBOARD_BACKLIGHT_ANIMATION_ENABLED = + InputProperties.enable_keyboard_backlight_animation().orElse(false); + + // To disable Custom keyboard backlight levels support via IDC files run: + // adb shell setprop persist.input.keyboard.backlight_custom_levels.enabled false (requires + // restart) + private static final boolean KEYBOARD_BACKLIGHT_CUSTOM_LEVELS_ENABLED = + InputProperties.enable_keyboard_backlight_custom_levels().orElse(true); + + // To disable als based ambient keyboard backlight control run: + // adb shell setprop persist.input.keyboard.ambient_backlight_control.enabled false (requires + // restart) + private static final boolean AMBIENT_KEYBOARD_BACKLIGHT_CONTROL_ENABLED = + InputProperties.enable_ambient_keyboard_backlight_control().orElse(true); + + private static Optional<Boolean> sKeyboardBacklightControlOverride = Optional.empty(); + private static Optional<Boolean> sKeyboardBacklightAnimationOverride = Optional.empty(); + private static Optional<Boolean> sKeyboardBacklightCustomLevelsOverride = Optional.empty(); + private static Optional<Boolean> sAmbientKeyboardBacklightControlOverride = Optional.empty(); + + public static boolean isKeyboardBacklightControlEnabled() { + return sKeyboardBacklightControlOverride.orElse(KEYBOARD_BACKLIGHT_CONTROL_ENABLED); + } + + public static boolean isKeyboardBacklightAnimationEnabled() { + return sKeyboardBacklightAnimationOverride.orElse(KEYBOARD_BACKLIGHT_ANIMATION_ENABLED); + } + + public static boolean isKeyboardBacklightCustomLevelsEnabled() { + return sKeyboardBacklightCustomLevelsOverride.orElse( + KEYBOARD_BACKLIGHT_CUSTOM_LEVELS_ENABLED); + } + + public static boolean isAmbientKeyboardBacklightControlEnabled() { + return sAmbientKeyboardBacklightControlOverride.orElse( + AMBIENT_KEYBOARD_BACKLIGHT_CONTROL_ENABLED); + } + + public static void setKeyboardBacklightControlEnabled(boolean enabled) { + sKeyboardBacklightControlOverride = Optional.of(enabled); + } + + public static void setKeyboardBacklightAnimationEnabled(boolean enabled) { + sKeyboardBacklightAnimationOverride = Optional.of(enabled); + } + + public static void setKeyboardBacklightCustomLevelsEnabled(boolean enabled) { + sKeyboardBacklightCustomLevelsOverride = Optional.of(enabled); + } + + public static void setAmbientKeyboardBacklightControlEnabled(boolean enabled) { + sAmbientKeyboardBacklightControlOverride = Optional.of(enabled); + } + + /** + * Clears all input feature flag overrides. + */ + public static void clearOverrides() { + sKeyboardBacklightControlOverride = Optional.empty(); + sKeyboardBacklightAnimationOverride = Optional.empty(); + sKeyboardBacklightCustomLevelsOverride = Optional.empty(); + sAmbientKeyboardBacklightControlOverride = Optional.empty(); + } +} diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java index 4cb22dbcf308..6a177e03102f 100644 --- a/services/core/java/com/android/server/input/InputManagerService.java +++ b/services/core/java/com/android/server/input/InputManagerService.java @@ -74,7 +74,6 @@ import android.os.RemoteCallbackList; import android.os.RemoteException; import android.os.ResultReceiver; import android.os.ShellCallback; -import android.os.SystemProperties; import android.os.UserHandle; import android.os.VibrationEffect; import android.os.vibrator.StepSegment; @@ -160,11 +159,6 @@ public class InputManagerService extends IInputManager.Stub private static final AdditionalDisplayInputProperties DEFAULT_ADDITIONAL_DISPLAY_INPUT_PROPERTIES = new AdditionalDisplayInputProperties(); - // To disable Keyboard backlight control via Framework, run: - // 'adb shell setprop persist.input.keyboard_backlight_control.enabled false' (requires restart) - private static final boolean KEYBOARD_BACKLIGHT_CONTROL_ENABLED = SystemProperties.getBoolean( - "persist.input.keyboard.backlight_control.enabled", true); - private final NativeInputManagerService mNative; private final Context mContext; @@ -399,10 +393,12 @@ public class InputManagerService extends IInputManager.Stub static class Injector { private final Context mContext; private final Looper mLooper; + private final UEventManager mUEventManager; - Injector(Context context, Looper looper) { + Injector(Context context, Looper looper, UEventManager uEventManager) { mContext = context; mLooper = looper; + mUEventManager = uEventManager; } Context getContext() { @@ -413,6 +409,10 @@ public class InputManagerService extends IInputManager.Stub return mLooper; } + UEventManager getUEventManager() { + return mUEventManager; + } + NativeInputManagerService getNativeService(InputManagerService service) { return new NativeInputManagerService.NativeImpl(service, mLooper.getQueue()); } @@ -423,7 +423,7 @@ public class InputManagerService extends IInputManager.Stub } public InputManagerService(Context context) { - this(new Injector(context, DisplayThread.get().getLooper())); + this(new Injector(context, DisplayThread.get().getLooper(), new UEventManager() {})); } @VisibleForTesting @@ -438,11 +438,12 @@ public class InputManagerService extends IInputManager.Stub mSettingsObserver = new InputSettingsObserver(mContext, mHandler, this, mNative); mKeyboardLayoutManager = new KeyboardLayoutManager(mContext, mNative, mDataStore, injector.getLooper()); - mBatteryController = new BatteryController(mContext, mNative, injector.getLooper()); - mKeyboardBacklightController = - KEYBOARD_BACKLIGHT_CONTROL_ENABLED ? new KeyboardBacklightController(mContext, - mNative, mDataStore, injector.getLooper()) - : new KeyboardBacklightControllerInterface() {}; + mBatteryController = new BatteryController(mContext, mNative, injector.getLooper(), + injector.getUEventManager()); + mKeyboardBacklightController = InputFeatureFlagProvider.isKeyboardBacklightControlEnabled() + ? new KeyboardBacklightController(mContext, mNative, mDataStore, + injector.getLooper(), injector.getUEventManager()) + : new KeyboardBacklightControllerInterface() {}; mKeyRemapper = new KeyRemapper(mContext, mNative, mDataStore, injector.getLooper()); mUseDevInputEventForAudioJack = diff --git a/services/core/java/com/android/server/input/InputSettingsObserver.java b/services/core/java/com/android/server/input/InputSettingsObserver.java index 651063e8841b..42591f40b22e 100644 --- a/services/core/java/com/android/server/input/InputSettingsObserver.java +++ b/services/core/java/com/android/server/input/InputSettingsObserver.java @@ -115,35 +115,34 @@ class InputSettingsObserver extends ContentObserver { return setting != 0; } - private int getPointerSpeedValue(String settingName) { - int speed = Settings.System.getIntForUser(mContext.getContentResolver(), - settingName, InputSettings.DEFAULT_POINTER_SPEED, UserHandle.USER_CURRENT); + private int constrainPointerSpeedValue(int speed) { return Math.min(Math.max(speed, InputSettings.MIN_POINTER_SPEED), InputSettings.MAX_POINTER_SPEED); } private void updateMousePointerSpeed() { - mNative.setPointerSpeed(getPointerSpeedValue(Settings.System.POINTER_SPEED)); + int speed = Settings.System.getIntForUser(mContext.getContentResolver(), + Settings.System.POINTER_SPEED, InputSettings.DEFAULT_POINTER_SPEED, + UserHandle.USER_CURRENT); + mNative.setPointerSpeed(constrainPointerSpeedValue(speed)); } private void updateTouchpadPointerSpeed() { mNative.setTouchpadPointerSpeed( - getPointerSpeedValue(Settings.System.TOUCHPAD_POINTER_SPEED)); + constrainPointerSpeedValue(InputSettings.getTouchpadPointerSpeed(mContext))); } private void updateTouchpadNaturalScrollingEnabled() { mNative.setTouchpadNaturalScrollingEnabled( - getBoolean(Settings.System.TOUCHPAD_NATURAL_SCROLLING, true)); + InputSettings.useTouchpadNaturalScrolling(mContext)); } private void updateTouchpadTapToClickEnabled() { - mNative.setTouchpadTapToClickEnabled( - getBoolean(Settings.System.TOUCHPAD_TAP_TO_CLICK, true)); + mNative.setTouchpadTapToClickEnabled(InputSettings.useTouchpadTapToClick(mContext)); } private void updateTouchpadRightClickZoneEnabled() { - mNative.setTouchpadRightClickZoneEnabled( - getBoolean(Settings.System.TOUCHPAD_RIGHT_CLICK_ZONE, false)); + mNative.setTouchpadRightClickZoneEnabled(InputSettings.useTouchpadRightClickZone(mContext)); } private void updateShowTouches() { diff --git a/services/core/java/com/android/server/input/KeyboardBacklightController.java b/services/core/java/com/android/server/input/KeyboardBacklightController.java index 48c346a2fe22..c3205afe14f2 100644 --- a/services/core/java/com/android/server/input/KeyboardBacklightController.java +++ b/services/core/java/com/android/server/input/KeyboardBacklightController.java @@ -16,7 +16,9 @@ package com.android.server.input; +import android.animation.ValueAnimator; import android.annotation.BinderThread; +import android.annotation.Nullable; import android.content.Context; import android.graphics.Color; import android.hardware.input.IKeyboardBacklightListener; @@ -45,6 +47,7 @@ import java.time.Duration; import java.util.Arrays; import java.util.Objects; import java.util.OptionalInt; +import java.util.TreeSet; /** * A thread-safe component of {@link InputManagerService} responsible for managing the keyboard @@ -69,7 +72,11 @@ final class KeyboardBacklightController implements private static final int MSG_NOTIFY_USER_INACTIVITY = 5; private static final int MSG_INTERACTIVE_STATE_CHANGED = 6; private static final int MAX_BRIGHTNESS = 255; - private static final int NUM_BRIGHTNESS_CHANGE_STEPS = 10; + private static final int DEFAULT_NUM_BRIGHTNESS_CHANGE_STEPS = 10; + @VisibleForTesting + static final int MAX_BRIGHTNESS_CHANGE_STEPS = 10; + private static final long TRANSITION_ANIMATION_DURATION_MILLIS = + Duration.ofSeconds(1).toMillis(); private static final String UEVENT_KEYBOARD_BACKLIGHT_TAG = "kbd_backlight"; @@ -77,7 +84,8 @@ final class KeyboardBacklightController implements static final long USER_INACTIVITY_THRESHOLD_MILLIS = Duration.ofSeconds(30).toMillis(); @VisibleForTesting - static final int[] BRIGHTNESS_VALUE_FOR_LEVEL = new int[NUM_BRIGHTNESS_CHANGE_STEPS + 1]; + static final int[] DEFAULT_BRIGHTNESS_VALUE_FOR_LEVEL = + new int[DEFAULT_NUM_BRIGHTNESS_CHANGE_STEPS + 1]; private final Context mContext; private final NativeInputManagerService mNative; @@ -85,6 +93,8 @@ final class KeyboardBacklightController implements @GuardedBy("mDataStore") private final PersistentDataStore mDataStore; private final Handler mHandler; + private final AnimatorFactory mAnimatorFactory; + private final UEventManager mUEventManager; // Always access on handler thread or need to lock this for synchronization. private final SparseArray<KeyboardBacklightState> mKeyboardBacklights = new SparseArray<>(1); // Maintains state if all backlights should be on or turned off @@ -97,22 +107,38 @@ final class KeyboardBacklightController implements private final SparseArray<KeyboardBacklightListenerRecord> mKeyboardBacklightListenerRecords = new SparseArray<>(); + private final AmbientKeyboardBacklightController mAmbientController; + @Nullable + private AmbientKeyboardBacklightController.AmbientKeyboardBacklightListener mAmbientListener; + + private int mAmbientBacklightValue = 0; + static { // Fixed brightness levels to avoid issues when converting back and forth from the // device brightness range to [0-255] - // Levels are: 0, 25, 51, ..., 255 - for (int i = 0; i <= NUM_BRIGHTNESS_CHANGE_STEPS; i++) { - BRIGHTNESS_VALUE_FOR_LEVEL[i] = (int) Math.floor( - ((float) i * MAX_BRIGHTNESS) / NUM_BRIGHTNESS_CHANGE_STEPS); + // Levels are: 0, 51, ..., 255 + for (int i = 0; i <= DEFAULT_NUM_BRIGHTNESS_CHANGE_STEPS; i++) { + DEFAULT_BRIGHTNESS_VALUE_FOR_LEVEL[i] = (int) Math.floor( + ((float) i * MAX_BRIGHTNESS) / DEFAULT_NUM_BRIGHTNESS_CHANGE_STEPS); } } KeyboardBacklightController(Context context, NativeInputManagerService nativeService, - PersistentDataStore dataStore, Looper looper) { + PersistentDataStore dataStore, Looper looper, UEventManager uEventManager) { + this(context, nativeService, dataStore, looper, ValueAnimator::ofInt, uEventManager); + } + + @VisibleForTesting + KeyboardBacklightController(Context context, NativeInputManagerService nativeService, + PersistentDataStore dataStore, Looper looper, AnimatorFactory animatorFactory, + UEventManager uEventManager) { mContext = context; mNative = nativeService; mDataStore = dataStore; mHandler = new Handler(looper, this::handleMessage); + mAnimatorFactory = animatorFactory; + mAmbientController = new AmbientKeyboardBacklightController(context, looper); + mUEventManager = uEventManager; } @Override @@ -129,13 +155,17 @@ final class KeyboardBacklightController implements // We want to observe creation of such LED nodes since they might be created after device // FD created and InputDevice creation logic doesn't initialize LED nodes which leads to // backlight not working. - UEventObserver observer = new UEventObserver() { + mUEventManager.addListener(new UEventManager.UEventListener() { @Override - public void onUEvent(UEvent event) { + public void onUEvent(UEventObserver.UEvent event) { onKeyboardBacklightUEvent(event); } - }; - observer.startObserving(UEVENT_KEYBOARD_BACKLIGHT_TAG); + }, UEVENT_KEYBOARD_BACKLIGHT_TAG); + + if (InputFeatureFlagProvider.isAmbientKeyboardBacklightControlEnabled()) { + // Start ambient backlight controller + mAmbientController.systemRunning(); + } } @Override @@ -168,43 +198,89 @@ final class KeyboardBacklightController implements if (inputDevice == null || state == null) { return; } - Light keyboardBacklight = state.mLight; // Follow preset levels of brightness defined in BRIGHTNESS_LEVELS - final int currBrightnessLevel = state.mBrightnessLevel; + final int currBrightnessLevel; + if (state.mUseAmbientController) { + int index = Arrays.binarySearch(state.mBrightnessValueForLevel, mAmbientBacklightValue); + // Set current level to the lower bound of the ambient value in the brightness array. + if (index < 0) { + int lowerBound = Math.max(0, -(index + 1) - 1); + currBrightnessLevel = + direction == Direction.DIRECTION_UP ? lowerBound : lowerBound + 1; + } else { + currBrightnessLevel = index; + } + } else { + currBrightnessLevel = state.mBrightnessLevel; + } final int newBrightnessLevel; if (direction == Direction.DIRECTION_UP) { - newBrightnessLevel = Math.min(currBrightnessLevel + 1, NUM_BRIGHTNESS_CHANGE_STEPS); + newBrightnessLevel = Math.min(currBrightnessLevel + 1, + state.getNumBrightnessChangeSteps()); } else { newBrightnessLevel = Math.max(currBrightnessLevel - 1, 0); } - updateBacklightState(deviceId, keyboardBacklight, newBrightnessLevel, - true /* isTriggeredByKeyPress */); + state.setBrightnessLevel(newBrightnessLevel); + + // Might need to stop listening to ALS since user has manually selected backlight + // level through keyboard up/down button + updateAmbientLightListener(); + + maybeBackupBacklightBrightness(inputDevice, state.mLight, + state.mBrightnessValueForLevel[newBrightnessLevel]); + + if (DEBUG) { + Slog.d(TAG, + "Changing state from " + state.mBrightnessLevel + " to " + newBrightnessLevel); + } + + synchronized (mKeyboardBacklightListenerRecords) { + for (int i = 0; i < mKeyboardBacklightListenerRecords.size(); i++) { + IKeyboardBacklightState callbackState = new IKeyboardBacklightState(); + callbackState.brightnessLevel = newBrightnessLevel; + callbackState.maxBrightnessLevel = state.getNumBrightnessChangeSteps(); + mKeyboardBacklightListenerRecords.valueAt(i).notifyKeyboardBacklightChanged( + deviceId, callbackState, true); + } + } + } + + private void maybeBackupBacklightBrightness(InputDevice inputDevice, Light keyboardBacklight, + int brightnessValue) { + // Don't back up or restore when ALS based keyboard backlight is enabled + if (InputFeatureFlagProvider.isAmbientKeyboardBacklightControlEnabled()) { + return; + } synchronized (mDataStore) { try { mDataStore.setKeyboardBacklightBrightness(inputDevice.getDescriptor(), keyboardBacklight.getId(), - BRIGHTNESS_VALUE_FOR_LEVEL[newBrightnessLevel]); + brightnessValue); } finally { mDataStore.saveIfNeeded(); } } } - private void restoreBacklightBrightness(InputDevice inputDevice, Light keyboardBacklight) { + private void maybeRestoreBacklightBrightness(InputDevice inputDevice, Light keyboardBacklight) { + // Don't back up or restore when ALS based keyboard backlight is enabled + if (InputFeatureFlagProvider.isAmbientKeyboardBacklightControlEnabled()) { + return; + } + KeyboardBacklightState state = mKeyboardBacklights.get(inputDevice.getId()); OptionalInt brightness; synchronized (mDataStore) { brightness = mDataStore.getKeyboardBacklightBrightness( inputDevice.getDescriptor(), keyboardBacklight.getId()); } - if (brightness.isPresent()) { + if (state != null && brightness.isPresent()) { int brightnessValue = Math.max(0, Math.min(MAX_BRIGHTNESS, brightness.getAsInt())); - int index = Arrays.binarySearch(BRIGHTNESS_VALUE_FOR_LEVEL, brightnessValue); - if (index < 0) { - index = Math.min(NUM_BRIGHTNESS_CHANGE_STEPS, -(index + 1)); + int newLevel = Arrays.binarySearch(state.mBrightnessValueForLevel, brightnessValue); + if (newLevel < 0) { + newLevel = Math.min(state.getNumBrightnessChangeSteps(), -(newLevel + 1)); } - updateBacklightState(inputDevice.getId(), keyboardBacklight, index, - false /* isTriggeredByKeyPress */); + state.setBrightnessLevel(newLevel); if (DEBUG) { Slog.d(TAG, "Restoring brightness level " + brightness.getAsInt()); } @@ -217,14 +293,10 @@ final class KeyboardBacklightController implements if (!mIsInteractive) { return; } - if (!mIsBacklightOn) { - mIsBacklightOn = true; - for (int i = 0; i < mKeyboardBacklights.size(); i++) { - int deviceId = mKeyboardBacklights.keyAt(i); - KeyboardBacklightState state = mKeyboardBacklights.valueAt(i); - updateBacklightState(deviceId, state.mLight, state.mBrightnessLevel, - false /* isTriggeredByKeyPress */); - } + mIsBacklightOn = true; + for (int i = 0; i < mKeyboardBacklights.size(); i++) { + KeyboardBacklightState state = mKeyboardBacklights.valueAt(i); + state.onBacklightStateChanged(); } mHandler.removeMessages(MSG_NOTIFY_USER_INACTIVITY); mHandler.sendEmptyMessageAtTime(MSG_NOTIFY_USER_INACTIVITY, @@ -232,14 +304,10 @@ final class KeyboardBacklightController implements } private void handleUserInactivity() { - if (mIsBacklightOn) { - mIsBacklightOn = false; - for (int i = 0; i < mKeyboardBacklights.size(); i++) { - int deviceId = mKeyboardBacklights.keyAt(i); - KeyboardBacklightState state = mKeyboardBacklights.valueAt(i); - updateBacklightState(deviceId, state.mLight, state.mBrightnessLevel, - false /* isTriggeredByKeyPress */); - } + mIsBacklightOn = false; + for (int i = 0; i < mKeyboardBacklights.size(); i++) { + KeyboardBacklightState state = mKeyboardBacklights.valueAt(i); + state.onBacklightStateChanged(); } } @@ -253,6 +321,16 @@ final class KeyboardBacklightController implements } else { handleUserInactivity(); } + updateAmbientLightListener(); + } + + @VisibleForTesting + public void handleAmbientLightValueChanged(int brightnessValue) { + mAmbientBacklightValue = brightnessValue; + for (int i = 0; i < mKeyboardBacklights.size(); i++) { + KeyboardBacklightState state = mKeyboardBacklights.valueAt(i); + state.onAmbientBacklightValueChanged(); + } } private boolean handleMessage(Message msg) { @@ -285,12 +363,14 @@ final class KeyboardBacklightController implements @Override public void onInputDeviceAdded(int deviceId) { onInputDeviceChanged(deviceId); + updateAmbientLightListener(); } @VisibleForTesting @Override public void onInputDeviceRemoved(int deviceId) { mKeyboardBacklights.remove(deviceId); + updateAmbientLightListener(); } @VisibleForTesting @@ -310,8 +390,8 @@ final class KeyboardBacklightController implements return; } // The keyboard backlight was added or changed. - mKeyboardBacklights.put(deviceId, new KeyboardBacklightState(keyboardBacklight)); - restoreBacklightBrightness(inputDevice, keyboardBacklight); + mKeyboardBacklights.put(deviceId, new KeyboardBacklightState(deviceId, keyboardBacklight)); + maybeRestoreBacklightBrightness(inputDevice, keyboardBacklight); } private InputDevice getInputDevice(int deviceId) { @@ -372,33 +452,6 @@ final class KeyboardBacklightController implements } } - private void updateBacklightState(int deviceId, Light light, int brightnessLevel, - boolean isTriggeredByKeyPress) { - KeyboardBacklightState state = mKeyboardBacklights.get(deviceId); - if (state == null) { - return; - } - - mNative.setLightColor(deviceId, light.getId(), - mIsBacklightOn ? Color.argb(BRIGHTNESS_VALUE_FOR_LEVEL[brightnessLevel], 0, 0, 0) - : 0); - if (DEBUG) { - Slog.d(TAG, "Changing state from " + state.mBrightnessLevel + " to " + brightnessLevel - + "(isBacklightOn = " + mIsBacklightOn + ")"); - } - state.mBrightnessLevel = brightnessLevel; - - synchronized (mKeyboardBacklightListenerRecords) { - for (int i = 0; i < mKeyboardBacklightListenerRecords.size(); i++) { - IKeyboardBacklightState callbackState = new IKeyboardBacklightState(); - callbackState.brightnessLevel = brightnessLevel; - callbackState.maxBrightnessLevel = NUM_BRIGHTNESS_CHANGE_STEPS; - mKeyboardBacklightListenerRecords.valueAt(i).notifyKeyboardBacklightChanged( - deviceId, callbackState, isTriggeredByKeyPress); - } - } - } - private void onKeyboardBacklightListenerDied(int pid) { synchronized (mKeyboardBacklightListenerRecords) { mKeyboardBacklightListenerRecords.remove(pid); @@ -416,6 +469,25 @@ final class KeyboardBacklightController implements } } + private void updateAmbientLightListener() { + if (!InputFeatureFlagProvider.isAmbientKeyboardBacklightControlEnabled()) { + return; + } + boolean needToListenAmbientLightSensor = false; + for (int i = 0; i < mKeyboardBacklights.size(); i++) { + needToListenAmbientLightSensor |= mKeyboardBacklights.valueAt(i).mUseAmbientController; + } + needToListenAmbientLightSensor &= mIsInteractive; + if (needToListenAmbientLightSensor && mAmbientListener == null) { + mAmbientListener = this::handleAmbientLightValueChanged; + mAmbientController.registerAmbientBacklightListener(mAmbientListener); + } + if (!needToListenAmbientLightSensor && mAmbientListener != null) { + mAmbientController.unregisterAmbientBacklightListener(mAmbientListener); + mAmbientListener = null; + } + } + private static boolean isValidBacklightNodePath(String devPath) { if (TextUtils.isEmpty(devPath)) { return false; @@ -436,10 +508,7 @@ final class KeyboardBacklightController implements @Override public void dump(PrintWriter pw) { IndentingPrintWriter ipw = new IndentingPrintWriter(pw); - ipw.println( - TAG + ": " + mKeyboardBacklights.size() + " keyboard backlights, isBacklightOn = " - + mIsBacklightOn); - + ipw.println(TAG + ": " + mKeyboardBacklights.size() + " keyboard backlights"); ipw.increaseIndent(); for (int i = 0; i < mKeyboardBacklights.size(); i++) { KeyboardBacklightState state = mKeyboardBacklights.valueAt(i); @@ -478,12 +547,99 @@ final class KeyboardBacklightController implements } } - private static class KeyboardBacklightState { + private class KeyboardBacklightState { + private final int mDeviceId; private final Light mLight; private int mBrightnessLevel; + private ValueAnimator mAnimator; + private final int[] mBrightnessValueForLevel; + private boolean mUseAmbientController = + InputFeatureFlagProvider.isAmbientKeyboardBacklightControlEnabled(); - KeyboardBacklightState(Light light) { + KeyboardBacklightState(int deviceId, Light light) { + mDeviceId = deviceId; mLight = light; + mBrightnessValueForLevel = setupBrightnessLevels(); + } + + private int[] setupBrightnessLevels() { + if (!InputFeatureFlagProvider.isKeyboardBacklightCustomLevelsEnabled()) { + return DEFAULT_BRIGHTNESS_VALUE_FOR_LEVEL; + } + int[] customLevels = mLight.getPreferredBrightnessLevels(); + if (customLevels == null || customLevels.length == 0) { + return DEFAULT_BRIGHTNESS_VALUE_FOR_LEVEL; + } + TreeSet<Integer> brightnessLevels = new TreeSet<>(); + brightnessLevels.add(0); + for (int level : customLevels) { + if (level > 0 && level < MAX_BRIGHTNESS) { + brightnessLevels.add(level); + } + } + brightnessLevels.add(MAX_BRIGHTNESS); + int brightnessChangeSteps = brightnessLevels.size() - 1; + if (brightnessChangeSteps > MAX_BRIGHTNESS_CHANGE_STEPS) { + return DEFAULT_BRIGHTNESS_VALUE_FOR_LEVEL; + } + int[] result = new int[brightnessLevels.size()]; + int index = 0; + for (int val : brightnessLevels) { + result[index++] = val; + } + return result; + } + + private int getNumBrightnessChangeSteps() { + return mBrightnessValueForLevel.length - 1; + } + + private void onBacklightStateChanged() { + int toValue = mUseAmbientController ? mAmbientBacklightValue + : mBrightnessValueForLevel[mBrightnessLevel]; + setBacklightValue(mIsBacklightOn ? toValue : 0); + } + private void setBrightnessLevel(int brightnessLevel) { + // Once we manually set level, disregard ambient light controller + mUseAmbientController = false; + if (mIsBacklightOn) { + setBacklightValue(mBrightnessValueForLevel[brightnessLevel]); + } + mBrightnessLevel = brightnessLevel; + } + + private void onAmbientBacklightValueChanged() { + if (mIsBacklightOn && mUseAmbientController) { + setBacklightValue(mAmbientBacklightValue); + } + } + + private void cancelAnimation() { + if (mAnimator != null && mAnimator.isRunning()) { + mAnimator.cancel(); + } + } + + private void setBacklightValue(int toValue) { + int fromValue = Color.alpha(mNative.getLightColor(mDeviceId, mLight.getId())); + if (fromValue == toValue) { + return; + } + if (InputFeatureFlagProvider.isKeyboardBacklightAnimationEnabled()) { + startAnimation(fromValue, toValue); + } else { + mNative.setLightColor(mDeviceId, mLight.getId(), Color.argb(toValue, 0, 0, 0)); + } + } + + private void startAnimation(int fromValue, int toValue) { + // Cancel any ongoing animation before starting a new one + cancelAnimation(); + mAnimator = mAnimatorFactory.makeIntAnimator(fromValue, toValue); + mAnimator.addUpdateListener( + (animation) -> mNative.setLightColor(mDeviceId, mLight.getId(), + Color.argb((int) animation.getAnimatedValue(), 0, 0, 0))); + mAnimator.setDuration(TRANSITION_ANIMATION_DURATION_MILLIS).start(); } @Override @@ -493,4 +649,9 @@ final class KeyboardBacklightController implements + "}"; } } + + @VisibleForTesting + interface AnimatorFactory { + ValueAnimator makeIntAnimator(int from, int to); + } } diff --git a/services/core/java/com/android/server/input/KeyboardLayoutManager.java b/services/core/java/com/android/server/input/KeyboardLayoutManager.java index 6ec4022fe15a..3ac15947ab8e 100644 --- a/services/core/java/com/android/server/input/KeyboardLayoutManager.java +++ b/services/core/java/com/android/server/input/KeyboardLayoutManager.java @@ -16,6 +16,10 @@ package com.android.server.input; +import static com.android.server.input.KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_DEVICE; +import static com.android.server.input.KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_USER; +import static com.android.server.input.KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD; + import android.annotation.AnyThread; import android.annotation.MainThread; import android.annotation.NonNull; @@ -67,6 +71,8 @@ import com.android.internal.inputmethod.InputMethodSubtypeHandle; import com.android.internal.messages.nano.SystemMessageProto; import com.android.internal.notification.SystemNotificationChannels; import com.android.internal.util.XmlUtils; +import com.android.server.input.KeyboardMetricsCollector.KeyboardConfigurationEvent; +import com.android.server.input.KeyboardMetricsCollector.LayoutSelectionCriteria; import com.android.server.inputmethod.InputMethodManagerInternal; import libcore.io.Streams; @@ -118,7 +124,7 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener { // This cache stores "best-matched" layouts so that we don't need to run the matching // algorithm repeatedly. @GuardedBy("mKeyboardLayoutCache") - private final Map<String, String> mKeyboardLayoutCache = new ArrayMap<>(); + private final Map<String, KeyboardLayoutInfo> mKeyboardLayoutCache = new ArrayMap<>(); private final Object mImeInfoLock = new Object(); @Nullable @GuardedBy("mImeInfoLock") @@ -194,7 +200,8 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener { setCurrentKeyboardLayoutForInputDevice(inputDevice.getIdentifier(), layout); } } - config.setCurrentLayout(layout); + config.setCurrentLayout( + new KeyboardLayoutInfo(layout, LAYOUT_SELECTION_CRITERIA_USER)); if (layout == null) { // In old settings show notification always until user manually selects a // layout in the settings. @@ -205,18 +212,19 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener { final InputDeviceIdentifier identifier = inputDevice.getIdentifier(); final String key = getLayoutDescriptor(identifier); Set<String> selectedLayouts = new HashSet<>(); - for (ImeInfo imeInfo : getImeInfoListForLayoutMapping()) { + List<ImeInfo> imeInfoList = getImeInfoListForLayoutMapping(); + List<KeyboardLayoutInfo> layoutInfoList = new ArrayList<>(); + boolean hasMissingLayout = false; + for (ImeInfo imeInfo : imeInfoList) { // Check if the layout has been previously configured - String layout = getKeyboardLayoutForInputDeviceInternal(identifier, - new ImeInfo(imeInfo.mUserId, imeInfo.mImeSubtypeHandle, - imeInfo.mImeSubtype)); - if (layout == null) { - // If even one layout not configured properly, we need to ask user to configure - // the keyboard properly from the Settings. - selectedLayouts.clear(); - break; + KeyboardLayoutInfo layoutInfo = getKeyboardLayoutForInputDeviceInternal(identifier, + imeInfo); + boolean noLayoutFound = layoutInfo == null || layoutInfo.mDescriptor == null; + if (!noLayoutFound) { + selectedLayouts.add(layoutInfo.mDescriptor); } - selectedLayouts.add(layout); + layoutInfoList.add(layoutInfo); + hasMissingLayout |= noLayoutFound; } if (DEBUG) { @@ -225,24 +233,38 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener { + selectedLayouts); } + // If even one layout not configured properly, we need to ask user to configure + // the keyboard properly from the Settings. + if (hasMissingLayout) { + selectedLayouts.clear(); + } + config.setConfiguredLayouts(selectedLayouts); // Update current layout: If there is a change then need to reload. synchronized (mImeInfoLock) { - String layout = getKeyboardLayoutForInputDeviceInternal( + KeyboardLayoutInfo layoutInfo = getKeyboardLayoutForInputDeviceInternal( inputDevice.getIdentifier(), mCurrentImeInfo); - if (!Objects.equals(layout, config.getCurrentLayout())) { - config.setCurrentLayout(layout); + if (!Objects.equals(layoutInfo, config.getCurrentLayout())) { + config.setCurrentLayout(layoutInfo); mHandler.sendEmptyMessage(MSG_RELOAD_KEYBOARD_LAYOUTS); } } synchronized (mDataStore) { try { + boolean isFirstConfiguration = !mDataStore.hasInputDeviceEntry(key); if (mDataStore.setSelectedKeyboardLayouts(key, selectedLayouts)) { // Need to show the notification only if layout selection changed // from the previous configuration needToShowNotification = true; + + // Logging keyboard configuration data to statsd only if the + // configuration changed from the previous configuration. Currently + // only logging for New Settings UI where we are using IME to decide + // the layout information. + logKeyboardConfigurationEvent(inputDevice, imeInfoList, layoutInfoList, + isFirstConfiguration); } } finally { mDataStore.saveIfNeeded(); @@ -401,7 +423,7 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener { @AnyThread @Nullable - public KeyboardLayout getKeyboardLayout(String keyboardLayoutDescriptor) { + public KeyboardLayout getKeyboardLayout(@NonNull String keyboardLayoutDescriptor) { Objects.requireNonNull(keyboardLayoutDescriptor, "keyboardLayoutDescriptor must not be null"); @@ -749,8 +771,9 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener { String keyboardLayoutDescriptor; if (useNewSettingsUi()) { synchronized (mImeInfoLock) { - keyboardLayoutDescriptor = getKeyboardLayoutForInputDeviceInternal(identifier, + KeyboardLayoutInfo layoutInfo = getKeyboardLayoutForInputDeviceInternal(identifier, mCurrentImeInfo); + keyboardLayoutDescriptor = layoutInfo == null ? null : layoutInfo.mDescriptor; } } else { keyboardLayoutDescriptor = getCurrentKeyboardLayoutForInputDevice(identifier); @@ -787,13 +810,13 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener { return null; } InputMethodSubtypeHandle subtypeHandle = InputMethodSubtypeHandle.of(imeInfo, imeSubtype); - String layout = getKeyboardLayoutForInputDeviceInternal(identifier, + KeyboardLayoutInfo layoutInfo = getKeyboardLayoutForInputDeviceInternal(identifier, new ImeInfo(userId, subtypeHandle, imeSubtype)); if (DEBUG) { Slog.d(TAG, "getKeyboardLayoutForInputDevice() " + identifier.toString() + ", userId : " - + userId + ", subtypeHandle = " + subtypeHandle + " -> " + layout); + + userId + ", subtypeHandle = " + subtypeHandle + " -> " + layoutInfo); } - return layout; + return layoutInfo != null ? layoutInfo.mDescriptor : null; } @AnyThread @@ -924,11 +947,11 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener { for (int i = 0; i < mConfiguredKeyboards.size(); i++) { InputDevice inputDevice = Objects.requireNonNull( getInputDevice(mConfiguredKeyboards.keyAt(i))); - String layout = getKeyboardLayoutForInputDeviceInternal(inputDevice.getIdentifier(), - mCurrentImeInfo); + KeyboardLayoutInfo layoutInfo = getKeyboardLayoutForInputDeviceInternal( + inputDevice.getIdentifier(), mCurrentImeInfo); KeyboardConfiguration config = mConfiguredKeyboards.valueAt(i); - if (!Objects.equals(layout, config.getCurrentLayout())) { - config.setCurrentLayout(layout); + if (!Objects.equals(layoutInfo, config.getCurrentLayout())) { + config.setCurrentLayout(layoutInfo); mHandler.sendEmptyMessage(MSG_RELOAD_KEYBOARD_LAYOUTS); return; } @@ -937,39 +960,40 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener { } @Nullable - private String getKeyboardLayoutForInputDeviceInternal(InputDeviceIdentifier identifier, - @Nullable ImeInfo imeInfo) { + private KeyboardLayoutInfo getKeyboardLayoutForInputDeviceInternal( + InputDeviceIdentifier identifier, @Nullable ImeInfo imeInfo) { InputDevice inputDevice = getInputDevice(identifier); if (inputDevice == null || inputDevice.isVirtual() || !inputDevice.isFullKeyboard()) { return null; } String key = createLayoutKey(identifier, imeInfo); - String layout; synchronized (mDataStore) { - layout = mDataStore.getKeyboardLayout(getLayoutDescriptor(identifier), key); - } - if (layout == null) { - synchronized (mKeyboardLayoutCache) { - // Check Auto-selected layout cache to see if layout had been previously selected - if (mKeyboardLayoutCache.containsKey(key)) { - layout = mKeyboardLayoutCache.get(key); - } else { - // NOTE: This list is already filtered based on IME Script code - KeyboardLayout[] layoutList = getKeyboardLayoutListForInputDeviceInternal( - identifier, imeInfo); - // Call auto-matching algorithm to find the best matching layout - layout = getDefaultKeyboardLayoutBasedOnImeInfo(inputDevice, imeInfo, - layoutList); - mKeyboardLayoutCache.put(key, layout); - } + String layout = mDataStore.getKeyboardLayout(getLayoutDescriptor(identifier), key); + if (layout != null) { + return new KeyboardLayoutInfo(layout, LAYOUT_SELECTION_CRITERIA_USER); + } + } + + synchronized (mKeyboardLayoutCache) { + // Check Auto-selected layout cache to see if layout had been previously selected + if (mKeyboardLayoutCache.containsKey(key)) { + return mKeyboardLayoutCache.get(key); + } else { + // NOTE: This list is already filtered based on IME Script code + KeyboardLayout[] layoutList = getKeyboardLayoutListForInputDeviceInternal( + identifier, imeInfo); + // Call auto-matching algorithm to find the best matching layout + KeyboardLayoutInfo layoutInfo = + getDefaultKeyboardLayoutBasedOnImeInfo(inputDevice, imeInfo, layoutList); + mKeyboardLayoutCache.put(key, layoutInfo); + return layoutInfo; } } - return layout; } @Nullable - private static String getDefaultKeyboardLayoutBasedOnImeInfo(InputDevice inputDevice, - @Nullable ImeInfo imeInfo, KeyboardLayout[] layoutList) { + private static KeyboardLayoutInfo getDefaultKeyboardLayoutBasedOnImeInfo( + InputDevice inputDevice, @Nullable ImeInfo imeInfo, KeyboardLayout[] layoutList) { Arrays.sort(layoutList); // Check <VendorID, ProductID> matching for explicitly declared custom KCM files. @@ -982,7 +1006,8 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener { + "vendor and product Ids. " + inputDevice.getIdentifier() + " : " + layout.getDescriptor()); } - return layout.getDescriptor(); + return new KeyboardLayoutInfo(layout.getDescriptor(), + LAYOUT_SELECTION_CRITERIA_DEVICE); } } @@ -999,7 +1024,7 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener { + "HW information (Language tag and Layout type). " + inputDevice.getIdentifier() + " : " + layoutDesc); } - return layoutDesc; + return new KeyboardLayoutInfo(layoutDesc, LAYOUT_SELECTION_CRITERIA_DEVICE); } } @@ -1021,7 +1046,10 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener { + "IME locale matching. " + inputDevice.getIdentifier() + " : " + layoutDesc); } - return layoutDesc; + if (layoutDesc != null) { + return new KeyboardLayoutInfo(layoutDesc, LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD); + } + return null; } @Nullable @@ -1227,6 +1255,26 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener { } } + private void logKeyboardConfigurationEvent(@NonNull InputDevice inputDevice, + @NonNull List<ImeInfo> imeInfoList, @NonNull List<KeyboardLayoutInfo> layoutInfoList, + boolean isFirstConfiguration) { + if (imeInfoList.isEmpty() || layoutInfoList.isEmpty()) { + return; + } + KeyboardConfigurationEvent.Builder configurationEventBuilder = + new KeyboardConfigurationEvent.Builder(inputDevice).setIsFirstTimeConfiguration( + isFirstConfiguration); + for (int i = 0; i < imeInfoList.size(); i++) { + KeyboardLayoutInfo layoutInfo = layoutInfoList.get(i); + boolean noLayoutFound = layoutInfo == null || layoutInfo.mDescriptor == null; + configurationEventBuilder.addLayoutSelection(imeInfoList.get(i).mImeSubtype, + noLayoutFound ? null : getKeyboardLayout(layoutInfo.mDescriptor), + noLayoutFound ? LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD + : layoutInfo.mSelectionCriteria); + } + KeyboardMetricsCollector.logKeyboardConfiguredAtom(configurationEventBuilder.build()); + } + private boolean handleMessage(Message msg) { switch (msg.what) { case MSG_UPDATE_EXISTING_DEVICES: @@ -1409,7 +1457,7 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener { // If null, it means no layout is selected for the device. @Nullable - private String mCurrentLayout; + private KeyboardLayoutInfo mCurrentLayout; private boolean hasConfiguredLayouts() { return mConfiguredLayouts != null && !mConfiguredLayouts.isEmpty(); @@ -1425,15 +1473,42 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener { } @Nullable - private String getCurrentLayout() { + private KeyboardLayoutInfo getCurrentLayout() { return mCurrentLayout; } - private void setCurrentLayout(String currentLayout) { + private void setCurrentLayout(KeyboardLayoutInfo currentLayout) { mCurrentLayout = currentLayout; } } + private static class KeyboardLayoutInfo { + @Nullable + private final String mDescriptor; + @LayoutSelectionCriteria + private final int mSelectionCriteria; + + private KeyboardLayoutInfo(@Nullable String descriptor, + @LayoutSelectionCriteria int selectionCriteria) { + mDescriptor = descriptor; + mSelectionCriteria = selectionCriteria; + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof KeyboardLayoutInfo) { + return Objects.equals(mDescriptor, ((KeyboardLayoutInfo) obj).mDescriptor) + && mSelectionCriteria == ((KeyboardLayoutInfo) obj).mSelectionCriteria; + } + return false; + } + + @Override + public int hashCode() { + return 31 * mSelectionCriteria + mDescriptor.hashCode(); + } + } + private interface KeyboardLayoutVisitor { void visitKeyboardLayout(Resources resources, int keyboardLayoutResId, KeyboardLayout layout); diff --git a/services/core/java/com/android/server/input/KeyboardMetricsCollector.java b/services/core/java/com/android/server/input/KeyboardMetricsCollector.java new file mode 100644 index 000000000000..19fa7a8e0aca --- /dev/null +++ b/services/core/java/com/android/server/input/KeyboardMetricsCollector.java @@ -0,0 +1,308 @@ +/* + * Copyright 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.input; + +import static java.lang.annotation.RetentionPolicy.SOURCE; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.hardware.input.KeyboardLayout; +import android.icu.util.ULocale; +import android.util.Log; +import android.util.Slog; +import android.util.proto.ProtoOutputStream; +import android.view.InputDevice; +import android.view.inputmethod.InputMethodSubtype; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.os.KeyboardConfiguredProto.KeyboardLayoutConfig; +import com.android.internal.os.KeyboardConfiguredProto.RepeatedKeyboardLayoutConfig; +import com.android.internal.util.FrameworkStatsLog; + +import java.lang.annotation.Retention; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** + * Collect Keyboard metrics + */ +public final class KeyboardMetricsCollector { + private static final String TAG = "KeyboardMetricCollector"; + + // To enable these logs, run: 'adb shell setprop log.tag.KeyboardMetricCollector DEBUG' + // (requires restart) + private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); + + @Retention(SOURCE) + @IntDef(prefix = { "LAYOUT_SELECTION_CRITERIA_" }, value = { + LAYOUT_SELECTION_CRITERIA_USER, + LAYOUT_SELECTION_CRITERIA_DEVICE, + LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD + }) + public @interface LayoutSelectionCriteria {} + + /** Manual selection by user */ + public static final int LAYOUT_SELECTION_CRITERIA_USER = 0; + + /** Auto-detection based on device provided language tag and layout type */ + public static final int LAYOUT_SELECTION_CRITERIA_DEVICE = 1; + + /** Auto-detection based on IME provided language tag and layout type */ + public static final int LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD = 2; + + @VisibleForTesting + static final String DEFAULT_LAYOUT = "Default"; + + /** + * Log keyboard system shortcuts for the proto + * {@link com.android.os.input.KeyboardSystemsEventReported} + * defined in "stats/atoms/input/input_extension_atoms.proto" + */ + public static void logKeyboardSystemsEventReportedAtom(InputDevice inputDevice, + int keyboardSystemEvent, int[] keyCode, int modifierState) { + int vendorId = inputDevice.getVendorId(); + int productId = inputDevice.getProductId(); + FrameworkStatsLog.write(FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED, + vendorId, productId, keyboardSystemEvent, keyCode, modifierState); + } + + /** + * Function to log the KeyboardConfigured + * {@link com.android.os.input.KeyboardConfigured} atom + * + * @param event {@link KeyboardConfigurationEvent} contains information about keyboard + * configuration. Use {@link KeyboardConfigurationEvent.Builder} to create the + * configuration event to log. + */ + public static void logKeyboardConfiguredAtom(KeyboardConfigurationEvent event) { + // Creating proto to log nested field KeyboardLayoutConfig in atom + ProtoOutputStream proto = new ProtoOutputStream(); + + for (LayoutConfiguration layoutConfiguration : event.getLayoutConfigurations()) { + addKeyboardLayoutConfigurationToProto(proto, layoutConfiguration); + } + // Push the atom to Statsd + FrameworkStatsLog.write(FrameworkStatsLog.KEYBOARD_CONFIGURED, + event.isFirstConfiguration(), event.getVendorId(), event.getProductId(), + proto.getBytes()); + + if (DEBUG) { + Slog.d(TAG, "Logging Keyboard configuration event: " + event); + } + } + + /** + * Populate the KeyboardLayoutConfig proto which is a repeated proto + * in the RepeatedKeyboardLayoutConfig proto with values from the + * {@link LayoutConfiguration} class + * The proto definitions can be found at: + * "frameworks/proto_logging/stats/atoms/input/input_extension_atoms.proto" + * + * @param proto Representing the nested proto RepeatedKeyboardLayoutConfig + * @param layoutConfiguration Class containing the fields for populating the + * KeyboardLayoutConfig proto + */ + private static void addKeyboardLayoutConfigurationToProto(ProtoOutputStream proto, + LayoutConfiguration layoutConfiguration) { + // Start a new KeyboardLayoutConfig proto. + long keyboardLayoutConfigToken = proto.start( + RepeatedKeyboardLayoutConfig.KEYBOARD_LAYOUT_CONFIG); + proto.write(KeyboardLayoutConfig.KEYBOARD_LANGUAGE_TAG, + layoutConfiguration.keyboardLanguageTag); + proto.write(KeyboardLayoutConfig.KEYBOARD_LAYOUT_TYPE, + layoutConfiguration.keyboardLayoutType); + proto.write(KeyboardLayoutConfig.KEYBOARD_LAYOUT_NAME, + layoutConfiguration.keyboardLayoutName); + proto.write(KeyboardLayoutConfig.LAYOUT_SELECTION_CRITERIA, + layoutConfiguration.layoutSelectionCriteria); + proto.end(keyboardLayoutConfigToken); + } + + /** + * Class representing the proto KeyboardLayoutConfig defined in + * "frameworks/proto_logging/stats/atoms/input/input_extension_atoms.proto + * + * @see com.android.os.input.KeyboardConfigured + */ + public static class KeyboardConfigurationEvent { + + private final InputDevice mInputDevice; + private final boolean mIsFirstConfiguration; + private final List<LayoutConfiguration> mLayoutConfigurations; + + private KeyboardConfigurationEvent(InputDevice inputDevice, boolean isFirstConfiguration, + List<LayoutConfiguration> layoutConfigurations) { + mInputDevice = inputDevice; + mIsFirstConfiguration = isFirstConfiguration; + mLayoutConfigurations = layoutConfigurations; + } + + public int getVendorId() { + return mInputDevice.getVendorId(); + } + + public int getProductId() { + return mInputDevice.getProductId(); + } + + public boolean isFirstConfiguration() { + return mIsFirstConfiguration; + } + + public List<LayoutConfiguration> getLayoutConfigurations() { + return mLayoutConfigurations; + } + + @Override + public String toString() { + return "InputDevice = {VendorId = " + Integer.toHexString(getVendorId()) + + ", ProductId = " + Integer.toHexString(getProductId()) + + "}, isFirstConfiguration = " + mIsFirstConfiguration + + ", LayoutConfigurations = " + mLayoutConfigurations; + } + + /** + * Builder class to help create {@link KeyboardConfigurationEvent}. + */ + public static class Builder { + @NonNull + private final InputDevice mInputDevice; + private boolean mIsFirstConfiguration; + private final List<InputMethodSubtype> mImeSubtypeList = new ArrayList<>(); + private final List<KeyboardLayout> mSelectedLayoutList = new ArrayList<>(); + private final List<Integer> mLayoutSelectionCriteriaList = new ArrayList<>(); + + public Builder(@NonNull InputDevice inputDevice) { + Objects.requireNonNull(inputDevice, "InputDevice provided should not be null"); + mInputDevice = inputDevice; + } + + /** + * Set whether this is the first time this keyboard is configured. + */ + public Builder setIsFirstTimeConfiguration(boolean isFirstTimeConfiguration) { + mIsFirstConfiguration = isFirstTimeConfiguration; + return this; + } + + /** + * Adds keyboard layout configuration info for a particular IME subtype language + */ + public Builder addLayoutSelection(@NonNull InputMethodSubtype imeSubtype, + @Nullable KeyboardLayout selectedLayout, + @LayoutSelectionCriteria int layoutSelectionCriteria) { + Objects.requireNonNull(imeSubtype, "IME subtype provided should not be null"); + if (!isValidSelectionCriteria(layoutSelectionCriteria)) { + throw new IllegalStateException("Invalid layout selection criteria"); + } + mImeSubtypeList.add(imeSubtype); + mSelectedLayoutList.add(selectedLayout); + mLayoutSelectionCriteriaList.add(layoutSelectionCriteria); + return this; + } + + /** + * Creates {@link KeyboardConfigurationEvent} from the provided information + */ + public KeyboardConfigurationEvent build() { + int size = mImeSubtypeList.size(); + if (size == 0) { + throw new IllegalStateException("Should have at least one configuration"); + } + List<LayoutConfiguration> configurationList = new ArrayList<>(); + for (int i = 0; i < size; i++) { + KeyboardLayout selectedLayout = mSelectedLayoutList.get(i); + @LayoutSelectionCriteria int layoutSelectionCriteria = + mLayoutSelectionCriteriaList.get(i); + InputMethodSubtype imeSubtype = mImeSubtypeList.get(i); + String keyboardLanguageTag; + String keyboardLayoutStringType; + if (layoutSelectionCriteria == LAYOUT_SELECTION_CRITERIA_DEVICE) { + keyboardLanguageTag = mInputDevice.getKeyboardLanguageTag(); + keyboardLayoutStringType = mInputDevice.getKeyboardLayoutType(); + } else { + ULocale pkLocale = imeSubtype.getPhysicalKeyboardHintLanguageTag(); + keyboardLanguageTag = pkLocale != null ? pkLocale.toLanguageTag() + : imeSubtype.getCanonicalizedLanguageTag(); + keyboardLayoutStringType = imeSubtype.getPhysicalKeyboardHintLayoutType(); + } + // Sanitize null values + String keyboardLayoutName = + selectedLayout == null ? DEFAULT_LAYOUT : selectedLayout.getLabel(); + keyboardLanguageTag = keyboardLanguageTag == null ? "" : keyboardLanguageTag; + int keyboardLayoutType = KeyboardLayout.LayoutType.getLayoutTypeEnumValue( + keyboardLayoutStringType); + + configurationList.add( + new LayoutConfiguration(keyboardLayoutType, keyboardLanguageTag, + keyboardLayoutName, layoutSelectionCriteria)); + } + return new KeyboardConfigurationEvent(mInputDevice, mIsFirstConfiguration, + configurationList); + } + } + } + + @VisibleForTesting + static class LayoutConfiguration { + // This should match enum values defined in "frameworks/base/core/res/res/values/attrs.xml" + public final int keyboardLayoutType; + public final String keyboardLanguageTag; + public final String keyboardLayoutName; + @LayoutSelectionCriteria + public final int layoutSelectionCriteria; + + private LayoutConfiguration(int keyboardLayoutType, String keyboardLanguageTag, + String keyboardLayoutName, @LayoutSelectionCriteria int layoutSelectionCriteria) { + this.keyboardLayoutType = keyboardLayoutType; + this.keyboardLanguageTag = keyboardLanguageTag; + this.keyboardLayoutName = keyboardLayoutName; + this.layoutSelectionCriteria = layoutSelectionCriteria; + } + + @Override + public String toString() { + return "{keyboardLanguageTag = " + keyboardLanguageTag + " keyboardLayoutType = " + + KeyboardLayout.LayoutType.getLayoutNameFromValue(keyboardLayoutType) + + " keyboardLayoutName = " + keyboardLayoutName + " layoutSelectionCriteria = " + + getStringForSelectionCriteria(layoutSelectionCriteria) + "}"; + } + } + + private static String getStringForSelectionCriteria( + @LayoutSelectionCriteria int layoutSelectionCriteria) { + switch (layoutSelectionCriteria) { + case LAYOUT_SELECTION_CRITERIA_USER: + return "LAYOUT_SELECTION_CRITERIA_USER"; + case LAYOUT_SELECTION_CRITERIA_DEVICE: + return "LAYOUT_SELECTION_CRITERIA_DEVICE"; + case LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD: + return "LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD"; + default: + return "INVALID_CRITERIA"; + } + } + + private static boolean isValidSelectionCriteria(int layoutSelectionCriteria) { + return layoutSelectionCriteria == LAYOUT_SELECTION_CRITERIA_USER + || layoutSelectionCriteria == LAYOUT_SELECTION_CRITERIA_DEVICE + || layoutSelectionCriteria == LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD; + } +} + diff --git a/services/core/java/com/android/server/input/PersistentDataStore.java b/services/core/java/com/android/server/input/PersistentDataStore.java index bce210d0a4a4..31083fd5de03 100644 --- a/services/core/java/com/android/server/input/PersistentDataStore.java +++ b/services/core/java/com/android/server/input/PersistentDataStore.java @@ -101,6 +101,10 @@ final class PersistentDataStore { } } + public boolean hasInputDeviceEntry(String inputDeviceDescriptor) { + return getInputDeviceState(inputDeviceDescriptor) != null; + } + public TouchCalibration getTouchCalibration(String inputDeviceDescriptor, int surfaceRotation) { InputDeviceState state = getInputDeviceState(inputDeviceDescriptor); if (state == null) { diff --git a/services/core/java/com/android/server/input/UEventManager.java b/services/core/java/com/android/server/input/UEventManager.java new file mode 100644 index 000000000000..17d87e4f0596 --- /dev/null +++ b/services/core/java/com/android/server/input/UEventManager.java @@ -0,0 +1,42 @@ +/* + * Copyright 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.input; + +import android.os.UEventObserver; + +/** An interface used to change the API of UEventObserver to a more test-friendly format. */ +interface UEventManager { + + abstract class UEventListener { + private final UEventObserver mObserver = new UEventObserver() { + @Override + public void onUEvent(UEvent event) { + UEventListener.this.onUEvent(event); + } + }; + + public abstract void onUEvent(UEventObserver.UEvent event); + } + + default void addListener(UEventListener listener, String match) { + listener.mObserver.startObserving(match); + } + + default void removeListener(UEventListener listener) { + listener.mObserver.stopObserving(); + } +} diff --git a/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java b/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java index ba9e280be49d..b0b1d676bc4b 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java @@ -352,6 +352,7 @@ final class InputMethodBindingController { clearCurMethodAndSessions(); mService.clearInputShownLocked(); mService.unbindCurrentClientLocked(UnbindReason.DISCONNECT_IME); + mService.resetSystemUiLocked(); } } } diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index 57f8d1478905..4dbd82065a66 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -633,8 +633,9 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub private InputMethodSubtype mCurrentSubtype; /** - * {@code true} if the IME has not been mostly hidden via {@link android.view.InsetsController} + * {@code true} if the IME has not been mostly hidden via {@link android.view.InsetsController}. */ + @GuardedBy("ImfLock.class") private boolean mCurPerceptible; /** @@ -748,33 +749,26 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub SparseArray<AccessibilitySessionState> mEnabledAccessibilitySessions = new SparseArray<>(); /** - * True if the device is currently interactive with user. The value is true initially. + * {@code true} if the device is currently interactive with the user, initially true. + * + * @see #handleSetInteractive */ + @GuardedBy("ImfLock.class") boolean mIsInteractive = true; + @GuardedBy("ImfLock.class") + @InputMethodService.BackDispositionMode int mBackDisposition = InputMethodService.BACK_DISPOSITION_DEFAULT; /** - * A set of status bits regarding the active IME. + * The {@link InputMethodService.ImeWindowVisibility} of the currently bound IME, + * or {@code 0} if no IME is bound. * - * <p>This value is a combination of following two bits:</p> - * <dl> - * <dt>{@link InputMethodService#IME_ACTIVE}</dt> - * <dd> - * If this bit is ON, connected IME is ready to accept touch/key events. - * </dd> - * <dt>{@link InputMethodService#IME_VISIBLE}</dt> - * <dd> - * If this bit is ON, some of IME view, e.g. software input, candidate view, is visible. - * </dd> - * <dt>{@link InputMethodService#IME_INVISIBLE}</dt> - * <dd> If this bit is ON, IME is ready with views from last EditorInfo but is - * currently invisible. - * </dd> - * </dl> - * <em>Do not update this value outside of {@link #setImeWindowStatus(IBinder, int, int)} and + * <p><em>Do not update this value outside of {@link #setImeWindowStatus(IBinder, int, int)} and * {@link InputMethodBindingController#unbindCurrentMethod()}.</em> */ + @GuardedBy("ImfLock.class") + @InputMethodService.ImeWindowVisibility int mImeWindowVis; private LocaleList mLastSystemLocales; @@ -1535,7 +1529,6 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub // Uh oh, current input method is no longer around! // Pick another one... Slog.i(TAG, "Current input method removed: " + curInputMethodId); - updateSystemUiLocked(0 /* vis */, mBackDisposition); if (!chooseNewDefaultIMELocked()) { changed = true; curIm = null; @@ -2938,7 +2931,6 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub sessionState.mSession.finishSession(); } catch (RemoteException e) { Slog.w(TAG, "Session failed to close due to remote exception", e); - updateSystemUiLocked(0 /* vis */, mBackDisposition); } sessionState.mSession = null; } @@ -3048,15 +3040,20 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub } @GuardedBy("ImfLock.class") - private boolean shouldShowImeSwitcherLocked(int visibility) { + private boolean shouldShowImeSwitcherLocked( + @InputMethodService.ImeWindowVisibility int visibility) { if (!mShowOngoingImeSwitcherForPhones) return false; + // When the IME switcher dialog is shown, the IME switcher button should be hidden. if (mMenuController.getSwitchingDialogLocked() != null) return false; + // When we are switching IMEs, the IME switcher button should be hidden. + if (!Objects.equals(getCurIdLocked(), getSelectedMethodIdLocked())) { + return false; + } if (mWindowManagerInternal.isKeyguardShowingAndNotOccluded() && mWindowManagerInternal.isKeyguardSecure(mSettings.getCurrentUserId())) { return false; } - if ((visibility & InputMethodService.IME_ACTIVE) == 0 - || (visibility & InputMethodService.IME_INVISIBLE) != 0) { + if ((visibility & InputMethodService.IME_ACTIVE) == 0) { return false; } if (mWindowManagerInternal.isHardKeyboardAvailable()) { @@ -3115,7 +3112,9 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub @BinderThread @SuppressWarnings("deprecation") - private void setImeWindowStatus(@NonNull IBinder token, int vis, int backDisposition) { + private void setImeWindowStatus(@NonNull IBinder token, + @InputMethodService.ImeWindowVisibility int vis, + @InputMethodService.BackDispositionMode int backDisposition) { final int topFocusedDisplayId = mWindowManagerInternal.getTopFocusedDisplayId(); synchronized (ImfLock.class) { @@ -3132,7 +3131,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub } mImeWindowVis = vis; mBackDisposition = backDisposition; - updateSystemUiLocked(vis, backDisposition); + updateSystemUiLocked(mImeWindowVis, mBackDisposition); } final boolean dismissImeOnBackKeyPressed; @@ -3167,37 +3166,46 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub private void updateImeWindowStatus(boolean disableImeIcon) { synchronized (ImfLock.class) { - if (disableImeIcon) { - updateSystemUiLocked(0, mBackDisposition); - } else { - updateSystemUiLocked(); - } + // TODO(b/285109020): disableImeIcon should be stored in a property like + // mIsSwitcherIconDisabled, but it is currently not reliably cleared. + updateSystemUiLocked(disableImeIcon ? 0 : mImeWindowVis, mBackDisposition); } } @GuardedBy("ImfLock.class") void updateSystemUiLocked() { + // This is only used by InputMethodMenuController to trigger the IME switcher icon + // visibility, by having {@code shouldShowImeSwitcherLocked} called, which depends on the + // visibility of the IME switcher dialog. updateSystemUiLocked(mImeWindowVis, mBackDisposition); } // Caution! This method is called in this class. Handle multi-user carefully @GuardedBy("ImfLock.class") - private void updateSystemUiLocked(int vis, int backDisposition) { + private void updateSystemUiLocked(@InputMethodService.ImeWindowVisibility int vis, + @InputMethodService.BackDispositionMode int backDisposition) { if (getCurTokenLocked() == null) { return; } if (DEBUG) { Slog.d(TAG, "IME window vis: " + vis - + " active: " + (vis & InputMethodService.IME_ACTIVE) - + " inv: " + (vis & InputMethodService.IME_INVISIBLE) + + " active: " + ((vis & InputMethodService.IME_ACTIVE) != 0) + + " visible: " + ((vis & InputMethodService.IME_VISIBLE) != 0) + + " backDisposition: " + backDisposition + + " isInteractive: " + mIsInteractive + + " curPerceptible: " + mCurPerceptible + " displayId: " + mCurTokenDisplayId); } // TODO: Move this clearing calling identity block to setImeWindowStatus after making sure - // all updateSystemUi happens on system privilege. + // all updateSystemUi happens on system privilege. final long ident = Binder.clearCallingIdentity(); try { - if (!mCurPerceptible) { + if (!mIsInteractive) { + // When we are not interactive, + // the visibility should be 0 (no IME icons should be shown). + vis = 0; + } else if (!mCurPerceptible) { if ((vis & InputMethodService.IME_VISIBLE) != 0) { vis &= ~InputMethodService.IME_VISIBLE; vis |= InputMethodService.IME_VISIBLE_IMPERCEPTIBLE; @@ -3205,7 +3213,12 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub } else { vis &= ~InputMethodService.IME_VISIBLE_IMPERCEPTIBLE; } - // mImeWindowVis should be updated before calling shouldShowImeSwitcherLocked(). + if (mMenuController.getSwitchingDialogLocked() != null + || !Objects.equals(getCurIdLocked(), getSelectedMethodIdLocked())) { + // When the IME switcher dialog is shown, or we are switching IMEs, + // the back button should be in the default state (as if the IME is not shown). + backDisposition = InputMethodService.BACK_DISPOSITION_ADJUST_NOTHING; + } final boolean needsToShowImeSwitcher = shouldShowImeSwitcherLocked(vis); if (mStatusBarManagerInternal != null) { mStatusBarManagerInternal.setImeWindowStatus(mCurTokenDisplayId, @@ -3527,7 +3540,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub return; } mCurPerceptible = perceptible; - updateSystemUiLocked(); + updateSystemUiLocked(mImeWindowVis, mBackDisposition); } }); } @@ -5089,8 +5102,11 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub private void handleSetInteractive(final boolean interactive) { synchronized (ImfLock.class) { + if (mIsInteractive == interactive) { + return; + } mIsInteractive = interactive; - updateSystemUiLocked(interactive ? mImeWindowVis : 0, mBackDisposition); + updateSystemUiLocked(mImeWindowVis, mBackDisposition); // Inform the current client of the change in active status if (mCurClient == null || mCurClient.mClient == null) { @@ -5769,7 +5785,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub // input target changed, in case seeing the dialog dismiss flickering during // the next focused window starting the input connection. if (mLastImeTargetWindow != mCurFocusedWindow) { - mMenuController.hideInputMethodMenu(); + mMenuController.hideInputMethodMenuLocked(); } } } @@ -6725,7 +6741,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub @BinderThread @Override - public void setImeWindowStatusAsync(int vis, int backDisposition) { + public void setImeWindowStatusAsync(@InputMethodService.ImeWindowVisibility int vis, + @InputMethodService.BackDispositionMode int backDisposition) { mImms.setImeWindowStatus(mToken, vis, backDisposition); } diff --git a/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java b/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java index c212e8e3c82c..c2ef83d06690 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java @@ -175,13 +175,13 @@ final class InputMethodMenuController { int subtypeId = mSubtypeIds[which]; adapter.mCheckedItem = which; adapter.notifyDataSetChanged(); - hideInputMethodMenu(); if (im != null) { if (subtypeId < 0 || subtypeId >= im.getSubtypeCount()) { subtypeId = NOT_A_SUBTYPE_ID; } mService.setInputMethodLocked(im.getId(), subtypeId); } + hideInputMethodMenuLocked(); } }; mDialogBuilder.setSingleChoiceItems(adapter, checkedItem, choiceListener); @@ -220,12 +220,18 @@ final class InputMethodMenuController { } } + /** + * Hides the input method switcher menu. + */ void hideInputMethodMenu() { synchronized (ImfLock.class) { hideInputMethodMenuLocked(); } } + /** + * Hides the input method switcher menu, synchronised version of {@link #hideInputMethodMenu}. + */ @GuardedBy("ImfLock.class") void hideInputMethodMenuLocked() { if (DEBUG) Slog.v(TAG, "Hide switching menu"); diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubService.java b/services/core/java/com/android/server/location/contexthub/ContextHubService.java index 5f783742bd26..93c66a19c89d 100644 --- a/services/core/java/com/android/server/location/contexthub/ContextHubService.java +++ b/services/core/java/com/android/server/location/contexthub/ContextHubService.java @@ -52,6 +52,7 @@ import android.os.ResultReceiver; import android.os.ShellCallback; import android.os.SystemClock; import android.os.UserHandle; +import android.os.UserManager; import android.provider.Settings; import android.util.Log; import android.util.Pair; @@ -169,6 +170,8 @@ public class ContextHubService extends IContextHubService.Stub { private SensorPrivacyManagerInternal mSensorPrivacyManagerInternal; + private UserManager mUserManager = null; + private final Map<Integer, AtomicLong> mLastRestartTimestampMap = new HashMap<>(); /** @@ -491,6 +494,14 @@ public class ContextHubService extends IContextHubService.Stub { return; } + if (mUserManager == null) { + mUserManager = mContext.getSystemService(UserManager.class); + if (mUserManager == null) { + Log.e(TAG, "Unable to get the UserManager service"); + return; + } + } + sendMicrophoneDisableSettingUpdateForCurrentUser(); if (mSensorPrivacyManagerInternal == null) { Log.e(TAG, "Unable to add a sensor privacy listener for all users"); @@ -499,8 +510,9 @@ public class ContextHubService extends IContextHubService.Stub { mSensorPrivacyManagerInternal.addSensorPrivacyListenerForAllUsers( SensorPrivacyManager.Sensors.MICROPHONE, (userId, enabled) -> { - if (userId == getCurrentUserId()) { - Log.d(TAG, "User: " + userId + "mic privacy: " + enabled); + // If we are in HSUM mode, any user can change the microphone setting + if (mUserManager.isHeadlessSystemUserMode() || userId == getCurrentUserId()) { + Log.d(TAG, "User: " + userId + " mic privacy: " + enabled); sendMicrophoneDisableSettingUpdate(enabled); } }); diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java index 2e62ef4f8566..a96e4adf1fee 100644 --- a/services/core/java/com/android/server/locksettings/LockSettingsService.java +++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java @@ -31,7 +31,6 @@ import static android.os.UserHandle.USER_ALL; import static android.os.UserHandle.USER_SYSTEM; import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_NONE; -import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD; import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD_OR_PIN; import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PIN; import static com.android.internal.widget.LockPatternUtils.CURRENT_LSKF_BASED_PROTECTOR_ID_KEY; @@ -40,8 +39,12 @@ import static com.android.internal.widget.LockPatternUtils.PIN_LENGTH_UNAVAILABL import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT; import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE; import static com.android.internal.widget.LockPatternUtils.USER_FRP; +import static com.android.internal.widget.LockPatternUtils.USER_REPAIR_MODE; import static com.android.internal.widget.LockPatternUtils.VERIFY_FLAG_REQUEST_GK_PW_HANDLE; +import static com.android.internal.widget.LockPatternUtils.VERIFY_FLAG_WRITE_REPAIR_MODE_PW; import static com.android.internal.widget.LockPatternUtils.frpCredentialEnabled; +import static com.android.internal.widget.LockPatternUtils.isSpecialUserId; +import static com.android.internal.widget.LockPatternUtils.pinOrPasswordQualityToCredentialType; import static com.android.internal.widget.LockPatternUtils.userOwnsFrpCredential; import static com.android.server.locksettings.SyntheticPasswordManager.TOKEN_TYPE_STRONG; import static com.android.server.locksettings.SyntheticPasswordManager.TOKEN_TYPE_WEAK; @@ -278,8 +281,6 @@ public class LockSettingsService extends ILockSettings.Stub { protected IGateKeeperService mGateKeeperService; protected IAuthSecret mAuthSecretService; - private static final String GSI_RUNNING_PROP = "ro.gsid.image_running"; - /** * The UIDs that are used for system credential storage in keystore. */ @@ -311,6 +312,7 @@ public class LockSettingsService extends ILockSettings.Stub { if (phase == PHASE_ACTIVITY_MANAGER_READY) { mLockSettingsService.migrateOldDataAfterSystemReady(); mLockSettingsService.loadEscrowData(); + mLockSettingsService.deleteRepairModePersistentDataIfNeeded(); } } @@ -541,7 +543,7 @@ public class LockSettingsService extends ILockSettings.Stub { } public boolean isGsiRunning() { - return SystemProperties.getInt(GSI_RUNNING_PROP, 0) > 0; + return LockPatternUtils.isGsiRunning(); } public FingerprintManager getFingerprintManager() { @@ -949,6 +951,16 @@ public class LockSettingsService extends ILockSettings.Stub { return success; } + @VisibleForTesting + void deleteRepairModePersistentDataIfNeeded() { + if (!LockPatternUtils.isRepairModeSupported(mContext) + || LockPatternUtils.isRepairModeActive(mContext) + || mInjector.isGsiRunning()) { + return; + } + mStorage.deleteRepairModePersistentData(); + } + // This is called when Weaver is guaranteed to be available (if the device supports Weaver). // It does any synthetic password related work that was delayed from earlier in the boot. private void onThirdPartyAppsStarted() { @@ -1279,8 +1291,8 @@ public class LockSettingsService extends ILockSettings.Stub { * {@link #CREDENTIAL_TYPE_PASSWORD} */ private int getCredentialTypeInternal(int userId) { - if (userId == USER_FRP) { - return getFrpCredentialType(); + if (isSpecialUserId(userId)) { + return mSpManager.getSpecialUserCredentialType(userId); } synchronized (mSpManager) { final long protectorId = getCurrentLskfBasedProtectorId(userId); @@ -1296,29 +1308,6 @@ public class LockSettingsService extends ILockSettings.Stub { } } - private int getFrpCredentialType() { - PersistentData data = mStorage.readPersistentDataBlock(); - if (data.type != PersistentData.TYPE_SP_GATEKEEPER && - data.type != PersistentData.TYPE_SP_WEAVER) { - return CREDENTIAL_TYPE_NONE; - } - int credentialType = SyntheticPasswordManager.getFrpCredentialType(data.payload); - if (credentialType != CREDENTIAL_TYPE_PASSWORD_OR_PIN) { - return credentialType; - } - return pinOrPasswordQualityToCredentialType(data.qualityForUi); - } - - private static int pinOrPasswordQualityToCredentialType(int quality) { - if (LockPatternUtils.isQualityAlphabeticPassword(quality)) { - return CREDENTIAL_TYPE_PASSWORD; - } - if (LockPatternUtils.isQualityNumericPin(quality)) { - return CREDENTIAL_TYPE_PIN; - } - throw new IllegalArgumentException("Quality is neither Pin nor password: " + quality); - } - private boolean isUserSecure(int userId) { return getCredentialTypeInternal(userId) != CREDENTIAL_TYPE_NONE; } @@ -1560,8 +1549,8 @@ public class LockSettingsService extends ILockSettings.Stub { * unlock operation. */ private void sendCredentialsOnUnlockIfRequired(LockscreenCredential credential, int userId) { - // Don't send credentials during the factory reset protection flow. - if (userId == USER_FRP) { + // Don't send credentials during the special user flow. + if (isSpecialUserId(userId)) { return; } @@ -2185,15 +2174,19 @@ public class LockSettingsService extends ILockSettings.Stub { Slog.e(TAG, "FRP credential can only be verified prior to provisioning."); return VerifyCredentialResponse.ERROR; } + if (userId == USER_REPAIR_MODE && !LockPatternUtils.isRepairModeActive(mContext)) { + Slog.e(TAG, "Repair mode is not active on the device."); + return VerifyCredentialResponse.ERROR; + } Slogf.i(TAG, "Verifying lockscreen credential for user %d", userId); final AuthenticationResult authResult; VerifyCredentialResponse response; synchronized (mSpManager) { - if (userId == USER_FRP) { - return mSpManager.verifyFrpCredential(getGateKeeperService(), credential, - progressCallback); + if (isSpecialUserId(userId)) { + return mSpManager.verifySpecialUserCredential(userId, getGateKeeperService(), + credential, progressCallback); } long protectorId = getCurrentLskfBasedProtectorId(userId); @@ -2202,6 +2195,12 @@ public class LockSettingsService extends ILockSettings.Stub { response = authResult.gkResponse; if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) { + if ((flags & VERIFY_FLAG_WRITE_REPAIR_MODE_PW) != 0) { + if (!mSpManager.writeRepairModeCredentialLocked(protectorId, userId)) { + Slog.e(TAG, "Failed to write repair mode credential"); + return VerifyCredentialResponse.ERROR; + } + } // credential has matched mBiometricDeferredQueue.addPendingLockoutResetForUser(userId, authResult.syntheticPassword.deriveGkPassword()); diff --git a/services/core/java/com/android/server/locksettings/LockSettingsStorage.java b/services/core/java/com/android/server/locksettings/LockSettingsStorage.java index 2fa637e030f1..1c5ecb70a3ae 100644 --- a/services/core/java/com/android/server/locksettings/LockSettingsStorage.java +++ b/services/core/java/com/android/server/locksettings/LockSettingsStorage.java @@ -19,7 +19,7 @@ package com.android.server.locksettings; import static android.content.Context.USER_SERVICE; import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE; -import static com.android.internal.widget.LockPatternUtils.USER_FRP; +import static com.android.internal.widget.LockPatternUtils.isSpecialUserId; import android.annotation.Nullable; import android.app.admin.DevicePolicyManager; @@ -90,6 +90,9 @@ class LockSettingsStorage { private static final String SYNTHETIC_PASSWORD_DIRECTORY = "spblob/"; + private static final String REPAIR_MODE_DIRECTORY = "repair-mode/"; + private static final String REPAIR_MODE_PERSISTENT_FILE = "pst"; + private static final Object DEFAULT = new Object(); private static final String[] SETTINGS_TO_BACKUP = new String[] { @@ -390,6 +393,29 @@ class LockSettingsStorage { } } + @VisibleForTesting + File getRepairModePersistentDataFile() { + final File directory = new File(Environment.getMetadataDirectory(), REPAIR_MODE_DIRECTORY); + return new File(directory, REPAIR_MODE_PERSISTENT_FILE); + } + + public PersistentData readRepairModePersistentData() { + final byte[] data = readFile(getRepairModePersistentDataFile()); + if (data == null) { + return PersistentData.NONE; + } + return PersistentData.fromBytes(data); + } + + public void writeRepairModePersistentData(int persistentType, int userId, byte[] payload) { + writeFile(getRepairModePersistentDataFile(), + PersistentData.toBytes(persistentType, userId, /* qualityForUi= */0, payload)); + } + + public void deleteRepairModePersistentData() { + deleteFile(getRepairModePersistentDataFile()); + } + /** * Writes the synthetic password state file for the given user ID, protector ID, and state name. * If the file already exists, then it is atomically replaced. @@ -510,7 +536,8 @@ class LockSettingsStorage { } public void setString(String key, String value, int userId) { - Preconditions.checkArgument(userId != USER_FRP, "cannot store lock settings for FRP user"); + Preconditions.checkArgument(!isSpecialUserId(userId), + "cannot store lock settings for special user: %d", userId); writeKeyValue(key, value, userId); if (ArrayUtils.contains(SETTINGS_TO_BACKUP, key)) { @@ -535,7 +562,7 @@ class LockSettingsStorage { } public String getString(String key, String defaultValue, int userId) { - if (userId == USER_FRP) { + if (isSpecialUserId(userId)) { return null; } return readKeyValue(key, defaultValue, userId); @@ -583,6 +610,17 @@ class LockSettingsStorage { } } + /** + * Provides a concrete data structure to represent the minimal information from + * a user's LSKF-based SP protector that is needed to verify the user's LSKF, + * in combination with the corresponding Gatekeeper enrollment or Weaver slot. + * It can be stored in {@link com.android.server.PersistentDataBlockService} for + * FRP to live across factory resets not initiated via the Settings UI. + * Written to {@link #REPAIR_MODE_PERSISTENT_FILE} to support verification for + * exiting repair mode, since the device runs with an empty data partition in + * repair mode and the same credential be provided to exit repair mode is + * required. + */ public static class PersistentData { static final byte VERSION_1 = 1; static final int VERSION_1_HEADER_SIZE = 1 + 1 + 4 + 4; @@ -685,6 +723,19 @@ class LockSettingsStorage { } pw.decreaseIndent(); } + // Dump repair mode file states + final File repairModeFile = getRepairModePersistentDataFile(); + if (repairModeFile.exists()) { + pw.println(TextUtils.formatSimple("Repair Mode [%s]:", repairModeFile.getParent())); + pw.increaseIndent(); + pw.println(TextUtils.formatSimple("%6d %s %s", repairModeFile.length(), + LockSettingsService.timestampToString(repairModeFile.lastModified()), + repairModeFile.getName())); + final PersistentData data = readRepairModePersistentData(); + pw.println(TextUtils.formatSimple("type: %d, user id: %d, payload size: %d", + data.type, data.userId, data.payload != null ? data.payload.length : 0)); + pw.decreaseIndent(); + } } static class DatabaseHelper extends SQLiteOpenHelper { diff --git a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java index 66f862ac9205..7349088c2b52 100644 --- a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java +++ b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java @@ -16,9 +16,14 @@ package com.android.server.locksettings; +import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_NONE; +import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD_OR_PIN; import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PIN; import static com.android.internal.widget.LockPatternUtils.EscrowTokenStateChangeCallback; import static com.android.internal.widget.LockPatternUtils.PIN_LENGTH_UNAVAILABLE; +import static com.android.internal.widget.LockPatternUtils.USER_FRP; +import static com.android.internal.widget.LockPatternUtils.USER_REPAIR_MODE; +import static com.android.internal.widget.LockPatternUtils.pinOrPasswordQualityToCredentialType; import android.annotation.IntDef; import android.annotation.NonNull; @@ -718,11 +723,30 @@ class SyntheticPasswordManager { return PasswordData.fromBytes(passwordData).credentialType; } - static int getFrpCredentialType(byte[] payload) { - if (payload == null) { + int getSpecialUserCredentialType(int userId) { + final PersistentData data = getSpecialUserPersistentData(userId); + if (data.type != PersistentData.TYPE_SP_GATEKEEPER + && data.type != PersistentData.TYPE_SP_WEAVER) { + return CREDENTIAL_TYPE_NONE; + } + if (data.payload == null) { return LockPatternUtils.CREDENTIAL_TYPE_NONE; } - return PasswordData.fromBytes(payload).credentialType; + final int credentialType = PasswordData.fromBytes(data.payload).credentialType; + if (credentialType != CREDENTIAL_TYPE_PASSWORD_OR_PIN) { + return credentialType; + } + return pinOrPasswordQualityToCredentialType(data.qualityForUi); + } + + private PersistentData getSpecialUserPersistentData(int userId) { + if (userId == USER_FRP) { + return mStorage.readPersistentDataBlock(); + } + if (userId == USER_REPAIR_MODE) { + return mStorage.readRepairModePersistentData(); + } + throw new IllegalArgumentException("Unknown special user id " + userId); } /** @@ -1005,10 +1029,10 @@ class SyntheticPasswordManager { return sizeOfCredential; } - public VerifyCredentialResponse verifyFrpCredential(IGateKeeperService gatekeeper, - LockscreenCredential userCredential, + public VerifyCredentialResponse verifySpecialUserCredential(int sourceUserId, + IGateKeeperService gatekeeper, LockscreenCredential userCredential, ICheckCredentialProgressCallback progressCallback) { - PersistentData persistentData = mStorage.readPersistentDataBlock(); + final PersistentData persistentData = getSpecialUserPersistentData(sourceUserId); if (persistentData.type == PersistentData.TYPE_SP_GATEKEEPER) { PasswordData pwd = PasswordData.fromBytes(persistentData.payload); byte[] stretchedLskf = stretchLskf(userCredential, pwd); @@ -1019,13 +1043,13 @@ class SyntheticPasswordManager { 0 /* challenge */, pwd.passwordHandle, stretchedLskfToGkPassword(stretchedLskf)); } catch (RemoteException e) { - Slog.e(TAG, "FRP verifyChallenge failed", e); + Slog.e(TAG, "Persistent data credential verifyChallenge failed", e); return VerifyCredentialResponse.ERROR; } return VerifyCredentialResponse.fromGateKeeperResponse(response); } else if (persistentData.type == PersistentData.TYPE_SP_WEAVER) { if (!isWeaverAvailable()) { - Slog.e(TAG, "No weaver service to verify SP-based FRP credential"); + Slog.e(TAG, "No weaver service to verify SP-based persistent data credential"); return VerifyCredentialResponse.ERROR; } PasswordData pwd = PasswordData.fromBytes(persistentData.payload); @@ -1114,6 +1138,57 @@ class SyntheticPasswordManager { } } + /** + * Writes the user's synthetic password data to the repair mode file. + * + * @param protectorId current LSKF based protectorId + * @param userId user id of the user + */ + public boolean writeRepairModeCredentialLocked(long protectorId, int userId) { + if (!shouldWriteRepairModeCredential(userId)) { + return false; + } + final byte[] data = loadState(PASSWORD_DATA_NAME, protectorId, userId); + if (data == null) { + Slogf.w(TAG, "Password data not found for user %d", userId); + return false; + } + final PasswordData pwd = PasswordData.fromBytes(data); + if (isNoneCredential(pwd)) { + Slogf.w(TAG, "User %d has NONE credential", userId); + return false; + } + Slogf.d(TAG, "Writing repair mode credential tied to user %d", userId); + final int weaverSlot = loadWeaverSlot(protectorId, userId); + if (weaverSlot != INVALID_WEAVER_SLOT) { + // write weaver password + mStorage.writeRepairModePersistentData( + PersistentData.TYPE_SP_WEAVER, weaverSlot, pwd.toBytes()); + } else { + // write gatekeeper password + mStorage.writeRepairModePersistentData( + PersistentData.TYPE_SP_GATEKEEPER, userId, pwd.toBytes()); + } + return true; + } + + private boolean shouldWriteRepairModeCredential(int userId) { + final UserInfo userInfo = mUserManager.getUserInfo(userId); + if (!LockPatternUtils.canUserEnterRepairMode(mContext, userInfo)) { + Slogf.w(TAG, "User %d can't enter repair mode", userId); + return false; + } + if (LockPatternUtils.isRepairModeActive(mContext)) { + Slog.w(TAG, "Can't write repair mode credential while repair mode is already active"); + return false; + } + if (LockPatternUtils.isGsiRunning()) { + Slog.w(TAG, "Can't write repair mode credential while GSI is running"); + return false; + } + return true; + } + private ArrayMap<Integer, ArrayMap<Long, TokenData>> tokenMap = new ArrayMap<>(); /** diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 488745c6ea97..5b3227388460 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -2344,6 +2344,7 @@ public class NotificationManagerService extends SystemService { mRankingHandler, mZenModeHelper, mPermissionHelper, + mPermissionManager, mNotificationChannelLogger, mAppOps, new SysUiStatsEvent.BuilderFactory(), @@ -6772,6 +6773,8 @@ public class NotificationManagerService extends SystemService { return false; } + mUsageStats.registerEnqueuedByAppAndAccepted(pkg); + if (info != null) { // Cache the shortcut synchronously after the associated notification is posted in case // the app unpublishes this shortcut immediately after posting the notification. If the @@ -7210,11 +7213,12 @@ public class NotificationManagerService extends SystemService { + " cannot create notifications"); } - // rate limit updates that aren't completed progress notifications - if (mNotificationsByKey.get(r.getSbn().getKey()) != null - && !r.getNotification().hasCompletedProgress() - && !isAutogroup) { - + // Rate limit updates that aren't completed progress notifications + // Search for the original one in the posted and not-yet-posted (enqueued) lists. + boolean isUpdate = mNotificationsByKey.get(r.getSbn().getKey()) != null + || findNotificationByListLocked(mEnqueuedNotifications, r.getSbn().getKey()) + != null; + if (isUpdate && !r.getNotification().hasCompletedProgress() && !isAutogroup) { final float appEnqueueRate = mUsageStats.getAppEnqueueRate(pkg); if (appEnqueueRate > mMaxPackageEnqueueRate) { mUsageStats.registerOverRateQuota(pkg); @@ -7839,15 +7843,8 @@ public class NotificationManagerService extends SystemService { boolean posted = false; synchronized (mNotificationLock) { try { - NotificationRecord r = null; - int N = mEnqueuedNotifications.size(); - for (int i = 0; i < N; i++) { - final NotificationRecord enqueued = mEnqueuedNotifications.get(i); - if (Objects.equals(key, enqueued.getKey())) { - r = enqueued; - break; - } - } + NotificationRecord r = findNotificationByListLocked(mEnqueuedNotifications, + key); if (r == null) { Slog.i(TAG, "Cannot find enqueued record for key: " + key); return false; @@ -9506,7 +9503,7 @@ public class NotificationManagerService extends SystemService { * Determine whether the userId applies to the notification in question, either because * they match exactly, or one of them is USER_ALL (which is treated as a wildcard). */ - private boolean notificationMatchesUserId(NotificationRecord r, int userId) { + private static boolean notificationMatchesUserId(NotificationRecord r, int userId) { return // looking for USER_ALL notifications? match everything userId == UserHandle.USER_ALL @@ -9865,9 +9862,9 @@ public class NotificationManagerService extends SystemService { return null; } - @GuardedBy("mNotificationLock") - private NotificationRecord findNotificationByListLocked(ArrayList<NotificationRecord> list, - String pkg, String tag, int id, int userId) { + @Nullable + private static NotificationRecord findNotificationByListLocked( + ArrayList<NotificationRecord> list, String pkg, String tag, int id, int userId) { final int len = list.size(); for (int i = 0; i < len; i++) { NotificationRecord r = list.get(i); @@ -9880,8 +9877,7 @@ public class NotificationManagerService extends SystemService { return null; } - @GuardedBy("mNotificationLock") - private List<NotificationRecord> findNotificationsByListLocked( + private static List<NotificationRecord> findNotificationsByListLocked( ArrayList<NotificationRecord> list, String pkg, String tag, int id, int userId) { List<NotificationRecord> matching = new ArrayList<>(); final int len = list.size(); @@ -9896,9 +9892,9 @@ public class NotificationManagerService extends SystemService { return matching; } - @GuardedBy("mNotificationLock") - private NotificationRecord findNotificationByListLocked(ArrayList<NotificationRecord> list, - String key) { + @Nullable + private static NotificationRecord findNotificationByListLocked( + ArrayList<NotificationRecord> list, String key) { final int N = list.size(); for (int i = 0; i < N; i++) { if (key.equals(list.get(i).getKey())) { @@ -9908,29 +9904,6 @@ public class NotificationManagerService extends SystemService { return null; } - /** - * There may be multiple records that match your criteria. For instance if there have been - * multiple notifications posted which are enqueued for the same pkg, tag, id, userId. This - * method will find all of them in the given list - * @return - */ - @GuardedBy("mNotificationLock") - private List<NotificationRecord> findEnqueuedNotificationsForCriteria( - String pkg, String tag, int id, int userId) { - final ArrayList<NotificationRecord> records = new ArrayList<>(); - final int n = mEnqueuedNotifications.size(); - for (int i = 0; i < n; i++) { - NotificationRecord r = mEnqueuedNotifications.get(i); - if (notificationMatchesUserId(r, userId) - && r.getSbn().getId() == id - && TextUtils.equals(r.getSbn().getTag(), tag) - && r.getSbn().getPackageName().equals(pkg)) { - records.add(r); - } - } - return records; - } - @GuardedBy("mNotificationLock") int indexOfNotificationLocked(String key) { final int N = mNotificationList.size(); diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java index c9a6c630d41b..0292a99f1122 100644 --- a/services/core/java/com/android/server/notification/NotificationRecord.java +++ b/services/core/java/com/android/server/notification/NotificationRecord.java @@ -27,6 +27,7 @@ import static android.service.notification.NotificationListenerService.Ranking.U import android.annotation.Nullable; import android.app.ActivityManager; import android.app.IActivityManager; +import android.app.KeyguardManager; import android.app.Notification; import android.app.NotificationChannel; import android.app.Person; @@ -46,6 +47,7 @@ import android.os.Binder; import android.os.Build; import android.os.Bundle; import android.os.IBinder; +import android.os.PowerManager; import android.os.UserHandle; import android.os.VibrationEffect; import android.provider.Settings; @@ -101,7 +103,8 @@ public final class NotificationRecord { final int mTargetSdkVersion; final int mOriginalFlags; private final Context mContext; - + private final KeyguardManager mKeyguardManager; + private final PowerManager mPowerManager; NotificationUsageStats.SingleNotificationStats stats; boolean isCanceled; IBinder permissionOwner; @@ -228,6 +231,8 @@ public final class NotificationRecord { mUpdateTimeMs = mCreationTimeMs; mInterruptionTimeMs = mCreationTimeMs; mContext = context; + mKeyguardManager = mContext.getSystemService(KeyguardManager.class); + mPowerManager = mContext.getSystemService(PowerManager.class); stats = new NotificationUsageStats.SingleNotificationStats(); mChannel = channel; mPreChannelsNotification = isPreChannelsNotification(); @@ -1619,6 +1624,11 @@ public final class NotificationRecord { return mPhoneNumbers; } + boolean isLocked() { + return mKeyguardManager.isKeyguardLocked() + || !mPowerManager.isInteractive(); // Unlocked AOD + } + @VisibleForTesting static final class Light { public final int color; diff --git a/services/core/java/com/android/server/notification/NotificationRecordLogger.java b/services/core/java/com/android/server/notification/NotificationRecordLogger.java index 0cc4fc4e0516..5ca882cc1bb9 100644 --- a/services/core/java/com/android/server/notification/NotificationRecordLogger.java +++ b/services/core/java/com/android/server/notification/NotificationRecordLogger.java @@ -31,9 +31,12 @@ import android.os.Bundle; import android.service.notification.NotificationListenerService; import android.service.notification.NotificationStats; +import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags; +import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags; import com.android.internal.logging.InstanceId; import com.android.internal.logging.UiEvent; import com.android.internal.logging.UiEventLogger; +import com.android.internal.util.FrameworkStatsLog; import java.util.ArrayList; import java.util.Objects; @@ -500,6 +503,8 @@ interface NotificationRecordLogger { final boolean is_foreground_service; final long timeout_millis; final boolean is_non_dismissible; + final int fsi_state; + final boolean is_locked; @DurationMillisLong long post_duration_millis; // Not final; calculated at the end. NotificationReported(NotificationRecordPair p, @@ -530,6 +535,20 @@ interface NotificationRecordLogger { this.is_foreground_service = NotificationRecordLogger.isForegroundService(p.r); this.timeout_millis = p.r.getSbn().getNotification().getTimeoutAfter(); this.is_non_dismissible = NotificationRecordLogger.isNonDismissible(p.r); + + final boolean isStickyHunFlagEnabled = SystemUiSystemPropertiesFlags.getResolver() + .isEnabled(NotificationFlags.SHOW_STICKY_HUN_FOR_DENIED_FSI); + + final boolean hasFullScreenIntent = + p.r.getSbn().getNotification().fullScreenIntent != null; + + final boolean hasFsiRequestedButDeniedFlag = (p.r.getSbn().getNotification().flags + & Notification.FLAG_FSI_REQUESTED_BUT_DENIED) != 0; + + this.fsi_state = NotificationRecordLogger.getFsiState(isStickyHunFlagEnabled, + hasFullScreenIntent, hasFsiRequestedButDeniedFlag, eventType); + + this.is_locked = p.r.isLocked(); } } @@ -558,7 +577,6 @@ interface NotificationRecordLogger { } /** - * @param r NotificationRecord * @return Whether the notification is a non-dismissible notification. */ static boolean isNonDismissible(@NonNull NotificationRecord r) { @@ -567,4 +585,28 @@ interface NotificationRecordLogger { } return (r.getNotification().flags & Notification.FLAG_NO_DISMISS) != 0; } + + /** + * @return FrameworkStatsLog enum of the state of the full screen intent posted with this + * notification. + */ + static int getFsiState(boolean isStickyHunFlagEnabled, + boolean hasFullScreenIntent, + boolean hasFsiRequestedButDeniedFlag, + NotificationReportedEvent eventType) { + + if (!isStickyHunFlagEnabled + || eventType == NotificationReportedEvent.NOTIFICATION_UPDATED) { + // Zeroes in protos take zero bandwidth, but non-zero numbers take bandwidth, + // so we should log 0 when possible. + return 0; + } + if (hasFullScreenIntent) { + return FrameworkStatsLog.NOTIFICATION_REPORTED__FSI_STATE__FSI_ALLOWED; + } + if (hasFsiRequestedButDeniedFlag) { + return FrameworkStatsLog.NOTIFICATION_REPORTED__FSI_STATE__FSI_DENIED; + } + return FrameworkStatsLog.NOTIFICATION_REPORTED__FSI_STATE__NO_FSI; + } } diff --git a/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java b/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java index feb75efe25f6..9da0e98c1775 100644 --- a/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java +++ b/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java @@ -75,7 +75,9 @@ class NotificationRecordLoggerImpl implements NotificationRecordLogger { notificationReported.is_foreground_service, notificationReported.timeout_millis, notificationReported.is_non_dismissible, - notificationReported.post_duration_millis); + notificationReported.post_duration_millis, + notificationReported.fsi_state, + notificationReported.is_locked); } @Override diff --git a/services/core/java/com/android/server/notification/NotificationUsageStats.java b/services/core/java/com/android/server/notification/NotificationUsageStats.java index ffe33a832ce4..e960f4ba11fd 100644 --- a/services/core/java/com/android/server/notification/NotificationUsageStats.java +++ b/services/core/java/com/android/server/notification/NotificationUsageStats.java @@ -16,23 +16,16 @@ package com.android.server.notification; -import static android.app.NotificationManager.IMPORTANCE_HIGH; - import android.app.Notification; -import android.content.ContentValues; import android.content.Context; -import android.database.Cursor; -import android.database.sqlite.SQLiteDatabase; -import android.database.sqlite.SQLiteFullException; -import android.database.sqlite.SQLiteOpenHelper; import android.os.Handler; -import android.os.HandlerThread; import android.os.Message; import android.os.SystemClock; import android.text.TextUtils; import android.util.ArraySet; import android.util.Log; +import com.android.internal.annotations.GuardedBy; import com.android.internal.logging.MetricsLogger; import com.android.server.notification.NotificationManagerService.DumpFilter; @@ -42,8 +35,6 @@ import org.json.JSONObject; import java.io.PrintWriter; import java.util.ArrayDeque; -import java.util.Calendar; -import java.util.GregorianCalendar; import java.util.HashMap; import java.util.Map; import java.util.Set; @@ -63,7 +54,6 @@ public class NotificationUsageStats { private static final String TAG = "NotificationUsageStats"; private static final boolean ENABLE_AGGREGATED_IN_MEMORY_STATS = true; - private static final boolean ENABLE_SQLITE_LOG = true; private static final AggregatedStats[] EMPTY_AGGREGATED_STATS = new AggregatedStats[0]; private static final String DEVICE_GLOBAL_STATS = "__global"; // packages start with letters private static final int MSG_EMIT = 1; @@ -73,12 +63,15 @@ public class NotificationUsageStats { public static final int FOUR_HOURS = 1000 * 60 * 60 * 4; private static final long EMIT_PERIOD = DEBUG ? TEN_SECONDS : FOUR_HOURS; - // Guarded by synchronized(this). + @GuardedBy("this") private final Map<String, AggregatedStats> mStats = new HashMap<>(); + @GuardedBy("this") private final ArrayDeque<AggregatedStats[]> mStatsArrays = new ArrayDeque<>(); + @GuardedBy("this") private ArraySet<String> mStatExpiredkeys = new ArraySet<>(); private final Context mContext; private final Handler mHandler; + @GuardedBy("this") private long mLastEmitTime; public NotificationUsageStats(Context context) { @@ -105,11 +98,7 @@ public class NotificationUsageStats { */ public synchronized float getAppEnqueueRate(String packageName) { AggregatedStats stats = getOrCreateAggregatedStatsLocked(packageName); - if (stats != null) { - return stats.getEnqueueRate(SystemClock.elapsedRealtime()); - } else { - return 0f; - } + return stats.getEnqueueRate(SystemClock.elapsedRealtime()); } /** @@ -117,11 +106,7 @@ public class NotificationUsageStats { */ public synchronized boolean isAlertRateLimited(String packageName) { AggregatedStats stats = getOrCreateAggregatedStatsLocked(packageName); - if (stats != null) { - return stats.isAlertRateLimited(); - } else { - return false; - } + return stats.isAlertRateLimited(); } /** @@ -136,16 +121,32 @@ public class NotificationUsageStats { } /** + * Called when a notification that was enqueued by an app is effectively enqueued to be + * posted. This is after rate checking, to update the rate. + * + * <p>Note that if we updated the arrival estimate <em>before</em> checking it, then an app + * enqueueing at slightly above the acceptable rate would never get their notifications + * accepted; updating afterwards allows the rate to dip below the threshold and thus lets + * through some of them. + */ + public synchronized void registerEnqueuedByAppAndAccepted(String packageName) { + final long now = SystemClock.elapsedRealtime(); + AggregatedStats[] aggregatedStatsArray = getAggregatedStatsLocked(packageName); + for (AggregatedStats stats : aggregatedStatsArray) { + stats.updateInterarrivalEstimate(now); + } + releaseAggregatedStatsLocked(aggregatedStatsArray); + } + + /** * Called when a notification has been posted. */ public synchronized void registerPostedByApp(NotificationRecord notification) { - final long now = SystemClock.elapsedRealtime(); - notification.stats.posttimeElapsedMs = now; + notification.stats.posttimeElapsedMs = SystemClock.elapsedRealtime(); AggregatedStats[] aggregatedStatsArray = getAggregatedStatsLocked(notification); for (AggregatedStats stats : aggregatedStatsArray) { stats.numPostedByApp++; - stats.updateInterarrivalEstimate(now); stats.countApiUse(notification); stats.numUndecoratedRemoteViews += (notification.hasUndecoratedRemoteView() ? 1 : 0); } @@ -161,7 +162,6 @@ public class NotificationUsageStats { AggregatedStats[] aggregatedStatsArray = getAggregatedStatsLocked(notification); for (AggregatedStats stats : aggregatedStatsArray) { stats.numUpdatedByApp++; - stats.updateInterarrivalEstimate(SystemClock.elapsedRealtime()); stats.countApiUse(notification); } releaseAggregatedStatsLocked(aggregatedStatsArray); @@ -257,12 +257,12 @@ public class NotificationUsageStats { } } - // Locked by this. + @GuardedBy("this") private AggregatedStats[] getAggregatedStatsLocked(NotificationRecord record) { return getAggregatedStatsLocked(record.getSbn().getPackageName()); } - // Locked by this. + @GuardedBy("this") private AggregatedStats[] getAggregatedStatsLocked(String packageName) { if (!ENABLE_AGGREGATED_IN_MEMORY_STATS) { return EMPTY_AGGREGATED_STATS; @@ -277,7 +277,7 @@ public class NotificationUsageStats { return array; } - // Locked by this. + @GuardedBy("this") private void releaseAggregatedStatsLocked(AggregatedStats[] array) { for(int i = 0; i < array.length; i++) { array[i] = null; @@ -285,7 +285,7 @@ public class NotificationUsageStats { mStatsArrays.offer(array); } - // Locked by this. + @GuardedBy("this") private AggregatedStats getOrCreateAggregatedStatsLocked(String key) { AggregatedStats result = mStats.get(key); if (result == null) { diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java index 4399a3ca46c5..838a0fb63a35 100644 --- a/services/core/java/com/android/server/notification/PreferencesHelper.java +++ b/services/core/java/com/android/server/notification/PreferencesHelper.java @@ -30,6 +30,9 @@ import static android.util.StatsLog.ANNOTATION_ID_IS_UID; import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_CHANNEL_GROUP_PREFERENCES; import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_CHANNEL_PREFERENCES; import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_PREFERENCES; +import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_PREFERENCES__FSI_STATE__NOT_REQUESTED; +import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_PREFERENCES__FSI_STATE__GRANTED; +import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_PREFERENCES__FSI_STATE__DENIED; import android.annotation.IntDef; import android.annotation.NonNull; @@ -41,6 +44,7 @@ import android.app.Notification; import android.app.NotificationChannel; import android.app.NotificationChannelGroup; import android.app.NotificationManager; +import android.content.AttributionSource; import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; @@ -53,6 +57,7 @@ import android.os.Binder; import android.os.Build; import android.os.Process; import android.os.UserHandle; +import android.permission.PermissionManager; import android.provider.Settings; import android.service.notification.ConversationChannelWrapper; import android.service.notification.NotificationListenerService; @@ -72,6 +77,8 @@ import android.util.proto.ProtoOutputStream; import com.android.internal.R; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags; +import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags; import com.android.internal.logging.MetricsLogger; import com.android.internal.util.Preconditions; import com.android.internal.util.XmlUtils; @@ -183,6 +190,7 @@ public class PreferencesHelper implements RankingConfig { private final RankingHandler mRankingHandler; private final ZenModeHelper mZenModeHelper; private final PermissionHelper mPermissionHelper; + private final PermissionManager mPermissionManager; private final NotificationChannelLogger mNotificationChannelLogger; private final AppOpsManager mAppOps; @@ -198,7 +206,7 @@ public class PreferencesHelper implements RankingConfig { private boolean mAllowInvalidShortcuts = false; public PreferencesHelper(Context context, PackageManager pm, RankingHandler rankingHandler, - ZenModeHelper zenHelper, PermissionHelper permHelper, + ZenModeHelper zenHelper, PermissionHelper permHelper, PermissionManager permManager, NotificationChannelLogger notificationChannelLogger, AppOpsManager appOpsManager, SysUiStatsEvent.BuilderFactory statsEventBuilderFactory, @@ -207,6 +215,7 @@ public class PreferencesHelper implements RankingConfig { mZenModeHelper = zenHelper; mRankingHandler = rankingHandler; mPermissionHelper = permHelper; + mPermissionManager = permManager; mPm = pm; mNotificationChannelLogger = notificationChannelLogger; mAppOps = appOpsManager; @@ -2027,6 +2036,43 @@ public class PreferencesHelper implements RankingConfig { } /** + * @return State of the full screen intent permission for this package. + */ + @VisibleForTesting + int getFsiState(String pkg, int uid, boolean requestedFSIPermission, boolean isFlagEnabled) { + if (!isFlagEnabled) { + return 0; + } + if (!requestedFSIPermission) { + return PACKAGE_NOTIFICATION_PREFERENCES__FSI_STATE__NOT_REQUESTED; + } + final AttributionSource attributionSource = + new AttributionSource.Builder(uid).setPackageName(pkg).build(); + + final int result = mPermissionManager.checkPermissionForPreflight( + android.Manifest.permission.USE_FULL_SCREEN_INTENT, attributionSource); + + if (result == PermissionManager.PERMISSION_GRANTED) { + return PACKAGE_NOTIFICATION_PREFERENCES__FSI_STATE__GRANTED; + } + return PACKAGE_NOTIFICATION_PREFERENCES__FSI_STATE__DENIED; + } + + /** + * @return True if the current full screen intent permission state for this package was set by + * the user. + */ + @VisibleForTesting + boolean isFsiPermissionUserSet(String pkg, int uid, int fsiState, int currentPermissionFlags, + boolean isStickyHunFlagEnabled) { + if (!isStickyHunFlagEnabled + || fsiState == PACKAGE_NOTIFICATION_PREFERENCES__FSI_STATE__NOT_REQUESTED) { + return false; + } + return (currentPermissionFlags & PackageManager.FLAG_PERMISSION_USER_SET) != 0; + } + + /** * Fills out {@link PackageNotificationPreferences} proto and wraps it in a {@link StatsEvent}. */ public void pullPackagePreferencesStats(List<StatsEvent> events, @@ -2070,7 +2116,33 @@ public class PreferencesHelper implements RankingConfig { event.writeInt(r.visibility); event.writeInt(r.lockedAppFields); - event.writeBoolean(importanceIsUserSet); // optional bool user_set_importance = 5; + + // optional bool user_set_importance = 5; + event.writeBoolean(importanceIsUserSet); + + // optional FsiState fsi_state = 6; + final boolean isStickyHunFlagEnabled = SystemUiSystemPropertiesFlags.getResolver() + .isEnabled(NotificationFlags.SHOW_STICKY_HUN_FOR_DENIED_FSI); + + final boolean requestedFSIPermission = mPermissionHelper.hasRequestedPermission( + android.Manifest.permission.USE_FULL_SCREEN_INTENT, r.pkg, r.uid); + + final int fsiState = getFsiState(r.pkg, r.uid, requestedFSIPermission, + isStickyHunFlagEnabled); + + event.writeInt(fsiState); + + // optional bool is_fsi_permission_user_set = 7; + final int currentPermissionFlags = mPm.getPermissionFlags( + android.Manifest.permission.USE_FULL_SCREEN_INTENT, r.pkg, + UserHandle.getUserHandleForUid(r.uid)); + + final boolean isUserSet = + isFsiPermissionUserSet(r.pkg, r.uid, fsiState, currentPermissionFlags, + isStickyHunFlagEnabled); + + event.writeBoolean(isUserSet); + events.add(event.build()); } } diff --git a/services/core/java/com/android/server/notification/RateEstimator.java b/services/core/java/com/android/server/notification/RateEstimator.java index a2f93dce2bca..eda96ac84b16 100644 --- a/services/core/java/com/android/server/notification/RateEstimator.java +++ b/services/core/java/com/android/server/notification/RateEstimator.java @@ -22,9 +22,10 @@ package com.android.server.notification; * * {@hide} */ -public class RateEstimator { - private static final double RATE_ALPHA = 0.8; +class RateEstimator { + private static final double RATE_ALPHA = 0.7; private static final double MINIMUM_DT = 0.0005; + private Long mLastEventTime; private double mInterarrivalTime; @@ -34,18 +35,12 @@ public class RateEstimator { } /** Update the estimate to account for an event that just happened. */ - public float update(long now) { - float rate; - if (mLastEventTime == null) { - // No last event time, rate is zero. - rate = 0f; - } else { + public void update(long now) { + if (mLastEventTime != null) { // Calculate the new inter-arrival time based on last event time. mInterarrivalTime = getInterarrivalEstimate(now); - rate = (float) (1.0 / mInterarrivalTime); } mLastEventTime = now; - return rate; } /** @return the estimated rate if there were a new event right now. */ diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 3132c5d713e3..a792b9c0dbdb 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -124,6 +124,7 @@ import android.hardware.hdmi.HdmiAudioSystemClient; import android.hardware.hdmi.HdmiControlManager; import android.hardware.hdmi.HdmiPlaybackClient; import android.hardware.hdmi.HdmiPlaybackClient.OneTouchPlayCallback; +import android.hardware.input.InputManager; import android.media.AudioManager; import android.media.AudioManagerInternal; import android.media.AudioSystem; @@ -205,6 +206,7 @@ import com.android.internal.policy.PhoneWindow; import com.android.internal.policy.TransitionAnimation; import com.android.internal.statusbar.IStatusBarService; import com.android.internal.util.ArrayUtils; +import com.android.internal.util.FrameworkStatsLog; import com.android.internal.widget.LockPatternUtils; import com.android.server.AccessibilityManagerInternal; import com.android.server.ExtconStateObserver; @@ -215,6 +217,7 @@ import com.android.server.SystemServiceManager; import com.android.server.UiThread; import com.android.server.display.BrightnessUtils; import com.android.server.input.InputManagerInternal; +import com.android.server.input.KeyboardMetricsCollector; import com.android.server.inputmethod.InputMethodManagerInternal; import com.android.server.pm.UserManagerInternal; import com.android.server.policy.KeyCombinationManager.TwoKeysCombinationRule; @@ -536,10 +539,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { int mShortPressOnSleepBehavior; int mShortPressOnWindowBehavior; int mPowerVolUpBehavior; - int mShortPressOnStemPrimaryBehavior; - int mDoublePressOnStemPrimaryBehavior; - int mTriplePressOnStemPrimaryBehavior; - int mLongPressOnStemPrimaryBehavior; boolean mStylusButtonsEnabled = true; boolean mHasSoftInput = false; boolean mHapticTextHandleEnabled; @@ -553,6 +552,12 @@ public class PhoneWindowManager implements WindowManagerPolicy { int mSearchKeyBehavior; ComponentName mSearchKeyTargetActivity; + // Key Behavior - Stem Primary + private int mShortPressOnStemPrimaryBehavior; + private int mDoublePressOnStemPrimaryBehavior; + private int mTriplePressOnStemPrimaryBehavior; + private int mLongPressOnStemPrimaryBehavior; + private boolean mHandleVolumeKeysInWM; private boolean mPendingKeyguardOccluded; @@ -595,8 +600,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { // What we do when the user double-taps on home private int mDoubleTapOnHomeBehavior; - // Whether to lock the device after the next app transition has finished. - boolean mLockAfterAppTransitionFinished; + // Whether to lock the device after the next dreaming transition has finished. + private boolean mLockAfterDreamingTransitionFinished; // Allowed theater mode wake actions private boolean mAllowTheaterModeWakeFromKey; @@ -676,6 +681,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { private static final int MSG_LAUNCH_ASSIST = 23; private static final int MSG_RINGER_TOGGLE_CHORD = 24; private static final int MSG_SWITCH_KEYBOARD_LAYOUT = 25; + private static final int MSG_LOG_KEYBOARD_SYSTEM_EVENT = 26; private class PolicyHandler extends Handler { @Override @@ -749,6 +755,9 @@ public class PhoneWindowManager implements WindowManagerPolicy { case MSG_SWITCH_KEYBOARD_LAYOUT: handleSwitchKeyboardLayout(msg.arg1, msg.arg2); break; + case MSG_LOG_KEYBOARD_SYSTEM_EVENT: + handleKeyboardSystemEvent(msg.arg2, (KeyEvent) msg.obj); + break; } } } @@ -815,7 +824,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { @Override public void onChange(boolean selfChange) { updateSettings(); - updateRotation(false); } } @@ -1095,7 +1103,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { synchronized (mLock) { // If the setting to lock instantly on power button press is true, then set the flag to // lock after the dream transition has finished. - mLockAfterAppTransitionFinished = + mLockAfterDreamingTransitionFinished = mLockPatternUtils.getPowerButtonInstantlyLocks(mCurrentUserId); } @@ -1991,6 +1999,21 @@ public class PhoneWindowManager implements WindowManagerPolicy { Supplier<GlobalActions> getGlobalActionsFactory() { return () -> new GlobalActions(mContext, mWindowManagerFuncs); } + + KeyguardServiceDelegate getKeyguardServiceDelegate() { + return new KeyguardServiceDelegate(mContext, + new StateCallback() { + @Override + public void onTrustedChanged() { + mWindowManagerFuncs.notifyKeyguardTrustedChanged(); + } + + @Override + public void onShowingChanged() { + mWindowManagerFuncs.onKeyguardShowingAndNotOccludedChanged(); + } + }); + } } /** {@inheritDoc} */ @@ -2229,37 +2252,28 @@ public class PhoneWindowManager implements WindowManagerPolicy { true /* notifyOccluded */); synchronized (mLock) { - mLockAfterAppTransitionFinished = false; + mLockAfterDreamingTransitionFinished = false; } } @Override public void onAppTransitionFinishedLocked(IBinder token) { synchronized (mLock) { - if (!mLockAfterAppTransitionFinished) { - return; + final DreamManagerInternal dreamManagerInternal = getDreamManagerInternal(); + // check both isDreaming and mLockAfterDreamingTransitionFinished before lockNow + // so it won't relock after dreaming has stopped + if (dreamManagerInternal != null && dreamManagerInternal.isDreaming() + && mLockAfterDreamingTransitionFinished) { + lockNow(null); } - mLockAfterAppTransitionFinished = false; + mLockAfterDreamingTransitionFinished = false; } - - lockNow(null); } }); mKeyguardDrawnTimeout = mContext.getResources().getInteger( com.android.internal.R.integer.config_keyguardDrawnTimeout); - mKeyguardDelegate = new KeyguardServiceDelegate(mContext, - new StateCallback() { - @Override - public void onTrustedChanged() { - mWindowManagerFuncs.notifyKeyguardTrustedChanged(); - } - - @Override - public void onShowingChanged() { - mWindowManagerFuncs.onKeyguardShowingAndNotOccludedChanged(); - } - }); + mKeyguardDelegate = injector.getKeyguardServiceDelegate(); initKeyCombinationRules(); initSingleKeyGestureRules(); mSideFpsEventHandler = new SideFpsEventHandler(mContext, mHandler, mPowerManager); @@ -2284,6 +2298,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { cancelPendingScreenshotChordAction(); } }); + if (mHasFeatureWatch) { mKeyCombinationManager.addRule( new TwoKeysCombinationRule(KEYCODE_POWER, KEYCODE_STEM_PRIMARY) { @@ -2903,7 +2918,30 @@ public class PhoneWindowManager implements WindowManagerPolicy { WindowManager.LayoutParams.TYPE_SYSTEM_ERROR, }; + /** + * Log the keyboard shortcuts without blocking the current thread. + * + * We won't log keyboard events when the input device is null + * or when it is virtual. + */ + private void handleKeyboardSystemEvent(int keyboardSystemEvent, KeyEvent event) { + final InputManager inputManager = mContext.getSystemService(InputManager.class); + final InputDevice inputDevice = inputManager != null + ? inputManager.getInputDevice(event.getDeviceId()) : null; + if (inputDevice != null && !inputDevice.isVirtual()) { + KeyboardMetricsCollector.logKeyboardSystemsEventReportedAtom( + inputDevice, keyboardSystemEvent, + new int[]{event.getKeyCode()}, event.getMetaState()); + } + } + + private void logKeyboardSystemsEvent(KeyEvent event, int keyboardSystemEvent) { + mHandler.obtainMessage(MSG_LOG_KEYBOARD_SYSTEM_EVENT, 0, keyboardSystemEvent, event) + .sendToTarget(); + } + // TODO(b/117479243): handle it in InputPolicy + // TODO (b/283241997): Add the remaining keyboard shortcut logging after refactoring /** {@inheritDoc} */ @Override public long interceptKeyBeforeDispatching(IBinder focusedToken, KeyEvent event, @@ -2957,6 +2995,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { switch(keyCode) { case KeyEvent.KEYCODE_HOME: + logKeyboardSystemsEvent(event, FrameworkStatsLog + .KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__HOME); return handleHomeShortcuts(displayId, focusedToken, event); case KeyEvent.KEYCODE_MENU: // Hijack modified menu keys for debugging features @@ -2974,6 +3014,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { case KeyEvent.KEYCODE_RECENT_APPS: if (down && repeatCount == 0) { showRecentApps(false /* triggeredFromAltTab */); + logKeyboardSystemsEvent(event, FrameworkStatsLog + .KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__RECENT_APPS); } return key_consumed; case KeyEvent.KEYCODE_APP_SWITCH: @@ -3005,6 +3047,12 @@ public class PhoneWindowManager implements WindowManagerPolicy { return key_consumed; } break; + case KeyEvent.KEYCODE_L: + if (down && event.isMetaPressed() && repeatCount == 0) { + lockNow(null /* options */); + return key_consumed; + } + break; case KeyEvent.KEYCODE_N: if (down && event.isMetaPressed()) { if (event.isCtrlPressed()) { diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java index efd8b6d9a943..6e9a22c7872b 100644 --- a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java +++ b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java @@ -19,6 +19,7 @@ package com.android.server.statusbar; import android.annotation.Nullable; import android.app.ITransientNotificationCallback; import android.hardware.fingerprint.IUdfpsRefreshRateRequestCallback; +import android.inputmethodservice.InputMethodService; import android.os.Bundle; import android.os.IBinder; import android.view.WindowInsets.Type.InsetsType; @@ -54,13 +55,13 @@ public interface StatusBarManagerInternal { * @param displayId The display to which the IME is bound to. * @param token The IME token. * @param vis Bit flags about the IME visibility. - * (e.g. {@link android.inputmethodservice.InputMethodService#IME_ACTIVE}) * @param backDisposition Bit flags about the IME back disposition. - * (e.g. {@link android.inputmethodservice.InputMethodService#BACK_DISPOSITION_DEFAULT}) * @param showImeSwitcher {@code true} when the IME switcher button should be shown. */ - void setImeWindowStatus(int displayId, IBinder token, int vis, - int backDisposition, boolean showImeSwitcher); + void setImeWindowStatus(int displayId, IBinder token, + @InputMethodService.ImeWindowVisibility int vis, + @InputMethodService.BackDispositionMode int backDisposition, + boolean showImeSwitcher); /** * See {@link android.app.StatusBarManager#setIcon(String, int, int, String)}. diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java index 044d30b368da..719b2d2f0355 100644 --- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java +++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java @@ -59,6 +59,7 @@ import android.hardware.biometrics.PromptInfo; import android.hardware.display.DisplayManager; import android.hardware.display.DisplayManager.DisplayListener; import android.hardware.fingerprint.IUdfpsRefreshRateRequestCallback; +import android.inputmethodservice.InputMethodService; import android.media.INearbyMediaDevicesProvider; import android.media.MediaRoute2Info; import android.net.Uri; @@ -516,7 +517,9 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D } @Override - public void setImeWindowStatus(int displayId, IBinder token, int vis, int backDisposition, + public void setImeWindowStatus(int displayId, IBinder token, + @InputMethodService.ImeWindowVisibility int vis, + @InputMethodService.BackDispositionMode int backDisposition, boolean showImeSwitcher) { StatusBarManagerService.this.setImeWindowStatus(displayId, token, vis, backDisposition, showImeSwitcher); @@ -1221,12 +1224,14 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D } @Override - public void setImeWindowStatus(int displayId, final IBinder token, final int vis, - final int backDisposition, final boolean showImeSwitcher) { + public void setImeWindowStatus(int displayId, final IBinder token, + @InputMethodService.ImeWindowVisibility final int vis, + @InputMethodService.BackDispositionMode final int backDisposition, + final boolean showImeSwitcher) { enforceStatusBar(); if (SPEW) { - Slog.d(TAG, "swetImeWindowStatus vis=" + vis + " backDisposition=" + backDisposition); + Slog.d(TAG, "setImeWindowStatus vis=" + vis + " backDisposition=" + backDisposition); } synchronized(mLock) { @@ -1289,7 +1294,9 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D private String mPackageName = "none"; private int mDisabled1 = 0; private int mDisabled2 = 0; + @InputMethodService.ImeWindowVisibility private int mImeWindowVis = 0; + @InputMethodService.BackDispositionMode private int mImeBackDisposition = 0; private boolean mShowImeSwitcher = false; private IBinder mImeToken = null; @@ -1334,7 +1341,8 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D return mDisabled1 == disabled1 && mDisabled2 == disabled2; } - private void setImeWindowState(final int vis, final int backDisposition, + private void setImeWindowState(@InputMethodService.ImeWindowVisibility final int vis, + @InputMethodService.BackDispositionMode final int backDisposition, final boolean showImeSwitcher, final IBinder token) { mImeWindowVis = vis; mImeBackDisposition = backDisposition; diff --git a/services/core/java/com/android/server/wallpaper/WallpaperDataParser.java b/services/core/java/com/android/server/wallpaper/WallpaperDataParser.java index 3e498d7d14ee..1133dba27ace 100644 --- a/services/core/java/com/android/server/wallpaper/WallpaperDataParser.java +++ b/services/core/java/com/android/server/wallpaper/WallpaperDataParser.java @@ -87,7 +87,7 @@ class WallpaperDataParser { mImageWallpaper = ComponentName.unflattenFromString( context.getResources().getString(R.string.image_wallpaper_component)); mIsLockscreenLiveWallpaperEnabled = - SystemProperties.getBoolean("persist.wm.debug.lockscreen_live_wallpaper", false); + SystemProperties.getBoolean("persist.wm.debug.lockscreen_live_wallpaper", true); } private JournaledFile makeJournaledFile(int userId) { diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java index e31b53cf4a29..3182dcc5078b 100644 --- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java +++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java @@ -366,6 +366,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub if (lockedWallpaper != null) { detachWallpaperLocked(lockedWallpaper); } + clearWallpaperBitmaps(mWallpaper.userId, FLAG_LOCK); mLockWallpaperMap.remove(wallpaper.userId); notifyColorsWhich |= FLAG_LOCK; } @@ -1417,6 +1418,10 @@ public class WallpaperManagerService extends IWallpaperManager.Stub } } + synchronized (mLock) { + saveSettingsLocked(mNewWallpaper.userId); + } + if (DEBUG) { Slog.v(TAG, "--- wallpaper changed --"); Slog.v(TAG, "new sysWp: " + mWallpaperMap.get(mCurrentUserId)); @@ -1588,7 +1593,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub mShuttingDown = false; mImageWallpaper = ComponentName.unflattenFromString( context.getResources().getString(R.string.image_wallpaper_component)); - mDefaultWallpaperComponent = WallpaperManager.getDefaultWallpaperComponent(context); + mDefaultWallpaperComponent = WallpaperManager.getCmfDefaultWallpaperComponent(context); mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class); mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class); mIPackageManager = AppGlobals.getPackageManager(); @@ -1604,7 +1609,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub mWallpaperCropper); mIsLockscreenLiveWallpaperEnabled = - SystemProperties.getBoolean("persist.wm.debug.lockscreen_live_wallpaper", false); + SystemProperties.getBoolean("persist.wm.debug.lockscreen_live_wallpaper", true); mIsMultiCropEnabled = SystemProperties.getBoolean("persist.wm.debug.wallpaper_multi_crop", false); LocalServices.addService(WallpaperManagerInternal.class, new LocalService()); @@ -1775,21 +1780,23 @@ public class WallpaperManagerService extends IWallpaperManager.Stub if (record.exists()) { Slog.w(TAG, "User:" + userID + ", wallpaper tyep = " + type + ", wallpaper fail detect!! reset to default wallpaper"); - clearWallpaperData(userID, type); + clearWallpaperBitmaps(userID, type); record.delete(); } }); } - private void clearWallpaperData(int userID, int wallpaperType) { + private void clearWallpaperBitmaps(int userID, int wallpaperType) { final WallpaperData wallpaper = new WallpaperData(userID, wallpaperType); - if (wallpaper.sourceExists()) { - wallpaper.wallpaperFile.delete(); - } - if (wallpaper.cropExists()) { - wallpaper.cropFile.delete(); - } + clearWallpaperBitmaps(wallpaper); + } + private boolean clearWallpaperBitmaps(WallpaperData wallpaper) { + boolean sourceExists = wallpaper.sourceExists(); + boolean cropExists = wallpaper.cropExists(); + if (sourceExists) wallpaper.wallpaperFile.delete(); + if (cropExists) wallpaper.cropFile.delete(); + return sourceExists || cropExists; } @Override @@ -1966,10 +1973,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub // files from the previous static wallpaper may still be stored in memory. // delete them in order to show the default wallpaper. - if (wallpaper.wallpaperFile.exists()) { - wallpaper.wallpaperFile.delete(); - wallpaper.cropFile.delete(); - } + clearWallpaperBitmaps(wallpaper); bindWallpaperComponentLocked(mImageWallpaper, true, false, fallback, reply); if ((wallpaper.mWhich & FLAG_SYSTEM) != 0) mHomeWallpaperWaitingForUnlock = true; @@ -1988,7 +1992,11 @@ public class WallpaperManagerService extends IWallpaperManager.Stub WallpaperData data = null; synchronized (mLock) { - clearWallpaperLocked(false, which, userId, null); + if (mIsLockscreenLiveWallpaperEnabled) { + clearWallpaperLocked(callingPackage, false, which, userId); + } else { + clearWallpaperLocked(false, which, userId, null); + } if (which == FLAG_LOCK) { data = mLockWallpaperMap.get(userId); @@ -2005,7 +2013,64 @@ public class WallpaperManagerService extends IWallpaperManager.Stub } } - void clearWallpaperLocked(boolean defaultFailed, int which, int userId, IRemoteCallback reply) { + private void clearWallpaperLocked(String callingPackage, boolean defaultFailed, + int which, int userId) { + + // Might need to bring it in the first time to establish our rewrite + if (!mWallpaperMap.contains(userId)) { + loadSettingsLocked(userId, false, FLAG_LOCK | FLAG_SYSTEM); + } + final WallpaperData wallpaper = mWallpaperMap.get(userId); + final WallpaperData lockWallpaper = mLockWallpaperMap.get(userId); + if (which == FLAG_LOCK && lockWallpaper == null) { + // It's already gone; we're done. + if (DEBUG) { + Slog.i(TAG, "Lock wallpaper already cleared"); + } + return; + } + + RuntimeException e = null; + try { + if (userId != mCurrentUserId && !hasCrossUserPermission()) return; + + final ComponentName component; + final int finalWhich; + + if ((which & FLAG_LOCK) > 0 && lockWallpaper != null) { + clearWallpaperBitmaps(lockWallpaper); + } + if ((which & FLAG_SYSTEM) > 0) { + clearWallpaperBitmaps(wallpaper); + } + + // lock only case: set the system wallpaper component to both screens + if (which == FLAG_LOCK) { + component = wallpaper.wallpaperComponent; + finalWhich = FLAG_LOCK | FLAG_SYSTEM; + } else { + component = defaultFailed ? mImageWallpaper : null; + finalWhich = which; + } + + boolean success = withCleanCallingIdentity(() -> setWallpaperComponent( + component, callingPackage, finalWhich, userId)); + if (success) return; + } catch (IllegalArgumentException e1) { + e = e1; + } + + // This can happen if the default wallpaper component doesn't + // exist. This should be a system configuration problem, but + // let's not let it crash the system and just live with no + // wallpaper. + Slog.e(TAG, "Default wallpaper component not found!", e); + withCleanCallingIdentity(() -> clearWallpaperComponentLocked(wallpaper)); + } + + // TODO(b/266818039) remove this version of the method + private void clearWallpaperLocked(boolean defaultFailed, int which, int userId, + IRemoteCallback reply) { if (which != FLAG_SYSTEM && which != FLAG_LOCK) { throw new IllegalArgumentException("Must specify exactly one kind of wallpaper to clear"); } @@ -2034,9 +2099,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub final long ident = Binder.clearCallingIdentity(); try { - if (wallpaper.wallpaperFile.exists()) { - wallpaper.wallpaperFile.delete(); - wallpaper.cropFile.delete(); + if (clearWallpaperBitmaps(wallpaper)) { if (which == FLAG_LOCK) { mLockWallpaperMap.remove(userId); final IWallpaperManagerCallback cb = mKeyguardListener; @@ -3098,8 +3161,13 @@ public class WallpaperManagerService extends IWallpaperManager.Stub // Migrate the bitmap files outright; no need to copy try { - Os.rename(sysWP.wallpaperFile.getAbsolutePath(), lockWP.wallpaperFile.getAbsolutePath()); - Os.rename(sysWP.cropFile.getAbsolutePath(), lockWP.cropFile.getAbsolutePath()); + if (!mIsLockscreenLiveWallpaperEnabled || sysWP.wallpaperFile.exists()) { + Os.rename(sysWP.wallpaperFile.getAbsolutePath(), + lockWP.wallpaperFile.getAbsolutePath()); + } + if (!mIsLockscreenLiveWallpaperEnabled || sysWP.cropFile.exists()) { + Os.rename(sysWP.cropFile.getAbsolutePath(), lockWP.cropFile.getAbsolutePath()); + } mLockWallpaperMap.put(userId, lockWP); if (mIsLockscreenLiveWallpaperEnabled) { SELinux.restorecon(lockWP.wallpaperFile); @@ -3107,9 +3175,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub } } catch (ErrnoException e) { Slog.e(TAG, "Can't migrate system wallpaper: " + e.getMessage()); - lockWP.wallpaperFile.delete(); - lockWP.cropFile.delete(); - return; + clearWallpaperBitmaps(lockWP); } } @@ -3164,16 +3230,17 @@ public class WallpaperManagerService extends IWallpaperManager.Stub } @VisibleForTesting - void setWallpaperComponent(ComponentName name, String callingPackage, + boolean setWallpaperComponent(ComponentName name, String callingPackage, @SetWallpaperFlags int which, int userId) { if (mIsLockscreenLiveWallpaperEnabled) { - setWallpaperComponentInternal(name, callingPackage, which, userId); + return setWallpaperComponentInternal(name, callingPackage, which, userId); } else { setWallpaperComponentInternalLegacy(name, callingPackage, which, userId); + return true; } } - private void setWallpaperComponentInternal(ComponentName name, String callingPackage, + private boolean setWallpaperComponentInternal(ComponentName name, String callingPackage, @SetWallpaperFlags int which, int userIdIn) { if (DEBUG) { Slog.v(TAG, "Setting new live wallpaper: which=" + which + ", component: " + name); @@ -3184,6 +3251,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub checkPermission(android.Manifest.permission.SET_WALLPAPER_COMPONENT); boolean shouldNotifyColors = false; + boolean bindSuccess; final WallpaperData newWallpaper; synchronized (mLock) { @@ -3232,7 +3300,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub */ boolean forceRebind = same && systemIsBoth && which == FLAG_SYSTEM; - boolean bindSuccess = bindWallpaperComponentLocked(name, /* force */ + bindSuccess = bindWallpaperComponentLocked(name, /* force */ forceRebind, /* fromUser */ true, newWallpaper, callback); if (bindSuccess) { if (!same) { @@ -3251,6 +3319,11 @@ public class WallpaperManagerService extends IWallpaperManager.Stub }); } } + boolean lockBitmapCleared = false; + if (!mImageWallpaper.equals(newWallpaper.wallpaperComponent)) { + clearWallpaperBitmaps(newWallpaper); + lockBitmapCleared = newWallpaper.mWhich == FLAG_LOCK; + } newWallpaper.wallpaperId = makeWallpaperIdLocked(); notifyCallbacksLocked(newWallpaper); shouldNotifyColors = true; @@ -3267,6 +3340,9 @@ public class WallpaperManagerService extends IWallpaperManager.Stub updateEngineFlags(newWallpaper); } } + if (!lockBitmapCleared) { + clearWallpaperBitmaps(newWallpaper.userId, FLAG_LOCK); + } mLockWallpaperMap.remove(newWallpaper.userId); } } @@ -3279,6 +3355,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub notifyWallpaperColorsChanged(newWallpaper, which); notifyWallpaperColorsChanged(mFallbackWallpaper, FLAG_SYSTEM); } + return bindSuccess; } // TODO(b/266818039) Remove this method diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index a0a45e67e77b..4262e94e2894 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -898,6 +898,11 @@ class ActivityStarter { } mLastStartReason = request.reason; mLastStartActivityTimeMs = System.currentTimeMillis(); + // Reset the ActivityRecord#mCurrentLaunchCanTurnScreenOn state of last start activity in + // case the state is not yet consumed during rapid activity launch. + if (mLastStartActivityRecord != null) { + mLastStartActivityRecord.setCurrentLaunchCanTurnScreenOn(false); + } mLastStartActivityRecord = null; final IApplicationThread caller = request.caller; @@ -1583,21 +1588,14 @@ class ActivityStarter { newTransition = null; } } - if (isTransientLaunch) { - if (forceTransientTransition) { - transitionController.collect(mLastStartActivityRecord); - transitionController.collect(mPriorAboveTask); - } - // `started` isn't guaranteed to be the actual relevant activity, so we must wait - // until after we launched to identify the relevant activity. - transitionController.setTransientLaunch(mLastStartActivityRecord, mPriorAboveTask); - if (forceTransientTransition) { - final DisplayContent dc = mLastStartActivityRecord.getDisplayContent(); - // update wallpaper target to TransientHide - dc.mWallpaperController.adjustWallpaperWindows(); - // execute transition because there is no change - transitionController.setReady(dc, true /* ready */); - } + if (forceTransientTransition) { + transitionController.collect(mLastStartActivityRecord); + transitionController.collect(mPriorAboveTask); + final DisplayContent dc = mLastStartActivityRecord.getDisplayContent(); + // update wallpaper target to TransientHide + dc.mWallpaperController.adjustWallpaperWindows(); + // execute transition because there is no change + transitionController.setReady(dc, true /* ready */); } if (!userLeaving) { // no-user-leaving implies not entering PiP. @@ -1686,7 +1684,9 @@ class ActivityStarter { } // When running transient transition, the transient launch target should keep on top. // So disallow the transient hide activity to move itself to front, e.g. trampoline. - if (!mAvoidMoveToFront && r.mTransitionController.isTransientHide(targetTask)) { + if (!mAvoidMoveToFront && (mService.mHomeProcess == null + || mService.mHomeProcess.mUid != realCallingUid) + && r.mTransitionController.isTransientHide(targetTask)) { mAvoidMoveToFront = true; } mPriorAboveTask = TaskDisplayArea.getRootTaskAbove(targetTask.getRootTask()); @@ -1705,6 +1705,7 @@ class ActivityStarter { activity.destroyIfPossible("Removes redundant singleInstance"); } } + recordTransientLaunchIfNeeded(targetTaskTop); // Recycle the target task for this launch. startResult = recycleTask(targetTask, targetTaskTop, reusedTask, intentGrants); if (startResult != START_SUCCESS) { @@ -1736,6 +1737,9 @@ class ActivityStarter { addOrReparentStartingActivity(targetTask, "adding to task"); } + // After activity is attached to task, but before actual start + recordTransientLaunchIfNeeded(mLastStartActivityRecord); + if (!mAvoidMoveToFront && mDoResume) { mTargetRootTask.getRootTask().moveToFront("reuseOrNewTask", targetTask); if (!mTargetRootTask.isTopRootTaskInDisplayArea() && mService.isDreaming() @@ -1832,6 +1836,14 @@ class ActivityStarter { return START_SUCCESS; } + private void recordTransientLaunchIfNeeded(ActivityRecord r) { + if (r == null || !mTransientLaunch) return; + final TransitionController controller = r.mTransitionController; + if (controller.isCollecting() && !controller.isTransientCollect(r)) { + controller.setTransientLaunch(r, mPriorAboveTask); + } + } + /** Returns the leaf task where the target activity may be placed. */ private Task computeTargetTask() { if (mStartActivity.resultTo == null && mInTask == null && !mAddingToTask @@ -2928,15 +2940,6 @@ class ActivityStarter { } } - // If the matching task is already in the adjacent task of the launch target. Adjust to use - // the adjacent task as its launch target. So the existing task will be launched into the - // closer one and won't be reparent redundantly. - final Task adjacentTargetTask = mTargetRootTask.getAdjacentTask(); - if (adjacentTargetTask != null && intentActivity.isDescendantOf(adjacentTargetTask) - && intentTask.isOnTop()) { - mTargetRootTask = adjacentTargetTask; - } - // If the target task is not in the front, then we need to bring it to the front... // except... well, with SINGLE_TASK_LAUNCH it's not entirely clear. We'd like to have // the same behavior as if a new instance was being started, which means not bringing it @@ -2977,7 +2980,9 @@ class ActivityStarter { // should be START_DELIVERED_TO_TOP instead of START_TASK_TO_FRONT. final boolean wasTopOfVisibleRootTask = intentActivity.isVisibleRequested() && intentActivity.inMultiWindowMode() - && intentActivity == mTargetRootTask.topRunningActivity(); + && intentActivity == mTargetRootTask.topRunningActivity() + && !intentActivity.mTransitionController.isTransientHide( + mTargetRootTask); // We only want to move to the front, if we aren't going to launch on a // different root task. If we launch on a different root task, we will put the // task on top there. diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index 47b51ac164b9..4a658d6abaf4 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -1411,29 +1411,39 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { final long origId = Binder.clearCallingIdentity(); // TODO(b/64750076): Check if calling pid should really be -1. - final int res = getActivityStartController() - .obtainStarter(intent, "startNextMatchingActivity") - .setCaller(r.app.getThread()) - .setResolvedType(r.resolvedType) - .setActivityInfo(aInfo) - .setResultTo(resultTo != null ? resultTo.token : null) - .setResultWho(resultWho) - .setRequestCode(requestCode) - .setCallingPid(-1) - .setCallingUid(r.launchedFromUid) - .setCallingPackage(r.launchedFromPackage) - .setCallingFeatureId(r.launchedFromFeatureId) - .setRealCallingPid(-1) - .setRealCallingUid(r.launchedFromUid) - .setActivityOptions(options) - .execute(); - Binder.restoreCallingIdentity(origId); + try { + if (options == null) { + options = new SafeActivityOptions(ActivityOptions.makeBasic()); + } - r.finishing = wasFinishing; - if (res != ActivityManager.START_SUCCESS) { - return false; + // Fixes b/230492947 + // Prevents background activity launch through #startNextMatchingActivity + // An activity going into the background could still go back to the foreground + // if the intent used matches both: + // - the activity in the background + // - a second activity. + options.getOptions(r).setAvoidMoveToFront(); + final int res = getActivityStartController() + .obtainStarter(intent, "startNextMatchingActivity") + .setCaller(r.app.getThread()) + .setResolvedType(r.resolvedType) + .setActivityInfo(aInfo) + .setResultTo(resultTo != null ? resultTo.token : null) + .setResultWho(resultWho) + .setRequestCode(requestCode) + .setCallingPid(-1) + .setCallingUid(r.launchedFromUid) + .setCallingPackage(r.launchedFromPackage) + .setCallingFeatureId(r.launchedFromFeatureId) + .setRealCallingPid(-1) + .setRealCallingUid(r.launchedFromUid) + .setActivityOptions(options) + .execute(); + r.finishing = wasFinishing; + return res == ActivityManager.START_SUCCESS; + } finally { + Binder.restoreCallingIdentity(origId); } - return true; } } diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java index 0171c200b56c..738797b809a5 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java +++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java @@ -1891,7 +1891,7 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { // DestroyActivityItem may be called first. final ActivityRecord top = task.getTopMostActivity(); if (top != null && top.finishing && !top.mAppStopped && top.lastVisibleTime > 0 - && !task.mKillProcessesOnDestroyed) { + && !task.mKillProcessesOnDestroyed && top.hasProcess()) { task.mKillProcessesOnDestroyed = true; mHandler.sendMessageDelayed( mHandler.obtainMessage(KILL_TASK_PROCESSES_TIMEOUT_MSG, task), @@ -2367,6 +2367,10 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { final ActivityRecord prevTopActivity = mTopResumedActivity; final Task topRootTask = mRootWindowContainer.getTopDisplayFocusedRootTask(); if (topRootTask == null || topRootTask.getTopResumedActivity() == prevTopActivity) { + if (topRootTask == null) { + // There's no focused task and there won't have any resumed activity either. + scheduleTopResumedActivityStateLossIfNeeded(); + } if (mService.isSleepingLocked()) { // There won't be a next resumed activity. The top process should still be updated // according to the current top focused activity. @@ -2376,16 +2380,7 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { } // Ask previous activity to release the top state. - final boolean prevActivityReceivedTopState = - prevTopActivity != null && !mTopResumedActivityWaitingForPrev; - // mTopResumedActivityWaitingForPrev == true at this point would mean that an activity - // before the prevTopActivity one hasn't reported back yet. So server never sent the top - // resumed state change message to prevTopActivity. - if (prevActivityReceivedTopState - && prevTopActivity.scheduleTopResumedActivityChanged(false /* onTop */)) { - scheduleTopResumedStateLossTimeout(prevTopActivity); - mTopResumedActivityWaitingForPrev = true; - } + scheduleTopResumedActivityStateLossIfNeeded(); // Update the current top activity. mTopResumedActivity = topRootTask.getTopResumedActivity(); @@ -2410,6 +2405,23 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { mService.updateTopApp(mTopResumedActivity); } + /** Schedule current top resumed activity state loss */ + private void scheduleTopResumedActivityStateLossIfNeeded() { + if (mTopResumedActivity == null) { + return; + } + + // mTopResumedActivityWaitingForPrev == true at this point would mean that an activity + // before the prevTopActivity one hasn't reported back yet. So server never sent the top + // resumed state change message to prevTopActivity. + if (!mTopResumedActivityWaitingForPrev + && mTopResumedActivity.scheduleTopResumedActivityChanged(false /* onTop */)) { + scheduleTopResumedStateLossTimeout(mTopResumedActivity); + mTopResumedActivityWaitingForPrev = true; + mTopResumedActivity = null; + } + } + /** Schedule top resumed state change if previous top activity already reported back. */ private void scheduleTopResumedActivityStateIfNeeded() { if (mTopResumedActivity != null && !mTopResumedActivityWaitingForPrev) { diff --git a/services/core/java/com/android/server/wm/DesktopModeLaunchParamsModifier.java b/services/core/java/com/android/server/wm/DesktopModeLaunchParamsModifier.java index e91c9d427c80..2b6b62e0d81a 100644 --- a/services/core/java/com/android/server/wm/DesktopModeLaunchParamsModifier.java +++ b/services/core/java/com/android/server/wm/DesktopModeLaunchParamsModifier.java @@ -80,8 +80,8 @@ public class DesktopModeLaunchParamsModifier implements LaunchParamsModifier { appendLog("not in bounds phase, skipping"); return RESULT_SKIP; } - if (!task.isActivityTypeStandard()) { - appendLog("not standard activity type, skipping"); + if (!task.isActivityTypeStandardOrUndefined()) { + appendLog("not standard or undefined activity type, skipping"); return RESULT_SKIP; } if (!currentParams.mBounds.isEmpty()) { diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index ef19eef22794..2dc133f060ff 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -1214,8 +1214,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp mDisplayRotationCompatPolicy = // Not checking DeviceConfig value here to allow enabling via DeviceConfig // without the need to restart the device. - mWmService.mLetterboxConfiguration.isCameraCompatTreatmentEnabled( - /* checkDeviceConfig */ false) + mWmService.mLetterboxConfiguration.isCameraCompatTreatmentEnabledAtBuildTime() ? new DisplayRotationCompatPolicy(this) : null; mRotationReversionController = new DisplayRotationReversionController(this); diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java index 95c953a5cf2d..664c0c43c1a7 100644 --- a/services/core/java/com/android/server/wm/DisplayPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayPolicy.java @@ -176,7 +176,7 @@ public class DisplayPolicy { // TODO(b/266197298): Remove this by a more general protocol from the insets providers. private static final boolean USE_CACHED_INSETS_FOR_DISPLAY_SWITCH = - SystemProperties.getBoolean("persist.wm.debug.cached_insets_switch", false); + SystemProperties.getBoolean("persist.wm.debug.cached_insets_switch", true); private final WindowManagerService mService; private final Context mContext; @@ -271,13 +271,13 @@ public class DisplayPolicy { private WindowState mSystemUiControllingWindow; // Candidate window to determine the color of navigation bar. The window needs to be top - // fullscreen-app windows or dim layers that are intersecting with the window frame of status - // bar. + // fullscreen-app windows or dim layers that are intersecting with the window frame of + // navigation bar. private WindowState mNavBarColorWindowCandidate; - // The window to determine opacity and background of translucent navigation bar. The window - // needs to be opaque. - private WindowState mNavBarBackgroundWindow; + // Candidate window to determine opacity and background of translucent navigation bar. + // The window frame must intersect the frame of navigation bar. + private WindowState mNavBarBackgroundWindowCandidate; /** * A collection of {@link AppearanceRegion} to indicate that which region of status bar applies @@ -961,12 +961,6 @@ public class DisplayPolicy { if (!win.mSession.mCanSetUnrestrictedGestureExclusion) { attrs.privateFlags &= ~PRIVATE_FLAG_UNRESTRICTED_GESTURE_EXCLUSION; } - - final InsetsSourceProvider provider = win.getControllableInsetProvider(); - if (provider != null && provider.getSource().insetsRoundedCornerFrame() - != attrs.insetsRoundedCornerFrame) { - provider.getSource().setInsetsRoundedCornerFrame(attrs.insetsRoundedCornerFrame); - } } /** @@ -1104,9 +1098,11 @@ public class DisplayPolicy { } else { overrideProviders = null; } - mDisplayContent.getInsetsStateController().getOrCreateSourceProvider( - provider.getId(), provider.getType()).setWindowContainer( - win, frameProvider, overrideProviders); + final InsetsSourceProvider sourceProvider = mDisplayContent + .getInsetsStateController().getOrCreateSourceProvider(provider.getId(), + provider.getType()); + sourceProvider.getSource().setFlags(provider.getFlags()); + sourceProvider.setWindowContainer(win, frameProvider, overrideProviders); mInsetsSourceWindowsExceptIme.add(win); } } @@ -1387,7 +1383,7 @@ public class DisplayPolicy { mBottomGestureHost = null; mTopFullscreenOpaqueWindowState = null; mNavBarColorWindowCandidate = null; - mNavBarBackgroundWindow = null; + mNavBarBackgroundWindowCandidate = null; mStatusBarAppearanceRegionList.clear(); mLetterboxDetails.clear(); mStatusBarBackgroundWindows.clear(); @@ -1514,8 +1510,8 @@ public class DisplayPolicy { mNavBarColorWindowCandidate = win; addSystemBarColorApp(win); } - if (mNavBarBackgroundWindow == null) { - mNavBarBackgroundWindow = win; + if (mNavBarBackgroundWindowCandidate == null) { + mNavBarBackgroundWindowCandidate = win; } } @@ -1531,20 +1527,32 @@ public class DisplayPolicy { } } else if (win.isDimming()) { if (mStatusBar != null) { - if (addStatusBarAppearanceRegionsForDimmingWindow( - win.mAttrs.insetsFlags.appearance & APPEARANCE_LIGHT_STATUS_BARS, - mStatusBar.getFrame(), win.getBounds(), win.getFrame())) { + // If the dim window is below status bar window, we should update the appearance + // region if needed. Otherwise, leave it as it is. + final int statusBarLayer = mStatusBar.mToken.getWindowLayerFromType(); + final int targetWindowLayer = win.mToken.getWindowLayerFromType(); + if (targetWindowLayer < statusBarLayer + && addStatusBarAppearanceRegionsForDimmingWindow( + win.mAttrs.insetsFlags.appearance & APPEARANCE_LIGHT_STATUS_BARS, + mStatusBar.getFrame(), win.getBounds(), win.getFrame())) { addSystemBarColorApp(win); } } if (isOverlappingWithNavBar(win) && mNavBarColorWindowCandidate == null) { mNavBarColorWindowCandidate = win; + addSystemBarColorApp(win); } - } else if (appWindow && attached == null && mNavBarColorWindowCandidate == null + } else if (appWindow && attached == null + && (mNavBarColorWindowCandidate == null || mNavBarBackgroundWindowCandidate == null) && win.getFrame().contains( getBarContentFrameForWindow(win, Type.navigationBars()))) { - mNavBarColorWindowCandidate = win; - addSystemBarColorApp(win); + if (mNavBarColorWindowCandidate == null) { + mNavBarColorWindowCandidate = win; + addSystemBarColorApp(win); + } + if (mNavBarBackgroundWindowCandidate == null) { + mNavBarBackgroundWindowCandidate = win; + } } } @@ -2282,8 +2290,8 @@ public class DisplayPolicy { && Arrays.equals(mLastLetterboxDetails, letterboxDetails)) { return; } - if (mDisplayContent.isDefaultDisplay && mLastFocusIsFullscreen != isFullscreen - && ((mLastAppearance ^ appearance) & APPEARANCE_LOW_PROFILE_BARS) != 0) { + if (mDisplayContent.isDefaultDisplay && (mLastFocusIsFullscreen != isFullscreen + || ((mLastAppearance ^ appearance) & APPEARANCE_LOW_PROFILE_BARS) != 0)) { mService.mInputManager.setSystemUiLightsOut( isFullscreen || (appearance & APPEARANCE_LOW_PROFILE_BARS) != 0); } @@ -2484,7 +2492,7 @@ public class DisplayPolicy { return win.isFullyTransparentBarAllowed(getBarContentFrameForWindow(win, type)); } - private boolean drawsBarBackground(WindowState win) { + private static boolean drawsBarBackground(WindowState win) { if (win == null) { return true; } @@ -2524,7 +2532,14 @@ public class DisplayPolicy { */ private int configureNavBarOpacity(int appearance, boolean multiWindowTaskVisible, boolean freeformRootTaskVisible) { - final boolean drawBackground = drawsBarBackground(mNavBarBackgroundWindow); + final WindowState navBackgroundWin = chooseNavigationBackgroundWindow( + mNavBarBackgroundWindowCandidate, + mDisplayContent.mInputMethodWindow, + mNavigationBarPosition); + final boolean drawBackground = navBackgroundWin != null + // There is no app window showing underneath nav bar. (e.g., The screen is locked.) + // Let system windows (ex: notification shade) draw nav bar background. + || mNavBarBackgroundWindowCandidate == null; if (mNavBarOpacityMode == NAV_BAR_FORCE_TRANSPARENT) { if (drawBackground) { @@ -2544,7 +2559,7 @@ public class DisplayPolicy { } } - if (!isFullyTransparentAllowed(mNavBarBackgroundWindow, Type.navigationBars())) { + if (!isFullyTransparentAllowed(navBackgroundWin, Type.navigationBars())) { appearance |= APPEARANCE_SEMI_TRANSPARENT_NAVIGATION_BARS; } @@ -2555,6 +2570,20 @@ public class DisplayPolicy { return appearance & ~APPEARANCE_OPAQUE_NAVIGATION_BARS; } + @VisibleForTesting + @Nullable + static WindowState chooseNavigationBackgroundWindow(WindowState candidate, + WindowState imeWindow, @NavigationBarPosition int navBarPosition) { + if (imeWindow != null && imeWindow.isVisible() && navBarPosition == NAV_BAR_BOTTOM + && drawsBarBackground(imeWindow)) { + return imeWindow; + } + if (drawsBarBackground(candidate)) { + return candidate; + } + return null; + } + private boolean isImmersiveMode(WindowState win) { if (win == null) { return false; @@ -2727,9 +2756,9 @@ public class DisplayPolicy { pw.print(prefix); pw.print("mNavBarColorWindowCandidate="); pw.println(mNavBarColorWindowCandidate); } - if (mNavBarBackgroundWindow != null) { - pw.print(prefix); pw.print("mNavBarBackgroundWindow="); - pw.println(mNavBarBackgroundWindow); + if (mNavBarBackgroundWindowCandidate != null) { + pw.print(prefix); pw.print("mNavBarBackgroundWindowCandidate="); + pw.println(mNavBarBackgroundWindowCandidate); } if (mLastStatusBarAppearanceRegions != null) { pw.print(prefix); pw.println("mLastStatusBarAppearanceRegions="); diff --git a/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java b/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java index 2b34bb22729d..f96f99d50053 100644 --- a/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayRotationCompatPolicy.java @@ -336,8 +336,7 @@ final class DisplayRotationCompatPolicy { * </ul> */ private boolean isTreatmentEnabledForDisplay() { - return mWmService.mLetterboxConfiguration.isCameraCompatTreatmentEnabled( - /* checkDeviceConfig */ true) + return mWmService.mLetterboxConfiguration.isCameraCompatTreatmentEnabled() && mDisplayContent.getIgnoreOrientationRequest() // TODO(b/225928882): Support camera compat rotation for external displays && mDisplayContent.getDisplay().getType() == TYPE_INTERNAL; diff --git a/services/core/java/com/android/server/wm/DisplayRotationImmersiveAppCompatPolicy.java b/services/core/java/com/android/server/wm/DisplayRotationImmersiveAppCompatPolicy.java index 74494ddd9f59..de70c4df7985 100644 --- a/services/core/java/com/android/server/wm/DisplayRotationImmersiveAppCompatPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayRotationImmersiveAppCompatPolicy.java @@ -44,7 +44,7 @@ final class DisplayRotationImmersiveAppCompatPolicy { @NonNull final DisplayRotation displayRotation, @NonNull final DisplayContent displayContent) { if (!letterboxConfiguration - .isDisplayRotationImmersiveAppCompatPolicyEnabled(/* checkDeviceConfig */ false)) { + .isDisplayRotationImmersiveAppCompatPolicyEnabledAtBuildTime()) { return null; } @@ -87,8 +87,7 @@ final class DisplayRotationImmersiveAppCompatPolicy { * @return {@code true}, if there is a need to lock screen rotation, {@code false} otherwise. */ boolean isRotationLockEnforced(@Surface.Rotation final int proposedRotation) { - if (!mLetterboxConfiguration.isDisplayRotationImmersiveAppCompatPolicyEnabled( - /* checkDeviceConfig */ true)) { + if (!mLetterboxConfiguration.isDisplayRotationImmersiveAppCompatPolicyEnabled()) { return false; } synchronized (mDisplayContent.mWmService.mGlobalLock) { diff --git a/services/core/java/com/android/server/wm/DragDropController.java b/services/core/java/com/android/server/wm/DragDropController.java index 258351436188..4a3d0c142e1d 100644 --- a/services/core/java/com/android/server/wm/DragDropController.java +++ b/services/core/java/com/android/server/wm/DragDropController.java @@ -181,7 +181,11 @@ class DragDropController { } } finally { if (surface != null) { - surface.release(); + try (final SurfaceControl.Transaction transaction = + mService.mTransactionFactory.get()) { + transaction.remove(surface); + transaction.apply(); + } } } } diff --git a/services/core/java/com/android/server/wm/EmbeddedWindowController.java b/services/core/java/com/android/server/wm/EmbeddedWindowController.java index 44d67687e260..98027bbed37f 100644 --- a/services/core/java/com/android/server/wm/EmbeddedWindowController.java +++ b/services/core/java/com/android/server/wm/EmbeddedWindowController.java @@ -23,6 +23,7 @@ import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; import static com.android.server.wm.WindowStateProto.IDENTIFIER; +import android.annotation.NonNull; import android.annotation.Nullable; import android.os.IBinder; import android.os.RemoteException; @@ -217,10 +218,10 @@ class EmbeddedWindowController { mHostWindowState.mInputWindowHandle.getInputApplicationHandle()); } - InputChannel openInputChannel() { + void openInputChannel(@NonNull InputChannel outInputChannel) { final String name = toString(); mInputChannel = mWmService.mInputManager.createInputChannel(name); - return mInputChannel; + mInputChannel.copyTo(outInputChannel); } void onRemoved() { diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java index daa823cf447f..798dc85ec11b 100644 --- a/services/core/java/com/android/server/wm/InsetsPolicy.java +++ b/services/core/java/com/android/server/wm/InsetsPolicy.java @@ -440,8 +440,8 @@ class InsetsPolicy { return originalState; } - void onInsetsModified(InsetsControlTarget caller) { - mStateController.onInsetsModified(caller); + void onRequestedVisibleTypesChanged(InsetsControlTarget caller) { + mStateController.onRequestedVisibleTypesChanged(caller); checkAbortTransient(caller); updateBarControlTarget(mFocusedWin); } diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java index 5e2618b00e2e..e1c865bb85be 100644 --- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java +++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java @@ -175,7 +175,7 @@ class InsetsSourceProvider { if (windowContainer == null) { setServerVisible(false); mSource.setVisibleFrame(null); - mSource.setInsetsRoundedCornerFrame(false); + mSource.setFlags(0, 0xffffffff); mSourceFrame.setEmpty(); } else { mWindowContainer.getInsetsSourceProviders().put(mSource.getId(), this); diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java index 249ead0a8509..addb5219c663 100644 --- a/services/core/java/com/android/server/wm/InsetsStateController.java +++ b/services/core/java/com/android/server/wm/InsetsStateController.java @@ -190,7 +190,7 @@ class InsetsStateController { } } - void onInsetsModified(InsetsControlTarget caller) { + void onRequestedVisibleTypesChanged(InsetsControlTarget caller) { boolean changed = false; for (int i = mProviders.size() - 1; i >= 0; i--) { changed |= mProviders.valueAt(i).updateClientVisibility(caller); @@ -352,7 +352,7 @@ class InsetsStateController { // to the clients, so that the clients can change the current visibilities to the // requested visibilities with animations. for (int i = newControlTargets.size() - 1; i >= 0; i--) { - onInsetsModified(newControlTargets.valueAt(i)); + onRequestedVisibleTypesChanged(newControlTargets.valueAt(i)); } newControlTargets.clear(); }); diff --git a/services/core/java/com/android/server/wm/LetterboxConfiguration.java b/services/core/java/com/android/server/wm/LetterboxConfiguration.java index 067a18c88aaa..09cd6a5aa688 100644 --- a/services/core/java/com/android/server/wm/LetterboxConfiguration.java +++ b/services/core/java/com/android/server/wm/LetterboxConfiguration.java @@ -18,11 +18,6 @@ package com.android.server.wm; import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM; import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME; -import static com.android.server.wm.LetterboxConfigurationDeviceConfig.KEY_ALLOW_IGNORE_ORIENTATION_REQUEST; -import static com.android.server.wm.LetterboxConfigurationDeviceConfig.KEY_ENABLE_CAMERA_COMPAT_TREATMENT; -import static com.android.server.wm.LetterboxConfigurationDeviceConfig.KEY_ENABLE_COMPAT_FAKE_FOCUS; -import static com.android.server.wm.LetterboxConfigurationDeviceConfig.KEY_ENABLE_DISPLAY_ROTATION_IMMERSIVE_APP_COMPAT_POLICY; -import static com.android.server.wm.LetterboxConfigurationDeviceConfig.KEY_ENABLE_LETTERBOX_TRANSLUCENT_ACTIVITY; import android.annotation.IntDef; import android.annotation.NonNull; @@ -44,6 +39,45 @@ final class LetterboxConfiguration { private static final String TAG = TAG_WITH_CLASS_NAME ? "LetterboxConfiguration" : TAG_ATM; + // Whether camera compatibility treatment is enabled. + // See DisplayRotationCompatPolicy for context. + private static final String KEY_ENABLE_CAMERA_COMPAT_TREATMENT = + "enable_compat_camera_treatment"; + + private static final boolean DEFAULT_VALUE_ENABLE_CAMERA_COMPAT_TREATMENT = true; + + // Whether enabling rotation compat policy for immersive apps that prevents auto + // rotation into non-optimal screen orientation while in fullscreen. This is needed + // because immersive apps, such as games, are often not optimized for all + // orientations and can have a poor UX when rotated. Additionally, some games rely + // on sensors for the gameplay so users can trigger such rotations accidentally + // when auto rotation is on. + private static final String KEY_ENABLE_DISPLAY_ROTATION_IMMERSIVE_APP_COMPAT_POLICY = + "enable_display_rotation_immersive_app_compat_policy"; + + private static final boolean DEFAULT_VALUE_ENABLE_DISPLAY_ROTATION_IMMERSIVE_APP_COMPAT_POLICY = + true; + + // Whether ignore orientation request is allowed + private static final String KEY_ALLOW_IGNORE_ORIENTATION_REQUEST = + "allow_ignore_orientation_request"; + + private static final boolean DEFAULT_VALUE_ALLOW_IGNORE_ORIENTATION_REQUEST = true; + + // Whether sending compat fake focus is enabled for unfocused apps in splitscreen. + // Some game engines wait to get focus before drawing the content of the app so + // this needs to be used otherwise the apps get blacked out when they are resumed + // and do not have focus yet. + private static final String KEY_ENABLE_COMPAT_FAKE_FOCUS = "enable_compat_fake_focus"; + + private static final boolean DEFAULT_VALUE_ENABLE_COMPAT_FAKE_FOCUS = true; + + // Whether translucent activities policy is enabled + private static final String KEY_ENABLE_LETTERBOX_TRANSLUCENT_ACTIVITY = + "enable_letterbox_translucent_activity"; + + private static final boolean DEFAULT_VALUE_ENABLE_LETTERBOX_TRANSLUCENT_ACTIVITY = true; + /** * Override of aspect ratio for fixed orientation letterboxing that is set via ADB with * set-fixed-orientation-letterbox-aspect-ratio or via {@link @@ -205,26 +239,13 @@ final class LetterboxConfiguration { // unresizable apps private boolean mIsDisplayAspectRatioEnabledForFixedOrientationLetterbox; - // Whether letterboxing strategy is enabled for translucent activities. If {@value false} - // all the feature is disabled - private boolean mTranslucentLetterboxingEnabled; - // Allows to enable letterboxing strategy for translucent activities ignoring flags. private boolean mTranslucentLetterboxingOverrideEnabled; - // Whether sending compat fake focus is enabled for unfocused apps in splitscreen. Some game - // engines wait to get focus before drawing the content of the app so this needs to be used - // otherwise the apps get blacked out when they are resumed and do not have focus yet. - private boolean mIsCompatFakeFocusEnabled; - // Whether we should use split screen aspect ratio for the activity when camera compat treatment // is enabled and activity is connected to the camera in fullscreen. private final boolean mIsCameraCompatSplitScreenAspectRatioEnabled; - // Whether camera compatibility treatment is enabled. - // See DisplayRotationCompatPolicy for context. - private final boolean mIsCameraCompatTreatmentEnabled; - // Whether activity "refresh" in camera compatibility treatment is enabled. // See RefreshCallbackItem for context. private boolean mIsCameraCompatTreatmentRefreshEnabled = true; @@ -240,15 +261,8 @@ final class LetterboxConfiguration { // LetterboxUiController#shouldIgnoreRequestedOrientation for details. private final boolean mIsPolicyForIgnoringRequestedOrientationEnabled; - // Whether enabling rotation compat policy for immersive apps that prevents auto rotation - // into non-optimal screen orientation while in fullscreen. This is needed because immersive - // apps, such as games, are often not optimized for all orientations and can have a poor UX - // when rotated. Additionally, some games rely on sensors for the gameplay so users can trigger - // such rotations accidentally when auto rotation is on. - private final boolean mIsDisplayRotationImmersiveAppCompatPolicyEnabled; - // Flags dynamically updated with {@link android.provider.DeviceConfig}. - @NonNull private final LetterboxConfigurationDeviceConfig mDeviceConfig; + @NonNull private final SynchedDeviceConfig mDeviceConfig; LetterboxConfiguration(@NonNull final Context systemUiContext) { this(systemUiContext, @@ -267,7 +281,6 @@ final class LetterboxConfiguration { LetterboxConfiguration(@NonNull final Context systemUiContext, @NonNull final LetterboxConfigurationPersister letterboxConfigurationPersister) { mContext = systemUiContext; - mDeviceConfig = new LetterboxConfigurationDeviceConfig(systemUiContext.getMainExecutor()); mFixedOrientationLetterboxAspectRatio = mContext.getResources().getFloat( R.dimen.config_fixedOrientationLetterboxAspectRatio); @@ -305,36 +318,34 @@ final class LetterboxConfiguration { mIsDisplayAspectRatioEnabledForFixedOrientationLetterbox = mContext.getResources() .getBoolean(R.bool .config_letterboxIsDisplayAspectRatioForFixedOrientationLetterboxEnabled); - mTranslucentLetterboxingEnabled = mContext.getResources().getBoolean( - R.bool.config_letterboxIsEnabledForTranslucentActivities); - mIsCameraCompatTreatmentEnabled = mContext.getResources().getBoolean( - R.bool.config_isWindowManagerCameraCompatTreatmentEnabled); mIsCameraCompatSplitScreenAspectRatioEnabled = mContext.getResources().getBoolean( R.bool.config_isWindowManagerCameraCompatSplitScreenAspectRatioEnabled); - mIsCompatFakeFocusEnabled = mContext.getResources().getBoolean( - R.bool.config_isCompatFakeFocusEnabled); mIsPolicyForIgnoringRequestedOrientationEnabled = mContext.getResources().getBoolean( R.bool.config_letterboxIsPolicyForIgnoringRequestedOrientationEnabled); - mIsDisplayRotationImmersiveAppCompatPolicyEnabled = mContext.getResources().getBoolean( - R.bool.config_letterboxIsDisplayRotationImmersiveAppCompatPolicyEnabled); - mDeviceConfig.updateFlagActiveStatus( - /* isActive */ mIsCameraCompatTreatmentEnabled, - /* key */ KEY_ENABLE_CAMERA_COMPAT_TREATMENT); - mDeviceConfig.updateFlagActiveStatus( - /* isActive */ mIsDisplayRotationImmersiveAppCompatPolicyEnabled, - /* key */ KEY_ENABLE_DISPLAY_ROTATION_IMMERSIVE_APP_COMPAT_POLICY); - mDeviceConfig.updateFlagActiveStatus( - /* isActive */ true, - /* key */ KEY_ALLOW_IGNORE_ORIENTATION_REQUEST); - mDeviceConfig.updateFlagActiveStatus( - /* isActive */ mIsCompatFakeFocusEnabled, - /* key */ KEY_ENABLE_COMPAT_FAKE_FOCUS); - mDeviceConfig.updateFlagActiveStatus( - /* isActive */ mTranslucentLetterboxingEnabled, - /* key */ KEY_ENABLE_LETTERBOX_TRANSLUCENT_ACTIVITY); mLetterboxConfigurationPersister = letterboxConfigurationPersister; mLetterboxConfigurationPersister.start(); + + mDeviceConfig = SynchedDeviceConfig.builder(DeviceConfig.NAMESPACE_WINDOW_MANAGER, + systemUiContext.getMainExecutor()) + .addDeviceConfigEntry(KEY_ENABLE_CAMERA_COMPAT_TREATMENT, + DEFAULT_VALUE_ENABLE_CAMERA_COMPAT_TREATMENT, + mContext.getResources().getBoolean( + R.bool.config_isWindowManagerCameraCompatTreatmentEnabled)) + .addDeviceConfigEntry(KEY_ENABLE_DISPLAY_ROTATION_IMMERSIVE_APP_COMPAT_POLICY, + DEFAULT_VALUE_ENABLE_DISPLAY_ROTATION_IMMERSIVE_APP_COMPAT_POLICY, + mContext.getResources().getBoolean(R.bool + .config_letterboxIsDisplayRotationImmersiveAppCompatPolicyEnabled)) + .addDeviceConfigEntry(KEY_ALLOW_IGNORE_ORIENTATION_REQUEST, + DEFAULT_VALUE_ALLOW_IGNORE_ORIENTATION_REQUEST, /* enabled */ true) + .addDeviceConfigEntry(KEY_ENABLE_COMPAT_FAKE_FOCUS, + DEFAULT_VALUE_ENABLE_COMPAT_FAKE_FOCUS, + mContext.getResources().getBoolean(R.bool.config_isCompatFakeFocusEnabled)) + .addDeviceConfigEntry(KEY_ENABLE_LETTERBOX_TRANSLUCENT_ACTIVITY, + DEFAULT_VALUE_ENABLE_LETTERBOX_TRANSLUCENT_ACTIVITY, + mContext.getResources().getBoolean( + R.bool.config_letterboxIsEnabledForTranslucentActivities)) + .build(); } /** @@ -342,7 +353,7 @@ final class LetterboxConfiguration { * via {@link android.provider.DeviceConfig}. */ boolean isIgnoreOrientationRequestAllowed() { - return mDeviceConfig.getFlag(KEY_ALLOW_IGNORE_ORIENTATION_REQUEST); + return mDeviceConfig.getFlagValue(KEY_ALLOW_IGNORE_ORIENTATION_REQUEST); } /** @@ -1049,28 +1060,21 @@ final class LetterboxConfiguration { } boolean isTranslucentLetterboxingEnabled() { - return mTranslucentLetterboxingOverrideEnabled || (mTranslucentLetterboxingEnabled - && mDeviceConfig.getFlag(KEY_ENABLE_LETTERBOX_TRANSLUCENT_ACTIVITY)); - } - - void setTranslucentLetterboxingEnabled(boolean translucentLetterboxingEnabled) { - mTranslucentLetterboxingEnabled = translucentLetterboxingEnabled; + return mTranslucentLetterboxingOverrideEnabled + || mDeviceConfig.getFlagValue(KEY_ENABLE_LETTERBOX_TRANSLUCENT_ACTIVITY); } void setTranslucentLetterboxingOverrideEnabled( boolean translucentLetterboxingOverrideEnabled) { mTranslucentLetterboxingOverrideEnabled = translucentLetterboxingOverrideEnabled; - setTranslucentLetterboxingEnabled(translucentLetterboxingOverrideEnabled); } /** * Resets whether we use the constraints override strategy for letterboxing when dealing - * with translucent activities {@link R.bool.config_letterboxIsEnabledForTranslucentActivities}. + * with translucent activities + * {@link mDeviceConfig.getFlagValue(KEY_ENABLE_LETTERBOX_TRANSLUCENT_ACTIVITY)}. */ void resetTranslucentLetterboxingEnabled() { - final boolean newValue = mContext.getResources().getBoolean( - R.bool.config_letterboxIsEnabledForTranslucentActivities); - setTranslucentLetterboxingEnabled(newValue); setTranslucentLetterboxingOverrideEnabled(false); } @@ -1100,15 +1104,7 @@ final class LetterboxConfiguration { /** Whether fake sending focus is enabled for unfocused apps in splitscreen */ boolean isCompatFakeFocusEnabled() { - return mIsCompatFakeFocusEnabled && mDeviceConfig.getFlag(KEY_ENABLE_COMPAT_FAKE_FOCUS); - } - - /** - * Overrides whether fake sending focus is enabled for unfocused apps in splitscreen - */ - @VisibleForTesting - void setIsCompatFakeFocusEnabled(boolean enabled) { - mIsCompatFakeFocusEnabled = enabled; + return mDeviceConfig.getFlagValue(KEY_ENABLE_COMPAT_FAKE_FOCUS); } /** @@ -1128,10 +1124,20 @@ final class LetterboxConfiguration { return mIsCameraCompatSplitScreenAspectRatioEnabled; } - /** Whether camera compatibility treatment is enabled. */ - boolean isCameraCompatTreatmentEnabled(boolean checkDeviceConfig) { - return mIsCameraCompatTreatmentEnabled && (!checkDeviceConfig - || mDeviceConfig.getFlag(KEY_ENABLE_CAMERA_COMPAT_TREATMENT)); + /** + * @return Whether camera compatibility treatment is currently enabled. + */ + boolean isCameraCompatTreatmentEnabled() { + return mDeviceConfig.getFlagValue(KEY_ENABLE_CAMERA_COMPAT_TREATMENT); + } + + /** + * @return Whether camera compatibility treatment is enabled at build time. This is used when + * we need to safely initialize a component before the {@link DeviceConfig} flag value is + * available. + */ + boolean isCameraCompatTreatmentEnabledAtBuildTime() { + return mDeviceConfig.isBuildTimeFlagEnabled(KEY_ENABLE_CAMERA_COMPAT_TREATMENT); } /** Whether camera compatibility refresh is enabled. */ @@ -1177,18 +1183,28 @@ final class LetterboxConfiguration { /** * Checks whether rotation compat policy for immersive apps that prevents auto rotation - * into non-optimal screen orientation while in fullscreen is enabled. + * into non-optimal screen orientation while in fullscreen is enabled at build time. This is + * used when we need to safely initialize a component before the {@link DeviceConfig} flag + * value is available. * * <p>This is needed because immersive apps, such as games, are often not optimized for all * orientations and can have a poor UX when rotated. Additionally, some games rely on sensors * for the gameplay so users can trigger such rotations accidentally when auto rotation is on. - * - * @param checkDeviceConfig whether should check both static config and a dynamic property - * from {@link DeviceConfig} or only static value. */ - boolean isDisplayRotationImmersiveAppCompatPolicyEnabled(final boolean checkDeviceConfig) { - return mIsDisplayRotationImmersiveAppCompatPolicyEnabled && (!checkDeviceConfig - || mDeviceConfig.getFlag(KEY_ENABLE_DISPLAY_ROTATION_IMMERSIVE_APP_COMPAT_POLICY)); + boolean isDisplayRotationImmersiveAppCompatPolicyEnabledAtBuildTime() { + return mDeviceConfig.isBuildTimeFlagEnabled( + KEY_ENABLE_DISPLAY_ROTATION_IMMERSIVE_APP_COMPAT_POLICY); } + /** + * Checks whether rotation compat policy for immersive apps that prevents auto rotation + * into non-optimal screen orientation while in fullscreen is currently enabled. + * + * <p>This is needed because immersive apps, such as games, are often not optimized for all + * orientations and can have a poor UX when rotated. Additionally, some games rely on sensors + * for the gameplay so users can trigger such rotations accidentally when auto rotation is on. + */ + boolean isDisplayRotationImmersiveAppCompatPolicyEnabled() { + return mDeviceConfig.getFlagValue(KEY_ENABLE_DISPLAY_ROTATION_IMMERSIVE_APP_COMPAT_POLICY); + } } diff --git a/services/core/java/com/android/server/wm/LetterboxConfigurationDeviceConfig.java b/services/core/java/com/android/server/wm/LetterboxConfigurationDeviceConfig.java deleted file mode 100644 index a739e42410a9..000000000000 --- a/services/core/java/com/android/server/wm/LetterboxConfigurationDeviceConfig.java +++ /dev/null @@ -1,180 +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.server.wm; - -import android.annotation.NonNull; -import android.provider.DeviceConfig; -import android.util.ArraySet; - -import com.android.internal.annotations.VisibleForTesting; - -import java.util.Map; -import java.util.concurrent.Executor; - -/** - * Utility class that caches {@link DeviceConfig} flags for app compat features and listens - * to updates by implementing {@link DeviceConfig.OnPropertiesChangedListener}. - */ -final class LetterboxConfigurationDeviceConfig - implements DeviceConfig.OnPropertiesChangedListener { - - static final String KEY_ENABLE_CAMERA_COMPAT_TREATMENT = "enable_compat_camera_treatment"; - private static final boolean DEFAULT_VALUE_ENABLE_CAMERA_COMPAT_TREATMENT = true; - - static final String KEY_ENABLE_DISPLAY_ROTATION_IMMERSIVE_APP_COMPAT_POLICY = - "enable_display_rotation_immersive_app_compat_policy"; - private static final boolean DEFAULT_VALUE_ENABLE_DISPLAY_ROTATION_IMMERSIVE_APP_COMPAT_POLICY = - true; - - static final String KEY_ALLOW_IGNORE_ORIENTATION_REQUEST = - "allow_ignore_orientation_request"; - private static final boolean DEFAULT_VALUE_ALLOW_IGNORE_ORIENTATION_REQUEST = true; - - static final String KEY_ENABLE_COMPAT_FAKE_FOCUS = "enable_compat_fake_focus"; - private static final boolean DEFAULT_VALUE_ENABLE_COMPAT_FAKE_FOCUS = true; - - static final String KEY_ENABLE_LETTERBOX_TRANSLUCENT_ACTIVITY = - "enable_letterbox_translucent_activity"; - - private static final boolean DEFAULT_VALUE_ENABLE_LETTERBOX_TRANSLUCENT_ACTIVITY = true; - - @VisibleForTesting - static final Map<String, Boolean> sKeyToDefaultValueMap = Map.of( - KEY_ENABLE_CAMERA_COMPAT_TREATMENT, - DEFAULT_VALUE_ENABLE_CAMERA_COMPAT_TREATMENT, - KEY_ENABLE_DISPLAY_ROTATION_IMMERSIVE_APP_COMPAT_POLICY, - DEFAULT_VALUE_ENABLE_DISPLAY_ROTATION_IMMERSIVE_APP_COMPAT_POLICY, - KEY_ALLOW_IGNORE_ORIENTATION_REQUEST, - DEFAULT_VALUE_ALLOW_IGNORE_ORIENTATION_REQUEST, - KEY_ENABLE_COMPAT_FAKE_FOCUS, - DEFAULT_VALUE_ENABLE_COMPAT_FAKE_FOCUS, - KEY_ENABLE_LETTERBOX_TRANSLUCENT_ACTIVITY, - DEFAULT_VALUE_ENABLE_LETTERBOX_TRANSLUCENT_ACTIVITY - ); - - // Whether camera compatibility treatment is enabled. - // See DisplayRotationCompatPolicy for context. - private boolean mIsCameraCompatTreatmentEnabled = - DEFAULT_VALUE_ENABLE_CAMERA_COMPAT_TREATMENT; - - // Whether enabling rotation compat policy for immersive apps that prevents auto rotation - // into non-optimal screen orientation while in fullscreen. This is needed because immersive - // apps, such as games, are often not optimized for all orientations and can have a poor UX - // when rotated. Additionally, some games rely on sensors for the gameplay so users can trigger - // such rotations accidentally when auto rotation is on. - private boolean mIsDisplayRotationImmersiveAppCompatPolicyEnabled = - DEFAULT_VALUE_ENABLE_DISPLAY_ROTATION_IMMERSIVE_APP_COMPAT_POLICY; - - // Whether enabling ignoreOrientationRequest is allowed on the device. - private boolean mIsAllowIgnoreOrientationRequest = - DEFAULT_VALUE_ALLOW_IGNORE_ORIENTATION_REQUEST; - - // Whether sending compat fake focus for split screen resumed activities is enabled. This is - // needed because some game engines wait to get focus before drawing the content of the app - // which isn't guaranteed by default in multi-window modes. - private boolean mIsCompatFakeFocusAllowed = DEFAULT_VALUE_ENABLE_COMPAT_FAKE_FOCUS; - - // Whether the letterbox strategy for transparent activities is allowed - private boolean mIsTranslucentLetterboxingAllowed = - DEFAULT_VALUE_ENABLE_LETTERBOX_TRANSLUCENT_ACTIVITY; - - // Set of active device configs that need to be updated in - // DeviceConfig.OnPropertiesChangedListener#onPropertiesChanged. - private final ArraySet<String> mActiveDeviceConfigsSet = new ArraySet<>(); - - LetterboxConfigurationDeviceConfig(@NonNull final Executor executor) { - DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_WINDOW_MANAGER, - executor, /* onPropertiesChangedListener */ this); - } - - @Override - public void onPropertiesChanged(@NonNull final DeviceConfig.Properties properties) { - for (int i = mActiveDeviceConfigsSet.size() - 1; i >= 0; i--) { - String key = mActiveDeviceConfigsSet.valueAt(i); - // Reads the new configuration, if the device config properties contain the key. - if (properties.getKeyset().contains(key)) { - readAndSaveValueFromDeviceConfig(key); - } - } - } - - /** - * Adds {@code key} to a set of flags that can be updated from the server if - * {@code isActive} is {@code true} and read it's current value from {@link DeviceConfig}. - */ - void updateFlagActiveStatus(boolean isActive, String key) { - if (!isActive) { - return; - } - mActiveDeviceConfigsSet.add(key); - readAndSaveValueFromDeviceConfig(key); - } - - /** - * Returns values of the {@code key} flag. - * - * @throws AssertionError {@code key} isn't recognised. - */ - boolean getFlag(String key) { - switch (key) { - case KEY_ENABLE_CAMERA_COMPAT_TREATMENT: - return mIsCameraCompatTreatmentEnabled; - case KEY_ENABLE_DISPLAY_ROTATION_IMMERSIVE_APP_COMPAT_POLICY: - return mIsDisplayRotationImmersiveAppCompatPolicyEnabled; - case KEY_ALLOW_IGNORE_ORIENTATION_REQUEST: - return mIsAllowIgnoreOrientationRequest; - case KEY_ENABLE_COMPAT_FAKE_FOCUS: - return mIsCompatFakeFocusAllowed; - case KEY_ENABLE_LETTERBOX_TRANSLUCENT_ACTIVITY: - return mIsTranslucentLetterboxingAllowed; - default: - throw new AssertionError("Unexpected flag name: " + key); - } - } - - private void readAndSaveValueFromDeviceConfig(String key) { - Boolean defaultValue = sKeyToDefaultValueMap.get(key); - if (defaultValue == null) { - throw new AssertionError("Haven't found default value for flag: " + key); - } - switch (key) { - case KEY_ENABLE_CAMERA_COMPAT_TREATMENT: - mIsCameraCompatTreatmentEnabled = getDeviceConfig(key, defaultValue); - break; - case KEY_ENABLE_DISPLAY_ROTATION_IMMERSIVE_APP_COMPAT_POLICY: - mIsDisplayRotationImmersiveAppCompatPolicyEnabled = - getDeviceConfig(key, defaultValue); - break; - case KEY_ALLOW_IGNORE_ORIENTATION_REQUEST: - mIsAllowIgnoreOrientationRequest = getDeviceConfig(key, defaultValue); - break; - case KEY_ENABLE_COMPAT_FAKE_FOCUS: - mIsCompatFakeFocusAllowed = getDeviceConfig(key, defaultValue); - break; - case KEY_ENABLE_LETTERBOX_TRANSLUCENT_ACTIVITY: - mIsTranslucentLetterboxingAllowed = getDeviceConfig(key, defaultValue); - break; - default: - throw new AssertionError("Unexpected flag name: " + key); - } - } - - private boolean getDeviceConfig(String key, boolean defaultValue) { - return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_WINDOW_MANAGER, - key, defaultValue); - } -} diff --git a/services/core/java/com/android/server/wm/LetterboxUiController.java b/services/core/java/com/android/server/wm/LetterboxUiController.java index d83c8612b9e9..a81683829396 100644 --- a/services/core/java/com/android/server/wm/LetterboxUiController.java +++ b/services/core/java/com/android/server/wm/LetterboxUiController.java @@ -294,18 +294,15 @@ final class LetterboxUiController { PROPERTY_COMPAT_ENABLE_FAKE_FOCUS); mBooleanPropertyCameraCompatAllowForceRotation = readComponentProperty(packageManager, mActivityRecord.packageName, - () -> mLetterboxConfiguration.isCameraCompatTreatmentEnabled( - /* checkDeviceConfig */ true), + () -> mLetterboxConfiguration.isCameraCompatTreatmentEnabled(), PROPERTY_CAMERA_COMPAT_ALLOW_FORCE_ROTATION); mBooleanPropertyCameraCompatAllowRefresh = readComponentProperty(packageManager, mActivityRecord.packageName, - () -> mLetterboxConfiguration.isCameraCompatTreatmentEnabled( - /* checkDeviceConfig */ true), + () -> mLetterboxConfiguration.isCameraCompatTreatmentEnabled(), PROPERTY_CAMERA_COMPAT_ALLOW_REFRESH); mBooleanPropertyCameraCompatEnableRefreshViaPause = readComponentProperty(packageManager, mActivityRecord.packageName, - () -> mLetterboxConfiguration.isCameraCompatTreatmentEnabled( - /* checkDeviceConfig */ true), + () -> mLetterboxConfiguration.isCameraCompatTreatmentEnabled(), PROPERTY_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE); mBooleanPropertyAllowOrientationOverride = @@ -697,7 +694,7 @@ final class LetterboxUiController { boolean shouldRefreshActivityForCameraCompat() { return shouldEnableWithOptOutOverrideAndProperty( /* gatingCondition */ () -> mLetterboxConfiguration - .isCameraCompatTreatmentEnabled(/* checkDeviceConfig */ true), + .isCameraCompatTreatmentEnabled(), mIsOverrideCameraCompatDisableRefreshEnabled, mBooleanPropertyCameraCompatAllowRefresh); } @@ -719,7 +716,7 @@ final class LetterboxUiController { boolean shouldRefreshActivityViaPauseForCameraCompat() { return shouldEnableWithOverrideAndProperty( /* gatingCondition */ () -> mLetterboxConfiguration - .isCameraCompatTreatmentEnabled(/* checkDeviceConfig */ true), + .isCameraCompatTreatmentEnabled(), mIsOverrideCameraCompatEnableRefreshViaPauseEnabled, mBooleanPropertyCameraCompatEnableRefreshViaPause); } @@ -738,7 +735,7 @@ final class LetterboxUiController { boolean shouldForceRotateForCameraCompat() { return shouldEnableWithOptOutOverrideAndProperty( /* gatingCondition */ () -> mLetterboxConfiguration - .isCameraCompatTreatmentEnabled(/* checkDeviceConfig */ true), + .isCameraCompatTreatmentEnabled(), mIsOverrideCameraCompatDisableForceRotationEnabled, mBooleanPropertyCameraCompatAllowForceRotation); } @@ -1428,7 +1425,8 @@ final class LetterboxUiController { for (int i = state.sourceSize() - 1; i >= 0; i--) { final InsetsSource source = state.sourceAt(i); if (source.getType() == WindowInsets.Type.navigationBars() - && source.insetsRoundedCornerFrame() && source.isVisible()) { + && source.hasFlags(InsetsSource.FLAG_INSETS_ROUNDED_CORNER) + && source.isVisible()) { return source; } } diff --git a/services/core/java/com/android/server/wm/RecentTasks.java b/services/core/java/com/android/server/wm/RecentTasks.java index e47787e97f20..9ef5ed051a13 100644 --- a/services/core/java/com/android/server/wm/RecentTasks.java +++ b/services/core/java/com/android/server/wm/RecentTasks.java @@ -1104,6 +1104,10 @@ class RecentTasks { // front unless overridden by the provided activity options mTasks.remove(taskIndex); mTasks.add(0, task); + if (taskIndex != 0) { + // Only notify when position changes + mTaskNotificationController.notifyTaskListUpdated(); + } if (DEBUG_RECENTS) { Slog.d(TAG_RECENTS, "addRecent: moving to top " + task @@ -1552,7 +1556,7 @@ class RecentTasks { task.affinity != null && task.affinity.equals(t.affinity); final boolean sameIntent = intent != null && intent.filterEquals(trIntent); boolean multiTasksAllowed = false; - final int flags = intent.getFlags(); + final int flags = intent != null ? intent.getFlags() : 0; if ((flags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_NEW_DOCUMENT)) != 0 && (flags & FLAG_ACTIVITY_MULTIPLE_TASK) != 0) { multiTasksAllowed = true; diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java index 7b10c6372b0e..b49c5fb2c25d 100644 --- a/services/core/java/com/android/server/wm/Session.java +++ b/services/core/java/com/android/server/wm/Session.java @@ -687,11 +687,11 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient { @Override public void updateRequestedVisibleTypes(IWindow window, @InsetsType int requestedVisibleTypes) { synchronized (mService.mGlobalLock) { - final WindowState windowState = mService.windowForClientLocked(this, window, + final WindowState win = mService.windowForClientLocked(this, window, false /* throwOnError */); - if (windowState != null) { - windowState.setRequestedVisibleTypes(requestedVisibleTypes); - windowState.getDisplayContent().getInsetsPolicy().onInsetsModified(windowState); + if (win != null) { + win.setRequestedVisibleTypes(requestedVisibleTypes); + win.getDisplayContent().getInsetsPolicy().onRequestedVisibleTypesChanged(win); } } } diff --git a/services/core/java/com/android/server/wm/SynchedDeviceConfig.java b/services/core/java/com/android/server/wm/SynchedDeviceConfig.java new file mode 100644 index 000000000000..c2e819e4c60b --- /dev/null +++ b/services/core/java/com/android/server/wm/SynchedDeviceConfig.java @@ -0,0 +1,190 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wm; + +import android.annotation.NonNull; +import android.provider.DeviceConfig; + +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.Executor; + +/** + * Utility class that caches {@link DeviceConfig} flags and listens to updates by implementing + * {@link DeviceConfig.OnPropertiesChangedListener}. + */ +final class SynchedDeviceConfig implements DeviceConfig.OnPropertiesChangedListener { + + private final String mNamespace; + private final Executor mExecutor; + + private final Map<String, SynchedDeviceConfigEntry> mDeviceConfigEntries; + + /** + * @param namespace The namespace for the {@link DeviceConfig} + * @param executor The {@link Executor} implementation to use when receiving updates + * @return the Builder implementation for the SynchedDeviceConfig + */ + @NonNull + static SynchedDeviceConfigBuilder builder(@NonNull String namespace, + @NonNull Executor executor) { + return new SynchedDeviceConfigBuilder(namespace, executor); + } + + private SynchedDeviceConfig(@NonNull String namespace, @NonNull Executor executor, + @NonNull Map<String, SynchedDeviceConfigEntry> deviceConfigEntries) { + mNamespace = namespace; + mExecutor = executor; + mDeviceConfigEntries = deviceConfigEntries; + } + + @Override + public void onPropertiesChanged(@NonNull final DeviceConfig.Properties properties) { + for (SynchedDeviceConfigEntry entry : mDeviceConfigEntries.values()) { + if (properties.getKeyset().contains(entry.mFlagKey)) { + entry.updateValue(properties.getBoolean(entry.mFlagKey, entry.mDefaultValue)); + } + } + } + + /** + * Builds the {@link SynchedDeviceConfig} and start listening to the {@link DeviceConfig} + * updates. + * + * @return The {@link SynchedDeviceConfig} + */ + @NonNull + private SynchedDeviceConfig start() { + DeviceConfig.addOnPropertiesChangedListener(mNamespace, + mExecutor, /* onPropertiesChangedListener */ this); + return this; + } + + /** + * Requests a {@link DeviceConfig} update for all the flags + */ + @NonNull + private SynchedDeviceConfig updateFlags() { + mDeviceConfigEntries.forEach((key, entry) -> entry.updateValue( + isDeviceConfigFlagEnabled(key, entry.mDefaultValue))); + return this; + } + + /** + * Returns values of the {@code key} flag with the following criteria: + * + * <ul> + * <li>{@code false} if the build time flag is disabled. + * <li>{@code defaultValue} if the build time flag is enabled and no {@link DeviceConfig} + * updates happened + * <li>Last value from {@link DeviceConfig} in case of updates. + * </ul> + * + * @throws IllegalArgumentException {@code key} isn't recognised. + */ + boolean getFlagValue(@NonNull String key) { + return findEntry(key).map(SynchedDeviceConfigEntry::getValue) + .orElseThrow(() -> new IllegalArgumentException("Unexpected flag name: " + key)); + } + + /** + * @return {@code true} if the flag for the given {@code key} was enabled at build time. + */ + boolean isBuildTimeFlagEnabled(@NonNull String key) { + return findEntry(key).map(SynchedDeviceConfigEntry::isBuildTimeFlagEnabled) + .orElseThrow(() -> new IllegalArgumentException("Unexpected flag name: " + key)); + } + + private boolean isDeviceConfigFlagEnabled(@NonNull String key, boolean defaultValue) { + return DeviceConfig.getBoolean(mNamespace, key, defaultValue); + } + + @NonNull + private Optional<SynchedDeviceConfigEntry> findEntry(@NonNull String key) { + return Optional.ofNullable(mDeviceConfigEntries.get(key)); + } + + static class SynchedDeviceConfigBuilder { + + private final String mNamespace; + private final Executor mExecutor; + + private final Map<String, SynchedDeviceConfigEntry> mDeviceConfigEntries = + new ConcurrentHashMap<>(); + + private SynchedDeviceConfigBuilder(@NonNull String namespace, @NonNull Executor executor) { + mNamespace = namespace; + mExecutor = executor; + } + + @NonNull + SynchedDeviceConfigBuilder addDeviceConfigEntry(@NonNull String key, + boolean defaultValue, boolean enabled) { + if (mDeviceConfigEntries.containsKey(key)) { + throw new AssertionError("Key already present: " + key); + } + mDeviceConfigEntries.put(key, + new SynchedDeviceConfigEntry(key, defaultValue, enabled)); + return this; + } + + @NonNull + SynchedDeviceConfig build() { + return new SynchedDeviceConfig(mNamespace, mExecutor, + mDeviceConfigEntries).updateFlags().start(); + } + } + + /** + * Contains all the information related to an entry to be managed by DeviceConfig + */ + private static class SynchedDeviceConfigEntry { + + // The key of the specific configuration flag + private final String mFlagKey; + + // The value of the flag at build time. + private final boolean mBuildTimeFlagEnabled; + + // The initial value of the flag when mBuildTimeFlagEnabled is true. + private final boolean mDefaultValue; + + // The current value of the flag when mBuildTimeFlagEnabled is true. + private volatile boolean mOverrideValue; + + private SynchedDeviceConfigEntry(@NonNull String flagKey, boolean defaultValue, + boolean enabled) { + mFlagKey = flagKey; + mOverrideValue = mDefaultValue = defaultValue; + mBuildTimeFlagEnabled = enabled; + } + + @NonNull + private void updateValue(boolean newValue) { + mOverrideValue = newValue; + } + + private boolean getValue() { + return mBuildTimeFlagEnabled && mOverrideValue; + } + + private boolean isBuildTimeFlagEnabled() { + return mBuildTimeFlagEnabled; + } + } +} diff --git a/services/core/java/com/android/server/wm/SystemGesturesPointerEventListener.java b/services/core/java/com/android/server/wm/SystemGesturesPointerEventListener.java index 878b33fa55ef..100735784fb1 100644 --- a/services/core/java/com/android/server/wm/SystemGesturesPointerEventListener.java +++ b/services/core/java/com/android/server/wm/SystemGesturesPointerEventListener.java @@ -20,6 +20,8 @@ import static android.view.DisplayCutout.BOUNDS_POSITION_BOTTOM; import static android.view.DisplayCutout.BOUNDS_POSITION_LEFT; import static android.view.DisplayCutout.BOUNDS_POSITION_RIGHT; import static android.view.DisplayCutout.BOUNDS_POSITION_TOP; +import static android.view.MotionEvent.AXIS_GESTURE_SWIPE_FINGER_COUNT; +import static android.view.MotionEvent.CLASSIFICATION_MULTI_FINGER_SWIPE; import android.annotation.NonNull; import android.content.Context; @@ -59,6 +61,12 @@ class SystemGesturesPointerEventListener implements PointerEventListener { private static final int SWIPE_FROM_RIGHT = 3; private static final int SWIPE_FROM_LEFT = 4; + private static final int TRACKPAD_SWIPE_NONE = 0; + private static final int TRACKPAD_SWIPE_FROM_TOP = 1; + private static final int TRACKPAD_SWIPE_FROM_BOTTOM = 2; + private static final int TRACKPAD_SWIPE_FROM_RIGHT = 3; + private static final int TRACKPAD_SWIPE_FROM_LEFT = 4; + private final Context mContext; private final Handler mHandler; private int mDisplayCutoutTouchableRegionSize; @@ -207,6 +215,25 @@ class SystemGesturesPointerEventListener implements PointerEventListener { break; case MotionEvent.ACTION_MOVE: if (mSwipeFireable) { + int trackpadSwipe = detectTrackpadThreeFingerSwipe(event); + mSwipeFireable = trackpadSwipe == TRACKPAD_SWIPE_NONE; + if (!mSwipeFireable) { + if (trackpadSwipe == TRACKPAD_SWIPE_FROM_TOP) { + if (DEBUG) Slog.d(TAG, "Firing onSwipeFromTop from trackpad"); + mCallbacks.onSwipeFromTop(); + } else if (trackpadSwipe == TRACKPAD_SWIPE_FROM_BOTTOM) { + if (DEBUG) Slog.d(TAG, "Firing onSwipeFromBottom from trackpad"); + mCallbacks.onSwipeFromBottom(); + } else if (trackpadSwipe == TRACKPAD_SWIPE_FROM_RIGHT) { + if (DEBUG) Slog.d(TAG, "Firing onSwipeFromRight from trackpad"); + mCallbacks.onSwipeFromRight(); + } else if (trackpadSwipe == TRACKPAD_SWIPE_FROM_LEFT) { + if (DEBUG) Slog.d(TAG, "Firing onSwipeFromLeft from trackpad"); + mCallbacks.onSwipeFromLeft(); + } + break; + } + final int swipe = detectSwipe(event); mSwipeFireable = swipe == SWIPE_NONE; if (swipe == SWIPE_FROM_TOP) { @@ -300,6 +327,31 @@ class SystemGesturesPointerEventListener implements PointerEventListener { return mDownPointers - 1; } + private int detectTrackpadThreeFingerSwipe(MotionEvent move) { + if (!isTrackpadThreeFingerSwipe(move)) { + return TRACKPAD_SWIPE_NONE; + } + + float dx = move.getX() - mDownX[0]; + float dy = move.getY() - mDownY[0]; + if (Math.abs(dx) < Math.abs(dy)) { + if (Math.abs(dy) > mSwipeDistanceThreshold) { + return dy > 0 ? TRACKPAD_SWIPE_FROM_TOP : TRACKPAD_SWIPE_FROM_BOTTOM; + } + } else { + if (Math.abs(dx) > mSwipeDistanceThreshold) { + return dx > 0 ? TRACKPAD_SWIPE_FROM_LEFT : TRACKPAD_SWIPE_FROM_RIGHT; + } + } + + return TRACKPAD_SWIPE_NONE; + } + + private static boolean isTrackpadThreeFingerSwipe(MotionEvent event) { + return event.getClassification() == CLASSIFICATION_MULTI_FINGER_SWIPE + && event.getAxisValue(AXIS_GESTURE_SWIPE_FINGER_COUNT) == 3; + } + private int detectSwipe(MotionEvent move) { final int historySize = move.getHistorySize(); final int pointerCount = move.getPointerCount(); diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index ceb80d6a1a8b..39772dda4792 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -49,6 +49,7 @@ import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; import static android.provider.Settings.Secure.USER_SETUP_COMPLETE; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.Display.INVALID_DISPLAY; +import static android.view.InsetsSource.FLAG_INSETS_ROUNDED_CORNER; import static android.view.SurfaceControl.METADATA_TASK_ID; import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES; @@ -1271,6 +1272,10 @@ class Task extends TaskFragment { if (isPersistable) { mLastTimeMoved = System.currentTimeMillis(); } + if (toTop && inRecents) { + // If task is in recents, ensure it is at the top + mTaskSupervisor.mRecentTasks.add(this); + } } // Close up recents linked list. @@ -1842,6 +1847,9 @@ class Task extends TaskFragment { td.setEnsureStatusBarContrastWhenTransparent( atd.getEnsureStatusBarContrastWhenTransparent()); } + if (td.getStatusBarAppearance() == 0) { + td.setStatusBarAppearance(atd.getStatusBarAppearance()); + } if (td.getNavigationBarColor() == 0) { td.setNavigationBarColor(atd.getNavigationBarColor()); td.setEnsureNavigationBarContrastWhenTransparent( @@ -2858,7 +2866,7 @@ class Task extends TaskFragment { getDisplayContent().getInsetsStateController().getRawInsetsState(); for (int i = state.sourceSize() - 1; i >= 0; i--) { final InsetsSource source = state.sourceAt(i); - if (source.insetsRoundedCornerFrame()) { + if (source.hasFlags(FLAG_INSETS_ROUNDED_CORNER)) { animationBounds.inset(source.calculateVisibleInsets(animationBounds)); } } diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java index 3d9edcac8eb1..e827027432c8 100644 --- a/services/core/java/com/android/server/wm/Transition.java +++ b/services/core/java/com/android/server/wm/Transition.java @@ -358,22 +358,30 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { mTransientLaunches.put(activity, restoreBelow); setTransientLaunchToChanges(activity); - if (restoreBelow != null) { - final Task transientRootTask = activity.getRootTask(); + final Task transientRootTask = activity.getRootTask(); + final WindowContainer<?> parent = restoreBelow != null ? restoreBelow.getParent() + : (transientRootTask != null ? transientRootTask.getParent() : null); + if (parent != null) { // Collect all visible tasks which can be occluded by the transient activity to // make sure they are in the participants so their visibilities can be updated when // finishing transition. - ((WindowContainer<?>) restoreBelow.getParent()).forAllTasks(t -> { + parent.forAllTasks(t -> { + // Skip transient-launch task + if (t == transientRootTask) return false; if (t.isVisibleRequested() && !t.isAlwaysOnTop() && !t.getWindowConfiguration().tasksAreFloating()) { - if (t.isRootTask() && t != transientRootTask) { + if (t.isRootTask()) { mTransientHideTasks.add(t); } if (t.isLeafTask()) { collect(t); } } - return t == restoreBelow; + return restoreBelow != null + // Stop at the restoreBelow task + ? t == restoreBelow + // Or stop at the last visible task if no restore-below (new task) + : (t.isRootTask() && t.fillsParent()); }); // Add FLAG_ABOVE_TRANSIENT_LAUNCH to the tree of transient-hide tasks, // so ChangeInfo#hasChanged() can return true to report the transition info. diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java index eb7b8c2555fb..0cf4e89571b5 100644 --- a/services/core/java/com/android/server/wm/WallpaperController.java +++ b/services/core/java/com/android/server/wm/WallpaperController.java @@ -247,7 +247,7 @@ class WallpaperController { resources.getBoolean( com.android.internal.R.bool.config_offsetWallpaperToCenterOfLargestDisplay); mIsLockscreenLiveWallpaperEnabled = - SystemProperties.getBoolean("persist.wm.debug.lockscreen_live_wallpaper", false); + SystemProperties.getBoolean("persist.wm.debug.lockscreen_live_wallpaper", true); } void resetLargestDisplay(Display display) { diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java index 0152666a830d..4bc4c266c114 100644 --- a/services/core/java/com/android/server/wm/WindowContainer.java +++ b/services/core/java/com/android/server/wm/WindowContainer.java @@ -4141,7 +4141,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< getDisplayContent().getInsetsStateController().getSourceProviders(); for (int i = providers.size(); i >= 0; i--) { final InsetsSourceProvider insetProvider = providers.valueAt(i); - if (!insetProvider.getSource().insetsRoundedCornerFrame()) { + if (!insetProvider.getSource().hasFlags(InsetsSource.FLAG_INSETS_ROUNDED_CORNER)) { return; } diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 67572bfcad18..bb3d10912724 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -4539,7 +4539,8 @@ public class WindowManagerService extends IWindowManager.Stub return; } dc.mRemoteInsetsControlTarget.setRequestedVisibleTypes(requestedVisibleTypes); - dc.getInsetsStateController().onInsetsModified(dc.mRemoteInsetsControlTarget); + dc.getInsetsStateController().onRequestedVisibleTypesChanged( + dc.mRemoteInsetsControlTarget); } } finally { Binder.restoreCallingIdentity(origId); @@ -7186,15 +7187,45 @@ public class WindowManagerService extends IWindowManager.Stub @Override public void requestAppKeyboardShortcuts(IResultReceiver receiver, int deviceId) { - mContext.enforceCallingOrSelfPermission(REGISTER_WINDOW_MANAGER_LISTENERS, - "requestAppKeyboardShortcuts"); + enforceRegisterWindowManagerListenersPermission("requestAppKeyboardShortcuts"); + WindowState focusedWindow = getFocusedWindow(); + if (focusedWindow == null || focusedWindow.mClient == null) { + notifyReceiverWithEmptyBundle(receiver); + return; + } try { - WindowState focusedWindow = getFocusedWindow(); - if (focusedWindow != null && focusedWindow.mClient != null) { - getFocusedWindow().mClient.requestAppKeyboardShortcuts(receiver, deviceId); - } + focusedWindow.mClient.requestAppKeyboardShortcuts(receiver, deviceId); } catch (RemoteException e) { + notifyReceiverWithEmptyBundle(receiver); + } + } + + @Override + public void requestImeKeyboardShortcuts(IResultReceiver receiver, int deviceId) { + enforceRegisterWindowManagerListenersPermission("requestImeKeyboardShortcuts"); + + WindowState imeWindow = mRoot.getCurrentInputMethodWindow(); + if (imeWindow == null || imeWindow.mClient == null) { + notifyReceiverWithEmptyBundle(receiver); + return; + } + try { + imeWindow.mClient.requestAppKeyboardShortcuts(receiver, deviceId); + } catch (RemoteException e) { + notifyReceiverWithEmptyBundle(receiver); + } + } + + private void enforceRegisterWindowManagerListenersPermission(String message) { + mContext.enforceCallingOrSelfPermission(REGISTER_WINDOW_MANAGER_LISTENERS, message); + } + + private static void notifyReceiverWithEmptyBundle(IResultReceiver receiver) { + try { + receiver.send(0, Bundle.EMPTY); + } catch (RemoteException e) { + ProtoLog.e(WM_ERROR, "unable to call receiver for empty keyboard shortcuts"); } } @@ -8271,6 +8302,13 @@ public class WindowManagerService extends IWindowManager.Stub @Override public void addTrustedTaskOverlay(int taskId, SurfaceControlViewHost.SurfacePackage overlay) { + if (overlay == null) { + throw new IllegalArgumentException("Invalid overlay passed in for task=" + taskId); + } else if (overlay.getSurfaceControl() == null + || !overlay.getSurfaceControl().isValid()) { + throw new IllegalArgumentException( + "Invalid overlay surfacecontrol passed in for task=" + taskId); + } synchronized (mGlobalLock) { final Task task = mRoot.getRootTask(taskId); if (task == null) { @@ -8283,6 +8321,13 @@ public class WindowManagerService extends IWindowManager.Stub @Override public void removeTrustedTaskOverlay(int taskId, SurfaceControlViewHost.SurfacePackage overlay) { + if (overlay == null) { + throw new IllegalArgumentException("Invalid overlay passed in for task=" + taskId); + } else if (overlay.getSurfaceControl() == null + || !overlay.getSurfaceControl().isValid()) { + throw new IllegalArgumentException( + "Invalid overlay surfacecontrol passed in for task=" + taskId); + } synchronized (mGlobalLock) { final Task task = mRoot.getRootTask(taskId); if (task == null) { @@ -8748,24 +8793,22 @@ public class WindowManagerService extends IWindowManager.Stub final int sanitizedType = sanitizeWindowType(session, displayId, windowToken, type); final InputApplicationHandle applicationHandle; final String name; - final InputChannel clientChannel; + Objects.requireNonNull(outInputChannel); synchronized (mGlobalLock) { EmbeddedWindowController.EmbeddedWindow win = new EmbeddedWindowController.EmbeddedWindow(session, this, window, mInputToWindowMap.get(hostInputToken), callingUid, callingPid, sanitizedType, displayId, focusGrantToken, inputHandleName, (flags & FLAG_NOT_FOCUSABLE) == 0); - clientChannel = win.openInputChannel(); - mEmbeddedWindowController.add(clientChannel.getToken(), win); + win.openInputChannel(outInputChannel); + mEmbeddedWindowController.add(outInputChannel.getToken(), win); applicationHandle = win.getApplicationHandle(); name = win.toString(); } - updateInputChannel(clientChannel.getToken(), callingUid, callingPid, displayId, surface, + updateInputChannel(outInputChannel.getToken(), callingUid, callingPid, displayId, surface, name, applicationHandle, flags, privateFlags, inputFeatures, sanitizedType, null /* region */, window); - - clientChannel.copyTo(outInputChannel); } boolean transferEmbeddedTouchFocusToHost(IWindow embeddedWindow) { diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index cc9ac76a265e..5579e5296cb0 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -1445,6 +1445,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP Slog.v(TAG_WM, "Win " + this + " config changed: " + getConfiguration()); } + final boolean dragResizingChanged = !mDragResizingChangeReported && isDragResizeChanged(); + final boolean attachedFrameChanged = LOCAL_LAYOUT && mLayoutAttached && getParentWindow().frameChanged(); @@ -1458,6 +1460,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP if (didFrameInsetsChange || configChanged || insetsChanged + || dragResizingChanged || shouldSendRedrawForSync() || attachedFrameChanged) { ProtoLog.v(WM_DEBUG_RESIZE, @@ -1478,7 +1481,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP // Reset the drawn state if the window need to redraw for the change, so the transition // can wait until it has finished drawing to start. - if ((configChanged || getOrientationChanging()) && isVisibleRequested()) { + if ((configChanged || getOrientationChanging() || dragResizingChanged) + && isVisibleRequested()) { winAnimator.mDrawState = DRAW_PENDING; if (mActivityRecord != null) { mActivityRecord.clearAllDrawn(); diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp index f0d718a30535..1e502e17b00e 100644 --- a/services/core/jni/com_android_server_input_InputManagerService.cpp +++ b/services/core/jni/com_android_server_input_InputManagerService.cpp @@ -355,6 +355,8 @@ public: void onPointerDownOutsideFocus(const sp<IBinder>& touchedToken) override; void setPointerCapture(const PointerCaptureRequest& request) override; void notifyDropWindow(const sp<IBinder>& token, float x, float y) override; + void notifyDeviceInteraction(int32_t deviceId, nsecs_t timestamp, + const std::set<int32_t>& uids) override; /* --- PointerControllerPolicyInterface implementation --- */ @@ -966,6 +968,15 @@ void NativeInputManager::notifyDropWindow(const sp<IBinder>& token, float x, flo checkAndClearExceptionFromCallback(env, "notifyDropWindow"); } +void NativeInputManager::notifyDeviceInteraction(int32_t deviceId, nsecs_t timestamp, + const std::set<int32_t>& uids) { + static const bool ENABLE_INPUT_DEVICE_USAGE_METRICS = + sysprop::InputProperties::enable_input_device_usage_metrics().value_or(true); + if (!ENABLE_INPUT_DEVICE_USAGE_METRICS) return; + + mInputManager->getMetricsCollector().notifyDeviceInteraction(deviceId, timestamp, uids); +} + void NativeInputManager::notifySensorEvent(int32_t deviceId, InputDeviceSensorType sensorType, InputDeviceSensorAccuracy accuracy, nsecs_t timestamp, const std::vector<float>& values) { @@ -1596,8 +1607,8 @@ PointerIconStyle NativeInputManager::getDefaultPointerIconId() { } PointerIconStyle NativeInputManager::getDefaultStylusIconId() { - // TODO: add resource for default stylus icon and change this - return PointerIconStyle::TYPE_CROSSHAIR; + // Use the empty icon as the default pointer icon for a hovering stylus. + return PointerIconStyle::TYPE_NULL; } PointerIconStyle NativeInputManager::getCustomPointerIconId() { @@ -2214,13 +2225,25 @@ static jobject nativeGetLights(JNIEnv* env, jobject nativeImplObj, jint deviceId jCapability |= env->GetStaticIntField(gLightClassInfo.clazz, gLightClassInfo.lightCapabilityColorRgb); } + + ScopedLocalRef<jintArray> jPreferredBrightnessLevels{env}; + if (!lightInfo.preferredBrightnessLevels.empty()) { + std::vector<int32_t> vec; + for (auto it : lightInfo.preferredBrightnessLevels) { + vec.push_back(ftl::to_underlying(it)); + } + jPreferredBrightnessLevels.reset(env->NewIntArray(vec.size())); + env->SetIntArrayRegion(jPreferredBrightnessLevels.get(), 0, vec.size(), vec.data()); + } + ScopedLocalRef<jobject> lightObj(env, env->NewObject(gLightClassInfo.clazz, gLightClassInfo.constructor, static_cast<jint>(lightInfo.id), env->NewStringUTF(lightInfo.name.c_str()), static_cast<jint>(lightInfo.ordinal), - jTypeId, jCapability)); + jTypeId, jCapability, + jPreferredBrightnessLevels.get())); // Add light object to list env->CallBooleanMethod(jLights, gArrayListClassInfo.add, lightObj.get()); } @@ -2846,7 +2869,7 @@ int register_android_server_InputManager(JNIEnv* env) { FIND_CLASS(gLightClassInfo.clazz, "android/hardware/lights/Light"); gLightClassInfo.clazz = jclass(env->NewGlobalRef(gLightClassInfo.clazz)); GET_METHOD_ID(gLightClassInfo.constructor, gLightClassInfo.clazz, "<init>", - "(ILjava/lang/String;III)V"); + "(ILjava/lang/String;III[I)V"); gLightClassInfo.clazz = jclass(env->NewGlobalRef(gLightClassInfo.clazz)); gLightClassInfo.lightTypeInput = diff --git a/services/core/xsd/display-device-config/display-device-config.xsd b/services/core/xsd/display-device-config/display-device-config.xsd index f96ca582c28f..7104a80c668d 100644 --- a/services/core/xsd/display-device-config/display-device-config.xsd +++ b/services/core/xsd/display-device-config/display-device-config.xsd @@ -46,6 +46,8 @@ <xs:annotation name="nonnull"/> <xs:annotation name="final"/> </xs:element> + <xs:element type="luxThrottling" name="luxThrottling" minOccurs="0" + maxOccurs="1"/> <xs:element type="highBrightnessMode" name="highBrightnessMode" minOccurs="0" maxOccurs="1"/> <xs:element type="displayQuirks" name="quirks" minOccurs="0" maxOccurs="1"/> @@ -137,6 +139,39 @@ </xs:sequence> </xs:complexType> + <xs:complexType name="luxThrottling"> + <xs:sequence> + <xs:element name="brightnessLimitMap" type="brightnessLimitMap" + maxOccurs="unbounded"> + <xs:annotation name="nonnull"/> + <xs:annotation name="final"/> + </xs:element> + </xs:sequence> + </xs:complexType> + + <xs:complexType name="brightnessLimitMap"> + <xs:sequence> + <xs:element name="type" type="PredefinedBrightnessLimitNames"> + <xs:annotation name="nonnull"/> + <xs:annotation name="final"/> + </xs:element> + <!-- lux level from light sensor to screen brightness recommended max value map. + Screen brightness recommended max value is to highBrightnessMode.transitionPoint and must be below that --> + <xs:element name="map" type="nonNegativeFloatToFloatMap"> + <xs:annotation name="nonnull"/> + <xs:annotation name="final"/> + </xs:element> + </xs:sequence> + </xs:complexType> + + <!-- Predefined type names as defined by DisplayDeviceConfig.BrightnessLimitMapType --> + <xs:simpleType name="PredefinedBrightnessLimitNames"> + <xs:restriction base="xs:string"> + <xs:enumeration value="default"/> + <xs:enumeration value="adaptive"/> + </xs:restriction> + </xs:simpleType> + <xs:complexType name="highBrightnessMode"> <xs:all> <xs:element name="transitionPoint" type="nonNegativeDecimal" minOccurs="1" @@ -575,4 +610,27 @@ <xs:annotation name="final"/> </xs:element> </xs:complexType> + + <!-- generic types --> + <xs:complexType name="nonNegativeFloatToFloatPoint"> + <xs:sequence> + <xs:element name="first" type="nonNegativeDecimal"> + <xs:annotation name="nonnull"/> + <xs:annotation name="final"/> + </xs:element> + <xs:element name="second" type="nonNegativeDecimal"> + <xs:annotation name="nonnull"/> + <xs:annotation name="final"/> + </xs:element> + </xs:sequence> + </xs:complexType> + + <xs:complexType name="nonNegativeFloatToFloatMap"> + <xs:sequence> + <xs:element name="point" type="nonNegativeFloatToFloatPoint" maxOccurs="unbounded"> + <xs:annotation name="nonnull"/> + <xs:annotation name="final"/> + </xs:element> + </xs:sequence> + </xs:complexType> </xs:schema> diff --git a/services/core/xsd/display-device-config/schema/current.txt b/services/core/xsd/display-device-config/schema/current.txt index ad6434e0c545..507c9dccda59 100644 --- a/services/core/xsd/display-device-config/schema/current.txt +++ b/services/core/xsd/display-device-config/schema/current.txt @@ -26,6 +26,14 @@ package com.android.server.display.config { method public final java.util.List<com.android.server.display.config.DisplayBrightnessPoint> getDisplayBrightnessPoint(); } + public class BrightnessLimitMap { + ctor public BrightnessLimitMap(); + method @NonNull public final com.android.server.display.config.NonNegativeFloatToFloatMap getMap(); + method @NonNull public final com.android.server.display.config.PredefinedBrightnessLimitNames getType(); + method public final void setMap(@NonNull com.android.server.display.config.NonNegativeFloatToFloatMap); + method public final void setType(@NonNull com.android.server.display.config.PredefinedBrightnessLimitNames); + } + public class BrightnessThresholds { ctor public BrightnessThresholds(); method public final com.android.server.display.config.ThresholdPoints getBrightnessThresholdPoints(); @@ -89,6 +97,7 @@ package com.android.server.display.config { method public final com.android.server.display.config.Thresholds getDisplayBrightnessChangeThresholdsIdle(); method public com.android.server.display.config.HighBrightnessMode getHighBrightnessMode(); method public final com.android.server.display.config.SensorDetails getLightSensor(); + method public com.android.server.display.config.LuxThrottling getLuxThrottling(); method @Nullable public final String getName(); method public final com.android.server.display.config.SensorDetails getProxSensor(); method public com.android.server.display.config.DisplayQuirks getQuirks(); @@ -115,6 +124,7 @@ package com.android.server.display.config { method public final void setDisplayBrightnessChangeThresholdsIdle(com.android.server.display.config.Thresholds); method public void setHighBrightnessMode(com.android.server.display.config.HighBrightnessMode); method public final void setLightSensor(com.android.server.display.config.SensorDetails); + method public void setLuxThrottling(com.android.server.display.config.LuxThrottling); method public final void setName(@Nullable String); method public final void setProxSensor(com.android.server.display.config.SensorDetails); method public void setQuirks(com.android.server.display.config.DisplayQuirks); @@ -173,6 +183,11 @@ package com.android.server.display.config { method public java.util.List<java.math.BigInteger> getItem(); } + public class LuxThrottling { + ctor public LuxThrottling(); + method @NonNull public final java.util.List<com.android.server.display.config.BrightnessLimitMap> getBrightnessLimitMap(); + } + public class NitsMap { ctor public NitsMap(); method public String getInterpolation(); @@ -180,6 +195,19 @@ package com.android.server.display.config { method public void setInterpolation(String); } + public class NonNegativeFloatToFloatMap { + ctor public NonNegativeFloatToFloatMap(); + method @NonNull public final java.util.List<com.android.server.display.config.NonNegativeFloatToFloatPoint> getPoint(); + } + + public class NonNegativeFloatToFloatPoint { + ctor public NonNegativeFloatToFloatPoint(); + method @NonNull public final java.math.BigDecimal getFirst(); + method @NonNull public final java.math.BigDecimal getSecond(); + method public final void setFirst(@NonNull java.math.BigDecimal); + method public final void setSecond(@NonNull java.math.BigDecimal); + } + public class Point { ctor public Point(); method @NonNull public final java.math.BigDecimal getNits(); @@ -188,6 +216,12 @@ package com.android.server.display.config { method public final void setValue(@NonNull java.math.BigDecimal); } + public enum PredefinedBrightnessLimitNames { + method public String getRawName(); + enum_constant public static final com.android.server.display.config.PredefinedBrightnessLimitNames _default; + enum_constant public static final com.android.server.display.config.PredefinedBrightnessLimitNames adaptive; + } + public class RefreshRateConfigs { ctor public RefreshRateConfigs(); method public final java.math.BigInteger getDefaultPeakRefreshRate(); diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index e44b8cdbaab0..417fc3949f2c 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -13598,6 +13598,9 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { : SecurityLog.TAG_USER_RESTRICTION_REMOVED; SecurityLog.writeEvent(eventTag, caller.getPackageName(), caller.getUserId(), key); } + + Slogf.i(LOG_TAG, "Changing user restriction %s to: %b caller: %s", + key, enabled, caller.toString()); } private void saveUserRestrictionsLocked(int userId) { @@ -14842,17 +14845,23 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { "Caller is not managed profile owner or device owner;" + " only managed profile owner or device owner may control the preferential" + " network service"); - synchronized (getLockObject()) { - final ActiveAdmin requiredAdmin = getDeviceOrProfileOwnerAdminLocked( - caller.getUserId()); - if (!requiredAdmin.mPreferentialNetworkServiceConfigs.equals( - preferentialNetworkServiceConfigs)) { - requiredAdmin.mPreferentialNetworkServiceConfigs = - new ArrayList<>(preferentialNetworkServiceConfigs); - saveSettingsLocked(caller.getUserId()); + + try { + updateNetworkPreferenceForUser(caller.getUserId(), preferentialNetworkServiceConfigs); + synchronized (getLockObject()) { + final ActiveAdmin requiredAdmin = getDeviceOrProfileOwnerAdminLocked( + caller.getUserId()); + if (!requiredAdmin.mPreferentialNetworkServiceConfigs.equals( + preferentialNetworkServiceConfigs)) { + requiredAdmin.mPreferentialNetworkServiceConfigs = + new ArrayList<>(preferentialNetworkServiceConfigs); + saveSettingsLocked(caller.getUserId()); + } } + } catch (Exception e) { + Slogf.e(LOG_TAG, "Failed to set preferential network service configs"); + throw e; } - updateNetworkPreferenceForUser(caller.getUserId(), preferentialNetworkServiceConfigs); DevicePolicyEventLogger .createEvent(DevicePolicyEnums.SET_PREFERENTIAL_NETWORK_SERVICE_ENABLED) .setBoolean(preferentialNetworkServiceConfigs diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index af841808992e..991248aceb4b 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -3255,6 +3255,12 @@ public final class SystemServer implements Dumpable { Slog.d(TAG, "ContentCaptureService disabled because resource is not overlaid"); return; } + if (!deviceHasConfigString(context, R.string.config_defaultContentProtectionService)) { + Slog.d( + TAG, + "ContentProtectionService disabled because resource is not overlaid," + + " ContentCaptureService still enabled"); + } } t.traceBegin("StartContentCaptureService"); diff --git a/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_empty_affected_cpus/policy0/affected_cpus b/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_empty_affected_cpus/policy0/affected_cpus new file mode 100644 index 000000000000..8b137891791f --- /dev/null +++ b/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_empty_affected_cpus/policy0/affected_cpus @@ -0,0 +1 @@ + diff --git a/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_empty_affected_cpus/policy0/related_cpus b/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_empty_affected_cpus/policy0/related_cpus new file mode 100644 index 000000000000..c227083464fb --- /dev/null +++ b/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_empty_affected_cpus/policy0/related_cpus @@ -0,0 +1 @@ +0
\ No newline at end of file diff --git a/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_empty_affected_cpus/policy0/scaling_cur_freq b/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_empty_affected_cpus/policy0/scaling_cur_freq new file mode 100644 index 000000000000..dadd9737778d --- /dev/null +++ b/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_empty_affected_cpus/policy0/scaling_cur_freq @@ -0,0 +1 @@ +1230000 diff --git a/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_empty_affected_cpus/policy0/scaling_max_freq b/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_empty_affected_cpus/policy0/scaling_max_freq new file mode 100644 index 000000000000..a93d6f7b2c09 --- /dev/null +++ b/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_empty_affected_cpus/policy0/scaling_max_freq @@ -0,0 +1 @@ +2500000 diff --git a/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_empty_affected_cpus/policy0/stats/time_in_state b/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_empty_affected_cpus/policy0/stats/time_in_state new file mode 100644 index 000000000000..5121f6661a7e --- /dev/null +++ b/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_empty_affected_cpus/policy0/stats/time_in_state @@ -0,0 +1,4 @@ +200000 500 +350000 500 +500000 1000 +2500000 100 diff --git a/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_empty_affected_cpus/policy1/affected_cpus b/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_empty_affected_cpus/policy1/affected_cpus new file mode 100644 index 000000000000..d00491fd7e5b --- /dev/null +++ b/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_empty_affected_cpus/policy1/affected_cpus @@ -0,0 +1 @@ +1 diff --git a/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_empty_affected_cpus/policy1/related_cpus b/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_empty_affected_cpus/policy1/related_cpus new file mode 100644 index 000000000000..56a6051ca2b0 --- /dev/null +++ b/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_empty_affected_cpus/policy1/related_cpus @@ -0,0 +1 @@ +1
\ No newline at end of file diff --git a/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_empty_affected_cpus/policy1/scaling_cur_freq b/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_empty_affected_cpus/policy1/scaling_cur_freq new file mode 100644 index 000000000000..2bc4ce96ec98 --- /dev/null +++ b/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_empty_affected_cpus/policy1/scaling_cur_freq @@ -0,0 +1 @@ +1450000 diff --git a/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_empty_affected_cpus/policy1/scaling_max_freq b/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_empty_affected_cpus/policy1/scaling_max_freq new file mode 100644 index 000000000000..c754f1a461ab --- /dev/null +++ b/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_empty_affected_cpus/policy1/scaling_max_freq @@ -0,0 +1 @@ +2800000 diff --git a/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_empty_affected_cpus/policy1/stats/time_in_state b/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_empty_affected_cpus/policy1/stats/time_in_state new file mode 100644 index 000000000000..7ed12cfce58d --- /dev/null +++ b/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_empty_affected_cpus/policy1/stats/time_in_state @@ -0,0 +1,4 @@ +200000 500 +350000 500 +500000 1000 +2800000 100 diff --git a/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_empty_related_cpus/policy0/affected_cpus b/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_empty_related_cpus/policy0/affected_cpus new file mode 100644 index 000000000000..573541ac9702 --- /dev/null +++ b/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_empty_related_cpus/policy0/affected_cpus @@ -0,0 +1 @@ +0 diff --git a/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_empty_related_cpus/policy0/related_cpus b/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_empty_related_cpus/policy0/related_cpus new file mode 100644 index 000000000000..8b137891791f --- /dev/null +++ b/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_empty_related_cpus/policy0/related_cpus @@ -0,0 +1 @@ + diff --git a/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_empty_related_cpus/policy0/scaling_cur_freq b/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_empty_related_cpus/policy0/scaling_cur_freq new file mode 100644 index 000000000000..dadd9737778d --- /dev/null +++ b/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_empty_related_cpus/policy0/scaling_cur_freq @@ -0,0 +1 @@ +1230000 diff --git a/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_empty_related_cpus/policy0/scaling_max_freq b/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_empty_related_cpus/policy0/scaling_max_freq new file mode 100644 index 000000000000..a93d6f7b2c09 --- /dev/null +++ b/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_empty_related_cpus/policy0/scaling_max_freq @@ -0,0 +1 @@ +2500000 diff --git a/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_empty_related_cpus/policy0/stats/time_in_state b/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_empty_related_cpus/policy0/stats/time_in_state new file mode 100644 index 000000000000..5121f6661a7e --- /dev/null +++ b/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_empty_related_cpus/policy0/stats/time_in_state @@ -0,0 +1,4 @@ +200000 500 +350000 500 +500000 1000 +2500000 100 diff --git a/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_empty_related_cpus/policy1/affected_cpus b/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_empty_related_cpus/policy1/affected_cpus new file mode 100644 index 000000000000..d00491fd7e5b --- /dev/null +++ b/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_empty_related_cpus/policy1/affected_cpus @@ -0,0 +1 @@ +1 diff --git a/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_empty_related_cpus/policy1/related_cpus b/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_empty_related_cpus/policy1/related_cpus new file mode 100644 index 000000000000..56a6051ca2b0 --- /dev/null +++ b/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_empty_related_cpus/policy1/related_cpus @@ -0,0 +1 @@ +1
\ No newline at end of file diff --git a/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_empty_related_cpus/policy1/scaling_cur_freq b/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_empty_related_cpus/policy1/scaling_cur_freq new file mode 100644 index 000000000000..2bc4ce96ec98 --- /dev/null +++ b/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_empty_related_cpus/policy1/scaling_cur_freq @@ -0,0 +1 @@ +1450000 diff --git a/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_empty_related_cpus/policy1/scaling_max_freq b/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_empty_related_cpus/policy1/scaling_max_freq new file mode 100644 index 000000000000..c754f1a461ab --- /dev/null +++ b/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_empty_related_cpus/policy1/scaling_max_freq @@ -0,0 +1 @@ +2800000 diff --git a/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_empty_related_cpus/policy1/stats/time_in_state b/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_empty_related_cpus/policy1/stats/time_in_state new file mode 100644 index 000000000000..7ed12cfce58d --- /dev/null +++ b/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_empty_related_cpus/policy1/stats/time_in_state @@ -0,0 +1,4 @@ +200000 500 +350000 500 +500000 1000 +2800000 100 diff --git a/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_time_in_state_2/policy0/scaling_max_freq b/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_time_in_state_2/policy0/scaling_max_freq index a93d6f7b2c09..d17275fcb4fc 100644 --- a/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_time_in_state_2/policy0/scaling_max_freq +++ b/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_time_in_state_2/policy0/scaling_max_freq @@ -1 +1 @@ -2500000 +2600000 diff --git a/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_time_in_state_2/policy1/scaling_max_freq b/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_time_in_state_2/policy1/scaling_max_freq index c754f1a461ab..79c41c7d3c28 100644 --- a/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_time_in_state_2/policy1/scaling_max_freq +++ b/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_time_in_state_2/policy1/scaling_max_freq @@ -1 +1 @@ -2800000 +2900000 diff --git a/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_time_in_state_2/policy2/scaling_max_freq b/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_time_in_state_2/policy2/scaling_max_freq index deebb18c2f77..526a7175060a 100644 --- a/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_time_in_state_2/policy2/scaling_max_freq +++ b/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpufreq_with_time_in_state_2/policy2/scaling_max_freq @@ -1 +1 @@ -2000000 +2100000 diff --git a/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpuset_with_empty_cpus/background/cpus b/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpuset_with_empty_cpus/background/cpus new file mode 100644 index 000000000000..8b137891791f --- /dev/null +++ b/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpuset_with_empty_cpus/background/cpus @@ -0,0 +1 @@ + diff --git a/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpuset_with_empty_cpus/top-app/cpus b/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpuset_with_empty_cpus/top-app/cpus new file mode 100644 index 000000000000..40c7bb2f1a2a --- /dev/null +++ b/services/tests/mockingservicestests/assets/CpuInfoReaderTest/valid_cpuset_with_empty_cpus/top-app/cpus @@ -0,0 +1 @@ +0-3 diff --git a/services/tests/mockingservicestests/src/com/android/server/cpu/CpuInfoReaderTest.java b/services/tests/mockingservicestests/src/com/android/server/cpu/CpuInfoReaderTest.java index 04f6f8b2e9f0..2fbe8aab73d0 100644 --- a/services/tests/mockingservicestests/src/com/android/server/cpu/CpuInfoReaderTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/cpu/CpuInfoReaderTest.java @@ -48,6 +48,11 @@ public final class CpuInfoReaderTest extends ExpectableTestCase { private static final String TAG = CpuInfoReaderTest.class.getSimpleName(); private static final String ROOT_DIR_NAME = "CpuInfoReaderTest"; private static final String VALID_CPUSET_DIR = "valid_cpuset"; + private static final String VALID_CPUSET_WITH_EMPTY_CPUS = "valid_cpuset_with_empty_cpus"; + private static final String VALID_CPUFREQ_WITH_EMPTY_AFFECTED_CPUS = + "valid_cpufreq_with_empty_affected_cpus"; + private static final String VALID_CPUFREQ_WITH_EMPTY_RELATED_CPUS = + "valid_cpufreq_with_empty_related_cpus"; private static final String VALID_CPUFREQ_WITH_TIME_IN_STATE_DIR = "valid_cpufreq_with_time_in_state"; private static final String VALID_CPUFREQ_WITH_TIME_IN_STATE_2_DIR = @@ -142,8 +147,8 @@ public final class CpuInfoReaderTest extends ExpectableTestCase { expectedCpuInfos.clear(); expectedCpuInfos.append(0, new CpuInfoReader.CpuInfo(/* cpuCore= */ 0, FLAG_CPUSET_CATEGORY_TOP_APP, /* isOnline= */ true, /* curCpuFreqKHz= */ 1_000_000, - /* maxCpuFreqKHz= */ 2_500_000, /* avgTimeInStateCpuFreqKHz= */ 419_354, - /* normalizedAvailableCpuFreqKHz= */ 2_425_919, + /* maxCpuFreqKHz= */ 2_600_000, /* avgTimeInStateCpuFreqKHz= */ 419_354, + /* normalizedAvailableCpuFreqKHz= */ 2_525_919, new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 10_000_000, /* niceTimeMillis= */ 1_000_000, /* systemTimeMillis= */ 10_000_000, /* idleTimeMillis= */ 110_000_000, /* iowaitTimeMillis= */ 1_100_000, @@ -152,8 +157,8 @@ public final class CpuInfoReaderTest extends ExpectableTestCase { /* guestNiceTimeMillis= */ 0))); expectedCpuInfos.append(1, new CpuInfoReader.CpuInfo(/* cpuCore= */ 1, FLAG_CPUSET_CATEGORY_TOP_APP, /* isOnline= */ true, /* curCpuFreqKHz= */ 2_800_000, - /* maxCpuFreqKHz= */ 2_800_000, /* avgTimeInStateCpuFreqKHz= */ 429_032, - /* normalizedAvailableCpuFreqKHz= */ 2_403_009, + /* maxCpuFreqKHz= */ 2_900_000, /* avgTimeInStateCpuFreqKHz= */ 429_032, + /* normalizedAvailableCpuFreqKHz= */ 2_503_009, new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 900_000, /* niceTimeMillis= */ 1_000_000, /* systemTimeMillis= */ 10_000_000, /* idleTimeMillis= */ 1_000_000, /* iowaitTimeMillis= */ 90_000, @@ -163,8 +168,8 @@ public final class CpuInfoReaderTest extends ExpectableTestCase { expectedCpuInfos.append(2, new CpuInfoReader.CpuInfo(/* cpuCore= */ 2, FLAG_CPUSET_CATEGORY_TOP_APP | FLAG_CPUSET_CATEGORY_BACKGROUND, /* isOnline= */ true, /* curCpuFreqKHz= */ 2_000_000, - /* maxCpuFreqKHz= */ 2_000_000, /* avgTimeInStateCpuFreqKHz= */ 403_225, - /* normalizedAvailableCpuFreqKHz= */ 1_688_209, + /* maxCpuFreqKHz= */ 2_100_000, /* avgTimeInStateCpuFreqKHz= */ 403_225, + /* normalizedAvailableCpuFreqKHz= */ 1_788_209, new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 10_000_000, /* niceTimeMillis= */ 2_000_000, /* systemTimeMillis= */ 0, /* idleTimeMillis= */ 10_000_000, /* iowaitTimeMillis= */ 1_000_000, @@ -174,7 +179,7 @@ public final class CpuInfoReaderTest extends ExpectableTestCase { expectedCpuInfos.append(3, new CpuInfoReader.CpuInfo(/* cpuCore= */ 3, FLAG_CPUSET_CATEGORY_TOP_APP | FLAG_CPUSET_CATEGORY_BACKGROUND, /* isOnline= */ false, /* curCpuFreqKHz= */ MISSING_FREQUENCY, - /* maxCpuFreqKHz= */ 2_000_000, /* avgTimeInStateCpuFreqKHz= */ MISSING_FREQUENCY, + /* maxCpuFreqKHz= */ 2_100_000, /* avgTimeInStateCpuFreqKHz= */ MISSING_FREQUENCY, /* normalizedAvailableCpuFreqKHz= */ MISSING_FREQUENCY, new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 2_000_000, /* niceTimeMillis= */ 1_000_000, /* systemTimeMillis= */ 1_000_000, @@ -403,6 +408,104 @@ public final class CpuInfoReaderTest extends ExpectableTestCase { } @Test + public void testReadCpuInfoWithEmptyRelatedCpus() throws Exception { + CpuInfoReader cpuInfoReader = newCpuInfoReader(getCacheFile(VALID_CPUSET_DIR), + getCacheFile(VALID_CPUFREQ_WITH_EMPTY_RELATED_CPUS), + getCacheFile(VALID_PROC_STAT)); + + SparseArray<CpuInfoReader.CpuInfo> actualCpuInfos = cpuInfoReader.readCpuInfos(); + SparseArray<CpuInfoReader.CpuInfo> expectedCpuInfos = new SparseArray<>(); + + expectedCpuInfos.append(1, new CpuInfoReader.CpuInfo(/* cpuCore= */ 1, + FLAG_CPUSET_CATEGORY_TOP_APP, /* isOnline= */ true, /* curCpuFreqKHz= */ 1_450_000, + /* maxCpuFreqKHz= */ 2_800_000, /* avgTimeInStateCpuFreqKHz= */ 502_380, + /* normalizedAvailableCpuFreqKHz= */ 2_693_525, + new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 28_949_280, + /* niceTimeMillis= */ 7_799_450, /* systemTimeMillis= */ 54_004_020, + /* idleTimeMillis= */ 402_707_120, /* iowaitTimeMillis= */ 1_186_960, + /* irqTimeMillis= */ 14_786_940, /* softirqTimeMillis= */ 1_498_130, + /* stealTimeMillis= */ 78_780, /* guestTimeMillis= */ 0, + /* guestNiceTimeMillis= */ 0))); + + compareCpuInfos("CPU infos with policy 0 containing an empty related_cpus file", + expectedCpuInfos, actualCpuInfos); + } + + @Test + public void testReadCpuInfoWithEmptyCpusetCpus() throws Exception { + CpuInfoReader cpuInfoReader = newCpuInfoReader(getCacheFile(VALID_CPUSET_WITH_EMPTY_CPUS), + getCacheFile(VALID_CPUFREQ_WITH_TIME_IN_STATE_DIR), + getCacheFile(VALID_PROC_STAT)); + + SparseArray<CpuInfoReader.CpuInfo> actualCpuInfos = cpuInfoReader.readCpuInfos(); + SparseArray<CpuInfoReader.CpuInfo> expectedCpuInfos = new SparseArray<>(); + expectedCpuInfos.append(0, new CpuInfoReader.CpuInfo(/* cpuCore= */ 0, + FLAG_CPUSET_CATEGORY_TOP_APP, /* isOnline= */ true, /* curCpuFreqKHz= */ 1_230_000, + /* maxCpuFreqKHz= */ 2_500_000, /* avgTimeInStateCpuFreqKHz= */ 488_095, + new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 32_249_610, + /* niceTimeMillis= */ 7_950_930, /* systemTimeMillis= */ 52_227_050, + /* idleTimeMillis= */ 409_036_950, /* iowaitTimeMillis= */ 1_322_810, + /* irqTimeMillis= */ 8_146_740, /* softirqTimeMillis= */ 428_970, + /* stealTimeMillis= */ 81_950, /* guestTimeMillis= */ 0, + /* guestNiceTimeMillis= */ 0))); + expectedCpuInfos.append(1, new CpuInfoReader.CpuInfo(/* cpuCore= */ 1, + FLAG_CPUSET_CATEGORY_TOP_APP, /* isOnline= */ true, /* curCpuFreqKHz= */ 1_450_000, + /* maxCpuFreqKHz= */ 2_800_000, /* avgTimeInStateCpuFreqKHz= */ 502_380, + new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 28_949_280, + /* niceTimeMillis= */ 7_799_450, /* systemTimeMillis= */ 54_004_020, + /* idleTimeMillis= */ 402_707_120, /* iowaitTimeMillis= */ 1_186_960, + /* irqTimeMillis= */ 14_786_940, /* softirqTimeMillis= */ 1_498_130, + /* stealTimeMillis= */ 78_780, /* guestTimeMillis= */ 0, + /* guestNiceTimeMillis= */ 0))); + expectedCpuInfos.append(2, new CpuInfoReader.CpuInfo(/* cpuCore= */ 2, + FLAG_CPUSET_CATEGORY_TOP_APP, /* isOnline= */ true, /* curCpuFreqKHz= */ 1_000_000, + /* maxCpuFreqKHz= */ 2_000_000, /* avgTimeInStateCpuFreqKHz= */ 464_285, + new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 28_959_280, + /* niceTimeMillis= */ 7_789_450, /* systemTimeMillis= */ 54_014_020, + /* idleTimeMillis= */ 402_717_120, /* iowaitTimeMillis= */ 1_166_960, + /* irqTimeMillis= */ 14_796_940, /* softirqTimeMillis= */ 1_478_130, + /* stealTimeMillis= */ 88_780, /* guestTimeMillis= */ 0, + /* guestNiceTimeMillis= */ 0))); + expectedCpuInfos.append(3, new CpuInfoReader.CpuInfo(/* cpuCore= */ 3, + FLAG_CPUSET_CATEGORY_TOP_APP, /* isOnline= */ true, /* curCpuFreqKHz= */ 1_000_000, + /* maxCpuFreqKHz= */ 2_000_000, /* avgTimeInStateCpuFreqKHz= */ 464_285, + /* normalizedAvailableCpuFreqKHz= */ 1_907_125, + new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 32_349_610, + /* niceTimeMillis= */ 7_850_930, /* systemTimeMillis= */ 52_127_050, + /* idleTimeMillis= */ 409_136_950, /* iowaitTimeMillis= */ 1_332_810, + /* irqTimeMillis= */ 8_136_740, /* softirqTimeMillis= */ 438_970, + /* stealTimeMillis= */ 71_950, /* guestTimeMillis= */ 0, + /* guestNiceTimeMillis= */ 0))); + + compareCpuInfos("CPU infos with empty background cpu set", expectedCpuInfos, + actualCpuInfos); + } + + @Test + public void testReadCpuInfoWithEmptyAffectedCpus() throws Exception { + CpuInfoReader cpuInfoReader = newCpuInfoReader(getCacheFile(VALID_CPUSET_DIR), + getCacheFile(VALID_CPUFREQ_WITH_EMPTY_AFFECTED_CPUS), + getCacheFile(VALID_PROC_STAT)); + + SparseArray<CpuInfoReader.CpuInfo> actualCpuInfos = cpuInfoReader.readCpuInfos(); + SparseArray<CpuInfoReader.CpuInfo> expectedCpuInfos = new SparseArray<>(); + + expectedCpuInfos.append(1, new CpuInfoReader.CpuInfo(/* cpuCore= */ 1, + FLAG_CPUSET_CATEGORY_TOP_APP, /* isOnline= */ true, /* curCpuFreqKHz= */ 1_450_000, + /* maxCpuFreqKHz= */ 2_800_000, /* avgTimeInStateCpuFreqKHz= */ 502_380, + /* normalizedAvailableCpuFreqKHz= */ 2_693_525, + new CpuInfoReader.CpuUsageStats(/* userTimeMillis= */ 28_949_280, + /* niceTimeMillis= */ 7_799_450, /* systemTimeMillis= */ 54_004_020, + /* idleTimeMillis= */ 402_707_120, /* iowaitTimeMillis= */ 1_186_960, + /* irqTimeMillis= */ 14_786_940, /* softirqTimeMillis= */ 1_498_130, + /* stealTimeMillis= */ 78_780, /* guestTimeMillis= */ 0, + /* guestNiceTimeMillis= */ 0))); + + compareCpuInfos("CPU infos with policy 0 containing an empty affected_cpus file", + expectedCpuInfos, actualCpuInfos); + } + + @Test public void testReadCpuInfoWithEmptyProcStat() throws Exception { File emptyFile = getCacheFile(EMPTY_FILE); assertWithMessage("Create empty file %s", emptyFile).that(emptyFile.createNewFile()) diff --git a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java index 170076098b7d..aaab4033d579 100644 --- a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java +++ b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java @@ -46,6 +46,7 @@ import android.hardware.SensorManager; import android.hardware.display.DisplayManagerInternal.DisplayPowerCallbacks; import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest; import android.os.Handler; +import android.os.IBinder; import android.os.Looper; import android.os.PowerManager; import android.os.SystemProperties; @@ -95,6 +96,10 @@ public final class DisplayPowerController2Test { private static final int SECOND_FOLLOWER_DISPLAY_ID = FOLLOWER_DISPLAY_ID + 1; private static final String SECOND_FOLLOWER_UNIQUE_DISPLAY_ID = "unique_id_789"; private static final float PROX_SENSOR_MAX_RANGE = 5; + private static final float BRIGHTNESS_RAMP_RATE_FAST_DECREASE = 0.3f; + private static final float BRIGHTNESS_RAMP_RATE_FAST_INCREASE = 0.4f; + private static final float BRIGHTNESS_RAMP_RATE_SLOW_DECREASE = 0.1f; + private static final float BRIGHTNESS_RAMP_RATE_SLOW_INCREASE = 0.2f; private OffsettableClock mClock; private TestLooper mTestLooper; @@ -296,6 +301,8 @@ public final class DisplayPowerController2Test { public void testDisplayBrightnessFollowers_BothDpcsSupportNits() { DisplayPowerControllerHolder followerDpc = createDisplayPowerController(FOLLOWER_DISPLAY_ID, FOLLOWER_UNIQUE_ID); + when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f); + when(followerDpc.displayPowerState.getColorFadeLevel()).thenReturn(1.0f); DisplayPowerRequest dpr = new DisplayPowerRequest(); mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); @@ -334,14 +341,18 @@ public final class DisplayPowerController2Test { when(mHolder.brightnessSetting.getBrightness()).thenReturn(brightness); listener.onBrightnessChanged(brightness); advanceTime(1); // Send messages, run updatePowerState - verify(mHolder.animator).animateTo(eq(brightness), anyFloat(), anyFloat()); - verify(followerDpc.animator).animateTo(eq(brightness), anyFloat(), anyFloat()); + verify(mHolder.animator).animateTo(eq(brightness), anyFloat(), + eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE)); + verify(followerDpc.animator).animateTo(eq(brightness), anyFloat(), + eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE)); } @Test public void testDisplayBrightnessFollowers_FollowerDoesNotSupportNits() { DisplayPowerControllerHolder followerDpc = createDisplayPowerController(FOLLOWER_DISPLAY_ID, FOLLOWER_UNIQUE_ID); + when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f); + when(followerDpc.displayPowerState.getColorFadeLevel()).thenReturn(1.0f); DisplayPowerRequest dpr = new DisplayPowerRequest(); mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); @@ -362,14 +373,18 @@ public final class DisplayPowerController2Test { when(mHolder.brightnessSetting.getBrightness()).thenReturn(brightness); listener.onBrightnessChanged(brightness); advanceTime(1); // Send messages, run updatePowerState - verify(mHolder.animator).animateTo(eq(brightness), anyFloat(), anyFloat()); - verify(followerDpc.animator).animateTo(eq(brightness), anyFloat(), anyFloat()); + verify(mHolder.animator).animateTo(eq(brightness), anyFloat(), + eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE)); + verify(followerDpc.animator).animateTo(eq(brightness), anyFloat(), + eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE)); } @Test public void testDisplayBrightnessFollowers_LeadDpcDoesNotSupportNits() { DisplayPowerControllerHolder followerDpc = createDisplayPowerController(FOLLOWER_DISPLAY_ID, FOLLOWER_UNIQUE_ID); + when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f); + when(followerDpc.displayPowerState.getColorFadeLevel()).thenReturn(1.0f); DisplayPowerRequest dpr = new DisplayPowerRequest(); mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); @@ -388,14 +403,18 @@ public final class DisplayPowerController2Test { when(mHolder.brightnessSetting.getBrightness()).thenReturn(brightness); listener.onBrightnessChanged(brightness); advanceTime(1); // Send messages, run updatePowerState - verify(mHolder.animator).animateTo(eq(brightness), anyFloat(), anyFloat()); - verify(followerDpc.animator).animateTo(eq(brightness), anyFloat(), anyFloat()); + verify(mHolder.animator).animateTo(eq(brightness), anyFloat(), + eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE)); + verify(followerDpc.animator).animateTo(eq(brightness), anyFloat(), + eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE)); } @Test public void testDisplayBrightnessFollowers_NeitherDpcSupportsNits() { DisplayPowerControllerHolder followerDpc = createDisplayPowerController(FOLLOWER_DISPLAY_ID, FOLLOWER_UNIQUE_ID); + when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f); + when(followerDpc.displayPowerState.getColorFadeLevel()).thenReturn(1.0f); DisplayPowerRequest dpr = new DisplayPowerRequest(); mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); @@ -416,8 +435,10 @@ public final class DisplayPowerController2Test { when(mHolder.brightnessSetting.getBrightness()).thenReturn(brightness); listener.onBrightnessChanged(brightness); advanceTime(1); // Send messages, run updatePowerState - verify(mHolder.animator).animateTo(eq(brightness), anyFloat(), anyFloat()); - verify(followerDpc.animator).animateTo(eq(brightness), anyFloat(), anyFloat()); + verify(mHolder.animator).animateTo(eq(brightness), anyFloat(), + eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE)); + verify(followerDpc.animator).animateTo(eq(brightness), anyFloat(), + eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE)); } @Test @@ -425,24 +446,70 @@ public final class DisplayPowerController2Test { Settings.System.putInt(mContext.getContentResolver(), Settings.System.SCREEN_BRIGHTNESS_MODE, Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC); - final float brightness = 0.4f; - final float nits = 300; - final float ambientLux = 3000; + DisplayPowerControllerHolder followerDpc = + createDisplayPowerController(FOLLOWER_DISPLAY_ID, FOLLOWER_UNIQUE_ID); + when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON); + when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f); + when(followerDpc.displayPowerState.getColorFadeLevel()).thenReturn(1.0f); + float leadBrightness = 0.1f; + float rawLeadBrightness = 0.3f; + float followerBrightness = 0.4f; + float nits = 300; + float ambientLux = 3000; when(mHolder.automaticBrightnessController.getRawAutomaticScreenBrightness()) - .thenReturn(brightness); - when(mHolder.automaticBrightnessController.getAutomaticScreenBrightness()) - .thenReturn(0.3f); - when(mHolder.automaticBrightnessController.convertToNits(brightness)).thenReturn(nits); + .thenReturn(rawLeadBrightness); + when(mHolder.automaticBrightnessController + .getAutomaticScreenBrightness(any(BrightnessEvent.class))) + .thenReturn(leadBrightness); + when(mHolder.automaticBrightnessController.convertToNits(rawLeadBrightness)) + .thenReturn(nits); when(mHolder.automaticBrightnessController.getAmbientLux()).thenReturn(ambientLux); - when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON); - DisplayPowerController2 followerDpc = mock(DisplayPowerController2.class); + when(followerDpc.automaticBrightnessController.convertToFloatScale(nits)) + .thenReturn(followerBrightness); + + mHolder.dpc.addDisplayBrightnessFollower(followerDpc.dpc); - mHolder.dpc.addDisplayBrightnessFollower(followerDpc); DisplayPowerRequest dpr = new DisplayPowerRequest(); mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); + followerDpc.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); advanceTime(1); // Run updatePowerState - verify(followerDpc).setBrightnessToFollow(brightness, nits, ambientLux); + verify(mHolder.animator).animateTo(eq(leadBrightness), anyFloat(), + eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE)); + // One triggered by handleBrightnessModeChange, another triggered by setBrightnessToFollow + verify(followerDpc.hbmController, times(2)).onAmbientLuxChange(ambientLux); + verify(followerDpc.animator, times(2)).animateTo(eq(followerBrightness), anyFloat(), + eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE)); + + when(mHolder.displayPowerState.getScreenBrightness()).thenReturn(leadBrightness); + when(followerDpc.displayPowerState.getScreenBrightness()).thenReturn(followerBrightness); + clearInvocations(mHolder.animator, followerDpc.animator); + + leadBrightness = 0.05f; + rawLeadBrightness = 0.2f; + followerBrightness = 0.3f; + nits = 200; + ambientLux = 2000; + when(mHolder.automaticBrightnessController.getRawAutomaticScreenBrightness()) + .thenReturn(rawLeadBrightness); + when(mHolder.automaticBrightnessController + .getAutomaticScreenBrightness(any(BrightnessEvent.class))) + .thenReturn(leadBrightness); + when(mHolder.automaticBrightnessController.convertToNits(rawLeadBrightness)) + .thenReturn(nits); + when(mHolder.automaticBrightnessController.getAmbientLux()).thenReturn(ambientLux); + when(followerDpc.automaticBrightnessController.convertToFloatScale(nits)) + .thenReturn(followerBrightness); + + mHolder.dpc.updateBrightness(); + advanceTime(1); // Run updatePowerState + + // The second time, the animation rate should be slow + verify(mHolder.animator).animateTo(eq(leadBrightness), anyFloat(), + eq(BRIGHTNESS_RAMP_RATE_SLOW_DECREASE)); + verify(followerDpc.hbmController).onAmbientLuxChange(ambientLux); + verify(followerDpc.animator).animateTo(eq(followerBrightness), anyFloat(), + eq(BRIGHTNESS_RAMP_RATE_SLOW_DECREASE)); } @Test @@ -451,6 +518,9 @@ public final class DisplayPowerController2Test { FOLLOWER_UNIQUE_ID); DisplayPowerControllerHolder secondFollowerDpc = createDisplayPowerController( SECOND_FOLLOWER_DISPLAY_ID, SECOND_FOLLOWER_UNIQUE_DISPLAY_ID); + when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f); + when(followerDpc.displayPowerState.getColorFadeLevel()).thenReturn(1.0f); + when(secondFollowerDpc.displayPowerState.getColorFadeLevel()).thenReturn(1.0f); DisplayPowerRequest dpr = new DisplayPowerRequest(); mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); @@ -472,9 +542,11 @@ public final class DisplayPowerController2Test { when(followerDpc.brightnessSetting.getBrightness()).thenReturn(initialFollowerBrightness); followerListener.onBrightnessChanged(initialFollowerBrightness); advanceTime(1); - verify(followerDpc.animator).animateTo(eq(initialFollowerBrightness), - anyFloat(), anyFloat()); + verify(followerDpc.animator).animateTo(eq(initialFollowerBrightness), anyFloat(), + eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE)); + when(followerDpc.displayPowerState.getScreenBrightness()) + .thenReturn(initialFollowerBrightness); mHolder.dpc.addDisplayBrightnessFollower(followerDpc.dpc); mHolder.dpc.addDisplayBrightnessFollower(secondFollowerDpc.dpc); @@ -491,17 +563,26 @@ public final class DisplayPowerController2Test { when(mHolder.brightnessSetting.getBrightness()).thenReturn(brightness); listener.onBrightnessChanged(brightness); advanceTime(1); // Send messages, run updatePowerState - verify(mHolder.animator).animateTo(eq(brightness), anyFloat(), anyFloat()); - verify(followerDpc.animator).animateTo(eq(brightness), anyFloat(), anyFloat()); - verify(secondFollowerDpc.animator).animateTo(eq(brightness), anyFloat(), anyFloat()); - + verify(mHolder.animator).animateTo(eq(brightness), anyFloat(), + eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE)); + verify(followerDpc.animator).animateTo(eq(brightness), anyFloat(), + eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE)); + verify(secondFollowerDpc.animator).animateTo(eq(brightness), anyFloat(), + eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE)); + + when(mHolder.displayPowerState.getScreenBrightness()).thenReturn(brightness); + when(followerDpc.displayPowerState.getScreenBrightness()).thenReturn(brightness); + when(secondFollowerDpc.displayPowerState.getScreenBrightness()).thenReturn(brightness); clearInvocations(mHolder.animator, followerDpc.animator, secondFollowerDpc.animator); // Remove the first follower and validate it goes back to its original brightness. mHolder.dpc.removeDisplayBrightnessFollower(followerDpc.dpc); advanceTime(1); - verify(followerDpc.animator).animateTo(eq(initialFollowerBrightness), - anyFloat(), anyFloat()); + verify(followerDpc.animator).animateTo(eq(initialFollowerBrightness), anyFloat(), + eq(BRIGHTNESS_RAMP_RATE_FAST_DECREASE)); + + when(followerDpc.displayPowerState.getScreenBrightness()) + .thenReturn(initialFollowerBrightness); clearInvocations(followerDpc.animator); // Change the brightness of the lead display and validate only the second follower responds @@ -515,9 +596,11 @@ public final class DisplayPowerController2Test { when(mHolder.brightnessSetting.getBrightness()).thenReturn(brightness); listener.onBrightnessChanged(brightness); advanceTime(1); // Send messages, run updatePowerState - verify(mHolder.animator).animateTo(eq(brightness), anyFloat(), anyFloat()); + verify(mHolder.animator).animateTo(eq(brightness), anyFloat(), + eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE)); verify(followerDpc.animator, never()).animateTo(anyFloat(), anyFloat(), anyFloat()); - verify(secondFollowerDpc.animator).animateTo(eq(brightness), anyFloat(), anyFloat()); + verify(secondFollowerDpc.animator).animateTo(eq(brightness), anyFloat(), + eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE)); } @Test @@ -527,6 +610,9 @@ public final class DisplayPowerController2Test { DisplayPowerControllerHolder secondFollowerHolder = createDisplayPowerController(SECOND_FOLLOWER_DISPLAY_ID, SECOND_FOLLOWER_UNIQUE_DISPLAY_ID); + when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f); + when(followerHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f); + when(secondFollowerHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f); DisplayPowerRequest dpr = new DisplayPowerRequest(); mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); @@ -556,10 +642,15 @@ public final class DisplayPowerController2Test { followerListener.onBrightnessChanged(initialFollowerBrightness); secondFollowerListener.onBrightnessChanged(initialFollowerBrightness); advanceTime(1); - verify(followerHolder.animator).animateTo(eq(initialFollowerBrightness), - anyFloat(), anyFloat()); - verify(secondFollowerHolder.animator).animateTo(eq(initialFollowerBrightness), - anyFloat(), anyFloat()); + verify(followerHolder.animator).animateTo(eq(initialFollowerBrightness), anyFloat(), + eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE)); + verify(secondFollowerHolder.animator).animateTo(eq(initialFollowerBrightness), anyFloat(), + eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE)); + + when(followerHolder.displayPowerState.getScreenBrightness()) + .thenReturn(initialFollowerBrightness); + when(secondFollowerHolder.displayPowerState.getScreenBrightness()) + .thenReturn(initialFollowerBrightness); mHolder.dpc.addDisplayBrightnessFollower(followerHolder.dpc); mHolder.dpc.addDisplayBrightnessFollower(secondFollowerHolder.dpc); @@ -576,19 +667,25 @@ public final class DisplayPowerController2Test { when(mHolder.brightnessSetting.getBrightness()).thenReturn(brightness); listener.onBrightnessChanged(brightness); advanceTime(1); // Send messages, run updatePowerState - verify(mHolder.animator).animateTo(eq(brightness), anyFloat(), anyFloat()); - verify(followerHolder.animator).animateTo(eq(brightness), anyFloat(), anyFloat()); - verify(secondFollowerHolder.animator).animateTo(eq(brightness), anyFloat(), anyFloat()); - + verify(mHolder.animator).animateTo(eq(brightness), anyFloat(), + eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE)); + verify(followerHolder.animator).animateTo(eq(brightness), anyFloat(), + eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE)); + verify(secondFollowerHolder.animator).animateTo(eq(brightness), anyFloat(), + eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE)); + + when(mHolder.displayPowerState.getScreenBrightness()).thenReturn(brightness); + when(followerHolder.displayPowerState.getScreenBrightness()).thenReturn(brightness); + when(secondFollowerHolder.displayPowerState.getScreenBrightness()).thenReturn(brightness); clearInvocations(mHolder.animator, followerHolder.animator, secondFollowerHolder.animator); // Stop the lead DPC and validate that the followers go back to their original brightness. mHolder.dpc.stop(); advanceTime(1); - verify(followerHolder.animator).animateTo(eq(initialFollowerBrightness), - anyFloat(), anyFloat()); - verify(secondFollowerHolder.animator).animateTo(eq(initialFollowerBrightness), - anyFloat(), anyFloat()); + verify(followerHolder.animator).animateTo(eq(initialFollowerBrightness), anyFloat(), + eq(BRIGHTNESS_RAMP_RATE_FAST_DECREASE)); + verify(secondFollowerHolder.animator).animateTo(eq(initialFollowerBrightness), anyFloat(), + eq(BRIGHTNESS_RAMP_RATE_FAST_DECREASE)); clearInvocations(followerHolder.animator, secondFollowerHolder.animator); } @@ -748,6 +845,155 @@ public final class DisplayPowerController2Test { } @Test + public void testAutoBrightnessEnabled_DisplayIsOn() { + Settings.System.putInt(mContext.getContentResolver(), + Settings.System.SCREEN_BRIGHTNESS_MODE, + Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC); + + DisplayPowerRequest dpr = new DisplayPowerRequest(); + dpr.policy = DisplayPowerRequest.POLICY_BRIGHT; + when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON); + mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); + advanceTime(1); // Run updatePowerState + + verify(mHolder.automaticBrightnessController).configure( + AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED, + /* configuration= */ null, PowerManager.BRIGHTNESS_INVALID_FLOAT, + /* userChangedBrightness= */ false, /* adjustment= */ 0, + /* userChangedAutoBrightnessAdjustment= */ false, DisplayPowerRequest.POLICY_BRIGHT, + /* shouldResetShortTermModel= */ false + ); + verify(mHolder.hbmController) + .setAutoBrightnessEnabled(AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED); + } + + @Test + public void testAutoBrightnessEnabled_DisplayIsInDoze() { + Settings.System.putInt(mContext.getContentResolver(), + Settings.System.SCREEN_BRIGHTNESS_MODE, + Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC); + mContext.getOrCreateTestableResources().addOverride( + com.android.internal.R.bool.config_allowAutoBrightnessWhileDozing, true); + mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID); + + DisplayPowerRequest dpr = new DisplayPowerRequest(); + dpr.policy = DisplayPowerRequest.POLICY_DOZE; + when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_DOZE); + mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); + advanceTime(1); // Run updatePowerState + + verify(mHolder.automaticBrightnessController).configure( + AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED, + /* configuration= */ null, PowerManager.BRIGHTNESS_INVALID_FLOAT, + /* userChangedBrightness= */ false, /* adjustment= */ 0, + /* userChangedAutoBrightnessAdjustment= */ false, DisplayPowerRequest.POLICY_DOZE, + /* shouldResetShortTermModel= */ false + ); + verify(mHolder.hbmController) + .setAutoBrightnessEnabled(AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED); + } + + @Test + public void testAutoBrightnessDisabled_ManualBrightnessMode() { + Settings.System.putInt(mContext.getContentResolver(), + Settings.System.SCREEN_BRIGHTNESS_MODE, + Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL); + + DisplayPowerRequest dpr = new DisplayPowerRequest(); + dpr.policy = DisplayPowerRequest.POLICY_BRIGHT; + when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON); + mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); + advanceTime(1); // Run updatePowerState + + // One triggered by the test, the other by handleBrightnessModeChange + verify(mHolder.automaticBrightnessController, times(2)).configure( + AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED, + /* configuration= */ null, PowerManager.BRIGHTNESS_INVALID_FLOAT, + /* userChangedBrightness= */ false, /* adjustment= */ 0, + /* userChangedAutoBrightnessAdjustment= */ false, DisplayPowerRequest.POLICY_BRIGHT, + /* shouldResetShortTermModel= */ false + ); + verify(mHolder.hbmController, times(2)) + .setAutoBrightnessEnabled(AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED); + } + + @Test + public void testAutoBrightnessDisabled_DisplayIsOff() { + Settings.System.putInt(mContext.getContentResolver(), + Settings.System.SCREEN_BRIGHTNESS_MODE, + Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC); + + DisplayPowerRequest dpr = new DisplayPowerRequest(); + dpr.policy = DisplayPowerRequest.POLICY_OFF; + when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_OFF); + mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); + advanceTime(1); // Run updatePowerState + + verify(mHolder.automaticBrightnessController).configure( + AutomaticBrightnessController.AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE, + /* configuration= */ null, PowerManager.BRIGHTNESS_INVALID_FLOAT, + /* userChangedBrightness= */ false, /* adjustment= */ 0, + /* userChangedAutoBrightnessAdjustment= */ false, DisplayPowerRequest.POLICY_OFF, + /* shouldResetShortTermModel= */ false + ); + verify(mHolder.hbmController).setAutoBrightnessEnabled( + AutomaticBrightnessController.AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE); + } + + @Test + public void testAutoBrightnessDisabled_DisplayIsInDoze() { + Settings.System.putInt(mContext.getContentResolver(), + Settings.System.SCREEN_BRIGHTNESS_MODE, + Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC); + mContext.getOrCreateTestableResources().addOverride( + com.android.internal.R.bool.config_allowAutoBrightnessWhileDozing, false); + mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID); + + DisplayPowerRequest dpr = new DisplayPowerRequest(); + dpr.policy = DisplayPowerRequest.POLICY_DOZE; + when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_DOZE); + mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); + advanceTime(1); // Run updatePowerState + + verify(mHolder.automaticBrightnessController).configure( + AutomaticBrightnessController.AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE, + /* configuration= */ null, PowerManager.BRIGHTNESS_INVALID_FLOAT, + /* userChangedBrightness= */ false, /* adjustment= */ 0, + /* userChangedAutoBrightnessAdjustment= */ false, DisplayPowerRequest.POLICY_DOZE, + /* shouldResetShortTermModel= */ false + ); + verify(mHolder.hbmController).setAutoBrightnessEnabled( + AutomaticBrightnessController.AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE); + } + + @Test + public void testAutoBrightnessDisabled_FollowerDisplay() { + Settings.System.putInt(mContext.getContentResolver(), + Settings.System.SCREEN_BRIGHTNESS_MODE, + Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC); + mHolder.dpc.setBrightnessToFollow(0.3f, -1, 0, /* slowChange= */ false); + + DisplayPowerRequest dpr = new DisplayPowerRequest(); + dpr.policy = DisplayPowerRequest.POLICY_BRIGHT; + when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON); + mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); + advanceTime(1); // Run updatePowerState + + // One triggered by the test, the other by handleBrightnessModeChange + verify(mHolder.automaticBrightnessController, times(2)).configure( + AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED, + /* configuration= */ null, PowerManager.BRIGHTNESS_INVALID_FLOAT, + /* userChangedBrightness= */ false, /* adjustment= */ 0, + /* userChangedAutoBrightnessAdjustment= */ false, DisplayPowerRequest.POLICY_BRIGHT, + /* shouldResetShortTermModel= */ false + ); + + // HBM should be allowed for the follower display + verify(mHolder.hbmController) + .setAutoBrightnessEnabled(AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED); + } + + @Test public void testBrightnessNitsPersistWhenDisplayDeviceChanges() { float brightness = 0.3f; float nits = 500; @@ -815,7 +1061,7 @@ public final class DisplayPowerController2Test { any(HysteresisLevels.class), any(HysteresisLevels.class), eq(mContext), - any(HighBrightnessModeController.class), + any(BrightnessRangeController.class), any(BrightnessThrottler.class), isNull(), anyInt(), @@ -903,6 +1149,14 @@ public final class DisplayPowerController2Test { }); when(displayDeviceConfigMock.getScreenOffBrightnessSensorValueToLux()) .thenReturn(new int[0]); + when(displayDeviceConfigMock.getBrightnessRampFastDecrease()) + .thenReturn(BRIGHTNESS_RAMP_RATE_FAST_DECREASE); + when(displayDeviceConfigMock.getBrightnessRampFastIncrease()) + .thenReturn(BRIGHTNESS_RAMP_RATE_FAST_INCREASE); + when(displayDeviceConfigMock.getBrightnessRampSlowDecrease()) + .thenReturn(BRIGHTNESS_RAMP_RATE_SLOW_DECREASE); + when(displayDeviceConfigMock.getBrightnessRampSlowIncrease()) + .thenReturn(BRIGHTNESS_RAMP_RATE_SLOW_INCREASE); } private DisplayPowerControllerHolder createDisplayPowerController(int displayId, @@ -922,10 +1176,13 @@ public final class DisplayPowerController2Test { final HysteresisLevels hysteresisLevels = mock(HysteresisLevels.class); final ScreenOffBrightnessSensorController screenOffBrightnessSensorController = mock(ScreenOffBrightnessSensorController.class); + final HighBrightnessModeController hbmController = mock(HighBrightnessModeController.class); + + when(hbmController.getCurrentBrightnessMax()).thenReturn(PowerManager.BRIGHTNESS_MAX); TestInjector injector = spy(new TestInjector(displayPowerState, animator, automaticBrightnessController, wakelockController, brightnessMappingStrategy, - hysteresisLevels, screenOffBrightnessSensorController)); + hysteresisLevels, screenOffBrightnessSensorController, hbmController)); final LogicalDisplay display = mock(LogicalDisplay.class); final DisplayDevice device = mock(DisplayDevice.class); @@ -943,8 +1200,8 @@ public final class DisplayPowerController2Test { return new DisplayPowerControllerHolder(dpc, display, displayPowerState, brightnessSetting, animator, automaticBrightnessController, wakelockController, - screenOffBrightnessSensorController, hbmMetadata, brightnessMappingStrategy, - injector); + screenOffBrightnessSensorController, hbmController, hbmMetadata, + brightnessMappingStrategy, injector); } /** @@ -960,6 +1217,7 @@ public final class DisplayPowerController2Test { public final AutomaticBrightnessController automaticBrightnessController; public final WakelockController wakelockController; public final ScreenOffBrightnessSensorController screenOffBrightnessSensorController; + public final HighBrightnessModeController hbmController; public final HighBrightnessModeMetadata hbmMetadata; public final BrightnessMappingStrategy brightnessMappingStrategy; public final DisplayPowerController2.Injector injector; @@ -970,6 +1228,7 @@ public final class DisplayPowerController2Test { AutomaticBrightnessController automaticBrightnessController, WakelockController wakelockController, ScreenOffBrightnessSensorController screenOffBrightnessSensorController, + HighBrightnessModeController hbmController, HighBrightnessModeMetadata hbmMetadata, BrightnessMappingStrategy brightnessMappingStrategy, DisplayPowerController2.Injector injector) { @@ -981,6 +1240,7 @@ public final class DisplayPowerController2Test { this.automaticBrightnessController = automaticBrightnessController; this.wakelockController = wakelockController; this.screenOffBrightnessSensorController = screenOffBrightnessSensorController; + this.hbmController = hbmController; this.hbmMetadata = hbmMetadata; this.brightnessMappingStrategy = brightnessMappingStrategy; this.injector = injector; @@ -995,13 +1255,15 @@ public final class DisplayPowerController2Test { private final BrightnessMappingStrategy mBrightnessMappingStrategy; private final HysteresisLevels mHysteresisLevels; private final ScreenOffBrightnessSensorController mScreenOffBrightnessSensorController; + private final HighBrightnessModeController mHighBrightnessModeController; TestInjector(DisplayPowerState dps, DualRampAnimator<DisplayPowerState> animator, AutomaticBrightnessController automaticBrightnessController, WakelockController wakelockController, BrightnessMappingStrategy brightnessMappingStrategy, HysteresisLevels hysteresisLevels, - ScreenOffBrightnessSensorController screenOffBrightnessSensorController) { + ScreenOffBrightnessSensorController screenOffBrightnessSensorController, + HighBrightnessModeController highBrightnessModeController) { mDisplayPowerState = dps; mAnimator = animator; mAutomaticBrightnessController = automaticBrightnessController; @@ -1009,6 +1271,7 @@ public final class DisplayPowerController2Test { mBrightnessMappingStrategy = brightnessMappingStrategy; mHysteresisLevels = hysteresisLevels; mScreenOffBrightnessSensorController = screenOffBrightnessSensorController; + mHighBrightnessModeController = highBrightnessModeController; } @Override @@ -1064,7 +1327,7 @@ public final class DisplayPowerController2Test { HysteresisLevels screenBrightnessThresholds, HysteresisLevels ambientBrightnessThresholdsIdle, HysteresisLevels screenBrightnessThresholdsIdle, Context context, - HighBrightnessModeController hbmController, + BrightnessRangeController brightnessRangeController, BrightnessThrottler brightnessThrottler, BrightnessMappingStrategy idleModeBrightnessMapper, int ambientLightHorizonShort, int ambientLightHorizonLong, float userLux, @@ -1102,5 +1365,15 @@ public final class DisplayPowerController2Test { BrightnessMappingStrategy brightnessMapper) { return mScreenOffBrightnessSensorController; } + + @Override + HighBrightnessModeController getHighBrightnessModeController(Handler handler, int width, + int height, IBinder displayToken, String displayUniqueId, float brightnessMin, + float brightnessMax, DisplayDeviceConfig.HighBrightnessModeData hbmData, + HighBrightnessModeController.HdrBrightnessDeviceConfig hdrBrightnessCfg, + Runnable hbmChangeCallback, HighBrightnessModeMetadata hbmMetadata, + Context context) { + return mHighBrightnessModeController; + } } } diff --git a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java index 5c0810fdca44..7d26913bd390 100644 --- a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java @@ -46,6 +46,7 @@ import android.hardware.SensorManager; import android.hardware.display.DisplayManagerInternal.DisplayPowerCallbacks; import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest; import android.os.Handler; +import android.os.IBinder; import android.os.Looper; import android.os.PowerManager; import android.os.SystemProperties; @@ -95,6 +96,10 @@ public final class DisplayPowerControllerTest { private static final int SECOND_FOLLOWER_DISPLAY_ID = FOLLOWER_DISPLAY_ID + 1; private static final String SECOND_FOLLOWER_UNIQUE_DISPLAY_ID = "unique_id_789"; private static final float PROX_SENSOR_MAX_RANGE = 5; + private static final float BRIGHTNESS_RAMP_RATE_FAST_DECREASE = 0.3f; + private static final float BRIGHTNESS_RAMP_RATE_FAST_INCREASE = 0.4f; + private static final float BRIGHTNESS_RAMP_RATE_SLOW_DECREASE = 0.1f; + private static final float BRIGHTNESS_RAMP_RATE_SLOW_INCREASE = 0.2f; private OffsettableClock mClock; private TestLooper mTestLooper; @@ -299,6 +304,8 @@ public final class DisplayPowerControllerTest { public void testDisplayBrightnessFollowers_BothDpcsSupportNits() { DisplayPowerControllerHolder followerDpc = createDisplayPowerController(FOLLOWER_DISPLAY_ID, FOLLOWER_UNIQUE_ID); + when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f); + when(followerDpc.displayPowerState.getColorFadeLevel()).thenReturn(1.0f); DisplayPowerRequest dpr = new DisplayPowerRequest(); mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); @@ -322,10 +329,13 @@ public final class DisplayPowerControllerTest { when(mHolder.brightnessSetting.getBrightness()).thenReturn(leadBrightness); listener.onBrightnessChanged(leadBrightness); advanceTime(1); // Send messages, run updatePowerState - verify(mHolder.animator).animateTo(eq(leadBrightness), anyFloat(), anyFloat()); + verify(mHolder.animator).animateTo(eq(leadBrightness), anyFloat(), + eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE)); verify(followerDpc.animator).animateTo(eq(followerBrightness), anyFloat(), - anyFloat()); + eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE)); + when(mHolder.displayPowerState.getScreenBrightness()).thenReturn(leadBrightness); + when(mHolder.displayPowerState.getScreenBrightness()).thenReturn(followerBrightness); clearInvocations(mHolder.animator, followerDpc.animator); // Test the same float scale value @@ -337,14 +347,18 @@ public final class DisplayPowerControllerTest { when(mHolder.brightnessSetting.getBrightness()).thenReturn(brightness); listener.onBrightnessChanged(brightness); advanceTime(1); // Send messages, run updatePowerState - verify(mHolder.animator).animateTo(eq(brightness), anyFloat(), anyFloat()); - verify(followerDpc.animator).animateTo(eq(brightness), anyFloat(), anyFloat()); + verify(mHolder.animator).animateTo(eq(brightness), anyFloat(), + eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE)); + verify(followerDpc.animator).animateTo(eq(brightness), anyFloat(), + eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE)); } @Test public void testDisplayBrightnessFollowers_FollowerDoesNotSupportNits() { DisplayPowerControllerHolder followerDpc = createDisplayPowerController(FOLLOWER_DISPLAY_ID, FOLLOWER_UNIQUE_ID); + when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f); + when(followerDpc.displayPowerState.getColorFadeLevel()).thenReturn(1.0f); DisplayPowerRequest dpr = new DisplayPowerRequest(); mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); @@ -365,14 +379,18 @@ public final class DisplayPowerControllerTest { when(mHolder.brightnessSetting.getBrightness()).thenReturn(brightness); listener.onBrightnessChanged(brightness); advanceTime(1); // Send messages, run updatePowerState - verify(mHolder.animator).animateTo(eq(brightness), anyFloat(), anyFloat()); - verify(followerDpc.animator).animateTo(eq(brightness), anyFloat(), anyFloat()); + verify(mHolder.animator).animateTo(eq(brightness), anyFloat(), + eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE)); + verify(followerDpc.animator).animateTo(eq(brightness), anyFloat(), + eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE)); } @Test public void testDisplayBrightnessFollowers_LeadDpcDoesNotSupportNits() { DisplayPowerControllerHolder followerDpc = createDisplayPowerController(FOLLOWER_DISPLAY_ID, FOLLOWER_UNIQUE_ID); + when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f); + when(followerDpc.displayPowerState.getColorFadeLevel()).thenReturn(1.0f); DisplayPowerRequest dpr = new DisplayPowerRequest(); mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); @@ -391,14 +409,18 @@ public final class DisplayPowerControllerTest { when(mHolder.brightnessSetting.getBrightness()).thenReturn(brightness); listener.onBrightnessChanged(brightness); advanceTime(1); // Send messages, run updatePowerState - verify(mHolder.animator).animateTo(eq(brightness), anyFloat(), anyFloat()); - verify(followerDpc.animator).animateTo(eq(brightness), anyFloat(), anyFloat()); + verify(mHolder.animator).animateTo(eq(brightness), anyFloat(), + eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE)); + verify(followerDpc.animator).animateTo(eq(brightness), anyFloat(), + eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE)); } @Test public void testDisplayBrightnessFollowers_NeitherDpcSupportsNits() { DisplayPowerControllerHolder followerDpc = createDisplayPowerController(FOLLOWER_DISPLAY_ID, FOLLOWER_UNIQUE_ID); + when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f); + when(followerDpc.displayPowerState.getColorFadeLevel()).thenReturn(1.0f); DisplayPowerRequest dpr = new DisplayPowerRequest(); mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); @@ -419,8 +441,10 @@ public final class DisplayPowerControllerTest { when(mHolder.brightnessSetting.getBrightness()).thenReturn(brightness); listener.onBrightnessChanged(brightness); advanceTime(1); // Send messages, run updatePowerState - verify(mHolder.animator).animateTo(eq(brightness), anyFloat(), anyFloat()); - verify(followerDpc.animator).animateTo(eq(brightness), anyFloat(), anyFloat()); + verify(mHolder.animator).animateTo(eq(brightness), anyFloat(), + eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE)); + verify(followerDpc.animator).animateTo(eq(brightness), anyFloat(), + eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE)); } @Test @@ -428,38 +452,86 @@ public final class DisplayPowerControllerTest { Settings.System.putInt(mContext.getContentResolver(), Settings.System.SCREEN_BRIGHTNESS_MODE, Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC); - final float brightness = 0.4f; - final float nits = 300; - final float ambientLux = 3000; + DisplayPowerControllerHolder followerDpc = + createDisplayPowerController(FOLLOWER_DISPLAY_ID, FOLLOWER_UNIQUE_ID); + when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON); + when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f); + when(followerDpc.displayPowerState.getColorFadeLevel()).thenReturn(1.0f); + float leadBrightness = 0.1f; + float rawLeadBrightness = 0.3f; + float followerBrightness = 0.4f; + float nits = 300; + float ambientLux = 3000; when(mHolder.automaticBrightnessController.getRawAutomaticScreenBrightness()) - .thenReturn(brightness); - when(mHolder.automaticBrightnessController.getAutomaticScreenBrightness()) - .thenReturn(0.3f); - when(mHolder.automaticBrightnessController.convertToNits(brightness)).thenReturn(nits); + .thenReturn(rawLeadBrightness); + when(mHolder.automaticBrightnessController + .getAutomaticScreenBrightness(any(BrightnessEvent.class))) + .thenReturn(leadBrightness); + when(mHolder.automaticBrightnessController.convertToNits(rawLeadBrightness)) + .thenReturn(nits); when(mHolder.automaticBrightnessController.getAmbientLux()).thenReturn(ambientLux); - when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON); - DisplayPowerController followerDpc = mock(DisplayPowerController.class); + when(followerDpc.automaticBrightnessController.convertToFloatScale(nits)) + .thenReturn(followerBrightness); + + mHolder.dpc.addDisplayBrightnessFollower(followerDpc.dpc); - mHolder.dpc.addDisplayBrightnessFollower(followerDpc); DisplayPowerRequest dpr = new DisplayPowerRequest(); mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); + followerDpc.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); + advanceTime(1); // Run updatePowerState + + verify(mHolder.animator).animateTo(eq(leadBrightness), anyFloat(), + eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE)); + // One triggered by handleBrightnessModeChange, another triggered by setBrightnessToFollow + verify(followerDpc.hbmController, times(2)).onAmbientLuxChange(ambientLux); + verify(followerDpc.animator, times(2)).animateTo(eq(followerBrightness), anyFloat(), + eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE)); + + when(mHolder.displayPowerState.getScreenBrightness()).thenReturn(leadBrightness); + when(followerDpc.displayPowerState.getScreenBrightness()).thenReturn(followerBrightness); + clearInvocations(mHolder.animator, followerDpc.animator); + + leadBrightness = 0.05f; + rawLeadBrightness = 0.2f; + followerBrightness = 0.3f; + nits = 200; + ambientLux = 2000; + when(mHolder.automaticBrightnessController.getRawAutomaticScreenBrightness()) + .thenReturn(rawLeadBrightness); + when(mHolder.automaticBrightnessController + .getAutomaticScreenBrightness(any(BrightnessEvent.class))) + .thenReturn(leadBrightness); + when(mHolder.automaticBrightnessController.convertToNits(rawLeadBrightness)) + .thenReturn(nits); + when(mHolder.automaticBrightnessController.getAmbientLux()).thenReturn(ambientLux); + when(followerDpc.automaticBrightnessController.convertToFloatScale(nits)) + .thenReturn(followerBrightness); + + mHolder.dpc.updateBrightness(); advanceTime(1); // Run updatePowerState - verify(followerDpc).setBrightnessToFollow(brightness, nits, ambientLux); + // The second time, the animation rate should be slow + verify(mHolder.animator).animateTo(eq(leadBrightness), anyFloat(), + eq(BRIGHTNESS_RAMP_RATE_SLOW_DECREASE)); + verify(followerDpc.hbmController).onAmbientLuxChange(ambientLux); + verify(followerDpc.animator).animateTo(eq(followerBrightness), anyFloat(), + eq(BRIGHTNESS_RAMP_RATE_SLOW_DECREASE)); } @Test public void testDisplayBrightnessFollowersRemoval_RemoveSingleFollower() { - DisplayPowerControllerHolder followerHolder = - createDisplayPowerController(FOLLOWER_DISPLAY_ID, FOLLOWER_UNIQUE_ID); - DisplayPowerControllerHolder secondFollowerHolder = - createDisplayPowerController(SECOND_FOLLOWER_DISPLAY_ID, - SECOND_FOLLOWER_UNIQUE_DISPLAY_ID); + DisplayPowerControllerHolder followerDpc = createDisplayPowerController(FOLLOWER_DISPLAY_ID, + FOLLOWER_UNIQUE_ID); + DisplayPowerControllerHolder secondFollowerDpc = createDisplayPowerController( + SECOND_FOLLOWER_DISPLAY_ID, SECOND_FOLLOWER_UNIQUE_DISPLAY_ID); + when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f); + when(followerDpc.displayPowerState.getColorFadeLevel()).thenReturn(1.0f); + when(secondFollowerDpc.displayPowerState.getColorFadeLevel()).thenReturn(1.0f); DisplayPowerRequest dpr = new DisplayPowerRequest(); mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); - followerHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); - secondFollowerHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); + followerDpc.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); + secondFollowerDpc.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); advanceTime(1); // Run updatePowerState ArgumentCaptor<BrightnessSetting.BrightnessSettingListener> listenerCaptor = @@ -470,58 +542,71 @@ public final class DisplayPowerControllerTest { // Set the initial brightness on the DPC we're going to remove so we have a fixed value for // it to return to. listenerCaptor = ArgumentCaptor.forClass(BrightnessSetting.BrightnessSettingListener.class); - verify(followerHolder.brightnessSetting).registerListener(listenerCaptor.capture()); + verify(followerDpc.brightnessSetting).registerListener(listenerCaptor.capture()); BrightnessSetting.BrightnessSettingListener followerListener = listenerCaptor.getValue(); final float initialFollowerBrightness = 0.3f; - when(followerHolder.brightnessSetting.getBrightness()).thenReturn( - initialFollowerBrightness); + when(followerDpc.brightnessSetting.getBrightness()).thenReturn(initialFollowerBrightness); followerListener.onBrightnessChanged(initialFollowerBrightness); advanceTime(1); - verify(followerHolder.animator).animateTo(eq(initialFollowerBrightness), - anyFloat(), anyFloat()); + verify(followerDpc.animator).animateTo(eq(initialFollowerBrightness), anyFloat(), + eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE)); - mHolder.dpc.addDisplayBrightnessFollower(followerHolder.dpc); - mHolder.dpc.addDisplayBrightnessFollower(secondFollowerHolder.dpc); - clearInvocations(followerHolder.animator); + when(followerDpc.displayPowerState.getScreenBrightness()) + .thenReturn(initialFollowerBrightness); + + mHolder.dpc.addDisplayBrightnessFollower(followerDpc.dpc); + mHolder.dpc.addDisplayBrightnessFollower(secondFollowerDpc.dpc); + clearInvocations(followerDpc.animator); // Validate both followers are correctly registered and receiving brightness updates float brightness = 0.6f; float nits = 600; when(mHolder.automaticBrightnessController.convertToNits(brightness)).thenReturn(nits); - when(followerHolder.automaticBrightnessController.convertToFloatScale(nits)) + when(followerDpc.automaticBrightnessController.convertToFloatScale(nits)) .thenReturn(brightness); - when(secondFollowerHolder.automaticBrightnessController.convertToFloatScale(nits)) + when(secondFollowerDpc.automaticBrightnessController.convertToFloatScale(nits)) .thenReturn(brightness); when(mHolder.brightnessSetting.getBrightness()).thenReturn(brightness); listener.onBrightnessChanged(brightness); advanceTime(1); // Send messages, run updatePowerState - verify(mHolder.animator).animateTo(eq(brightness), anyFloat(), anyFloat()); - verify(followerHolder.animator).animateTo(eq(brightness), anyFloat(), anyFloat()); - verify(secondFollowerHolder.animator).animateTo(eq(brightness), anyFloat(), anyFloat()); - - clearInvocations(mHolder.animator, followerHolder.animator, secondFollowerHolder.animator); + verify(mHolder.animator).animateTo(eq(brightness), anyFloat(), + eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE)); + verify(followerDpc.animator).animateTo(eq(brightness), anyFloat(), + eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE)); + verify(secondFollowerDpc.animator).animateTo(eq(brightness), anyFloat(), + eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE)); + + when(mHolder.displayPowerState.getScreenBrightness()).thenReturn(brightness); + when(followerDpc.displayPowerState.getScreenBrightness()).thenReturn(brightness); + when(secondFollowerDpc.displayPowerState.getScreenBrightness()).thenReturn(brightness); + clearInvocations(mHolder.animator, followerDpc.animator, secondFollowerDpc.animator); // Remove the first follower and validate it goes back to its original brightness. - mHolder.dpc.removeDisplayBrightnessFollower(followerHolder.dpc); + mHolder.dpc.removeDisplayBrightnessFollower(followerDpc.dpc); advanceTime(1); - verify(followerHolder.animator).animateTo(eq(initialFollowerBrightness), - anyFloat(), anyFloat()); - clearInvocations(followerHolder.animator); + verify(followerDpc.animator).animateTo(eq(initialFollowerBrightness), anyFloat(), + eq(BRIGHTNESS_RAMP_RATE_FAST_DECREASE)); + + when(followerDpc.displayPowerState.getScreenBrightness()) + .thenReturn(initialFollowerBrightness); + clearInvocations(followerDpc.animator); // Change the brightness of the lead display and validate only the second follower responds brightness = 0.7f; nits = 700; when(mHolder.automaticBrightnessController.convertToNits(brightness)).thenReturn(nits); - when(followerHolder.automaticBrightnessController.convertToFloatScale(nits)) + when(followerDpc.automaticBrightnessController.convertToFloatScale(nits)) .thenReturn(brightness); - when(secondFollowerHolder.automaticBrightnessController.convertToFloatScale(nits)) + when(secondFollowerDpc.automaticBrightnessController.convertToFloatScale(nits)) .thenReturn(brightness); when(mHolder.brightnessSetting.getBrightness()).thenReturn(brightness); listener.onBrightnessChanged(brightness); advanceTime(1); // Send messages, run updatePowerState - verify(mHolder.animator).animateTo(eq(brightness), anyFloat(), anyFloat()); - verify(followerHolder.animator, never()).animateTo(anyFloat(), anyFloat(), anyFloat()); - verify(secondFollowerHolder.animator).animateTo(eq(brightness), anyFloat(), anyFloat()); + verify(mHolder.animator).animateTo(eq(brightness), anyFloat(), + eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE)); + verify(followerDpc.animator, never()).animateTo(anyFloat(), anyFloat(), anyFloat()); + verify(secondFollowerDpc.animator).animateTo(eq(brightness), anyFloat(), + eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE)); } @Test @@ -531,6 +616,9 @@ public final class DisplayPowerControllerTest { DisplayPowerControllerHolder secondFollowerHolder = createDisplayPowerController(SECOND_FOLLOWER_DISPLAY_ID, SECOND_FOLLOWER_UNIQUE_DISPLAY_ID); + when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f); + when(followerHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f); + when(secondFollowerHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f); DisplayPowerRequest dpr = new DisplayPowerRequest(); mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); @@ -560,10 +648,15 @@ public final class DisplayPowerControllerTest { followerListener.onBrightnessChanged(initialFollowerBrightness); secondFollowerListener.onBrightnessChanged(initialFollowerBrightness); advanceTime(1); - verify(followerHolder.animator).animateTo(eq(initialFollowerBrightness), - anyFloat(), anyFloat()); - verify(secondFollowerHolder.animator).animateTo(eq(initialFollowerBrightness), - anyFloat(), anyFloat()); + verify(followerHolder.animator).animateTo(eq(initialFollowerBrightness), anyFloat(), + eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE)); + verify(secondFollowerHolder.animator).animateTo(eq(initialFollowerBrightness), anyFloat(), + eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE)); + + when(followerHolder.displayPowerState.getScreenBrightness()) + .thenReturn(initialFollowerBrightness); + when(secondFollowerHolder.displayPowerState.getScreenBrightness()) + .thenReturn(initialFollowerBrightness); mHolder.dpc.addDisplayBrightnessFollower(followerHolder.dpc); mHolder.dpc.addDisplayBrightnessFollower(secondFollowerHolder.dpc); @@ -580,19 +673,25 @@ public final class DisplayPowerControllerTest { when(mHolder.brightnessSetting.getBrightness()).thenReturn(brightness); listener.onBrightnessChanged(brightness); advanceTime(1); // Send messages, run updatePowerState - verify(mHolder.animator).animateTo(eq(brightness), anyFloat(), anyFloat()); - verify(followerHolder.animator).animateTo(eq(brightness), anyFloat(), anyFloat()); - verify(secondFollowerHolder.animator).animateTo(eq(brightness), anyFloat(), anyFloat()); - + verify(mHolder.animator).animateTo(eq(brightness), anyFloat(), + eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE)); + verify(followerHolder.animator).animateTo(eq(brightness), anyFloat(), + eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE)); + verify(secondFollowerHolder.animator).animateTo(eq(brightness), anyFloat(), + eq(BRIGHTNESS_RAMP_RATE_FAST_INCREASE)); + + when(mHolder.displayPowerState.getScreenBrightness()).thenReturn(brightness); + when(followerHolder.displayPowerState.getScreenBrightness()).thenReturn(brightness); + when(secondFollowerHolder.displayPowerState.getScreenBrightness()).thenReturn(brightness); clearInvocations(mHolder.animator, followerHolder.animator, secondFollowerHolder.animator); // Stop the lead DPC and validate that the followers go back to their original brightness. mHolder.dpc.stop(); advanceTime(1); - verify(followerHolder.animator).animateTo(eq(initialFollowerBrightness), - anyFloat(), anyFloat()); - verify(secondFollowerHolder.animator).animateTo(eq(initialFollowerBrightness), - anyFloat(), anyFloat()); + verify(followerHolder.animator).animateTo(eq(initialFollowerBrightness), anyFloat(), + eq(BRIGHTNESS_RAMP_RATE_FAST_DECREASE)); + verify(secondFollowerHolder.animator).animateTo(eq(initialFollowerBrightness), anyFloat(), + eq(BRIGHTNESS_RAMP_RATE_FAST_DECREASE)); clearInvocations(followerHolder.animator, secondFollowerHolder.animator); } @@ -751,6 +850,155 @@ public final class DisplayPowerControllerTest { } @Test + public void testAutoBrightnessEnabled_DisplayIsOn() { + Settings.System.putInt(mContext.getContentResolver(), + Settings.System.SCREEN_BRIGHTNESS_MODE, + Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC); + + DisplayPowerRequest dpr = new DisplayPowerRequest(); + dpr.policy = DisplayPowerRequest.POLICY_BRIGHT; + when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON); + mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); + advanceTime(1); // Run updatePowerState + + verify(mHolder.automaticBrightnessController).configure( + AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED, + /* configuration= */ null, PowerManager.BRIGHTNESS_INVALID_FLOAT, + /* userChangedBrightness= */ false, /* adjustment= */ 0, + /* userChangedAutoBrightnessAdjustment= */ false, DisplayPowerRequest.POLICY_BRIGHT, + /* shouldResetShortTermModel= */ false + ); + verify(mHolder.hbmController) + .setAutoBrightnessEnabled(AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED); + } + + @Test + public void testAutoBrightnessEnabled_DisplayIsInDoze() { + Settings.System.putInt(mContext.getContentResolver(), + Settings.System.SCREEN_BRIGHTNESS_MODE, + Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC); + mContext.getOrCreateTestableResources().addOverride( + com.android.internal.R.bool.config_allowAutoBrightnessWhileDozing, true); + mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID); + + DisplayPowerRequest dpr = new DisplayPowerRequest(); + dpr.policy = DisplayPowerRequest.POLICY_DOZE; + when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_DOZE); + mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); + advanceTime(1); // Run updatePowerState + + verify(mHolder.automaticBrightnessController).configure( + AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED, + /* configuration= */ null, PowerManager.BRIGHTNESS_INVALID_FLOAT, + /* userChangedBrightness= */ false, /* adjustment= */ 0, + /* userChangedAutoBrightnessAdjustment= */ false, DisplayPowerRequest.POLICY_DOZE, + /* shouldResetShortTermModel= */ false + ); + verify(mHolder.hbmController) + .setAutoBrightnessEnabled(AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED); + } + + @Test + public void testAutoBrightnessDisabled_ManualBrightnessMode() { + Settings.System.putInt(mContext.getContentResolver(), + Settings.System.SCREEN_BRIGHTNESS_MODE, + Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL); + + DisplayPowerRequest dpr = new DisplayPowerRequest(); + dpr.policy = DisplayPowerRequest.POLICY_BRIGHT; + when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON); + mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); + advanceTime(1); // Run updatePowerState + + // One triggered by the test, the other by handleBrightnessModeChange + verify(mHolder.automaticBrightnessController, times(2)).configure( + AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED, + /* configuration= */ null, PowerManager.BRIGHTNESS_INVALID_FLOAT, + /* userChangedBrightness= */ false, /* adjustment= */ 0, + /* userChangedAutoBrightnessAdjustment= */ false, DisplayPowerRequest.POLICY_BRIGHT, + /* shouldResetShortTermModel= */ false + ); + verify(mHolder.hbmController, times(2)) + .setAutoBrightnessEnabled(AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED); + } + + @Test + public void testAutoBrightnessDisabled_DisplayIsOff() { + Settings.System.putInt(mContext.getContentResolver(), + Settings.System.SCREEN_BRIGHTNESS_MODE, + Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC); + + DisplayPowerRequest dpr = new DisplayPowerRequest(); + dpr.policy = DisplayPowerRequest.POLICY_OFF; + when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_OFF); + mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); + advanceTime(1); // Run updatePowerState + + verify(mHolder.automaticBrightnessController).configure( + AutomaticBrightnessController.AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE, + /* configuration= */ null, PowerManager.BRIGHTNESS_INVALID_FLOAT, + /* userChangedBrightness= */ false, /* adjustment= */ 0, + /* userChangedAutoBrightnessAdjustment= */ false, DisplayPowerRequest.POLICY_OFF, + /* shouldResetShortTermModel= */ false + ); + verify(mHolder.hbmController).setAutoBrightnessEnabled( + AutomaticBrightnessController.AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE); + } + + @Test + public void testAutoBrightnessDisabled_DisplayIsInDoze() { + Settings.System.putInt(mContext.getContentResolver(), + Settings.System.SCREEN_BRIGHTNESS_MODE, + Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC); + mContext.getOrCreateTestableResources().addOverride( + com.android.internal.R.bool.config_allowAutoBrightnessWhileDozing, false); + mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID); + + DisplayPowerRequest dpr = new DisplayPowerRequest(); + dpr.policy = DisplayPowerRequest.POLICY_DOZE; + when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_DOZE); + mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); + advanceTime(1); // Run updatePowerState + + verify(mHolder.automaticBrightnessController).configure( + AutomaticBrightnessController.AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE, + /* configuration= */ null, PowerManager.BRIGHTNESS_INVALID_FLOAT, + /* userChangedBrightness= */ false, /* adjustment= */ 0, + /* userChangedAutoBrightnessAdjustment= */ false, DisplayPowerRequest.POLICY_DOZE, + /* shouldResetShortTermModel= */ false + ); + verify(mHolder.hbmController).setAutoBrightnessEnabled( + AutomaticBrightnessController.AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE); + } + + @Test + public void testAutoBrightnessDisabled_FollowerDisplay() { + Settings.System.putInt(mContext.getContentResolver(), + Settings.System.SCREEN_BRIGHTNESS_MODE, + Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC); + mHolder.dpc.setBrightnessToFollow(0.3f, -1, 0, /* slowChange= */ false); + + DisplayPowerRequest dpr = new DisplayPowerRequest(); + dpr.policy = DisplayPowerRequest.POLICY_BRIGHT; + when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON); + mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); + advanceTime(1); // Run updatePowerState + + // One triggered by the test, the other by handleBrightnessModeChange + verify(mHolder.automaticBrightnessController, times(2)).configure( + AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED, + /* configuration= */ null, PowerManager.BRIGHTNESS_INVALID_FLOAT, + /* userChangedBrightness= */ false, /* adjustment= */ 0, + /* userChangedAutoBrightnessAdjustment= */ false, DisplayPowerRequest.POLICY_BRIGHT, + /* shouldResetShortTermModel= */ false + ); + + // HBM should be allowed for the follower display + verify(mHolder.hbmController) + .setAutoBrightnessEnabled(AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED); + } + + @Test public void testBrightnessNitsPersistWhenDisplayDeviceChanges() { float brightness = 0.3f; float nits = 500; @@ -819,7 +1067,7 @@ public final class DisplayPowerControllerTest { any(HysteresisLevels.class), any(HysteresisLevels.class), eq(mContext), - any(HighBrightnessModeController.class), + any(BrightnessRangeController.class), any(BrightnessThrottler.class), isNull(), anyInt(), @@ -907,6 +1155,14 @@ public final class DisplayPowerControllerTest { }); when(displayDeviceConfigMock.getScreenOffBrightnessSensorValueToLux()) .thenReturn(new int[0]); + when(displayDeviceConfigMock.getBrightnessRampFastDecrease()) + .thenReturn(BRIGHTNESS_RAMP_RATE_FAST_DECREASE); + when(displayDeviceConfigMock.getBrightnessRampFastIncrease()) + .thenReturn(BRIGHTNESS_RAMP_RATE_FAST_INCREASE); + when(displayDeviceConfigMock.getBrightnessRampSlowDecrease()) + .thenReturn(BRIGHTNESS_RAMP_RATE_SLOW_DECREASE); + when(displayDeviceConfigMock.getBrightnessRampSlowIncrease()) + .thenReturn(BRIGHTNESS_RAMP_RATE_SLOW_INCREASE); } private DisplayPowerControllerHolder createDisplayPowerController(int displayId, @@ -925,10 +1181,13 @@ public final class DisplayPowerControllerTest { final HysteresisLevels hysteresisLevels = mock(HysteresisLevels.class); final ScreenOffBrightnessSensorController screenOffBrightnessSensorController = mock(ScreenOffBrightnessSensorController.class); + final HighBrightnessModeController hbmController = mock(HighBrightnessModeController.class); + + when(hbmController.getCurrentBrightnessMax()).thenReturn(PowerManager.BRIGHTNESS_MAX); DisplayPowerController.Injector injector = spy(new TestInjector(displayPowerState, animator, automaticBrightnessController, brightnessMappingStrategy, hysteresisLevels, - screenOffBrightnessSensorController)); + screenOffBrightnessSensorController, hbmController)); final LogicalDisplay display = mock(LogicalDisplay.class); final DisplayDevice device = mock(DisplayDevice.class); @@ -946,7 +1205,7 @@ public final class DisplayPowerControllerTest { return new DisplayPowerControllerHolder(dpc, display, displayPowerState, brightnessSetting, animator, automaticBrightnessController, screenOffBrightnessSensorController, - hbmMetadata, brightnessMappingStrategy, injector); + hbmController, hbmMetadata, brightnessMappingStrategy, injector); } /** @@ -961,6 +1220,7 @@ public final class DisplayPowerControllerTest { public final DualRampAnimator<DisplayPowerState> animator; public final AutomaticBrightnessController automaticBrightnessController; public final ScreenOffBrightnessSensorController screenOffBrightnessSensorController; + public final HighBrightnessModeController hbmController; public final HighBrightnessModeMetadata hbmMetadata; public final BrightnessMappingStrategy brightnessMappingStrategy; public final DisplayPowerController.Injector injector; @@ -970,6 +1230,7 @@ public final class DisplayPowerControllerTest { DualRampAnimator<DisplayPowerState> animator, AutomaticBrightnessController automaticBrightnessController, ScreenOffBrightnessSensorController screenOffBrightnessSensorController, + HighBrightnessModeController hbmController, HighBrightnessModeMetadata hbmMetadata, BrightnessMappingStrategy brightnessMappingStrategy, DisplayPowerController.Injector injector) { @@ -980,6 +1241,7 @@ public final class DisplayPowerControllerTest { this.animator = animator; this.automaticBrightnessController = automaticBrightnessController; this.screenOffBrightnessSensorController = screenOffBrightnessSensorController; + this.hbmController = hbmController; this.hbmMetadata = hbmMetadata; this.brightnessMappingStrategy = brightnessMappingStrategy; this.injector = injector; @@ -993,18 +1255,21 @@ public final class DisplayPowerControllerTest { private final BrightnessMappingStrategy mBrightnessMappingStrategy; private final HysteresisLevels mHysteresisLevels; private final ScreenOffBrightnessSensorController mScreenOffBrightnessSensorController; + private final HighBrightnessModeController mHighBrightnessModeController; TestInjector(DisplayPowerState dps, DualRampAnimator<DisplayPowerState> animator, AutomaticBrightnessController automaticBrightnessController, BrightnessMappingStrategy brightnessMappingStrategy, HysteresisLevels hysteresisLevels, - ScreenOffBrightnessSensorController screenOffBrightnessSensorController) { + ScreenOffBrightnessSensorController screenOffBrightnessSensorController, + HighBrightnessModeController highBrightnessModeController) { mDisplayPowerState = dps; mAnimator = animator; mAutomaticBrightnessController = automaticBrightnessController; mBrightnessMappingStrategy = brightnessMappingStrategy; mHysteresisLevels = hysteresisLevels; mScreenOffBrightnessSensorController = screenOffBrightnessSensorController; + mHighBrightnessModeController = highBrightnessModeController; } @Override @@ -1038,7 +1303,7 @@ public final class DisplayPowerControllerTest { HysteresisLevels screenBrightnessThresholds, HysteresisLevels ambientBrightnessThresholdsIdle, HysteresisLevels screenBrightnessThresholdsIdle, Context context, - HighBrightnessModeController hbmController, + BrightnessRangeController brightnessRangeController, BrightnessThrottler brightnessThrottler, BrightnessMappingStrategy idleModeBrightnessMapper, int ambientLightHorizonShort, int ambientLightHorizonLong, float userLux, @@ -1076,5 +1341,15 @@ public final class DisplayPowerControllerTest { BrightnessMappingStrategy brightnessMapper) { return mScreenOffBrightnessSensorController; } + + @Override + HighBrightnessModeController getHighBrightnessModeController(Handler handler, int width, + int height, IBinder displayToken, String displayUniqueId, float brightnessMin, + float brightnessMax, DisplayDeviceConfig.HighBrightnessModeData hbmData, + HighBrightnessModeController.HdrBrightnessDeviceConfig hdrBrightnessCfg, + Runnable hbmChangeCallback, HighBrightnessModeMetadata hbmMetadata, + Context context) { + return mHighBrightnessModeController; + } } } diff --git a/services/tests/mockingservicestests/src/com/android/server/dreams/DreamManagerServiceMockingTest.java b/services/tests/mockingservicestests/src/com/android/server/dreams/DreamManagerServiceMockingTest.java new file mode 100644 index 000000000000..c02cbd1c9d08 --- /dev/null +++ b/services/tests/mockingservicestests/src/com/android/server/dreams/DreamManagerServiceMockingTest.java @@ -0,0 +1,120 @@ +/* + * Copyright 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.dreams; + +import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; + +import android.app.ActivityManagerInternal; +import android.content.ContextWrapper; +import android.content.pm.UserInfo; +import android.content.res.Resources; +import android.os.PowerManagerInternal; +import android.os.UserHandle; +import android.os.UserManager; +import android.provider.Settings; + +import androidx.test.InstrumentationRegistry; + +import com.android.server.LocalServices; +import com.android.server.SystemService; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.mockito.MockitoSession; +import org.mockito.quality.Strictness; + +/** + * Collection of tests for exercising the {@link DreamManagerService} lifecycle. + */ +public class DreamManagerServiceMockingTest { + private ContextWrapper mContextSpy; + private Resources mResourcesSpy; + + @Mock + private ActivityManagerInternal mActivityManagerInternalMock; + + @Mock + private PowerManagerInternal mPowerManagerInternalMock; + + @Mock + private UserManager mUserManagerMock; + + private MockitoSession mMockitoSession; + + private static <T> void addLocalServiceMock(Class<T> clazz, T mock) { + LocalServices.removeServiceForTest(clazz); + LocalServices.addService(clazz, mock); + } + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + + mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getContext())); + mResourcesSpy = spy(mContextSpy.getResources()); + when(mContextSpy.getResources()).thenReturn(mResourcesSpy); + + addLocalServiceMock(ActivityManagerInternal.class, mActivityManagerInternalMock); + addLocalServiceMock(PowerManagerInternal.class, mPowerManagerInternalMock); + + when(mContextSpy.getSystemService(UserManager.class)).thenReturn(mUserManagerMock); + mMockitoSession = mockitoSession() + .initMocks(this) + .strictness(Strictness.LENIENT) + .mockStatic(Settings.Secure.class) + .startMocking(); + } + + @After + public void tearDown() throws Exception { + mMockitoSession.finishMocking(); + LocalServices.removeServiceForTest(ActivityManagerInternal.class); + LocalServices.removeServiceForTest(PowerManagerInternal.class); + } + + private DreamManagerService createService() { + return new DreamManagerService(mContextSpy); + } + + @Test + public void testSettingsQueryUserChange() { + final DreamManagerService service = createService(); + + final SystemService.TargetUser from = + new SystemService.TargetUser(mock(UserInfo.class)); + final SystemService.TargetUser to = + new SystemService.TargetUser(mock(UserInfo.class)); + + service.onUserSwitching(from, to); + + verify(() -> Settings.Secure.getIntForUser(any(), + eq(Settings.Secure.SCREENSAVER_ENABLED), + anyInt(), + eq(UserHandle.USER_CURRENT))); + } +} diff --git a/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java b/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java index 206af5b6aea6..bc5e72095a1c 100644 --- a/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java +++ b/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java @@ -92,7 +92,6 @@ import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; import org.junit.ClassRule; -import org.junit.Ignore; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; @@ -175,12 +174,12 @@ public class WallpaperManagerServiceTests { sImageWallpaperComponentName = ComponentName.unflattenFromString( sContext.getResources().getString(R.string.image_wallpaper_component)); // Mock default wallpaper as image wallpaper if there is no pre-defined default wallpaper. - sDefaultWallpaperComponent = WallpaperManager.getDefaultWallpaperComponent(sContext); + sDefaultWallpaperComponent = WallpaperManager.getCmfDefaultWallpaperComponent(sContext); if (sDefaultWallpaperComponent == null) { sDefaultWallpaperComponent = sImageWallpaperComponentName; doReturn(sImageWallpaperComponentName).when(() -> - WallpaperManager.getDefaultWallpaperComponent(any())); + WallpaperManager.getCmfDefaultWallpaperComponent(any())); } else { sContext.addMockService(sDefaultWallpaperComponent, sWallpaperService); } @@ -300,10 +299,8 @@ public class WallpaperManagerServiceTests { /** * Tests setWallpaperComponent and clearWallpaper should work as expected. - * TODO ignored since the assumption never passes. to be investigated. */ @Test - @Ignore("b/264533465") public void testSetThenClearComponent() { // Skip if there is no pre-defined default wallpaper component. assumeThat(sDefaultWallpaperComponent, diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp index 16e1b457ad96..ee3a773580a3 100644 --- a/services/tests/servicestests/Android.bp +++ b/services/tests/servicestests/Android.bp @@ -28,6 +28,7 @@ android_test { "services.accessibility", "services.appwidget", "services.autofill", + "services.contentcapture", "services.backup", "services.companion", "services.core", diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceProviderTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceProviderTest.java index 31a58cd67d3e..d1d6e9d41b1f 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceProviderTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceProviderTest.java @@ -36,7 +36,6 @@ import android.os.UserHandle; import android.os.UserManager; import android.platform.test.annotations.Presubmit; -import androidx.annotation.NonNull; import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; @@ -73,7 +72,7 @@ public class FaceProviderTest { private SensorProps[] mSensorProps; private LockoutResetDispatcher mLockoutResetDispatcher; - private TestableFaceProvider mFaceProvider; + private FaceProvider mFaceProvider; private static void waitForIdle() { InstrumentationRegistry.getInstrumentation().waitForIdleSync(); @@ -98,8 +97,9 @@ public class FaceProviderTest { mLockoutResetDispatcher = new LockoutResetDispatcher(mContext); - mFaceProvider = new TestableFaceProvider(mDaemon, mContext, mBiometricStateCallback, - mSensorProps, TAG, mLockoutResetDispatcher, mBiometricContext); + mFaceProvider = new FaceProvider(mContext, mBiometricStateCallback, + mSensorProps, TAG, mLockoutResetDispatcher, mBiometricContext, + mDaemon); } @Test @@ -130,6 +130,7 @@ public class FaceProviderTest { for (SensorProps prop : mSensorProps) { final BiometricScheduler scheduler = mFaceProvider.mFaceSensors.get(prop.commonProps.sensorId).getScheduler(); + scheduler.reset(); for (int i = 0; i < numFakeOperations; i++) { final HalClientMonitor testMonitor = mock(HalClientMonitor.class); when(testMonitor.getFreshDaemon()).thenReturn(new Object()); @@ -142,7 +143,7 @@ public class FaceProviderTest { for (SensorProps prop : mSensorProps) { final BiometricScheduler scheduler = mFaceProvider.mFaceSensors.get(prop.commonProps.sensorId).getScheduler(); - assertEquals(numFakeOperations, scheduler.getCurrentPendingCount()); + assertEquals(numFakeOperations - 1, scheduler.getCurrentPendingCount()); assertNotNull(scheduler.getCurrentClient()); } @@ -159,25 +160,4 @@ public class FaceProviderTest { assertEquals(0, scheduler.getCurrentPendingCount()); } } - - private static class TestableFaceProvider extends FaceProvider { - private final IFace mDaemon; - - TestableFaceProvider(@NonNull IFace daemon, - @NonNull Context context, - @NonNull BiometricStateCallback biometricStateCallback, - @NonNull SensorProps[] props, - @NonNull String halInstanceName, - @NonNull LockoutResetDispatcher lockoutResetDispatcher, - @NonNull BiometricContext biometricContext) { - super(context, biometricStateCallback, props, halInstanceName, lockoutResetDispatcher, - biometricContext); - mDaemon = daemon; - } - - @Override - synchronized IFace getHalInstance() { - return mDaemon; - } - } } diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProviderTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProviderTest.java index 9c01de6f0461..8f6efffcbff8 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProviderTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProviderTest.java @@ -39,7 +39,6 @@ import android.os.UserHandle; import android.os.UserManager; import android.platform.test.annotations.Presubmit; -import androidx.annotation.NonNull; import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; @@ -81,7 +80,7 @@ public class FingerprintProviderTest { private SensorProps[] mSensorProps; private LockoutResetDispatcher mLockoutResetDispatcher; - private TestableFingerprintProvider mFingerprintProvider; + private FingerprintProvider mFingerprintProvider; private static void waitForIdle() { InstrumentationRegistry.getInstrumentation().waitForIdleSync(); @@ -110,17 +109,13 @@ public class FingerprintProviderTest { mLockoutResetDispatcher = new LockoutResetDispatcher(mContext); - mFingerprintProvider = new TestableFingerprintProvider(mDaemon, mContext, + mFingerprintProvider = new FingerprintProvider(mContext, mBiometricStateCallback, mSensorProps, TAG, mLockoutResetDispatcher, - mGestureAvailabilityDispatcher, mBiometricContext); + mGestureAvailabilityDispatcher, mBiometricContext, mDaemon); } @Test public void testAddingSensors() { - mFingerprintProvider = new TestableFingerprintProvider(mDaemon, mContext, - mBiometricStateCallback, mSensorProps, TAG, mLockoutResetDispatcher, - mGestureAvailabilityDispatcher, mBiometricContext); - waitForIdle(); for (SensorProps prop : mSensorProps) { @@ -147,6 +142,7 @@ public class FingerprintProviderTest { final BiometricScheduler scheduler = mFingerprintProvider.mFingerprintSensors.get(prop.commonProps.sensorId) .getScheduler(); + scheduler.reset(); for (int i = 0; i < numFakeOperations; i++) { final HalClientMonitor testMonitor = mock(HalClientMonitor.class); when(testMonitor.getFreshDaemon()).thenReturn(new Object()); @@ -160,7 +156,7 @@ public class FingerprintProviderTest { final BiometricScheduler scheduler = mFingerprintProvider.mFingerprintSensors.get(prop.commonProps.sensorId) .getScheduler(); - assertEquals(numFakeOperations, scheduler.getCurrentPendingCount()); + assertEquals(numFakeOperations - 1, scheduler.getCurrentPendingCount()); assertNotNull(scheduler.getCurrentClient()); } @@ -178,26 +174,4 @@ public class FingerprintProviderTest { assertEquals(0, scheduler.getCurrentPendingCount()); } } - - private static class TestableFingerprintProvider extends FingerprintProvider { - private final IFingerprint mDaemon; - - TestableFingerprintProvider(@NonNull IFingerprint daemon, - @NonNull Context context, - @NonNull BiometricStateCallback biometricStateCallback, - @NonNull SensorProps[] props, - @NonNull String halInstanceName, - @NonNull LockoutResetDispatcher lockoutResetDispatcher, - @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher, - @NonNull BiometricContext biometricContext) { - super(context, biometricStateCallback, props, halInstanceName, lockoutResetDispatcher, - gestureAvailabilityDispatcher, biometricContext); - mDaemon = daemon; - } - - @Override - synchronized IFingerprint getHalInstance() { - return mDaemon; - } - } } diff --git a/services/tests/servicestests/src/com/android/server/contentcapture/ContentCaptureManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/contentcapture/ContentCaptureManagerServiceTest.java new file mode 100644 index 000000000000..e4571194b37d --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/contentcapture/ContentCaptureManagerServiceTest.java @@ -0,0 +1,427 @@ +/* + * Copyright (C) 2013 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.contentcapture; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.verifyZeroInteractions; +import static org.mockito.Mockito.when; + +import android.annotation.NonNull; +import android.content.ComponentName; +import android.content.ContentCaptureOptions; +import android.content.Context; +import android.content.pm.PackageManager; +import android.content.pm.ParceledListSlice; +import android.content.pm.UserInfo; +import android.service.contentcapture.ContentCaptureServiceInfo; +import android.view.contentcapture.ContentCaptureEvent; + +import androidx.test.core.app.ApplicationProvider; +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.filters.SmallTest; + +import com.android.server.LocalServices; +import com.android.server.contentprotection.ContentProtectionBlocklistManager; +import com.android.server.contentprotection.RemoteContentProtectionService; +import com.android.server.pm.UserManagerInternal; + +import com.google.common.collect.ImmutableList; + +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; + +/** + * Test for {@link ContentCaptureManagerService}. + * + * <p>Run with: {@code atest + * FrameworksServicesTests:com.android.server.contentcapture.ContentCaptureManagerServiceTest} + */ +@RunWith(AndroidJUnit4.class) +@SmallTest +@SuppressWarnings("GuardedBy") // Service not really running, no need to expose locks +public class ContentCaptureManagerServiceTest { + + private static final int USER_ID = 1234; + + private static final String PACKAGE_NAME = "com.test.package"; + + private static final ComponentName COMPONENT_NAME = + new ComponentName(PACKAGE_NAME, "TestClass"); + + private static final ContentCaptureEvent EVENT = + new ContentCaptureEvent(/* sessionId= */ 100, /* type= */ 200); + + private static final ParceledListSlice<ContentCaptureEvent> PARCELED_EVENTS = + new ParceledListSlice<>(ImmutableList.of(EVENT)); + + private static final Context sContext = ApplicationProvider.getApplicationContext(); + + @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule(); + + @Mock private UserManagerInternal mMockUserManagerInternal; + + @Mock private ContentProtectionBlocklistManager mMockContentProtectionBlocklistManager; + + @Mock private ContentCaptureServiceInfo mMockContentCaptureServiceInfo; + + @Mock private RemoteContentProtectionService mMockRemoteContentProtectionService; + + private boolean mDevCfgEnableContentProtectionReceiver; + + private int mContentProtectionBlocklistManagersCreated; + + private int mContentProtectionServiceInfosCreated; + + private int mRemoteContentProtectionServicesCreated; + + private String mConfigDefaultContentProtectionService = COMPONENT_NAME.flattenToString(); + + private boolean mContentProtectionServiceInfoConstructorShouldThrow; + + private ContentCaptureManagerService mContentCaptureManagerService; + + @Before + public void setup() { + when(mMockUserManagerInternal.getUserInfos()).thenReturn(new UserInfo[0]); + LocalServices.removeServiceForTest(UserManagerInternal.class); + LocalServices.addService(UserManagerInternal.class, mMockUserManagerInternal); + mContentCaptureManagerService = new TestContentCaptureManagerService(); + } + + @Test + public void constructor_contentProtection_flagDisabled_noBlocklistManager() { + assertThat(mContentProtectionBlocklistManagersCreated).isEqualTo(0); + assertThat(mContentProtectionServiceInfosCreated).isEqualTo(0); + verifyZeroInteractions(mMockContentProtectionBlocklistManager); + } + + @Test + public void constructor_contentProtection_componentNameNull_noBlocklistManager() { + mConfigDefaultContentProtectionService = null; + + mContentCaptureManagerService = new TestContentCaptureManagerService(); + + assertThat(mContentProtectionBlocklistManagersCreated).isEqualTo(0); + assertThat(mContentProtectionServiceInfosCreated).isEqualTo(0); + verifyZeroInteractions(mMockContentProtectionBlocklistManager); + } + + @Test + public void constructor_contentProtection_componentNameBlank_noBlocklistManager() { + mConfigDefaultContentProtectionService = " "; + + mContentCaptureManagerService = new TestContentCaptureManagerService(); + + assertThat(mContentProtectionBlocklistManagersCreated).isEqualTo(0); + assertThat(mContentProtectionServiceInfosCreated).isEqualTo(0); + verifyZeroInteractions(mMockContentProtectionBlocklistManager); + } + + @Test + public void constructor_contentProtection_enabled_createsBlocklistManager() { + mDevCfgEnableContentProtectionReceiver = true; + + mContentCaptureManagerService = new TestContentCaptureManagerService(); + + assertThat(mContentProtectionBlocklistManagersCreated).isEqualTo(1); + assertThat(mContentProtectionServiceInfosCreated).isEqualTo(0); + verify(mMockContentProtectionBlocklistManager).updateBlocklist(anyInt()); + } + + @Test + public void setFineTuneParamsFromDeviceConfig_doesNotUpdateContentProtectionBlocklist() { + mDevCfgEnableContentProtectionReceiver = true; + mContentCaptureManagerService = new TestContentCaptureManagerService(); + mContentCaptureManagerService.mDevCfgContentProtectionAppsBlocklistSize += 100; + verify(mMockContentProtectionBlocklistManager).updateBlocklist(anyInt()); + + mContentCaptureManagerService.setFineTuneParamsFromDeviceConfig(); + + verifyNoMoreInteractions(mMockContentProtectionBlocklistManager); + } + + @Test + public void getOptions_contentCaptureDisabled_contentProtectionDisabled() { + mDevCfgEnableContentProtectionReceiver = true; + mContentCaptureManagerService = new TestContentCaptureManagerService(); + + ContentCaptureOptions actual = + mContentCaptureManagerService.mGlobalContentCaptureOptions.getOptions( + USER_ID, PACKAGE_NAME); + + assertThat(actual).isNull(); + verify(mMockContentProtectionBlocklistManager).isAllowed(PACKAGE_NAME); + } + + @Test + public void getOptions_contentCaptureDisabled_contentProtectionEnabled() { + when(mMockContentProtectionBlocklistManager.isAllowed(PACKAGE_NAME)).thenReturn(true); + mDevCfgEnableContentProtectionReceiver = true; + mContentCaptureManagerService = new TestContentCaptureManagerService(); + + ContentCaptureOptions actual = + mContentCaptureManagerService.mGlobalContentCaptureOptions.getOptions( + USER_ID, PACKAGE_NAME); + + assertThat(actual).isNotNull(); + assertThat(actual.enableReceiver).isFalse(); + assertThat(actual.contentProtectionOptions).isNotNull(); + assertThat(actual.contentProtectionOptions.enableReceiver).isTrue(); + assertThat(actual.whitelistedComponents).isNull(); + } + + @Test + public void getOptions_contentCaptureEnabled_contentProtectionDisabled() { + mDevCfgEnableContentProtectionReceiver = true; + mContentCaptureManagerService = new TestContentCaptureManagerService(); + mContentCaptureManagerService.mGlobalContentCaptureOptions.setWhitelist( + USER_ID, ImmutableList.of(PACKAGE_NAME), /* components= */ null); + + ContentCaptureOptions actual = + mContentCaptureManagerService.mGlobalContentCaptureOptions.getOptions( + USER_ID, PACKAGE_NAME); + + assertThat(actual).isNotNull(); + assertThat(actual.enableReceiver).isTrue(); + assertThat(actual.contentProtectionOptions).isNotNull(); + assertThat(actual.contentProtectionOptions.enableReceiver).isFalse(); + assertThat(actual.whitelistedComponents).isNull(); + verify(mMockContentProtectionBlocklistManager).isAllowed(PACKAGE_NAME); + } + + @Test + public void getOptions_contentCaptureEnabled_contentProtectionEnabled() { + when(mMockContentProtectionBlocklistManager.isAllowed(PACKAGE_NAME)).thenReturn(true); + mDevCfgEnableContentProtectionReceiver = true; + mContentCaptureManagerService = new TestContentCaptureManagerService(); + mContentCaptureManagerService.mGlobalContentCaptureOptions.setWhitelist( + USER_ID, ImmutableList.of(PACKAGE_NAME), /* components= */ null); + + ContentCaptureOptions actual = + mContentCaptureManagerService.mGlobalContentCaptureOptions.getOptions( + USER_ID, PACKAGE_NAME); + + assertThat(actual).isNotNull(); + assertThat(actual.enableReceiver).isTrue(); + assertThat(actual.contentProtectionOptions).isNotNull(); + assertThat(actual.contentProtectionOptions.enableReceiver).isTrue(); + assertThat(actual.whitelistedComponents).isNull(); + } + + @Test + public void isWhitelisted_packageName_contentCaptureDisabled_contentProtectionDisabled() { + mDevCfgEnableContentProtectionReceiver = true; + mContentCaptureManagerService = new TestContentCaptureManagerService(); + + boolean actual = + mContentCaptureManagerService.mGlobalContentCaptureOptions.isWhitelisted( + USER_ID, PACKAGE_NAME); + + assertThat(actual).isFalse(); + verify(mMockContentProtectionBlocklistManager).isAllowed(PACKAGE_NAME); + } + + @Test + public void isWhitelisted_packageName_contentCaptureDisabled_contentProtectionEnabled() { + when(mMockContentProtectionBlocklistManager.isAllowed(PACKAGE_NAME)).thenReturn(true); + mDevCfgEnableContentProtectionReceiver = true; + mContentCaptureManagerService = new TestContentCaptureManagerService(); + + boolean actual = + mContentCaptureManagerService.mGlobalContentCaptureOptions.isWhitelisted( + USER_ID, PACKAGE_NAME); + + assertThat(actual).isTrue(); + } + + @Test + public void isWhitelisted_packageName_contentCaptureEnabled_contentProtectionNotChecked() { + mDevCfgEnableContentProtectionReceiver = true; + mContentCaptureManagerService = new TestContentCaptureManagerService(); + mContentCaptureManagerService.mGlobalContentCaptureOptions.setWhitelist( + USER_ID, ImmutableList.of(PACKAGE_NAME), /* components= */ null); + + boolean actual = + mContentCaptureManagerService.mGlobalContentCaptureOptions.isWhitelisted( + USER_ID, PACKAGE_NAME); + + assertThat(actual).isTrue(); + verify(mMockContentProtectionBlocklistManager, never()).isAllowed(anyString()); + } + + @Test + public void isWhitelisted_componentName_contentCaptureDisabled_contentProtectionDisabled() { + mDevCfgEnableContentProtectionReceiver = true; + mContentCaptureManagerService = new TestContentCaptureManagerService(); + + boolean actual = + mContentCaptureManagerService.mGlobalContentCaptureOptions.isWhitelisted( + USER_ID, COMPONENT_NAME); + + assertThat(actual).isFalse(); + verify(mMockContentProtectionBlocklistManager).isAllowed(PACKAGE_NAME); + } + + @Test + public void isWhitelisted_componentName_contentCaptureDisabled_contentProtectionEnabled() { + when(mMockContentProtectionBlocklistManager.isAllowed(PACKAGE_NAME)).thenReturn(true); + mDevCfgEnableContentProtectionReceiver = true; + mContentCaptureManagerService = new TestContentCaptureManagerService(); + + boolean actual = + mContentCaptureManagerService.mGlobalContentCaptureOptions.isWhitelisted( + USER_ID, COMPONENT_NAME); + + assertThat(actual).isTrue(); + } + + @Test + public void isWhitelisted_componentName_contentCaptureEnabled_contentProtectionNotChecked() { + mDevCfgEnableContentProtectionReceiver = true; + mContentCaptureManagerService = new TestContentCaptureManagerService(); + mContentCaptureManagerService.mGlobalContentCaptureOptions.setWhitelist( + USER_ID, /* packageNames= */ null, ImmutableList.of(COMPONENT_NAME)); + + boolean actual = + mContentCaptureManagerService.mGlobalContentCaptureOptions.isWhitelisted( + USER_ID, COMPONENT_NAME); + + assertThat(actual).isTrue(); + verify(mMockContentProtectionBlocklistManager, never()).isAllowed(anyString()); + } + + @Test + public void isContentProtectionReceiverEnabled_withoutBlocklistManager() { + boolean actual = + mContentCaptureManagerService.mGlobalContentCaptureOptions.isWhitelisted( + USER_ID, PACKAGE_NAME); + + assertThat(actual).isFalse(); + verify(mMockContentProtectionBlocklistManager, never()).isAllowed(anyString()); + } + + @Test + public void isContentProtectionReceiverEnabled_disabledWithFlag() { + mDevCfgEnableContentProtectionReceiver = true; + mContentCaptureManagerService = new TestContentCaptureManagerService(); + mContentCaptureManagerService.mDevCfgEnableContentProtectionReceiver = false; + + boolean actual = + mContentCaptureManagerService.mGlobalContentCaptureOptions.isWhitelisted( + USER_ID, PACKAGE_NAME); + + assertThat(actual).isFalse(); + verify(mMockContentProtectionBlocklistManager, never()).isAllowed(anyString()); + } + + @Test + public void onLoginDetected_disabledAfterConstructor() { + mDevCfgEnableContentProtectionReceiver = true; + mContentCaptureManagerService = new TestContentCaptureManagerService(); + mContentCaptureManagerService.mDevCfgEnableContentProtectionReceiver = false; + + mContentCaptureManagerService + .getContentCaptureManagerServiceStub() + .onLoginDetected(PARCELED_EVENTS); + + assertThat(mContentProtectionServiceInfosCreated).isEqualTo(0); + assertThat(mRemoteContentProtectionServicesCreated).isEqualTo(0); + verifyZeroInteractions(mMockRemoteContentProtectionService); + } + + @Test + public void onLoginDetected_invalidPermissions() { + mDevCfgEnableContentProtectionReceiver = true; + mContentProtectionServiceInfoConstructorShouldThrow = true; + mContentCaptureManagerService = new TestContentCaptureManagerService(); + + mContentCaptureManagerService + .getContentCaptureManagerServiceStub() + .onLoginDetected(PARCELED_EVENTS); + + assertThat(mContentProtectionServiceInfosCreated).isEqualTo(1); + assertThat(mRemoteContentProtectionServicesCreated).isEqualTo(0); + verifyZeroInteractions(mMockRemoteContentProtectionService); + } + + @Test + public void onLoginDetected_enabled() { + mDevCfgEnableContentProtectionReceiver = true; + mContentCaptureManagerService = new TestContentCaptureManagerService(); + + mContentCaptureManagerService + .getContentCaptureManagerServiceStub() + .onLoginDetected(PARCELED_EVENTS); + + assertThat(mContentProtectionServiceInfosCreated).isEqualTo(1); + assertThat(mRemoteContentProtectionServicesCreated).isEqualTo(1); + verify(mMockRemoteContentProtectionService).onLoginDetected(PARCELED_EVENTS); + } + + private class TestContentCaptureManagerService extends ContentCaptureManagerService { + + TestContentCaptureManagerService() { + super(sContext); + this.mDevCfgEnableContentProtectionReceiver = + ContentCaptureManagerServiceTest.this.mDevCfgEnableContentProtectionReceiver; + } + + @Override + protected boolean getEnableContentProtectionReceiverLocked() { + return ContentCaptureManagerServiceTest.this.mDevCfgEnableContentProtectionReceiver; + } + + @Override + protected ContentProtectionBlocklistManager createContentProtectionBlocklistManager() { + mContentProtectionBlocklistManagersCreated++; + return mMockContentProtectionBlocklistManager; + } + + @Override + protected String getContentProtectionServiceFlatComponentName() { + return mConfigDefaultContentProtectionService; + } + + @Override + protected ContentCaptureServiceInfo createContentProtectionServiceInfo( + @NonNull ComponentName componentName) throws PackageManager.NameNotFoundException { + mContentProtectionServiceInfosCreated++; + if (mContentProtectionServiceInfoConstructorShouldThrow) { + throw new RuntimeException("TEST RUNTIME EXCEPTION"); + } + return mMockContentCaptureServiceInfo; + } + + @Override + protected RemoteContentProtectionService createRemoteContentProtectionService( + @NonNull ComponentName componentName) { + mRemoteContentProtectionServicesCreated++; + return mMockRemoteContentProtectionService; + } + } +} diff --git a/services/tests/servicestests/src/com/android/server/contentprotection/ContentProtectionBlocklistManagerTest.java b/services/tests/servicestests/src/com/android/server/contentprotection/ContentProtectionBlocklistManagerTest.java new file mode 100644 index 000000000000..ba9956a63dca --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/contentprotection/ContentProtectionBlocklistManagerTest.java @@ -0,0 +1,236 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.contentprotection; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; +import static org.mockito.Mockito.when; + +import android.annotation.NonNull; +import android.content.pm.PackageInfo; + +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.filters.SmallTest; + +import com.google.common.collect.ImmutableList; + +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.ArrayList; +import java.util.List; + +/** + * Test for {@link ContentProtectionBlocklistManager}. + * + * <p>Run with: {@code atest + * FrameworksServicesTests: + * com.android.server.contentprotection.ContentProtectionBlocklistManagerTest} + */ +@RunWith(AndroidJUnit4.class) +@SmallTest +public class ContentProtectionBlocklistManagerTest { + + private static final String FIRST_PACKAGE_NAME = "com.test.first.package.name"; + + private static final String SECOND_PACKAGE_NAME = "com.test.second.package.name"; + + private static final String UNLISTED_PACKAGE_NAME = "com.test.unlisted.package.name"; + + private static final String PACKAGE_NAME_BLOCKLIST_FILENAME = + "/product/etc/res/raw/content_protection/package_name_blocklist.txt"; + + private static final PackageInfo PACKAGE_INFO = new PackageInfo(); + + private static final List<String> LINES = + ImmutableList.of(FIRST_PACKAGE_NAME, SECOND_PACKAGE_NAME); + + @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule(); + + @Mock private ContentProtectionPackageManager mMockContentProtectionPackageManager; + + private final List<String> mReadRawFiles = new ArrayList<>(); + + private ContentProtectionBlocklistManager mContentProtectionBlocklistManager; + + @Before + public void setup() { + mContentProtectionBlocklistManager = new TestContentProtectionBlocklistManager(); + } + + @Test + public void isAllowed_blocklistNotLoaded() { + boolean actual = mContentProtectionBlocklistManager.isAllowed(FIRST_PACKAGE_NAME); + + assertThat(actual).isFalse(); + assertThat(mReadRawFiles).isEmpty(); + verifyZeroInteractions(mMockContentProtectionPackageManager); + } + + @Test + public void isAllowed_inBlocklist() { + mContentProtectionBlocklistManager.updateBlocklist(LINES.size()); + + boolean actual = mContentProtectionBlocklistManager.isAllowed(FIRST_PACKAGE_NAME); + + assertThat(actual).isFalse(); + verifyZeroInteractions(mMockContentProtectionPackageManager); + } + + @Test + public void isAllowed_packageInfoNotFound() { + mContentProtectionBlocklistManager.updateBlocklist(LINES.size()); + when(mMockContentProtectionPackageManager.getPackageInfo(UNLISTED_PACKAGE_NAME)) + .thenReturn(null); + + boolean actual = mContentProtectionBlocklistManager.isAllowed(UNLISTED_PACKAGE_NAME); + + assertThat(actual).isFalse(); + verify(mMockContentProtectionPackageManager, never()) + .hasRequestedInternetPermissions(any()); + verify(mMockContentProtectionPackageManager, never()).isSystemApp(any()); + verify(mMockContentProtectionPackageManager, never()).isUpdatedSystemApp(any()); + } + + @Test + public void isAllowed_notRequestedInternet() { + mContentProtectionBlocklistManager.updateBlocklist(LINES.size()); + when(mMockContentProtectionPackageManager.getPackageInfo(UNLISTED_PACKAGE_NAME)) + .thenReturn(PACKAGE_INFO); + when(mMockContentProtectionPackageManager.hasRequestedInternetPermissions(PACKAGE_INFO)) + .thenReturn(false); + + boolean actual = mContentProtectionBlocklistManager.isAllowed(UNLISTED_PACKAGE_NAME); + + assertThat(actual).isFalse(); + verify(mMockContentProtectionPackageManager, never()).isSystemApp(any()); + verify(mMockContentProtectionPackageManager, never()).isUpdatedSystemApp(any()); + } + + @Test + public void isAllowed_systemApp() { + mContentProtectionBlocklistManager.updateBlocklist(LINES.size()); + when(mMockContentProtectionPackageManager.getPackageInfo(UNLISTED_PACKAGE_NAME)) + .thenReturn(PACKAGE_INFO); + when(mMockContentProtectionPackageManager.hasRequestedInternetPermissions(PACKAGE_INFO)) + .thenReturn(true); + when(mMockContentProtectionPackageManager.isSystemApp(PACKAGE_INFO)).thenReturn(true); + + boolean actual = mContentProtectionBlocklistManager.isAllowed(UNLISTED_PACKAGE_NAME); + + assertThat(actual).isFalse(); + verify(mMockContentProtectionPackageManager, never()).isUpdatedSystemApp(any()); + } + + @Test + public void isAllowed_updatedSystemApp() { + mContentProtectionBlocklistManager.updateBlocklist(LINES.size()); + when(mMockContentProtectionPackageManager.getPackageInfo(UNLISTED_PACKAGE_NAME)) + .thenReturn(PACKAGE_INFO); + when(mMockContentProtectionPackageManager.hasRequestedInternetPermissions(PACKAGE_INFO)) + .thenReturn(true); + when(mMockContentProtectionPackageManager.isSystemApp(PACKAGE_INFO)).thenReturn(true); + when(mMockContentProtectionPackageManager.isUpdatedSystemApp(PACKAGE_INFO)) + .thenReturn(true); + + boolean actual = mContentProtectionBlocklistManager.isAllowed(UNLISTED_PACKAGE_NAME); + + assertThat(actual).isFalse(); + } + + @Test + public void isAllowed_allowed() { + mContentProtectionBlocklistManager.updateBlocklist(LINES.size()); + when(mMockContentProtectionPackageManager.getPackageInfo(UNLISTED_PACKAGE_NAME)) + .thenReturn(PACKAGE_INFO); + when(mMockContentProtectionPackageManager.hasRequestedInternetPermissions(PACKAGE_INFO)) + .thenReturn(true); + when(mMockContentProtectionPackageManager.isSystemApp(PACKAGE_INFO)).thenReturn(false); + when(mMockContentProtectionPackageManager.isUpdatedSystemApp(PACKAGE_INFO)) + .thenReturn(false); + + boolean actual = mContentProtectionBlocklistManager.isAllowed(UNLISTED_PACKAGE_NAME); + + assertThat(actual).isTrue(); + } + + @Test + public void updateBlocklist_negativeSize() { + mContentProtectionBlocklistManager.updateBlocklist(/* blocklistSize= */ -1); + assertThat(mReadRawFiles).isEmpty(); + + mContentProtectionBlocklistManager.isAllowed(FIRST_PACKAGE_NAME); + verify(mMockContentProtectionPackageManager).getPackageInfo(FIRST_PACKAGE_NAME); + } + + @Test + public void updateBlocklist_zeroSize() { + mContentProtectionBlocklistManager.updateBlocklist(/* blocklistSize= */ 0); + assertThat(mReadRawFiles).isEmpty(); + + mContentProtectionBlocklistManager.isAllowed(FIRST_PACKAGE_NAME); + verify(mMockContentProtectionPackageManager).getPackageInfo(FIRST_PACKAGE_NAME); + } + + @Test + public void updateBlocklist_positiveSize_belowTotal() { + mContentProtectionBlocklistManager.updateBlocklist(/* blocklistSize= */ 1); + assertThat(mReadRawFiles).containsExactly(PACKAGE_NAME_BLOCKLIST_FILENAME); + + mContentProtectionBlocklistManager.isAllowed(FIRST_PACKAGE_NAME); + mContentProtectionBlocklistManager.isAllowed(SECOND_PACKAGE_NAME); + + verify(mMockContentProtectionPackageManager, never()).getPackageInfo(FIRST_PACKAGE_NAME); + verify(mMockContentProtectionPackageManager).getPackageInfo(SECOND_PACKAGE_NAME); + } + + @Test + public void updateBlocklist_positiveSize_aboveTotal() { + mContentProtectionBlocklistManager.updateBlocklist(LINES.size() + 1); + assertThat(mReadRawFiles).containsExactly(PACKAGE_NAME_BLOCKLIST_FILENAME); + + mContentProtectionBlocklistManager.isAllowed(FIRST_PACKAGE_NAME); + mContentProtectionBlocklistManager.isAllowed(SECOND_PACKAGE_NAME); + + verify(mMockContentProtectionPackageManager, never()).getPackageInfo(FIRST_PACKAGE_NAME); + verify(mMockContentProtectionPackageManager, never()).getPackageInfo(SECOND_PACKAGE_NAME); + } + + private final class TestContentProtectionBlocklistManager + extends ContentProtectionBlocklistManager { + + TestContentProtectionBlocklistManager() { + super(mMockContentProtectionPackageManager); + } + + @Override + protected List<String> readLinesFromRawFile(@NonNull String filename) { + mReadRawFiles.add(filename); + return LINES; + } + } +} diff --git a/services/tests/servicestests/src/com/android/server/contentprotection/ContentProtectionPackageManagerTest.java b/services/tests/servicestests/src/com/android/server/contentprotection/ContentProtectionPackageManagerTest.java new file mode 100644 index 000000000000..7d45ea4ce39a --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/contentprotection/ContentProtectionPackageManagerTest.java @@ -0,0 +1,207 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.contentprotection; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.when; + +import android.Manifest.permission; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.content.pm.PackageManager.PackageInfoFlags; +import android.testing.TestableContext; + +import androidx.test.core.app.ApplicationProvider; +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.filters.SmallTest; + +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; + +/** + * Test for {@link ContentProtectionPackageManager}. + * + * <p>Run with: {@code atest + * FrameworksServicesTests:com.android.server.contentprotection.ContentProtectionPackageManagerTest} + */ +@RunWith(AndroidJUnit4.class) +@SmallTest +public class ContentProtectionPackageManagerTest { + private static final String PACKAGE_NAME = "PACKAGE_NAME"; + + private static final PackageInfo EMPTY_PACKAGE_INFO = new PackageInfo(); + + private static final PackageInfo SYSTEM_APP_PACKAGE_INFO = createSystemAppPackageInfo(); + + private static final PackageInfo UPDATED_SYSTEM_APP_PACKAGE_INFO = + createUpdatedSystemAppPackageInfo(); + + @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule(); + + @Rule + public final TestableContext mContext = + new TestableContext(ApplicationProvider.getApplicationContext()); + + @Mock private PackageManager mMockPackageManager; + + private ContentProtectionPackageManager mContentProtectionPackageManager; + + @Before + public void setup() { + mContext.setMockPackageManager(mMockPackageManager); + mContentProtectionPackageManager = new ContentProtectionPackageManager(mContext); + } + + @Test + public void getPackageInfo_found() throws Exception { + PackageInfo expected = createPackageInfo(/* flags= */ 0); + when(mMockPackageManager.getPackageInfo(eq(PACKAGE_NAME), any(PackageInfoFlags.class))) + .thenReturn(expected); + + PackageInfo actual = mContentProtectionPackageManager.getPackageInfo(PACKAGE_NAME); + + assertThat(actual).isEqualTo(expected); + } + + @Test + public void getPackageInfo_notFound() throws Exception { + when(mMockPackageManager.getPackageInfo(eq(PACKAGE_NAME), any(PackageInfoFlags.class))) + .thenThrow(new NameNotFoundException()); + + PackageInfo actual = mContentProtectionPackageManager.getPackageInfo(PACKAGE_NAME); + + assertThat(actual).isNull(); + } + + @Test + public void getPackageInfo_null() { + PackageInfo actual = mContentProtectionPackageManager.getPackageInfo(PACKAGE_NAME); + + assertThat(actual).isNull(); + } + + @Test + public void isSystemApp_true() { + boolean actual = mContentProtectionPackageManager.isSystemApp(SYSTEM_APP_PACKAGE_INFO); + + assertThat(actual).isTrue(); + } + + @Test + public void isSystemApp_false() { + boolean actual = + mContentProtectionPackageManager.isSystemApp(UPDATED_SYSTEM_APP_PACKAGE_INFO); + + assertThat(actual).isFalse(); + } + + @Test + public void isSystemApp_noApplicationInfo() { + boolean actual = mContentProtectionPackageManager.isSystemApp(EMPTY_PACKAGE_INFO); + + assertThat(actual).isFalse(); + } + + @Test + public void isUpdatedSystemApp_true() { + boolean actual = + mContentProtectionPackageManager.isUpdatedSystemApp( + UPDATED_SYSTEM_APP_PACKAGE_INFO); + + assertThat(actual).isTrue(); + } + + @Test + public void isUpdatedSystemApp_false() { + boolean actual = + mContentProtectionPackageManager.isUpdatedSystemApp(SYSTEM_APP_PACKAGE_INFO); + + assertThat(actual).isFalse(); + } + + @Test + public void isUpdatedSystemApp_noApplicationInfo() { + boolean actual = mContentProtectionPackageManager.isUpdatedSystemApp(EMPTY_PACKAGE_INFO); + + assertThat(actual).isFalse(); + } + + @Test + public void hasRequestedInternetPermissions_true() { + PackageInfo packageInfo = createPackageInfo(new String[] {permission.INTERNET}); + + boolean actual = + mContentProtectionPackageManager.hasRequestedInternetPermissions(packageInfo); + + assertThat(actual).isTrue(); + } + + @Test + public void hasRequestedInternetPermissions_false() { + PackageInfo packageInfo = createPackageInfo(new String[] {permission.ACCESS_FINE_LOCATION}); + + boolean actual = + mContentProtectionPackageManager.hasRequestedInternetPermissions(packageInfo); + + assertThat(actual).isFalse(); + } + + @Test + public void hasRequestedInternetPermissions_noRequestedPermissions() { + boolean actual = + mContentProtectionPackageManager.hasRequestedInternetPermissions( + EMPTY_PACKAGE_INFO); + + assertThat(actual).isFalse(); + } + + private static PackageInfo createSystemAppPackageInfo() { + return createPackageInfo(ApplicationInfo.FLAG_SYSTEM); + } + + private static PackageInfo createUpdatedSystemAppPackageInfo() { + return createPackageInfo(ApplicationInfo.FLAG_UPDATED_SYSTEM_APP); + } + + private static PackageInfo createPackageInfo(int flags) { + return createPackageInfo(flags, /* requestedPermissions= */ new String[0]); + } + + private static PackageInfo createPackageInfo(String[] requestedPermissions) { + return createPackageInfo(/* flags= */ 0, requestedPermissions); + } + + private static PackageInfo createPackageInfo(int flags, String[] requestedPermissions) { + PackageInfo packageInfo = new PackageInfo(); + packageInfo.packageName = PACKAGE_NAME; + packageInfo.applicationInfo = new ApplicationInfo(); + packageInfo.applicationInfo.packageName = PACKAGE_NAME; + packageInfo.applicationInfo.flags = flags; + packageInfo.requestedPermissions = requestedPermissions; + return packageInfo; + } +} diff --git a/services/tests/servicestests/src/com/android/server/contentprotection/RemoteContentProtectionServiceTest.java b/services/tests/servicestests/src/com/android/server/contentprotection/RemoteContentProtectionServiceTest.java new file mode 100644 index 000000000000..9135ef3a1286 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/contentprotection/RemoteContentProtectionServiceTest.java @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.contentprotection; + +import static com.google.common.truth.Truth.assertThat; + +import static org.junit.Assert.fail; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; + +import android.annotation.NonNull; +import android.content.ComponentName; +import android.content.Context; +import android.content.pm.ParceledListSlice; +import android.os.UserHandle; +import android.service.contentcapture.IContentProtectionService; +import android.view.contentcapture.ContentCaptureEvent; + +import androidx.test.core.app.ApplicationProvider; +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.filters.SmallTest; + +import com.android.internal.infra.AndroidFuture; +import com.android.internal.infra.ServiceConnector; + +import com.google.common.collect.ImmutableList; + +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; + +/** + * Test for {@link RemoteContentProtectionService}. + * + * <p>Run with: {@code atest + * FrameworksServicesTests:com.android.server.contentprotection.RemoteContentProtectionServiceTest} + */ +@RunWith(AndroidJUnit4.class) +@SmallTest +public class RemoteContentProtectionServiceTest { + + private final Context mContext = ApplicationProvider.getApplicationContext(); + + @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule(); + + @Mock private IContentProtectionService mMockContentProtectionService; + + private RemoteContentProtectionService mRemoteContentProtectionService; + + private int mConnectCallCount = 0; + + @Before + public void setup() { + ComponentName componentName = new ComponentName(mContext.getPackageName(), "TestClass"); + mRemoteContentProtectionService = + new TestRemoteContentProtectionService(mContext, componentName); + } + + @Test + public void doesNotAutoConnect() { + assertThat(mConnectCallCount).isEqualTo(0); + verifyZeroInteractions(mMockContentProtectionService); + } + + @Test + public void getAutoDisconnectTimeoutMs() { + long actual = mRemoteContentProtectionService.getAutoDisconnectTimeoutMs(); + + assertThat(actual).isEqualTo(3000L); + } + + @Test + public void onLoginDetected() throws Exception { + ContentCaptureEvent event = + new ContentCaptureEvent(/* sessionId= */ 1111, /* type= */ 2222); + ParceledListSlice<ContentCaptureEvent> events = + new ParceledListSlice<>(ImmutableList.of(event)); + + mRemoteContentProtectionService.onLoginDetected(events); + + verify(mMockContentProtectionService).onLoginDetected(events); + } + + private final class TestRemoteContentProtectionService extends RemoteContentProtectionService { + + TestRemoteContentProtectionService(Context context, ComponentName componentName) { + super(context, componentName, UserHandle.myUserId(), /* bindAllowInstant= */ false); + } + + @Override // from ServiceConnector + public synchronized AndroidFuture<IContentProtectionService> connect() { + mConnectCallCount++; + return AndroidFuture.completedFuture(mMockContentProtectionService); + } + + @Override // from ServiceConnector + public boolean run(@NonNull ServiceConnector.VoidJob<IContentProtectionService> job) { + try { + job.run(mMockContentProtectionService); + } catch (Exception ex) { + fail("Unexpected exception: " + ex); + } + return true; + } + } +} diff --git a/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java b/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java index 962e86776ea2..a6acd60f3bd7 100644 --- a/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java @@ -85,7 +85,7 @@ public class AutomaticBrightnessControllerTest { @Mock HysteresisLevels mAmbientBrightnessThresholdsIdle; @Mock HysteresisLevels mScreenBrightnessThresholdsIdle; @Mock Handler mNoOpHandler; - @Mock HighBrightnessModeController mHbmController; + @Mock BrightnessRangeController mBrightnessRangeController; @Mock BrightnessThrottler mBrightnessThrottler; @Before @@ -134,12 +134,15 @@ public class AutomaticBrightnessControllerTest { DARKENING_LIGHT_DEBOUNCE_CONFIG, RESET_AMBIENT_LUX_AFTER_WARMUP_CONFIG, mAmbientBrightnessThresholds, mScreenBrightnessThresholds, mAmbientBrightnessThresholdsIdle, mScreenBrightnessThresholdsIdle, - mContext, mHbmController, mBrightnessThrottler, mIdleBrightnessMappingStrategy, - AMBIENT_LIGHT_HORIZON_SHORT, AMBIENT_LIGHT_HORIZON_LONG, userLux, userBrightness + mContext, mBrightnessRangeController, mBrightnessThrottler, + mIdleBrightnessMappingStrategy, AMBIENT_LIGHT_HORIZON_SHORT, + AMBIENT_LIGHT_HORIZON_LONG, userLux, userBrightness ); - when(mHbmController.getCurrentBrightnessMax()).thenReturn(BRIGHTNESS_MAX_FLOAT); - when(mHbmController.getCurrentBrightnessMin()).thenReturn(BRIGHTNESS_MIN_FLOAT); + when(mBrightnessRangeController.getCurrentBrightnessMax()).thenReturn( + BRIGHTNESS_MAX_FLOAT); + when(mBrightnessRangeController.getCurrentBrightnessMin()).thenReturn( + BRIGHTNESS_MIN_FLOAT); // Disable brightness throttling by default. Individual tests can enable it as needed. when(mBrightnessThrottler.getBrightnessCap()).thenReturn(BRIGHTNESS_MAX_FLOAT); when(mBrightnessThrottler.isThrottled()).thenReturn(false); diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayDeviceConfigTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayDeviceConfigTest.java index 5837b21b89fd..708421d2a431 100644 --- a/services/tests/servicestests/src/com/android/server/display/DisplayDeviceConfigTest.java +++ b/services/tests/servicestests/src/com/android/server/display/DisplayDeviceConfigTest.java @@ -52,6 +52,7 @@ import java.nio.file.Path; import java.util.ArrayList; import java.util.HashMap; import java.util.List; +import java.util.Map; @SmallTest @RunWith(AndroidJUnit4.class) @@ -376,6 +377,116 @@ public final class DisplayDeviceConfigTest { assertEquals(90, testMap.get(Temperature.THROTTLING_EMERGENCY).max, SMALL_DELTA); } + @Test + public void testValidLuxThrottling() throws Exception { + setupDisplayDeviceConfigFromDisplayConfigFile(); + + Map<DisplayDeviceConfig.BrightnessLimitMapType, Map<Float, Float>> luxThrottlingData = + mDisplayDeviceConfig.getLuxThrottlingData(); + assertEquals(2, luxThrottlingData.size()); + + Map<Float, Float> adaptiveOnBrightnessPoints = luxThrottlingData.get( + DisplayDeviceConfig.BrightnessLimitMapType.ADAPTIVE); + assertEquals(2, adaptiveOnBrightnessPoints.size()); + assertEquals(0.3f, adaptiveOnBrightnessPoints.get(1000f), SMALL_DELTA); + assertEquals(0.5f, adaptiveOnBrightnessPoints.get(5000f), SMALL_DELTA); + + Map<Float, Float> adaptiveOffBrightnessPoints = luxThrottlingData.get( + DisplayDeviceConfig.BrightnessLimitMapType.DEFAULT); + assertEquals(2, adaptiveOffBrightnessPoints.size()); + assertEquals(0.35f, adaptiveOffBrightnessPoints.get(1500f), SMALL_DELTA); + assertEquals(0.55f, adaptiveOffBrightnessPoints.get(5500f), SMALL_DELTA); + } + + @Test + public void testInvalidLuxThrottling() throws Exception { + setupDisplayDeviceConfigFromDisplayConfigFile(getContent(getInvalidLuxThrottling())); + + Map<DisplayDeviceConfig.BrightnessLimitMapType, Map<Float, Float>> luxThrottlingData = + mDisplayDeviceConfig.getLuxThrottlingData(); + assertEquals(1, luxThrottlingData.size()); + + Map<Float, Float> adaptiveOnBrightnessPoints = luxThrottlingData.get( + DisplayDeviceConfig.BrightnessLimitMapType.ADAPTIVE); + assertEquals(1, adaptiveOnBrightnessPoints.size()); + assertEquals(0.3f, adaptiveOnBrightnessPoints.get(1000f), SMALL_DELTA); + } + + private String getValidLuxThrottling() { + return "<luxThrottling>\n" + + " <brightnessLimitMap>\n" + + " <type>adaptive</type>\n" + + " <map>\n" + + " <point>" + + " <first>1000</first>\n" + + " <second>0.3</second>\n" + + " </point>" + + " <point>" + + " <first>5000</first>\n" + + " <second>0.5</second>\n" + + " </point>" + + " </map>\n" + + " </brightnessLimitMap>\n" + + " <brightnessLimitMap>\n" + + " <type>default</type>\n" + + " <map>\n" + + " <point>" + + " <first>1500</first>\n" + + " <second>0.35</second>\n" + + " </point>" + + " <point>" + + " <first>5500</first>\n" + + " <second>0.55</second>\n" + + " </point>" + + " </map>\n" + + " </brightnessLimitMap>\n" + + "</luxThrottling>"; + } + + private String getInvalidLuxThrottling() { + return "<luxThrottling>\n" + + " <brightnessLimitMap>\n" + + " <type>adaptive</type>\n" + + " <map>\n" + + " <point>" + + " <first>1000</first>\n" + + " <second>0.3</second>\n" + + " </point>" + + " <point>" // second > hbm.transitionPoint, skipped + + " <first>1500</first>\n" + + " <second>0.9</second>\n" + + " </point>" + + " <point>" // same lux value, skipped + + " <first>1000</first>\n" + + " <second>0.5</second>\n" + + " </point>" + + " </map>\n" + + " </brightnessLimitMap>\n" + + " <brightnessLimitMap>\n" // Same type, skipped + + " <type>adaptive</type>\n" + + " <map>\n" + + " <point>" + + " <first>2000</first>\n" + + " <second>0.35</second>\n" + + " </point>" + + " <point>" + + " <first>6000</first>\n" + + " <second>0.55</second>\n" + + " </point>" + + " </map>\n" + + " </brightnessLimitMap>\n" + + " <brightnessLimitMap>\n" // Invalid points only, skipped + + " <type>default</type>\n" + + " <map>\n" + + " <point>" + + " <first>2500</first>\n" + + " <second>0.99</second>\n" + + " </point>" + + " </map>\n" + + " </brightnessLimitMap>\n" + + "</luxThrottling>"; + } + private String getRefreshThermalThrottlingMaps() { return "<refreshRateThrottlingMap>\n" + " <refreshRateThrottlingPoint>\n" @@ -405,6 +516,10 @@ public final class DisplayDeviceConfigTest { } private String getContent() { + return getContent(getValidLuxThrottling()); + } + + private String getContent(String brightnessCapConfig) { return "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n" + "<displayConfiguration>\n" + "<name>Example Display</name>" @@ -462,6 +577,7 @@ public final class DisplayDeviceConfigTest { + "</point>\n" + "</sdrHdrRatioMap>\n" + "</highBrightnessMode>\n" + + brightnessCapConfig + "<screenOffBrightnessSensor>\n" + "<type>sensor_12345</type>\n" + "<name>Sensor 12345</name>\n" @@ -731,8 +847,12 @@ public final class DisplayDeviceConfigTest { } private void setupDisplayDeviceConfigFromDisplayConfigFile() throws IOException { + setupDisplayDeviceConfigFromDisplayConfigFile(getContent()); + } + + private void setupDisplayDeviceConfigFromDisplayConfigFile(String content) throws IOException { Path tempFile = Files.createTempFile("display_config", ".tmp"); - Files.write(tempFile, getContent().getBytes(StandardCharsets.UTF_8)); + Files.write(tempFile, content.getBytes(StandardCharsets.UTF_8)); mDisplayDeviceConfig = new DisplayDeviceConfig(mContext); mDisplayDeviceConfig.initFromFile(tempFile.toFile()); } diff --git a/services/tests/servicestests/src/com/android/server/display/NormalBrightnessModeControllerTest.java b/services/tests/servicestests/src/com/android/server/display/NormalBrightnessModeControllerTest.java new file mode 100644 index 000000000000..c379d6b79ee7 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/display/NormalBrightnessModeControllerTest.java @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.display; + +import static org.junit.Assert.assertEquals; + +import android.os.PowerManager; + +import androidx.test.filters.SmallTest; + +import com.android.internal.annotations.Keep; +import com.android.server.display.DisplayDeviceConfig.BrightnessLimitMapType; + +import com.google.common.collect.ImmutableMap; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.HashMap; +import java.util.Map; + +import junitparams.JUnitParamsRunner; +import junitparams.Parameters; + +@SmallTest +@RunWith(JUnitParamsRunner.class) +public class NormalBrightnessModeControllerTest { + private static final float FLOAT_TOLERANCE = 0.001f; + + private final NormalBrightnessModeController mController = new NormalBrightnessModeController(); + + @Keep + private static Object[][] brightnessData() { + return new Object[][]{ + // no brightness config + {0, AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED, new HashMap<>(), + PowerManager.BRIGHTNESS_MAX}, + {0, AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED, new HashMap<>(), + PowerManager.BRIGHTNESS_MAX}, + {100, AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED, new HashMap<>(), + PowerManager.BRIGHTNESS_MAX}, + // Auto brightness - on, config only for default + {100, AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED, ImmutableMap.of( + BrightnessLimitMapType.DEFAULT, + ImmutableMap.of(99f, 0.1f, 101f, 0.2f) + ), 0.2f}, + // Auto brightness - off, config only for default + {100, AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED, ImmutableMap.of( + BrightnessLimitMapType.DEFAULT, + ImmutableMap.of(99f, 0.1f, 101f, 0.2f) + ), 0.2f}, + // Auto brightness - off, config only for adaptive + {100, AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED, ImmutableMap.of( + BrightnessLimitMapType.ADAPTIVE, + ImmutableMap.of(99f, 0.1f, 101f, 0.2f) + ), PowerManager.BRIGHTNESS_MAX}, + // Auto brightness - on, config only for adaptive + {100, AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED, ImmutableMap.of( + BrightnessLimitMapType.ADAPTIVE, + ImmutableMap.of(99f, 0.1f, 101f, 0.2f) + ), 0.2f}, + // Auto brightness - on, config for both + {100, AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED, ImmutableMap.of( + BrightnessLimitMapType.DEFAULT, + ImmutableMap.of(99f, 0.1f, 101f, 0.2f), + BrightnessLimitMapType.ADAPTIVE, + ImmutableMap.of(99f, 0.3f, 101f, 0.4f) + ), 0.4f}, + // Auto brightness - off, config for both + {100, AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED, ImmutableMap.of( + BrightnessLimitMapType.DEFAULT, + ImmutableMap.of(99f, 0.1f, 101f, 0.2f), + BrightnessLimitMapType.ADAPTIVE, + ImmutableMap.of(99f, 0.3f, 101f, 0.4f) + ), 0.2f}, + // Auto brightness - on, config for both, ambient high + {1000, AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED, ImmutableMap.of( + BrightnessLimitMapType.DEFAULT, + ImmutableMap.of(1000f, 0.1f, 2000f, 0.2f), + BrightnessLimitMapType.ADAPTIVE, + ImmutableMap.of(99f, 0.3f, 101f, 0.4f) + ), PowerManager.BRIGHTNESS_MAX}, + }; + } + + @Test + @Parameters(method = "brightnessData") + public void testReturnsCorrectMaxBrightness(float ambientLux, int autoBrightnessState, + Map<BrightnessLimitMapType, Map<Float, Float>> maxBrightnessConfig, + float expectedBrightness) { + setupController(ambientLux, autoBrightnessState, maxBrightnessConfig); + + assertEquals(expectedBrightness, mController.getCurrentBrightnessMax(), FLOAT_TOLERANCE); + } + + private void setupController(float ambientLux, int autoBrightnessState, + Map<BrightnessLimitMapType, Map<Float, Float>> maxBrightnessConfig) { + mController.onAmbientLuxChange(ambientLux); + mController.setAutoBrightnessState(autoBrightnessState); + mController.resetNbmData(maxBrightnessConfig); + } +} diff --git a/services/tests/servicestests/src/com/android/server/display/PersistentDataStoreTest.java b/services/tests/servicestests/src/com/android/server/display/PersistentDataStoreTest.java index 817b245a78bf..642f54c25a46 100644 --- a/services/tests/servicestests/src/com/android/server/display/PersistentDataStoreTest.java +++ b/services/tests/servicestests/src/com/android/server/display/PersistentDataStoreTest.java @@ -60,6 +60,114 @@ public class PersistentDataStoreTest { } @Test + public void testLoadBrightness() { + final String uniqueDisplayId = "test:123"; + final DisplayDevice testDisplayDevice = new DisplayDevice( + null, null, uniqueDisplayId, null) { + @Override + public boolean hasStableUniqueId() { + return true; + } + + @Override + public DisplayDeviceInfo getDisplayDeviceInfoLocked() { + return null; + } + }; + + String contents = "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n" + + "<display-manager-state>\n" + + " <display-states>\n" + + " <display unique-id=\"test:123\">\n" + + " <brightness-value user-serial=\"1\">0.1</brightness-value>\n" + + " <brightness-value user-serial=\"2\">0.2</brightness-value>\n" + + " </display>\n" + + " </display-states>\n" + + "</display-manager-state>\n"; + + InputStream is = new ByteArrayInputStream(contents.getBytes(StandardCharsets.UTF_8)); + mInjector.setReadStream(is); + mDataStore.loadIfNeeded(); + + float brightness = mDataStore.getBrightness(testDisplayDevice, 1); + assertEquals(0.1, brightness, 0.01); + + brightness = mDataStore.getBrightness(testDisplayDevice, 2); + assertEquals(0.2, brightness, 0.01); + } + + @Test + public void testSetBrightness_brightnessTagWithNoUserId_updatesToBrightnessTagWithUserId() { + final String uniqueDisplayId = "test:123"; + final DisplayDevice testDisplayDevice = + new DisplayDevice(null, null, uniqueDisplayId, null) { + @Override + public boolean hasStableUniqueId() { + return true; + } + + @Override + public DisplayDeviceInfo getDisplayDeviceInfoLocked() { + return null; + } + }; + + String contents = "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n" + + "<display-manager-state>\n" + + " <display-states>\n" + + " <color-mode>0</color-mode>\n" + + " <display unique-id=\"test:123\">\n" + + " <brightness-value>0.5</brightness-value>\n" + + " </display>\n" + + " </display-states>\n" + + "</display-manager-state>\n"; + + InputStream is = new ByteArrayInputStream(contents.getBytes(StandardCharsets.UTF_8)); + mInjector.setReadStream(is); + mDataStore.loadIfNeeded(); + + float user1Brightness = mDataStore.getBrightness(testDisplayDevice, 1 /* userSerial */); + float user2Brightness = mDataStore.getBrightness(testDisplayDevice, 2 /* userSerial */); + assertEquals(0.5, user1Brightness, 0.01); + assertEquals(0.5, user2Brightness, 0.01); + + // Override the value for user 2. Default user must have been removed. + mDataStore.setBrightness(testDisplayDevice, 0.2f, 2 /* userSerial */ /* brightness*/); + + user1Brightness = mDataStore.getBrightness(testDisplayDevice, 1 /* userSerial */); + user2Brightness = mDataStore.getBrightness(testDisplayDevice, 2 /* userSerial */); + assertTrue(Float.isNaN(user1Brightness)); + assertEquals(0.2f, user2Brightness, 0.01); + + // Override the value for user 1. User-specific brightness values should co-exist. + mDataStore.setBrightness(testDisplayDevice, 0.1f, 1 /* userSerial */ /* brightness*/); + user1Brightness = mDataStore.getBrightness(testDisplayDevice, 1 /* userSerial */); + user2Brightness = mDataStore.getBrightness(testDisplayDevice, 2 /* userSerial */); + assertEquals(0.1f, user1Brightness, 0.01); + assertEquals(0.2f, user2Brightness, 0.01); + + // Validate saveIfNeeded writes user-specific brightnes. + final ByteArrayOutputStream baos = new ByteArrayOutputStream(); + mInjector.setWriteStream(baos); + mDataStore.saveIfNeeded(); + mTestLooper.dispatchAll(); + assertTrue(mInjector.wasWriteSuccessful()); + TestInjector newInjector = new TestInjector(); + PersistentDataStore newDataStore = new PersistentDataStore(newInjector); + ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); + newInjector.setReadStream(bais); + newDataStore.loadIfNeeded(); + + user1Brightness = newDataStore.getBrightness(testDisplayDevice, 1 /* userSerial */); + user2Brightness = newDataStore.getBrightness(testDisplayDevice, 2 /* userSerial */); + float unknownUserBrightness = + newDataStore.getBrightness(testDisplayDevice, 999 /* userSerial */); + assertEquals(0.1f, user1Brightness, 0.01); + assertEquals(0.2f, user2Brightness, 0.01); + assertTrue(Float.isNaN(unknownUserBrightness)); + } + + @Test public void testLoadingBrightnessConfigurations() { String contents = "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n" + "<display-manager-state>\n" @@ -374,7 +482,7 @@ public class PersistentDataStoreTest { ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); newInjector.setReadStream(bais); newDataStore.loadIfNeeded(); - assertTrue(Float.isNaN(mDataStore.getBrightness(testDisplayDevice))); + assertTrue(Float.isNaN(mDataStore.getBrightness(testDisplayDevice, 1 /* userSerial */))); } @Test diff --git a/services/tests/servicestests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java b/services/tests/servicestests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java index e6d3bbc53c83..c4f483810478 100644 --- a/services/tests/servicestests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java @@ -19,6 +19,7 @@ package com.android.server.display.brightness; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; @@ -247,6 +248,7 @@ public final class DisplayBrightnessControllerTest { 0.0f); verify(mBrightnessChangeExecutor).execute(mOnBrightnessChangeRunnable); verify(mBrightnessSetting).setBrightness(brightnessValue); + verify(mBrightnessSetting).setUserSerial(anyInt()); // Does nothing if the value is invalid mDisplayBrightnessController.updateScreenBrightnessSetting(Float.NaN); @@ -358,4 +360,28 @@ public final class DisplayBrightnessControllerTest { verify(mBrightnessSetting, never()).getBrightnessNitsForDefaultDisplay(); verify(mBrightnessSetting, never()).setBrightness(brightness); } + + @Test + public void testChangeBrightnessNitsWhenUserChanges() { + float brightnessValue1 = 0.3f; + float nits1 = 200f; + float brightnessValue2 = 0.5f; + float nits2 = 300f; + AutomaticBrightnessController automaticBrightnessController = + mock(AutomaticBrightnessController.class); + when(automaticBrightnessController.convertToNits(brightnessValue1)).thenReturn(nits1); + when(automaticBrightnessController.convertToNits(brightnessValue2)).thenReturn(nits2); + mDisplayBrightnessController.setAutomaticBrightnessController( + automaticBrightnessController); + + mDisplayBrightnessController.setBrightness(brightnessValue1, 1 /* user-serial */); + verify(mBrightnessSetting).setUserSerial(1); + verify(mBrightnessSetting).setBrightness(brightnessValue1); + verify(mBrightnessSetting).setBrightnessNitsForDefaultDisplay(nits1); + + mDisplayBrightnessController.setBrightness(brightnessValue2, 2 /* user-serial */); + verify(mBrightnessSetting).setUserSerial(2); + verify(mBrightnessSetting).setBrightness(brightnessValue2); + verify(mBrightnessSetting).setBrightnessNitsForDefaultDisplay(nits2); + } } diff --git a/services/tests/servicestests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java b/services/tests/servicestests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java index d9cf15b35c2b..b652576a75c8 100644 --- a/services/tests/servicestests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java +++ b/services/tests/servicestests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java @@ -96,32 +96,101 @@ public class AutomaticBrightnessStrategyTest { } @Test - public void setAutoBrightnessWhenDisabled() { + public void testAutoBrightnessState_AutoBrightnessDisabled() { mAutomaticBrightnessStrategy.setUseAutoBrightness(false); int targetDisplayState = Display.STATE_ON; boolean allowAutoBrightnessWhileDozing = false; - float brightnessState = Float.NaN; + int brightnessReason = BrightnessReason.REASON_UNKNOWN; + int policy = DisplayManagerInternal.DisplayPowerRequest.POLICY_BRIGHT; + float lastUserSetBrightness = 0.2f; + boolean userSetBrightnessChanged = true; + mAutomaticBrightnessStrategy.updatePendingAutoBrightnessAdjustments(true); + mAutomaticBrightnessStrategy.setAutoBrightnessState(targetDisplayState, + allowAutoBrightnessWhileDozing, brightnessReason, policy, lastUserSetBrightness, + userSetBrightnessChanged); + verify(mAutomaticBrightnessController) + .configure(AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED, + mBrightnessConfiguration, + lastUserSetBrightness, + userSetBrightnessChanged, 0.5f, + false, policy, true); + assertFalse(mAutomaticBrightnessStrategy.isAutoBrightnessEnabled()); + assertFalse(mAutomaticBrightnessStrategy.isAutoBrightnessDisabledDueToDisplayOff()); + } + + @Test + public void testAutoBrightnessState_DisplayIsOff() { + mAutomaticBrightnessStrategy.setUseAutoBrightness(true); + int targetDisplayState = Display.STATE_OFF; + boolean allowAutoBrightnessWhileDozing = false; + int brightnessReason = BrightnessReason.REASON_UNKNOWN; + int policy = DisplayManagerInternal.DisplayPowerRequest.POLICY_OFF; + float lastUserSetBrightness = 0.2f; + boolean userSetBrightnessChanged = true; + mAutomaticBrightnessStrategy.updatePendingAutoBrightnessAdjustments(true); + mAutomaticBrightnessStrategy.setAutoBrightnessState(targetDisplayState, + allowAutoBrightnessWhileDozing, brightnessReason, policy, lastUserSetBrightness, + userSetBrightnessChanged); + verify(mAutomaticBrightnessController) + .configure(AutomaticBrightnessController.AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE, + mBrightnessConfiguration, + lastUserSetBrightness, + userSetBrightnessChanged, 0.5f, + false, policy, true); + assertFalse(mAutomaticBrightnessStrategy.isAutoBrightnessEnabled()); + assertTrue(mAutomaticBrightnessStrategy.isAutoBrightnessDisabledDueToDisplayOff()); + } + + @Test + public void testAutoBrightnessState_DisplayIsInDoze_ConfigDoesNotAllow() { + mAutomaticBrightnessStrategy.setUseAutoBrightness(true); + int targetDisplayState = Display.STATE_DOZE; + boolean allowAutoBrightnessWhileDozing = false; + int brightnessReason = BrightnessReason.REASON_UNKNOWN; + int policy = DisplayManagerInternal.DisplayPowerRequest.POLICY_DOZE; + float lastUserSetBrightness = 0.2f; + boolean userSetBrightnessChanged = true; + mAutomaticBrightnessStrategy.updatePendingAutoBrightnessAdjustments(true); + mAutomaticBrightnessStrategy.setAutoBrightnessState(targetDisplayState, + allowAutoBrightnessWhileDozing, brightnessReason, policy, lastUserSetBrightness, + userSetBrightnessChanged); + verify(mAutomaticBrightnessController) + .configure(AutomaticBrightnessController.AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE, + mBrightnessConfiguration, + lastUserSetBrightness, + userSetBrightnessChanged, 0.5f, + false, policy, true); + assertFalse(mAutomaticBrightnessStrategy.isAutoBrightnessEnabled()); + assertTrue(mAutomaticBrightnessStrategy.isAutoBrightnessDisabledDueToDisplayOff()); + } + + @Test + public void testAutoBrightnessState_BrightnessReasonIsOverride() { + mAutomaticBrightnessStrategy.setUseAutoBrightness(true); + int targetDisplayState = Display.STATE_ON; + boolean allowAutoBrightnessWhileDozing = false; int brightnessReason = BrightnessReason.REASON_OVERRIDE; int policy = DisplayManagerInternal.DisplayPowerRequest.POLICY_BRIGHT; float lastUserSetBrightness = 0.2f; boolean userSetBrightnessChanged = true; mAutomaticBrightnessStrategy.updatePendingAutoBrightnessAdjustments(true); mAutomaticBrightnessStrategy.setAutoBrightnessState(targetDisplayState, - allowAutoBrightnessWhileDozing, brightnessState, brightnessReason, policy, - lastUserSetBrightness, userSetBrightnessChanged); + allowAutoBrightnessWhileDozing, brightnessReason, policy, lastUserSetBrightness, + userSetBrightnessChanged); verify(mAutomaticBrightnessController) .configure(AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED, mBrightnessConfiguration, lastUserSetBrightness, userSetBrightnessChanged, 0.5f, false, policy, true); + assertFalse(mAutomaticBrightnessStrategy.isAutoBrightnessEnabled()); + assertFalse(mAutomaticBrightnessStrategy.isAutoBrightnessDisabledDueToDisplayOff()); } @Test - public void setAutoBrightnessWhenEnabledAndDisplayIsDozing() { + public void testAutoBrightnessState_DisplayIsInDoze_ConfigDoesAllow() { mAutomaticBrightnessStrategy.setUseAutoBrightness(true); int targetDisplayState = Display.STATE_DOZE; - float brightnessState = Float.NaN; boolean allowAutoBrightnessWhileDozing = true; int brightnessReason = BrightnessReason.REASON_DOZE; int policy = DisplayManagerInternal.DisplayPowerRequest.POLICY_DOZE; @@ -131,23 +200,24 @@ public class AutomaticBrightnessStrategyTest { Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ, 0.4f); mAutomaticBrightnessStrategy.updatePendingAutoBrightnessAdjustments(false); mAutomaticBrightnessStrategy.setAutoBrightnessState(targetDisplayState, - allowAutoBrightnessWhileDozing, brightnessState, brightnessReason, policy, - lastUserSetBrightness, userSetBrightnessChanged); + allowAutoBrightnessWhileDozing, brightnessReason, policy, lastUserSetBrightness, + userSetBrightnessChanged); verify(mAutomaticBrightnessController) .configure(AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED, mBrightnessConfiguration, lastUserSetBrightness, userSetBrightnessChanged, 0.4f, true, policy, true); + assertTrue(mAutomaticBrightnessStrategy.isAutoBrightnessEnabled()); + assertFalse(mAutomaticBrightnessStrategy.isAutoBrightnessDisabledDueToDisplayOff()); } @Test - public void setAutoBrightnessWhenEnabledAndDisplayIsOn() { + public void testAutoBrightnessState_DisplayIsOn() { mAutomaticBrightnessStrategy.setUseAutoBrightness(true); int targetDisplayState = Display.STATE_ON; - float brightnessState = Float.NaN; boolean allowAutoBrightnessWhileDozing = false; - int brightnessReason = BrightnessReason.REASON_OVERRIDE; + int brightnessReason = BrightnessReason.REASON_UNKNOWN; float lastUserSetBrightness = 0.2f; boolean userSetBrightnessChanged = true; int policy = DisplayManagerInternal.DisplayPowerRequest.POLICY_BRIGHT; @@ -156,14 +226,16 @@ public class AutomaticBrightnessStrategyTest { Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ, pendingBrightnessAdjustment); mAutomaticBrightnessStrategy.updatePendingAutoBrightnessAdjustments(false); mAutomaticBrightnessStrategy.setAutoBrightnessState(targetDisplayState, - allowAutoBrightnessWhileDozing, brightnessState, brightnessReason, policy, - lastUserSetBrightness, userSetBrightnessChanged); + allowAutoBrightnessWhileDozing, brightnessReason, policy, lastUserSetBrightness, + userSetBrightnessChanged); verify(mAutomaticBrightnessController) .configure(AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED, mBrightnessConfiguration, lastUserSetBrightness, userSetBrightnessChanged, pendingBrightnessAdjustment, true, policy, true); + assertTrue(mAutomaticBrightnessStrategy.isAutoBrightnessEnabled()); + assertFalse(mAutomaticBrightnessStrategy.isAutoBrightnessDisabledDueToDisplayOff()); } @Test @@ -210,7 +282,6 @@ public class AutomaticBrightnessStrategyTest { when(mAutomaticBrightnessController.getAutomaticScreenBrightnessAdjustment()).thenReturn( autoBrightnessAdjustment); mAutomaticBrightnessStrategy.adjustAutomaticBrightnessStateIfValid(brightnessState); - assertTrue(mAutomaticBrightnessStrategy.hasAppliedAutoBrightness()); assertEquals(autoBrightnessAdjustment, mAutomaticBrightnessStrategy.getAutoBrightnessAdjustment(), 0.0f); assertEquals(autoBrightnessAdjustment, Settings.System.getFloatForUser( @@ -222,7 +293,6 @@ public class AutomaticBrightnessStrategyTest { float invalidBrightness = -0.5f; mAutomaticBrightnessStrategy .adjustAutomaticBrightnessStateIfValid(invalidBrightness); - assertFalse(mAutomaticBrightnessStrategy.hasAppliedAutoBrightness()); assertEquals(autoBrightnessAdjustment, mAutomaticBrightnessStrategy.getAutoBrightnessAdjustment(), 0.0f); assertEquals(0, diff --git a/services/tests/servicestests/src/com/android/server/input/AmbientKeyboardBacklightControllerTests.kt b/services/tests/servicestests/src/com/android/server/input/AmbientKeyboardBacklightControllerTests.kt new file mode 100644 index 000000000000..98b46286e977 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/input/AmbientKeyboardBacklightControllerTests.kt @@ -0,0 +1,355 @@ +/* + * Copyright 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.input + +import android.content.Context +import android.content.ContextWrapper +import android.content.res.Resources +import android.hardware.Sensor +import android.hardware.SensorEvent +import android.hardware.SensorEventListener +import android.hardware.SensorManager +import android.hardware.display.DisplayManagerInternal +import android.hardware.input.InputSensorInfo +import android.os.Handler +import android.os.test.TestLooper +import android.platform.test.annotations.Presubmit +import android.util.TypedValue +import android.view.Display +import android.view.DisplayInfo +import androidx.test.core.app.ApplicationProvider +import com.android.internal.R +import com.android.server.LocalServices +import com.android.server.input.AmbientKeyboardBacklightController.HYSTERESIS_THRESHOLD +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Assert.assertThrows +import org.junit.Assert.assertTrue +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.mockito.Mock +import org.mockito.Mockito.any +import org.mockito.Mockito.anyBoolean +import org.mockito.Mockito.anyInt +import org.mockito.Mockito.eq +import org.mockito.Mockito.spy +import org.mockito.Mockito.`when` +import org.mockito.junit.MockitoJUnit + +/** + * Tests for {@link AmbientKeyboardBacklightController}. + * + * Build/Install/Run: + * atest FrameworksServicesTests:AmbientKeyboardBacklightControllerTests + */ +@Presubmit +class AmbientKeyboardBacklightControllerTests { + + companion object { + const val DEFAULT_DISPLAY_UNIQUE_ID = "uniqueId_1" + const val SENSOR_NAME = "test_sensor_name" + const val SENSOR_TYPE = "test_sensor_type" + } + + @get:Rule + val rule = MockitoJUnit.rule()!! + + private lateinit var context: Context + private lateinit var testLooper: TestLooper + private lateinit var ambientController: AmbientKeyboardBacklightController + + @Mock + private lateinit var resources: Resources + + @Mock + private lateinit var lightSensorInfo: InputSensorInfo + + @Mock + private lateinit var sensorManager: SensorManager + + @Mock + private lateinit var displayManagerInternal: DisplayManagerInternal + private lateinit var lightSensor: Sensor + + private var currentDisplayInfo = DisplayInfo() + private var lastBrightnessCallback: Int = 0 + private var listenerRegistered: Boolean = false + private var listenerRegistrationCount: Int = 0 + + @Before + fun setup() { + context = spy(ContextWrapper(ApplicationProvider.getApplicationContext())) + `when`(context.resources).thenReturn(resources) + setupBrightnessSteps() + setupSensor() + testLooper = TestLooper() + ambientController = AmbientKeyboardBacklightController(context, testLooper.looper) + ambientController.systemRunning() + testLooper.dispatchAll() + } + + private fun setupBrightnessSteps() { + val brightnessValues = intArrayOf(100, 200, 0) + val decreaseThresholds = intArrayOf(-1, 900, 1900) + val increaseThresholds = intArrayOf(1000, 2000, -1) + `when`(resources.getIntArray(R.array.config_autoKeyboardBacklightBrightnessValues)) + .thenReturn(brightnessValues) + `when`(resources.getIntArray(R.array.config_autoKeyboardBacklightDecreaseLuxThreshold)) + .thenReturn(decreaseThresholds) + `when`(resources.getIntArray(R.array.config_autoKeyboardBacklightIncreaseLuxThreshold)) + .thenReturn(increaseThresholds) + `when`( + resources.getValue( + eq(R.dimen.config_autoKeyboardBrightnessSmoothingConstant), + any(TypedValue::class.java), + anyBoolean() + ) + ).then { + val args = it.arguments + val outValue = args[1] as TypedValue + outValue.data = java.lang.Float.floatToRawIntBits(1.0f) + Unit + } + } + + private fun setupSensor() { + LocalServices.removeServiceForTest(DisplayManagerInternal::class.java) + LocalServices.addService(DisplayManagerInternal::class.java, displayManagerInternal) + currentDisplayInfo.uniqueId = DEFAULT_DISPLAY_UNIQUE_ID + `when`(displayManagerInternal.getDisplayInfo(Display.DEFAULT_DISPLAY)).thenReturn( + currentDisplayInfo + ) + val sensorData = DisplayManagerInternal.AmbientLightSensorData(SENSOR_NAME, SENSOR_TYPE) + `when`(displayManagerInternal.getAmbientLightSensorData(Display.DEFAULT_DISPLAY)) + .thenReturn(sensorData) + + `when`(lightSensorInfo.name).thenReturn(SENSOR_NAME) + `when`(lightSensorInfo.stringType).thenReturn(SENSOR_TYPE) + lightSensor = Sensor(lightSensorInfo) + `when`(context.getSystemService(eq(Context.SENSOR_SERVICE))).thenReturn(sensorManager) + `when`(sensorManager.getSensorList(anyInt())).thenReturn(listOf(lightSensor)) + `when`( + sensorManager.registerListener( + any(), + eq(lightSensor), + anyInt(), + any(Handler::class.java) + ) + ).then { + listenerRegistered = true + listenerRegistrationCount++ + true + } + `when`( + sensorManager.unregisterListener( + any(SensorEventListener::class.java), + eq(lightSensor) + ) + ).then { + listenerRegistered = false + Unit + } + } + + private fun setupSensorWithInitialLux(luxValue: Float) { + ambientController.registerAmbientBacklightListener { brightnessValue: Int -> + lastBrightnessCallback = brightnessValue + } + sendAmbientLuxValue(luxValue) + testLooper.dispatchAll() + } + + @Test + fun testInitialAmbientLux_sendsCallbackImmediately() { + setupSensorWithInitialLux(500F) + + assertEquals( + "Should receive immediate callback for first lux change", + 100, + lastBrightnessCallback + ) + } + + @Test + fun testBrightnessIncrease_afterInitialLuxChanges() { + setupSensorWithInitialLux(500F) + + // Current state: Step 1 [value = 100, increaseThreshold = 1000, decreaseThreshold = -1] + repeat(HYSTERESIS_THRESHOLD) { + sendAmbientLuxValue(1500F) + } + testLooper.dispatchAll() + + assertEquals( + "Should receive brightness change callback for increasing lux change", + 200, + lastBrightnessCallback + ) + } + + @Test + fun testBrightnessDecrease_afterInitialLuxChanges() { + setupSensorWithInitialLux(1500F) + + // Current state: Step 2 [value = 200, increaseThreshold = 2000, decreaseThreshold = 900] + repeat(HYSTERESIS_THRESHOLD) { + sendAmbientLuxValue(500F) + } + testLooper.dispatchAll() + + assertEquals( + "Should receive brightness change callback for decreasing lux change", + 100, + lastBrightnessCallback + ) + } + + @Test + fun testRegisterAmbientListener_throwsExceptionOnRegisteringDuplicate() { + val ambientListener = + AmbientKeyboardBacklightController.AmbientKeyboardBacklightListener { } + ambientController.registerAmbientBacklightListener(ambientListener) + + assertThrows(IllegalStateException::class.java) { + ambientController.registerAmbientBacklightListener( + ambientListener + ) + } + } + + @Test + fun testUnregisterAmbientListener_throwsExceptionOnUnregisteringNonExistent() { + val ambientListener = + AmbientKeyboardBacklightController.AmbientKeyboardBacklightListener { } + assertThrows(IllegalStateException::class.java) { + ambientController.unregisterAmbientBacklightListener( + ambientListener + ) + } + } + + @Test + fun testSensorListenerRegistered_onRegisterUnregisterAmbientListener() { + assertEquals( + "Should not have a sensor listener registered at init", + 0, + listenerRegistrationCount + ) + assertFalse("Should not have a sensor listener registered at init", listenerRegistered) + + val ambientListener1 = + AmbientKeyboardBacklightController.AmbientKeyboardBacklightListener { } + ambientController.registerAmbientBacklightListener(ambientListener1) + assertEquals( + "Should register a new sensor listener", 1, listenerRegistrationCount + ) + assertTrue("Should have sensor listener registered", listenerRegistered) + + val ambientListener2 = + AmbientKeyboardBacklightController.AmbientKeyboardBacklightListener { } + ambientController.registerAmbientBacklightListener(ambientListener2) + assertEquals( + "Should not register a new sensor listener when adding a second ambient listener", + 1, + listenerRegistrationCount + ) + assertTrue("Should have sensor listener registered", listenerRegistered) + + ambientController.unregisterAmbientBacklightListener(ambientListener1) + assertTrue("Should have sensor listener registered", listenerRegistered) + + ambientController.unregisterAmbientBacklightListener(ambientListener2) + assertFalse( + "Should not have sensor listener registered if there are no ambient listeners", + listenerRegistered + ) + } + + @Test + fun testDisplayChange_shouldNotReRegisterListener_ifUniqueIdSame() { + setupSensorWithInitialLux(0F) + + val count = listenerRegistrationCount + ambientController.onDisplayChanged(Display.DEFAULT_DISPLAY) + testLooper.dispatchAll() + + assertEquals( + "Should not re-register listener on display change if unique is same", + count, + listenerRegistrationCount + ) + } + + @Test + fun testDisplayChange_shouldReRegisterListener_ifUniqueIdChanges() { + setupSensorWithInitialLux(0F) + + val count = listenerRegistrationCount + currentDisplayInfo.uniqueId = "xyz" + ambientController.onDisplayChanged(Display.DEFAULT_DISPLAY) + testLooper.dispatchAll() + + assertEquals( + "Should re-register listener on display change if unique id changed", + count + 1, + listenerRegistrationCount + ) + } + + @Test + fun testBrightnessDoesntChange_betweenIncreaseAndDecreaseThresholds() { + setupSensorWithInitialLux(1001F) + + // Previous state: Step 1 [value = 100, increaseThreshold = 1000, decreaseThreshold = -1] + // Current state: Step 2 [value = 200, increaseThreshold = 2000, decreaseThreshold = 900] + lastBrightnessCallback = -1 + repeat(HYSTERESIS_THRESHOLD) { + sendAmbientLuxValue(999F) + } + testLooper.dispatchAll() + + assertEquals( + "Should not receive any callback for brightness change", + -1, + lastBrightnessCallback + ) + } + + @Test + fun testBrightnessDoesntChange_onChangeOccurringLessThanHysteresisThreshold() { + setupSensorWithInitialLux(1001F) + + // Previous state: Step 1 [value = 100, increaseThreshold = 1000, decreaseThreshold = -1] + // Current state: Step 2 [value = 200, increaseThreshold = 2000, decreaseThreshold = 900] + lastBrightnessCallback = -1 + repeat(HYSTERESIS_THRESHOLD - 1) { + sendAmbientLuxValue(2001F) + } + testLooper.dispatchAll() + + assertEquals( + "Should not receive any callback for brightness change", + -1, + lastBrightnessCallback + ) + } + + private fun sendAmbientLuxValue(luxValue: Float) { + ambientController.onSensorChanged(SensorEvent(lightSensor, 0, 0, floatArrayOf(luxValue))) + } +} diff --git a/services/tests/servicestests/src/com/android/server/input/BatteryControllerTests.kt b/services/tests/servicestests/src/com/android/server/input/BatteryControllerTests.kt index 416b1f49f5d9..c36122b7e788 100644 --- a/services/tests/servicestests/src/com/android/server/input/BatteryControllerTests.kt +++ b/services/tests/servicestests/src/com/android/server/input/BatteryControllerTests.kt @@ -40,8 +40,7 @@ import androidx.test.core.app.ApplicationProvider import com.android.server.input.BatteryController.BluetoothBatteryManager import com.android.server.input.BatteryController.BluetoothBatteryManager.BluetoothBatteryListener import com.android.server.input.BatteryController.POLLING_PERIOD_MILLIS -import com.android.server.input.BatteryController.UEventManager -import com.android.server.input.BatteryController.UEventManager.UEventBatteryListener +import com.android.server.input.BatteryController.UEventBatteryListener import com.android.server.input.BatteryController.USI_BATTERY_VALIDITY_DURATION_MILLIS import org.hamcrest.Description import org.hamcrest.Matcher diff --git a/services/tests/servicestests/src/com/android/server/input/InputManagerServiceTests.kt b/services/tests/servicestests/src/com/android/server/input/InputManagerServiceTests.kt index 677144c144a7..72066ea951e3 100644 --- a/services/tests/servicestests/src/com/android/server/input/InputManagerServiceTests.kt +++ b/services/tests/servicestests/src/com/android/server/input/InputManagerServiceTests.kt @@ -23,9 +23,12 @@ import android.hardware.display.DisplayViewport import android.os.IInputConstants import android.os.test.TestLooper import android.platform.test.annotations.Presubmit +import android.provider.Settings +import android.test.mock.MockContentResolver import android.view.Display import android.view.PointerIcon import androidx.test.InstrumentationRegistry +import com.android.internal.util.test.FakeSettingsProvider import com.google.common.truth.Truth.assertThat import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue @@ -33,6 +36,8 @@ import org.junit.Before import org.junit.Rule import org.junit.Test import org.mockito.ArgumentMatchers.any +import org.mockito.ArgumentMatchers.anyBoolean +import org.mockito.ArgumentMatchers.anyFloat import org.mockito.ArgumentMatchers.anyInt import org.mockito.ArgumentMatchers.eq import org.mockito.Mock @@ -44,7 +49,9 @@ import org.mockito.Mockito.spy import org.mockito.Mockito.times import org.mockito.Mockito.verify import org.mockito.Mockito.verifyNoMoreInteractions +import org.mockito.Mockito.verifyZeroInteractions import org.mockito.junit.MockitoJUnit +import org.mockito.stubbing.OngoingStubbing import java.util.concurrent.CountDownLatch import java.util.concurrent.TimeUnit @@ -58,7 +65,10 @@ import java.util.concurrent.TimeUnit class InputManagerServiceTests { @get:Rule - val rule = MockitoJUnit.rule()!! + val mockitoRule = MockitoJUnit.rule()!! + + @get:Rule + val fakeSettingsProviderRule = FakeSettingsProvider.rule()!! @Mock private lateinit var native: NativeInputManagerService @@ -66,17 +76,25 @@ class InputManagerServiceTests { @Mock private lateinit var wmCallbacks: InputManagerService.WindowManagerCallbacks + @Mock + private lateinit var uEventManager: UEventManager + private lateinit var service: InputManagerService private lateinit var localService: InputManagerInternal private lateinit var context: Context private lateinit var testLooper: TestLooper + private lateinit var contentResolver: MockContentResolver @Before fun setup() { context = spy(ContextWrapper(InstrumentationRegistry.getContext())) + contentResolver = MockContentResolver(context) + contentResolver.addProvider(Settings.AUTHORITY, FakeSettingsProvider()) + whenever(context.contentResolver).thenReturn(contentResolver) testLooper = TestLooper() service = - InputManagerService(object : InputManagerService.Injector(context, testLooper.looper) { + InputManagerService(object : InputManagerService.Injector( + context, testLooper.looper, uEventManager) { override fun getNativeService( service: InputManagerService? ): NativeInputManagerService { @@ -92,9 +110,36 @@ class InputManagerServiceTests { } @Test + fun testStart() { + verifyZeroInteractions(native) + + service.start() + verify(native).start() + } + + @Test + fun testInputSettingsUpdatedOnSystemRunning() { + verifyZeroInteractions(native) + + service.systemRunning() + + verify(native).setPointerSpeed(anyInt()) + verify(native).setTouchpadPointerSpeed(anyInt()) + verify(native).setTouchpadNaturalScrollingEnabled(anyBoolean()) + verify(native).setTouchpadTapToClickEnabled(anyBoolean()) + verify(native).setTouchpadRightClickZoneEnabled(anyBoolean()) + verify(native).setShowTouches(anyBoolean()) + verify(native).reloadPointerIcons() + verify(native).notifyKeyGestureTimeoutsChanged() + verify(native).setMotionClassifierEnabled(anyBoolean()) + verify(native).setMaximumObscuringOpacityForTouch(anyFloat()) + verify(native).setStylusPointerIconEnabled(anyBoolean()) + } + + @Test fun testPointerDisplayUpdatesWhenDisplayViewportsChanged() { val displayId = 123 - `when`(wmCallbacks.pointerDisplayId).thenReturn(displayId) + whenever(wmCallbacks.pointerDisplayId).thenReturn(displayId) val viewports = listOf<DisplayViewport>() localService.setDisplayViewports(viewports) verify(native).setDisplayViewports(any(Array<DisplayViewport>::class.java)) @@ -337,3 +382,5 @@ class InputManagerServiceTests { thread.join(100 /*millis*/) } } + +private fun <T> whenever(methodCall: T): OngoingStubbing<T> = `when`(methodCall) diff --git a/services/tests/servicestests/src/com/android/server/input/KeyboardBacklightControllerTests.kt b/services/tests/servicestests/src/com/android/server/input/KeyboardBacklightControllerTests.kt index 64c05dc8ab84..3f4a4fba841e 100644 --- a/services/tests/servicestests/src/com/android/server/input/KeyboardBacklightControllerTests.kt +++ b/services/tests/servicestests/src/com/android/server/input/KeyboardBacklightControllerTests.kt @@ -16,6 +16,7 @@ package com.android.server.input +import android.animation.ValueAnimator import android.content.Context import android.content.ContextWrapper import android.graphics.Color @@ -29,11 +30,15 @@ import android.os.UEventObserver import android.os.test.TestLooper import android.platform.test.annotations.Presubmit import android.view.InputDevice +import androidx.test.annotation.UiThreadTest import androidx.test.core.app.ApplicationProvider -import com.android.server.input.KeyboardBacklightController.BRIGHTNESS_VALUE_FOR_LEVEL +import com.android.server.input.KeyboardBacklightController.DEFAULT_BRIGHTNESS_VALUE_FOR_LEVEL +import com.android.server.input.KeyboardBacklightController.MAX_BRIGHTNESS_CHANGE_STEPS import com.android.server.input.KeyboardBacklightController.USER_INACTIVITY_THRESHOLD_MILLIS import org.junit.After import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Assert.assertNotEquals import org.junit.Assert.assertNotNull import org.junit.Assert.assertNull import org.junit.Assert.assertTrue @@ -63,12 +68,20 @@ private fun createKeyboard(deviceId: Int): InputDevice = .build() private fun createLight(lightId: Int, lightType: Int): Light = + createLight( + lightId, + lightType, + null + ) + +private fun createLight(lightId: Int, lightType: Int, suggestedBrightnessLevels: IntArray?): Light = Light( lightId, "Light $lightId", 1, lightType, - Light.LIGHT_CAPABILITY_BRIGHTNESS + Light.LIGHT_CAPABILITY_BRIGHTNESS, + suggestedBrightnessLevels ) /** * Tests for {@link KeyboardBacklightController}. @@ -92,6 +105,8 @@ class KeyboardBacklightControllerTests { private lateinit var iInputManager: IInputManager @Mock private lateinit var native: NativeInputManagerService + @Mock + private lateinit var uEventManager: UEventManager private lateinit var keyboardBacklightController: KeyboardBacklightController private lateinit var context: Context private lateinit var dataStore: PersistentDataStore @@ -99,6 +114,7 @@ class KeyboardBacklightControllerTests { private var lightColorMap: HashMap<Int, Int> = HashMap() private var lastBacklightState: KeyboardBacklightState? = null private var sysfsNodeChanges = 0 + private var lastAnimationValues = IntArray(2) @Before fun setup() { @@ -115,8 +131,8 @@ class KeyboardBacklightControllerTests { override fun finishWrite(fos: FileOutputStream?, success: Boolean) {} }) testLooper = TestLooper() - keyboardBacklightController = - KeyboardBacklightController(context, native, dataStore, testLooper.looper) + keyboardBacklightController = KeyboardBacklightController(context, native, dataStore, + testLooper.looper, FakeAnimatorFactory(), uEventManager) InputManagerGlobal.resetInstance(iInputManager) val inputManager = InputManager(context) `when`(context.getSystemService(eq(Context.INPUT_SERVICE))).thenReturn(inputManager) @@ -125,6 +141,10 @@ class KeyboardBacklightControllerTests { val args = it.arguments lightColorMap.put(args[1] as Int, args[2] as Int) } + `when`(native.getLightColor(anyInt(), anyInt())).thenAnswer { + val args = it.arguments + lightColorMap.getOrDefault(args[1] as Int, 0) + } lightColorMap.clear() `when`(native.sysfsNodeChanged(any())).then { sysfsNodeChanges++ @@ -138,271 +158,257 @@ class KeyboardBacklightControllerTests { @Test fun testKeyboardBacklightIncrementDecrement() { - val keyboardWithBacklight = createKeyboard(DEVICE_ID) - val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT) - `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight) - `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight)) - keyboardBacklightController.onInputDeviceAdded(DEVICE_ID) + KeyboardBacklightFlags( + animationEnabled = false, + customLevelsEnabled = false, + ambientControlEnabled = false + ).use { + val keyboardWithBacklight = createKeyboard(DEVICE_ID) + val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT) + `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight) + `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight)) + keyboardBacklightController.onInputDeviceAdded(DEVICE_ID) - for (level in 1 until BRIGHTNESS_VALUE_FOR_LEVEL.size) { - incrementKeyboardBacklight(DEVICE_ID) - assertEquals( - "Light value for level $level mismatched", - Color.argb(BRIGHTNESS_VALUE_FOR_LEVEL[level], 0, 0, 0), - lightColorMap[LIGHT_ID] - ) - assertEquals( - "Light value for level $level must be correctly stored in the datastore", - BRIGHTNESS_VALUE_FOR_LEVEL[level], - dataStore.getKeyboardBacklightBrightness( - keyboardWithBacklight.descriptor, - LIGHT_ID - ).asInt - ) + assertIncrementDecrementForLevels(keyboardWithBacklight, keyboardBacklight, + DEFAULT_BRIGHTNESS_VALUE_FOR_LEVEL) } - - // Increment above max level - incrementKeyboardBacklight(DEVICE_ID) - assertEquals( - "Light value for max level mismatched", - Color.argb(MAX_BRIGHTNESS, 0, 0, 0), - lightColorMap[LIGHT_ID] - ) - assertEquals( - "Light value for max level must be correctly stored in the datastore", - MAX_BRIGHTNESS, - dataStore.getKeyboardBacklightBrightness( - keyboardWithBacklight.descriptor, - LIGHT_ID - ).asInt - ) - - for (level in BRIGHTNESS_VALUE_FOR_LEVEL.size - 2 downTo 0) { - decrementKeyboardBacklight(DEVICE_ID) - assertEquals( - "Light value for level $level mismatched", - Color.argb(BRIGHTNESS_VALUE_FOR_LEVEL[level], 0, 0, 0), - lightColorMap[LIGHT_ID] - ) - assertEquals( - "Light value for level $level must be correctly stored in the datastore", - BRIGHTNESS_VALUE_FOR_LEVEL[level], - dataStore.getKeyboardBacklightBrightness( - keyboardWithBacklight.descriptor, - LIGHT_ID - ).asInt - ) - } - - // Decrement below min level - decrementKeyboardBacklight(DEVICE_ID) - assertEquals( - "Light value for min level mismatched", - Color.argb(0, 0, 0, 0), - lightColorMap[LIGHT_ID] - ) - assertEquals( - "Light value for min level must be correctly stored in the datastore", - 0, - dataStore.getKeyboardBacklightBrightness( - keyboardWithBacklight.descriptor, - LIGHT_ID - ).asInt - ) } @Test fun testKeyboardWithoutBacklight() { - val keyboardWithoutBacklight = createKeyboard(DEVICE_ID) - val keyboardInputLight = createLight(LIGHT_ID, Light.LIGHT_TYPE_INPUT) - `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithoutBacklight) - `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardInputLight)) - keyboardBacklightController.onInputDeviceAdded(DEVICE_ID) - - incrementKeyboardBacklight(DEVICE_ID) - assertTrue("Non Keyboard backlights should not change", lightColorMap.isEmpty()) + KeyboardBacklightFlags( + animationEnabled = false, + customLevelsEnabled = false, + ambientControlEnabled = false + ).use { + val keyboardWithoutBacklight = createKeyboard(DEVICE_ID) + val keyboardInputLight = createLight(LIGHT_ID, Light.LIGHT_TYPE_INPUT) + `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithoutBacklight) + `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardInputLight)) + keyboardBacklightController.onInputDeviceAdded(DEVICE_ID) + + incrementKeyboardBacklight(DEVICE_ID) + assertTrue("Non Keyboard backlights should not change", lightColorMap.isEmpty()) + } } @Test fun testKeyboardWithMultipleLight() { - val keyboardWithBacklight = createKeyboard(DEVICE_ID) - val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT) - val keyboardInputLight = createLight(SECOND_LIGHT_ID, Light.LIGHT_TYPE_INPUT) - `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight) - `when`(iInputManager.getLights(DEVICE_ID)).thenReturn( - listOf( - keyboardBacklight, - keyboardInputLight + KeyboardBacklightFlags( + animationEnabled = false, + customLevelsEnabled = false, + ambientControlEnabled = false + ).use { + val keyboardWithBacklight = createKeyboard(DEVICE_ID) + val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT) + val keyboardInputLight = createLight(SECOND_LIGHT_ID, Light.LIGHT_TYPE_INPUT) + `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight) + `when`(iInputManager.getLights(DEVICE_ID)).thenReturn( + listOf( + keyboardBacklight, + keyboardInputLight + ) ) - ) - keyboardBacklightController.onInputDeviceAdded(DEVICE_ID) + keyboardBacklightController.onInputDeviceAdded(DEVICE_ID) - incrementKeyboardBacklight(DEVICE_ID) - assertEquals("Only keyboard backlights should change", 1, lightColorMap.size) - assertNotNull("Keyboard backlight should change", lightColorMap[LIGHT_ID]) - assertNull("Input lights should not change", lightColorMap[SECOND_LIGHT_ID]) + incrementKeyboardBacklight(DEVICE_ID) + assertEquals("Only keyboard backlights should change", 1, lightColorMap.size) + assertNotNull("Keyboard backlight should change", lightColorMap[LIGHT_ID]) + assertNull("Input lights should not change", lightColorMap[SECOND_LIGHT_ID]) + } } @Test fun testRestoreBacklightOnInputDeviceAdded() { - val keyboardWithBacklight = createKeyboard(DEVICE_ID) - val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT) - `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight) - `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight)) - - for (level in 1 until BRIGHTNESS_VALUE_FOR_LEVEL.size) { - dataStore.setKeyboardBacklightBrightness( + KeyboardBacklightFlags( + animationEnabled = false, + customLevelsEnabled = false, + ambientControlEnabled = false + ).use { + val keyboardWithBacklight = createKeyboard(DEVICE_ID) + val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT) + `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight) + `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight)) + + for (level in 1 until DEFAULT_BRIGHTNESS_VALUE_FOR_LEVEL.size) { + dataStore.setKeyboardBacklightBrightness( keyboardWithBacklight.descriptor, LIGHT_ID, - BRIGHTNESS_VALUE_FOR_LEVEL[level] - 1 - ) - - keyboardBacklightController.onInputDeviceAdded(DEVICE_ID) - keyboardBacklightController.notifyUserActivity() - testLooper.dispatchNext() - assertEquals( - "Keyboard backlight level should be restored to the level saved in the data " + - "store", - Color.argb(BRIGHTNESS_VALUE_FOR_LEVEL[level], 0, 0, 0), + DEFAULT_BRIGHTNESS_VALUE_FOR_LEVEL[level] - 1 + ) + + keyboardBacklightController.onInputDeviceAdded(DEVICE_ID) + keyboardBacklightController.notifyUserActivity() + testLooper.dispatchNext() + assertEquals( + "Keyboard backlight level should be restored to the level saved in the " + + "data store", + Color.argb(DEFAULT_BRIGHTNESS_VALUE_FOR_LEVEL[level], 0, 0, 0), lightColorMap[LIGHT_ID] - ) - keyboardBacklightController.onInputDeviceRemoved(DEVICE_ID) + ) + keyboardBacklightController.onInputDeviceRemoved(DEVICE_ID) + } } } @Test fun testRestoreBacklightOnInputDeviceChanged() { - val keyboardWithBacklight = createKeyboard(DEVICE_ID) - val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT) - `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight) - dataStore.setKeyboardBacklightBrightness( - keyboardWithBacklight.descriptor, - LIGHT_ID, - MAX_BRIGHTNESS - ) + KeyboardBacklightFlags( + animationEnabled = false, + customLevelsEnabled = false, + ambientControlEnabled = false + ).use { + val keyboardWithBacklight = createKeyboard(DEVICE_ID) + val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT) + `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight) + dataStore.setKeyboardBacklightBrightness( + keyboardWithBacklight.descriptor, + LIGHT_ID, + MAX_BRIGHTNESS + ) - keyboardBacklightController.onInputDeviceAdded(DEVICE_ID) - keyboardBacklightController.notifyUserActivity() - testLooper.dispatchNext() - assertTrue( - "Keyboard backlight should not be changed until its added", - lightColorMap.isEmpty() - ) + keyboardBacklightController.onInputDeviceAdded(DEVICE_ID) + keyboardBacklightController.notifyUserActivity() + testLooper.dispatchNext() + assertTrue( + "Keyboard backlight should not be changed until its added", + lightColorMap.isEmpty() + ) - `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight)) - keyboardBacklightController.onInputDeviceChanged(DEVICE_ID) - keyboardBacklightController.notifyUserActivity() - testLooper.dispatchNext() - assertEquals( - "Keyboard backlight level should be restored to the level saved in the data store", - Color.argb(MAX_BRIGHTNESS, 0, 0, 0), - lightColorMap[LIGHT_ID] - ) + `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight)) + keyboardBacklightController.onInputDeviceChanged(DEVICE_ID) + keyboardBacklightController.notifyUserActivity() + testLooper.dispatchNext() + assertEquals( + "Keyboard backlight level should be restored to the level saved in the data store", + Color.argb(MAX_BRIGHTNESS, 0, 0, 0), + lightColorMap[LIGHT_ID] + ) + } } @Test fun testKeyboardBacklight_registerUnregisterListener() { - val keyboardWithBacklight = createKeyboard(DEVICE_ID) - val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT) - `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight) - `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight)) - keyboardBacklightController.onInputDeviceAdded(DEVICE_ID) + KeyboardBacklightFlags( + animationEnabled = false, + customLevelsEnabled = false, + ambientControlEnabled = false + ).use { + val keyboardWithBacklight = createKeyboard(DEVICE_ID) + val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT) + val maxLevel = DEFAULT_BRIGHTNESS_VALUE_FOR_LEVEL.size - 1 + `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight) + `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight)) + keyboardBacklightController.onInputDeviceAdded(DEVICE_ID) - // Register backlight listener - val listener = KeyboardBacklightListener() - keyboardBacklightController.registerKeyboardBacklightListener(listener, 0) + // Register backlight listener + val listener = KeyboardBacklightListener() + keyboardBacklightController.registerKeyboardBacklightListener(listener, 0) - lastBacklightState = null - keyboardBacklightController.incrementKeyboardBacklight(DEVICE_ID) - testLooper.dispatchNext() + lastBacklightState = null + keyboardBacklightController.incrementKeyboardBacklight(DEVICE_ID) + testLooper.dispatchNext() - assertEquals( - "Backlight state device Id should be $DEVICE_ID", - DEVICE_ID, - lastBacklightState!!.deviceId - ) - assertEquals( - "Backlight state brightnessLevel should be " + 1, - 1, - lastBacklightState!!.brightnessLevel - ) - assertEquals( - "Backlight state maxBrightnessLevel should be " + (BRIGHTNESS_VALUE_FOR_LEVEL.size - 1), - (BRIGHTNESS_VALUE_FOR_LEVEL.size - 1), - lastBacklightState!!.maxBrightnessLevel - ) - assertEquals( - "Backlight state isTriggeredByKeyPress should be true", - true, - lastBacklightState!!.isTriggeredByKeyPress - ) + assertEquals( + "Backlight state device Id should be $DEVICE_ID", + DEVICE_ID, + lastBacklightState!!.deviceId + ) + assertEquals( + "Backlight state brightnessLevel should be 1", + 1, + lastBacklightState!!.brightnessLevel + ) + assertEquals( + "Backlight state maxBrightnessLevel should be $maxLevel", + maxLevel, + lastBacklightState!!.maxBrightnessLevel + ) + assertEquals( + "Backlight state isTriggeredByKeyPress should be true", + true, + lastBacklightState!!.isTriggeredByKeyPress + ) - // Unregister listener - keyboardBacklightController.unregisterKeyboardBacklightListener(listener, 0) + // Unregister listener + keyboardBacklightController.unregisterKeyboardBacklightListener(listener, 0) - lastBacklightState = null - incrementKeyboardBacklight(DEVICE_ID) + lastBacklightState = null + incrementKeyboardBacklight(DEVICE_ID) - assertNull("Listener should not receive any updates", lastBacklightState) + assertNull("Listener should not receive any updates", lastBacklightState) + } } @Test fun testKeyboardBacklight_userActivity() { - val keyboardWithBacklight = createKeyboard(DEVICE_ID) - val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT) - `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight) - `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight)) - dataStore.setKeyboardBacklightBrightness( - keyboardWithBacklight.descriptor, - LIGHT_ID, - MAX_BRIGHTNESS - ) + KeyboardBacklightFlags( + animationEnabled = false, + customLevelsEnabled = false, + ambientControlEnabled = false + ).use { + val keyboardWithBacklight = createKeyboard(DEVICE_ID) + val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT) + `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight) + `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight)) + dataStore.setKeyboardBacklightBrightness( + keyboardWithBacklight.descriptor, + LIGHT_ID, + MAX_BRIGHTNESS + ) - keyboardBacklightController.onInputDeviceAdded(DEVICE_ID) - keyboardBacklightController.notifyUserActivity() - testLooper.dispatchNext() - assertEquals( - "Keyboard backlight level should be restored to the level saved in the data store", - Color.argb(MAX_BRIGHTNESS, 0, 0, 0), - lightColorMap[LIGHT_ID] - ) + keyboardBacklightController.onInputDeviceAdded(DEVICE_ID) + keyboardBacklightController.notifyUserActivity() + testLooper.dispatchNext() + assertEquals( + "Keyboard backlight level should be restored to the level saved in the data store", + Color.argb(MAX_BRIGHTNESS, 0, 0, 0), + lightColorMap[LIGHT_ID] + ) - testLooper.moveTimeForward(USER_INACTIVITY_THRESHOLD_MILLIS + 1000) - testLooper.dispatchNext() - assertEquals( - "Keyboard backlight level should be turned off after inactivity", - 0, - lightColorMap[LIGHT_ID] - ) + testLooper.moveTimeForward(USER_INACTIVITY_THRESHOLD_MILLIS + 1000) + testLooper.dispatchNext() + assertEquals( + "Keyboard backlight level should be turned off after inactivity", + 0, + lightColorMap[LIGHT_ID] + ) + } } @Test fun testKeyboardBacklight_displayOnOff() { - val keyboardWithBacklight = createKeyboard(DEVICE_ID) - val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT) - `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight) - `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight)) - dataStore.setKeyboardBacklightBrightness( - keyboardWithBacklight.descriptor, - LIGHT_ID, - MAX_BRIGHTNESS - ) + KeyboardBacklightFlags( + animationEnabled = false, + customLevelsEnabled = false, + ambientControlEnabled = false + ).use { + val keyboardWithBacklight = createKeyboard(DEVICE_ID) + val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT) + `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight) + `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight)) + dataStore.setKeyboardBacklightBrightness( + keyboardWithBacklight.descriptor, + LIGHT_ID, + MAX_BRIGHTNESS + ) - keyboardBacklightController.onInputDeviceAdded(DEVICE_ID) - keyboardBacklightController.handleInteractiveStateChange(true /* isDisplayOn */) - assertEquals( - "Keyboard backlight level should be restored to the level saved in the data " + - "store when display turned on", - Color.argb(MAX_BRIGHTNESS, 0, 0, 0), - lightColorMap[LIGHT_ID] - ) + keyboardBacklightController.onInputDeviceAdded(DEVICE_ID) + keyboardBacklightController.handleInteractiveStateChange(true /* isDisplayOn */) + assertEquals( + "Keyboard backlight level should be restored to the level saved in the data " + + "store when display turned on", + Color.argb(MAX_BRIGHTNESS, 0, 0, 0), + lightColorMap[LIGHT_ID] + ) - keyboardBacklightController.handleInteractiveStateChange(false /* isDisplayOn */) - assertEquals( - "Keyboard backlight level should be turned off after display is turned off", - 0, - lightColorMap[LIGHT_ID] - ) + keyboardBacklightController.handleInteractiveStateChange(false /* isDisplayOn */) + assertEquals( + "Keyboard backlight level should be turned off after display is turned off", + 0, + lightColorMap[LIGHT_ID] + ) + } } @Test @@ -463,6 +469,316 @@ class KeyboardBacklightControllerTests { ) } + @Test + @UiThreadTest + fun testKeyboardBacklightAnimation_onChangeLevels() { + KeyboardBacklightFlags( + animationEnabled = true, + customLevelsEnabled = false, + ambientControlEnabled = false + ).use { + val keyboardWithBacklight = createKeyboard(DEVICE_ID) + val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT) + `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight) + `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight)) + keyboardBacklightController.onInputDeviceAdded(DEVICE_ID) + + incrementKeyboardBacklight(DEVICE_ID) + assertEquals( + "Should start animation from level 0", + DEFAULT_BRIGHTNESS_VALUE_FOR_LEVEL[0], + lastAnimationValues[0] + ) + assertEquals( + "Should start animation to level 1", + DEFAULT_BRIGHTNESS_VALUE_FOR_LEVEL[1], + lastAnimationValues[1] + ) + } + } + + @Test + fun testKeyboardBacklightPreferredLevels() { + KeyboardBacklightFlags( + animationEnabled = false, + customLevelsEnabled = true, + ambientControlEnabled = false + ).use { + val keyboardWithBacklight = createKeyboard(DEVICE_ID) + val suggestedLevels = intArrayOf(0, 22, 63, 135, 196, 255) + val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT, + suggestedLevels) + `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight) + `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight)) + keyboardBacklightController.onInputDeviceAdded(DEVICE_ID) + + assertIncrementDecrementForLevels(keyboardWithBacklight, keyboardBacklight, + suggestedLevels) + } + } + + @Test + fun testKeyboardBacklightPreferredLevels_moreThanMax_shouldUseDefault() { + KeyboardBacklightFlags( + animationEnabled = false, + customLevelsEnabled = true, + ambientControlEnabled = false + ).use { + val keyboardWithBacklight = createKeyboard(DEVICE_ID) + val suggestedLevels = IntArray(MAX_BRIGHTNESS_CHANGE_STEPS + 1) { 10 * (it + 1) } + val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT, + suggestedLevels) + `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight) + `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight)) + keyboardBacklightController.onInputDeviceAdded(DEVICE_ID) + + assertIncrementDecrementForLevels(keyboardWithBacklight, keyboardBacklight, + DEFAULT_BRIGHTNESS_VALUE_FOR_LEVEL) + } + } + + @Test + fun testKeyboardBacklightPreferredLevels_mustHaveZeroAndMaxBrightnessAsBounds() { + KeyboardBacklightFlags( + animationEnabled = false, + customLevelsEnabled = true, + ambientControlEnabled = false + ).use { + val keyboardWithBacklight = createKeyboard(DEVICE_ID) + val suggestedLevels = intArrayOf(22, 63, 135, 196) + val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT, + suggestedLevels) + `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight) + `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight)) + keyboardBacklightController.onInputDeviceAdded(DEVICE_ID) + + // Framework will add the lowest and maximum levels if not provided via config + assertIncrementDecrementForLevels(keyboardWithBacklight, keyboardBacklight, + intArrayOf(0, 22, 63, 135, 196, 255)) + } + } + + @Test + fun testKeyboardBacklightPreferredLevels_dropsOutOfBoundsLevels() { + KeyboardBacklightFlags( + animationEnabled = false, + customLevelsEnabled = true, + ambientControlEnabled = false + ).use { + val keyboardWithBacklight = createKeyboard(DEVICE_ID) + val suggestedLevels = intArrayOf(22, 63, 135, 400, 196, 1000) + val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT, + suggestedLevels) + `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight) + `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight)) + keyboardBacklightController.onInputDeviceAdded(DEVICE_ID) + + // Framework will drop out of bound levels in the config + assertIncrementDecrementForLevels(keyboardWithBacklight, keyboardBacklight, + intArrayOf(0, 22, 63, 135, 196, 255)) + } + } + + @Test + fun testAmbientBacklightControl_doesntRestoreBacklightLevel() { + KeyboardBacklightFlags( + animationEnabled = false, + customLevelsEnabled = false, + ambientControlEnabled = true + ).use { + val keyboardWithBacklight = createKeyboard(DEVICE_ID) + val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT) + `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight) + `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight)) + + dataStore.setKeyboardBacklightBrightness( + keyboardWithBacklight.descriptor, + LIGHT_ID, + DEFAULT_BRIGHTNESS_VALUE_FOR_LEVEL[1] + ) + + keyboardBacklightController.onInputDeviceAdded(DEVICE_ID) + keyboardBacklightController.notifyUserActivity() + testLooper.dispatchNext() + assertNull( + "Keyboard backlight level should not be restored to the saved level", + lightColorMap[LIGHT_ID] + ) + } + } + + @Test + fun testAmbientBacklightControl_doesntBackupBacklightLevel() { + KeyboardBacklightFlags( + animationEnabled = false, + customLevelsEnabled = false, + ambientControlEnabled = true + ).use { + val keyboardWithBacklight = createKeyboard(DEVICE_ID) + val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT) + `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight) + `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight)) + + keyboardBacklightController.onInputDeviceAdded(DEVICE_ID) + incrementKeyboardBacklight(DEVICE_ID) + assertFalse( + "Light value should not be backed up if ambient control is enabled", + dataStore.getKeyboardBacklightBrightness( + keyboardWithBacklight.descriptor, LIGHT_ID + ).isPresent + ) + } + } + + @Test + fun testAmbientBacklightControl_incrementLevel_afterAmbientChange() { + KeyboardBacklightFlags( + animationEnabled = false, + customLevelsEnabled = false, + ambientControlEnabled = true + ).use { + val keyboardWithBacklight = createKeyboard(DEVICE_ID) + val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT) + `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight) + `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight)) + keyboardBacklightController.onInputDeviceAdded(DEVICE_ID) + sendAmbientBacklightValue(1) + assertEquals( + "Light value should be changed to ambient provided value", + Color.argb(1, 0, 0, 0), + lightColorMap[LIGHT_ID] + ) + + incrementKeyboardBacklight(DEVICE_ID) + + assertEquals( + "Light value for level after increment post Ambient change is mismatched", + Color.argb(DEFAULT_BRIGHTNESS_VALUE_FOR_LEVEL[1], 0, 0, 0), + lightColorMap[LIGHT_ID] + ) + } + } + + @Test + fun testAmbientBacklightControl_decrementLevel_afterAmbientChange() { + KeyboardBacklightFlags( + animationEnabled = false, + customLevelsEnabled = false, + ambientControlEnabled = true + ).use { + val keyboardWithBacklight = createKeyboard(DEVICE_ID) + val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT) + `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight) + `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight)) + keyboardBacklightController.onInputDeviceAdded(DEVICE_ID) + sendAmbientBacklightValue(254) + assertEquals( + "Light value should be changed to ambient provided value", + Color.argb(254, 0, 0, 0), + lightColorMap[LIGHT_ID] + ) + + decrementKeyboardBacklight(DEVICE_ID) + + val numLevels = DEFAULT_BRIGHTNESS_VALUE_FOR_LEVEL.size + assertEquals( + "Light value for level after decrement post Ambient change is mismatched", + Color.argb(DEFAULT_BRIGHTNESS_VALUE_FOR_LEVEL[numLevels - 2], 0, 0, 0), + lightColorMap[LIGHT_ID] + ) + } + } + + @Test + fun testAmbientBacklightControl_ambientChanges_afterManualChange() { + KeyboardBacklightFlags( + animationEnabled = false, + customLevelsEnabled = false, + ambientControlEnabled = true + ).use { + val keyboardWithBacklight = createKeyboard(DEVICE_ID) + val keyboardBacklight = createLight(LIGHT_ID, Light.LIGHT_TYPE_KEYBOARD_BACKLIGHT) + `when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardWithBacklight) + `when`(iInputManager.getLights(DEVICE_ID)).thenReturn(listOf(keyboardBacklight)) + keyboardBacklightController.onInputDeviceAdded(DEVICE_ID) + incrementKeyboardBacklight(DEVICE_ID) + assertEquals( + "Light value should be changed to the first level", + Color.argb(DEFAULT_BRIGHTNESS_VALUE_FOR_LEVEL[1], 0, 0, 0), + lightColorMap[LIGHT_ID] + ) + + sendAmbientBacklightValue(100) + assertNotEquals( + "Light value should not change based on ambient changes after manual changes", + Color.argb(100, 0, 0, 0), + lightColorMap[LIGHT_ID] + ) + } + } + + private fun assertIncrementDecrementForLevels( + device: InputDevice, + light: Light, + expectedLevels: IntArray + ) { + val deviceId = device.id + val lightId = light.id + for (level in 1 until expectedLevels.size) { + incrementKeyboardBacklight(deviceId) + assertEquals( + "Light value for level $level mismatched", + Color.argb(expectedLevels[level], 0, 0, 0), + lightColorMap[lightId] + ) + assertEquals( + "Light value for level $level must be correctly stored in the datastore", + expectedLevels[level], + dataStore.getKeyboardBacklightBrightness(device.descriptor, lightId).asInt + ) + } + + // Increment above max level + incrementKeyboardBacklight(deviceId) + assertEquals( + "Light value for max level mismatched", + Color.argb(MAX_BRIGHTNESS, 0, 0, 0), + lightColorMap[lightId] + ) + assertEquals( + "Light value for max level must be correctly stored in the datastore", + MAX_BRIGHTNESS, + dataStore.getKeyboardBacklightBrightness(device.descriptor, lightId).asInt + ) + + for (level in expectedLevels.size - 2 downTo 0) { + decrementKeyboardBacklight(deviceId) + assertEquals( + "Light value for level $level mismatched", + Color.argb(expectedLevels[level], 0, 0, 0), + lightColorMap[lightId] + ) + assertEquals( + "Light value for level $level must be correctly stored in the datastore", + expectedLevels[level], + dataStore.getKeyboardBacklightBrightness(device.descriptor, lightId).asInt + ) + } + + // Decrement below min level + decrementKeyboardBacklight(deviceId) + assertEquals( + "Light value for min level mismatched", + Color.argb(0, 0, 0, 0), + lightColorMap[lightId] + ) + assertEquals( + "Light value for min level must be correctly stored in the datastore", + 0, + dataStore.getKeyboardBacklightBrightness(device.descriptor, lightId).asInt + ) + } + inner class KeyboardBacklightListener : IKeyboardBacklightListener.Stub() { override fun onBrightnessChanged( deviceId: Int, @@ -490,10 +806,41 @@ class KeyboardBacklightControllerTests { testLooper.dispatchAll() } + private fun sendAmbientBacklightValue(brightnessValue: Int) { + keyboardBacklightController.handleAmbientLightValueChanged(brightnessValue) + keyboardBacklightController.notifyUserActivity() + testLooper.dispatchAll() + } + class KeyboardBacklightState( val deviceId: Int, val brightnessLevel: Int, val maxBrightnessLevel: Int, val isTriggeredByKeyPress: Boolean ) + + private inner class KeyboardBacklightFlags constructor( + animationEnabled: Boolean, + customLevelsEnabled: Boolean, + ambientControlEnabled: Boolean + ) : AutoCloseable { + init { + InputFeatureFlagProvider.setKeyboardBacklightAnimationEnabled(animationEnabled) + InputFeatureFlagProvider.setKeyboardBacklightCustomLevelsEnabled(customLevelsEnabled) + InputFeatureFlagProvider + .setAmbientKeyboardBacklightControlEnabled(ambientControlEnabled) + } + + override fun close() { + InputFeatureFlagProvider.clearOverrides() + } + } + + private inner class FakeAnimatorFactory : KeyboardBacklightController.AnimatorFactory { + override fun makeIntAnimator(from: Int, to: Int): ValueAnimator { + lastAnimationValues[0] = from + lastAnimationValues[1] = to + return ValueAnimator.ofInt(from, to) + } + } } diff --git a/services/tests/servicestests/src/com/android/server/input/KeyboardMetricsCollectorTests.kt b/services/tests/servicestests/src/com/android/server/input/KeyboardMetricsCollectorTests.kt new file mode 100644 index 000000000000..c9724a3b4309 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/input/KeyboardMetricsCollectorTests.kt @@ -0,0 +1,179 @@ +/* + * Copyright 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.input + +import android.hardware.input.KeyboardLayout +import android.icu.util.ULocale +import android.platform.test.annotations.Presubmit +import android.view.InputDevice +import android.view.inputmethod.InputMethodSubtype +import org.junit.Assert.assertEquals +import org.junit.Assert.assertThrows +import org.junit.Assert.assertTrue +import org.junit.Test + +private fun createKeyboard( + deviceId: Int, + vendorId: Int, + productId: Int, + languageTag: String?, + layoutType: String? +): InputDevice = + InputDevice.Builder() + .setId(deviceId) + .setName("Device $deviceId") + .setDescriptor("descriptor $deviceId") + .setSources(InputDevice.SOURCE_KEYBOARD) + .setKeyboardType(InputDevice.KEYBOARD_TYPE_ALPHABETIC) + .setExternal(true) + .setVendorId(vendorId) + .setProductId(productId) + .setKeyboardLanguageTag(languageTag) + .setKeyboardLayoutType(layoutType) + .build() + +private fun createImeSubtype( + imeSubtypeId: Int, + languageTag: String, + layoutType: String +): InputMethodSubtype = + InputMethodSubtype.InputMethodSubtypeBuilder().setSubtypeId(imeSubtypeId) + .setPhysicalKeyboardHint(ULocale.forLanguageTag(languageTag), layoutType).build() + +/** + * Tests for {@link KeyboardMetricsCollector}. + * + * Build/Install/Run: + * atest FrameworksServicesTests:KeyboardMetricsCollectorTests + */ +@Presubmit +class KeyboardMetricsCollectorTests { + + companion object { + const val DEVICE_ID = 1 + const val DEFAULT_VENDOR_ID = 123 + const val DEFAULT_PRODUCT_ID = 456 + } + + @Test + fun testCreateKeyboardConfigurationEvent_throwsExceptionWithoutAnyLayoutConfiguration() { + assertThrows(IllegalStateException::class.java) { + KeyboardMetricsCollector.KeyboardConfigurationEvent.Builder( + createKeyboard( + DEVICE_ID, + DEFAULT_VENDOR_ID, + DEFAULT_PRODUCT_ID, + null, + null + ) + ).build() + } + } + + @Test + fun testCreateKeyboardConfigurationEvent_throwsExceptionWithInvalidLayoutSelectionCriteria() { + assertThrows(IllegalStateException::class.java) { + KeyboardMetricsCollector.KeyboardConfigurationEvent.Builder( + createKeyboard( + DEVICE_ID, + DEFAULT_VENDOR_ID, + DEFAULT_PRODUCT_ID, + null, + null + ) + ).addLayoutSelection(createImeSubtype(1, "en-US", "qwerty"), null, 123).build() + } + } + + @Test + fun testCreateKeyboardConfigurationEvent_withMultipleConfigurations() { + val builder = KeyboardMetricsCollector.KeyboardConfigurationEvent.Builder( + createKeyboard( + DEVICE_ID, + DEFAULT_VENDOR_ID, + DEFAULT_PRODUCT_ID, + "de-CH", + "qwertz" + ) + ) + val event = builder.addLayoutSelection( + createImeSubtype(1, "en-US", "qwerty"), + KeyboardLayout(null, "English(US)(Qwerty)", null, 0, null, 0, 0, 0), + KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD + ).addLayoutSelection( + createImeSubtype(2, "en-US", "azerty"), + null, + KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_USER + ).addLayoutSelection( + createImeSubtype(3, "en-US", "qwerty"), + KeyboardLayout(null, "German", null, 0, null, 0, 0, 0), + KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_DEVICE + ).setIsFirstTimeConfiguration(true).build() + + assertEquals( + "KeyboardConfigurationEvent should pick vendor ID from provided InputDevice", + DEFAULT_VENDOR_ID, + event.vendorId + ) + assertEquals( + "KeyboardConfigurationEvent should pick product ID from provided InputDevice", + DEFAULT_PRODUCT_ID, + event.productId + ) + assertTrue(event.isFirstConfiguration) + + assertEquals( + "KeyboardConfigurationEvent should contain 3 configurations provided", + 3, + event.layoutConfigurations.size + ) + assertExpectedLayoutConfiguration( + event.layoutConfigurations[0], + "en-US", + KeyboardLayout.LayoutType.getLayoutTypeEnumValue("qwerty"), + "English(US)(Qwerty)", + KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD + ) + assertExpectedLayoutConfiguration( + event.layoutConfigurations[1], + "en-US", + KeyboardLayout.LayoutType.getLayoutTypeEnumValue("azerty"), + KeyboardMetricsCollector.DEFAULT_LAYOUT, + KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_USER + ) + assertExpectedLayoutConfiguration( + event.layoutConfigurations[2], + "de-CH", + KeyboardLayout.LayoutType.getLayoutTypeEnumValue("qwertz"), + "German", + KeyboardMetricsCollector.LAYOUT_SELECTION_CRITERIA_DEVICE + ) + } + + private fun assertExpectedLayoutConfiguration( + configuration: KeyboardMetricsCollector.LayoutConfiguration, + expectedLanguageTag: String, + expectedLayoutType: Int, + expectedSelectedLayout: String, + expectedLayoutSelectionCriteria: Int + ) { + assertEquals(expectedLanguageTag, configuration.keyboardLanguageTag) + assertEquals(expectedLayoutType, configuration.keyboardLayoutType) + assertEquals(expectedSelectedLayout, configuration.keyboardLayoutName) + assertEquals(expectedLayoutSelectionCriteria, configuration.layoutSelectionCriteria) + } +}
\ No newline at end of file diff --git a/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java b/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java index e960e995f6ce..fe2ac176949d 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java @@ -254,6 +254,8 @@ public abstract class BaseLockSettingsServiceTests { .thenReturn(true); when(res.getBoolean(eq(com.android.internal.R.bool.config_strongAuthRequiredOnBoot))) .thenReturn(true); + when(res.getBoolean(eq(com.android.internal.R.bool.config_repairModeSupported))) + .thenReturn(true); return res; } diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTestable.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTestable.java index 36dc6c5f9f95..a029db922372 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTestable.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTestable.java @@ -84,6 +84,11 @@ public class LockSettingsStorageTestable extends LockSettingsStorage { } @Override + File getRepairModePersistentDataFile() { + return remapToStorageDir(super.getRepairModePersistentDataFile()); + } + + @Override PersistentDataBlockManagerInternal getPersistentDataBlockManager() { return mPersistentDataBlockManager; } diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTests.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTests.java index 10ed882f343f..23f14f8468bb 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTests.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTests.java @@ -457,6 +457,31 @@ public class LockSettingsStorageTests { assertEquals(2, PersistentData.TYPE_SP_WEAVER); } + @Test + public void testRepairMode_emptyPersistentData() { + assertSame(PersistentData.NONE, mStorage.readPersistentDataBlock()); + } + + @Test + public void testRepairMode_writeGatekeeperPersistentData() { + mStorage.writeRepairModePersistentData( + PersistentData.TYPE_SP_GATEKEEPER, SOME_USER_ID, PAYLOAD); + + final PersistentData data = mStorage.readRepairModePersistentData(); + assertEquals(PersistentData.TYPE_SP_GATEKEEPER, data.type); + assertArrayEquals(PAYLOAD, data.payload); + } + + @Test + public void testRepairMode_writeWeaverPersistentData() { + mStorage.writeRepairModePersistentData( + PersistentData.TYPE_SP_WEAVER, SOME_USER_ID, PAYLOAD); + + final PersistentData data = mStorage.readRepairModePersistentData(); + assertEquals(PersistentData.TYPE_SP_WEAVER, data.type); + assertArrayEquals(PAYLOAD, data.payload); + } + private static void assertArrayEquals(byte[] expected, byte[] actual) { if (!Arrays.equals(expected, actual)) { fail("expected:<" + Arrays.toString(expected) + diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockscreenRepairModeTest.java b/services/tests/servicestests/src/com/android/server/locksettings/LockscreenRepairModeTest.java new file mode 100644 index 000000000000..70150c507460 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/locksettings/LockscreenRepairModeTest.java @@ -0,0 +1,206 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.locksettings; + +import static com.android.internal.widget.LockPatternUtils.USER_REPAIR_MODE; +import static com.android.internal.widget.LockPatternUtils.VERIFY_FLAG_WRITE_REPAIR_MODE_PW; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertSame; + +import android.app.PropertyInvalidatedCache; +import android.platform.test.annotations.Presubmit; +import android.provider.Settings; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import com.android.internal.widget.LockPatternUtils; +import com.android.internal.widget.VerifyCredentialResponse; +import com.android.server.locksettings.LockSettingsStorage.PersistentData; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +@SmallTest +@Presubmit +@RunWith(AndroidJUnit4.class) +public class LockscreenRepairModeTest extends BaseLockSettingsServiceTests { + + @Before + public void setUp() throws Exception { + PropertyInvalidatedCache.disableForTestMode(); + mService.initializeSyntheticPassword(PRIMARY_USER_ID); + } + + @Test + public void verifyPin_writeRepairModePW() { + mService.setLockCredential(newPin("1234"), nonePassword(), PRIMARY_USER_ID); + assertSame(PersistentData.NONE, mStorage.readRepairModePersistentData()); + + assertEquals(VerifyCredentialResponse.RESPONSE_OK, + mService.verifyCredential( + newPin("1234"), PRIMARY_USER_ID, VERIFY_FLAG_WRITE_REPAIR_MODE_PW) + .getResponseCode()); + assertEquals(LockPatternUtils.CREDENTIAL_TYPE_PIN, + mService.getCredentialType(USER_REPAIR_MODE)); + } + + @Test + public void verifyPattern_writeRepairModePW() { + mService.setLockCredential(newPattern("4321"), nonePassword(), PRIMARY_USER_ID); + assertSame(PersistentData.NONE, mStorage.readRepairModePersistentData()); + + assertEquals(VerifyCredentialResponse.RESPONSE_OK, + mService.verifyCredential( + newPattern("4321"), PRIMARY_USER_ID, VERIFY_FLAG_WRITE_REPAIR_MODE_PW) + .getResponseCode()); + assertEquals(LockPatternUtils.CREDENTIAL_TYPE_PATTERN, + mService.getCredentialType(USER_REPAIR_MODE)); + } + + @Test + public void verifyPassword_writeRepairModePW() { + mService.setLockCredential(newPassword("4321"), nonePassword(), PRIMARY_USER_ID); + assertSame(PersistentData.NONE, mStorage.readRepairModePersistentData()); + + assertEquals(VerifyCredentialResponse.RESPONSE_OK, + mService.verifyCredential( + newPassword("4321"), PRIMARY_USER_ID, VERIFY_FLAG_WRITE_REPAIR_MODE_PW) + .getResponseCode()); + assertEquals(LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, + mService.getCredentialType(USER_REPAIR_MODE)); + } + + @Test + public void verifyCredential_writeRepairModePW_repairModeActive() { + mService.setLockCredential(newPin("1234"), nonePassword(), PRIMARY_USER_ID); + assertSame(PersistentData.NONE, mStorage.readRepairModePersistentData()); + + setRepairModeActive(true); + assertEquals(VerifyCredentialResponse.RESPONSE_ERROR, + mService.verifyCredential( + newPin("1234"), PRIMARY_USER_ID, VERIFY_FLAG_WRITE_REPAIR_MODE_PW) + .getResponseCode()); + assertSame(PersistentData.NONE, mStorage.readRepairModePersistentData()); + } + + @Test + public void deleteRepairModePersistentData() { + mService.setLockCredential(newPin("1234"), nonePassword(), PRIMARY_USER_ID); + assertSame(PersistentData.NONE, mStorage.readRepairModePersistentData()); + assertEquals(VerifyCredentialResponse.RESPONSE_OK, + mService.verifyCredential( + newPin("1234"), PRIMARY_USER_ID, VERIFY_FLAG_WRITE_REPAIR_MODE_PW) + .getResponseCode()); + assertEquals(LockPatternUtils.CREDENTIAL_TYPE_PIN, + mService.getCredentialType(USER_REPAIR_MODE)); + + mService.deleteRepairModePersistentDataIfNeeded(); + assertSame(PersistentData.NONE, mStorage.readRepairModePersistentData()); + } + + @Test + public void verifyPin_userRepairMode() { + mService.setLockCredential(newPin("1234"), nonePassword(), PRIMARY_USER_ID); + assertSame(PersistentData.NONE, mStorage.readRepairModePersistentData()); + assertEquals(VerifyCredentialResponse.RESPONSE_OK, + mService.verifyCredential( + newPin("1234"), PRIMARY_USER_ID, VERIFY_FLAG_WRITE_REPAIR_MODE_PW) + .getResponseCode()); + setRepairModeActive(true); + + assertEquals(LockPatternUtils.CREDENTIAL_TYPE_PIN, + mService.getCredentialType(USER_REPAIR_MODE)); + assertEquals(VerifyCredentialResponse.RESPONSE_OK, + mService.verifyCredential(newPin("1234"), USER_REPAIR_MODE, 0 /* flags */) + .getResponseCode()); + } + + @Test + public void verifyPattern_userRepairMode() { + mService.setLockCredential(newPattern("4321"), nonePassword(), PRIMARY_USER_ID); + assertSame(PersistentData.NONE, mStorage.readRepairModePersistentData()); + assertEquals(VerifyCredentialResponse.RESPONSE_OK, + mService.verifyCredential( + newPattern("4321"), PRIMARY_USER_ID, VERIFY_FLAG_WRITE_REPAIR_MODE_PW) + .getResponseCode()); + setRepairModeActive(true); + + assertEquals(LockPatternUtils.CREDENTIAL_TYPE_PATTERN, + mService.getCredentialType(USER_REPAIR_MODE)); + assertEquals(VerifyCredentialResponse.RESPONSE_OK, + mService.verifyCredential(newPattern("4321"), USER_REPAIR_MODE, 0 /* flags */) + .getResponseCode()); + } + + @Test + public void verifyPassword_userRepairMode() { + mService.setLockCredential(newPassword("4321"), nonePassword(), PRIMARY_USER_ID); + assertSame(PersistentData.NONE, mStorage.readRepairModePersistentData()); + assertEquals(VerifyCredentialResponse.RESPONSE_OK, + mService.verifyCredential( + newPassword("4321"), PRIMARY_USER_ID, VERIFY_FLAG_WRITE_REPAIR_MODE_PW) + .getResponseCode()); + setRepairModeActive(true); + + assertEquals(LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, + mService.getCredentialType(USER_REPAIR_MODE)); + assertEquals(VerifyCredentialResponse.RESPONSE_OK, + mService.verifyCredential(newPassword("4321"), USER_REPAIR_MODE, 0 /* flags */) + .getResponseCode()); + } + + @Test + public void verifyCredential_userRepairMode_repairModeIsNotActive() { + mService.setLockCredential(newPin("1234"), nonePassword(), PRIMARY_USER_ID); + assertSame(PersistentData.NONE, mStorage.readRepairModePersistentData()); + assertEquals(VerifyCredentialResponse.RESPONSE_OK, + mService.verifyCredential( + newPin("1234"), PRIMARY_USER_ID, VERIFY_FLAG_WRITE_REPAIR_MODE_PW) + .getResponseCode()); + + assertEquals(LockPatternUtils.CREDENTIAL_TYPE_PIN, + mService.getCredentialType(USER_REPAIR_MODE)); + assertEquals(VerifyCredentialResponse.RESPONSE_ERROR, + mService.verifyCredential(newPin("1234"), USER_REPAIR_MODE, 0 /* flags */) + .getResponseCode()); + } + + @Test + public void verifyCredential_userRepairMode_wrongPin() { + mService.setLockCredential(newPin("1234"), nonePassword(), PRIMARY_USER_ID); + assertSame(PersistentData.NONE, mStorage.readRepairModePersistentData()); + assertEquals(VerifyCredentialResponse.RESPONSE_OK, + mService.verifyCredential( + newPin("1234"), PRIMARY_USER_ID, VERIFY_FLAG_WRITE_REPAIR_MODE_PW) + .getResponseCode()); + setRepairModeActive(true); + + assertEquals(LockPatternUtils.CREDENTIAL_TYPE_PIN, + mService.getCredentialType(USER_REPAIR_MODE)); + assertEquals(VerifyCredentialResponse.RESPONSE_ERROR, + mService.verifyCredential(newPin("5678"), USER_REPAIR_MODE, 0 /* flags */) + .getResponseCode()); + } + + private void setRepairModeActive(boolean active) { + Settings.Global.putInt(mContext.getContentResolver(), + Settings.Global.REPAIR_MODE_ACTIVE, active ? 1 : 0); + } +} diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java index 6bcda3fbcf43..dc86e5fbe1f3 100644 --- a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java @@ -53,6 +53,8 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -545,23 +547,42 @@ public final class UserManagerTest { public void testRemoveUserWhenPossible_withProfiles() throws Exception { assumeHeadlessModeEnabled(); assumeCloneEnabled(); - final UserInfo parentUser = createUser("Human User", /* flags= */ 0); - final UserInfo cloneProfileUser = createProfileForUser("Clone Profile user", + final List<String> profileTypesToCreate = Arrays.asList( UserManager.USER_TYPE_PROFILE_CLONE, - parentUser.id); + UserManager.USER_TYPE_PROFILE_MANAGED + ); + + final UserInfo parentUser = createUser("Human User", /* flags= */ 0); + assertWithMessage("Could not create parent user") + .that(parentUser).isNotNull(); + + final List<Integer> profileIds = new ArrayList<>(); + for (String profileType : profileTypesToCreate) { + final String name = profileType.substring(profileType.lastIndexOf('.') + 1); + if (mUserManager.canAddMoreProfilesToUser(profileType, parentUser.id)) { + final UserInfo profile = createProfileForUser(name, profileType, parentUser.id); + assertWithMessage("Could not create " + name) + .that(profile).isNotNull(); + profileIds.add(profile.id); + } else { + Slog.w(TAG, "Can not add " + name + " to user #" + parentUser.id); + } + } - final UserInfo workProfileUser = createProfileForUser("Work Profile user", - UserManager.USER_TYPE_PROFILE_MANAGED, - parentUser.id); + // Test shouldn't pass or fail unless it's allowed to add profiles to secondary users. + assumeTrue("Not possible to create any profiles to user #" + parentUser.id, + profileIds.size() > 0); assertThat(mUserManager.removeUserWhenPossible(parentUser.getUserHandle(), /* overrideDevicePolicy= */ false)) .isEqualTo(UserManager.REMOVE_RESULT_REMOVED); waitForUserRemoval(parentUser.id); - assertThat(hasUser(parentUser.id)).isFalse(); - assertThat(hasUser(cloneProfileUser.id)).isFalse(); - assertThat(hasUser(workProfileUser.id)).isFalse(); + assertWithMessage("Parent user still exists") + .that(hasUser(parentUser.id)).isFalse(); + profileIds.forEach(id -> + assertWithMessage("Profile still exists") + .that(hasUser(id)).isFalse()); } /** Tests creating a FULL user via specifying userType. */ diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java index cebc5409c795..eaf483869be4 100755 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -83,6 +83,7 @@ import static com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.No import static com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags.SHOW_STICKY_HUN_FOR_DENIED_FSI; import static com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags.WAKE_LOCK_FOR_POSTING_NOTIFICATION; import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN; +import static com.android.server.notification.NotificationManagerService.DEFAULT_MAX_NOTIFICATION_ENQUEUE_RATE; import static com.android.server.notification.NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_ADJUSTED; import static com.android.server.notification.NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_POSTED; import static com.android.server.notification.NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_UPDATED; @@ -705,6 +706,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { @After public void assertAllTrackersFinishedOrCancelled() { + waitForIdle(); // Finish async work. // Verify that no trackers were left dangling. for (PostNotificationTracker tracker : mPostNotificationTrackerFactory.mCreatedTrackers) { assertThat(tracker.isOngoing()).isFalse(); @@ -11924,6 +11926,103 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { assertFalse(n.isUserInitiatedJob()); } + @Test + public void enqueue_updatesEnqueueRate() throws Exception { + Notification n = generateNotificationRecord(null).getNotification(); + + mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag", 0, n, mUserId); + // Don't waitForIdle() here. We want to verify the "intermediate" state. + + verify(mUsageStats).registerEnqueuedByApp(eq(PKG)); + verify(mUsageStats).registerEnqueuedByAppAndAccepted(eq(PKG)); + verify(mUsageStats, never()).registerPostedByApp(any()); + + waitForIdle(); + } + + @Test + public void enqueue_withPost_updatesEnqueueRateAndPost() throws Exception { + Notification n = generateNotificationRecord(null).getNotification(); + + mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag", 0, n, mUserId); + waitForIdle(); + + verify(mUsageStats).registerEnqueuedByApp(eq(PKG)); + verify(mUsageStats).registerEnqueuedByAppAndAccepted(eq(PKG)); + verify(mUsageStats).registerPostedByApp(any()); + } + + @Test + public void enqueueNew_whenOverEnqueueRate_accepts() throws Exception { + Notification n = generateNotificationRecord(null).getNotification(); + when(mUsageStats.getAppEnqueueRate(eq(PKG))) + .thenReturn(DEFAULT_MAX_NOTIFICATION_ENQUEUE_RATE + 1f); + + mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag", 0, n, mUserId); + waitForIdle(); + + assertThat(mService.mNotificationsByKey).hasSize(1); + verify(mUsageStats).registerEnqueuedByApp(eq(PKG)); + verify(mUsageStats).registerEnqueuedByAppAndAccepted(eq(PKG)); + verify(mUsageStats).registerPostedByApp(any()); + } + + @Test + public void enqueueUpdate_whenBelowMaxEnqueueRate_accepts() throws Exception { + // Post the first version. + Notification original = generateNotificationRecord(null).getNotification(); + original.when = 111; + mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag", 0, original, mUserId); + waitForIdle(); + assertThat(mService.mNotificationList).hasSize(1); + assertThat(mService.mNotificationList.get(0).getNotification().when).isEqualTo(111); + + reset(mUsageStats); + when(mUsageStats.getAppEnqueueRate(eq(PKG))) + .thenReturn(DEFAULT_MAX_NOTIFICATION_ENQUEUE_RATE - 1f); + + // Post the update. + Notification update = generateNotificationRecord(null).getNotification(); + update.when = 222; + mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag", 0, update, mUserId); + waitForIdle(); + + verify(mUsageStats).registerEnqueuedByApp(eq(PKG)); + verify(mUsageStats).registerEnqueuedByAppAndAccepted(eq(PKG)); + verify(mUsageStats, never()).registerPostedByApp(any()); + verify(mUsageStats).registerUpdatedByApp(any(), any()); + assertThat(mService.mNotificationList).hasSize(1); + assertThat(mService.mNotificationList.get(0).getNotification().when).isEqualTo(222); + } + + @Test + public void enqueueUpdate_whenAboveMaxEnqueueRate_rejects() throws Exception { + // Post the first version. + Notification original = generateNotificationRecord(null).getNotification(); + original.when = 111; + mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag", 0, original, mUserId); + waitForIdle(); + assertThat(mService.mNotificationList).hasSize(1); + assertThat(mService.mNotificationList.get(0).getNotification().when).isEqualTo(111); + + reset(mUsageStats); + when(mUsageStats.getAppEnqueueRate(eq(PKG))) + .thenReturn(DEFAULT_MAX_NOTIFICATION_ENQUEUE_RATE + 1f); + + // Post the update. + Notification update = generateNotificationRecord(null).getNotification(); + update.when = 222; + mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag", 0, update, mUserId); + waitForIdle(); + + verify(mUsageStats).registerEnqueuedByApp(eq(PKG)); + verify(mUsageStats, never()).registerEnqueuedByAppAndAccepted(any()); + verify(mUsageStats, never()).registerPostedByApp(any()); + verify(mUsageStats, never()).registerUpdatedByApp(any(), any()); + assertThat(mService.mNotificationList).hasSize(1); + assertThat(mService.mNotificationList.get(0).getNotification().when).isEqualTo(111); // old + } + private void setDpmAppOppsExemptFromDismissal(boolean isOn) { DeviceConfig.setProperty( DeviceConfig.NAMESPACE_DEVICE_POLICY_MANAGER, diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordLoggerTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordLoggerTest.java index beab107ec556..0b147c333dd2 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordLoggerTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordLoggerTest.java @@ -18,6 +18,8 @@ package com.android.server.notification; import static android.app.Notification.FLAG_FOREGROUND_SERVICE; import static android.app.NotificationManager.IMPORTANCE_DEFAULT; +import static com.android.server.notification.NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_POSTED; +import static com.android.server.notification.NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_UPDATED; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -33,6 +35,7 @@ import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; import com.android.server.UiServiceTestCase; +import com.android.internal.util.FrameworkStatsLog; import org.junit.Test; import org.junit.runner.RunWith; @@ -155,4 +158,54 @@ public class NotificationRecordLoggerTest extends UiServiceTestCase { // Then: should return false assertFalse(NotificationRecordLogger.isNonDismissible(p.r)); } + + @Test + public void testGetFsiState_stickyHunFlagDisabled_zero() { + final int fsiState = NotificationRecordLogger.getFsiState( + /* isStickyHunFlagEnabled= */ false, + /* hasFullScreenIntent= */ true, + /* hasFsiRequestedButDeniedFlag= */ true, + /* eventType= */ NOTIFICATION_POSTED); + assertEquals(0, fsiState); + } + + @Test + public void testGetFsiState_isUpdate_zero() { + final int fsiState = NotificationRecordLogger.getFsiState( + /* isStickyHunFlagEnabled= */ true, + /* hasFullScreenIntent= */ true, + /* hasFsiRequestedButDeniedFlag= */ true, + /* eventType= */ NOTIFICATION_UPDATED); + assertEquals(0, fsiState); + } + + @Test + public void testGetFsiState_hasFsi_allowedEnum() { + final int fsiState = NotificationRecordLogger.getFsiState( + /* isStickyHunFlagEnabled= */ true, + /* hasFullScreenIntent= */ true, + /* hasFsiRequestedButDeniedFlag= */ false, + /* eventType= */ NOTIFICATION_POSTED); + assertEquals(FrameworkStatsLog.NOTIFICATION_REPORTED__FSI_STATE__FSI_ALLOWED, fsiState); + } + + @Test + public void testGetFsiState_fsiPermissionDenied_deniedEnum() { + final int fsiState = NotificationRecordLogger.getFsiState( + /* isStickyHunFlagEnabled= */ true, + /* hasFullScreenIntent= */ false, + /* hasFsiRequestedButDeniedFlag= */ true, + /* eventType= */ NOTIFICATION_POSTED); + assertEquals(FrameworkStatsLog.NOTIFICATION_REPORTED__FSI_STATE__FSI_DENIED, fsiState); + } + + @Test + public void testGetFsiState_noFsi_noFsiEnum() { + final int fsiState = NotificationRecordLogger.getFsiState( + /* isStickyHunFlagEnabled= */ true, + /* hasFullScreenIntent= */ false, + /* hasFsiRequestedButDeniedFlag= */ false, + /* eventType= */ NOTIFICATION_POSTED); + assertEquals(FrameworkStatsLog.NOTIFICATION_REPORTED__FSI_STATE__NO_FSI, fsiState); + } } diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationVisitUrisTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationVisitUrisTest.java index 6668f8520820..a88ab1863671 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationVisitUrisTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationVisitUrisTest.java @@ -115,11 +115,19 @@ public class NotificationVisitUrisTest extends UiServiceTestCase { PREFERRED_CONSTRUCTORS = ImmutableMap.of( Notification.Builder.class, Notification.Builder.class.getConstructor(Context.class, String.class)); + + EXCLUDED_SETTERS_OVERLOADS = ImmutableMultimap.<Class<?>, Method>builder() + .put(RemoteViews.class, + // b/245950570: Tries to connect to service and will crash. + RemoteViews.class.getMethod("setRemoteAdapter", + int.class, Intent.class)) + .build(); } catch (NoSuchMethodException e) { throw new RuntimeException(e); } } + // Setters that shouldn't be called, for various reasons (but NOT because they are KNOWN_BAD). private static final Multimap<Class<?>, String> EXCLUDED_SETTERS = ImmutableMultimap.<Class<?>, String>builder() // Handled by testAllStyles(). @@ -134,6 +142,9 @@ public class NotificationVisitUrisTest extends UiServiceTestCase { .put(RemoteViews.class, "mergeRemoteViews") .build(); + // Same as above, but specific overloads that should not be called. + private static final Multimap<Class<?>, Method> EXCLUDED_SETTERS_OVERLOADS; + private Context mContext; @Rule @@ -146,10 +157,12 @@ public class NotificationVisitUrisTest extends UiServiceTestCase { @Test // This is a meta-test, checks that the generators are not broken. public void verifyTest() { - Generated<Notification> notification = buildNotification(mContext, /* styleClass= */ null, - /* extenderClass= */ null, /* actionExtenderClass= */ null, + Generated<Notification> notification = buildNotification(mContext, + /* styleClass= */ Notification.MessagingStyle.class, + /* extenderClass= */ Notification.WearableExtender.class, + /* actionExtenderClass= */ Notification.Action.WearableExtender.class, /* includeRemoteViews= */ true); - assertThat(notification.includedUris.size()).isAtLeast(20); + assertThat(notification.includedUris.size()).isAtLeast(730); } @Test @@ -479,6 +492,7 @@ public class NotificationVisitUrisTest extends UiServiceTestCase { || method.getReturnType().equals(clazz)) && method.getParameterCount() >= 1 && !EXCLUDED_SETTERS.containsEntry(clazz, method.getName()) + && !EXCLUDED_SETTERS_OVERLOADS.containsEntry(clazz, method) && Arrays.stream(method.getParameterTypes()) .noneMatch(excludingParameterTypes::contains)) { methods.put(method.getName(), method); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java index 48ad86da1bc5..47340c1aef06 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java @@ -36,6 +36,9 @@ import static android.util.StatsLog.ANNOTATION_ID_IS_UID; import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_CHANNEL_PREFERENCES; import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_PREFERENCES; +import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_PREFERENCES__FSI_STATE__NOT_REQUESTED; +import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_PREFERENCES__FSI_STATE__GRANTED; +import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_PREFERENCES__FSI_STATE__DENIED; import static com.android.os.AtomsProto.PackageNotificationChannelPreferences.CHANNEL_ID_FIELD_NUMBER; import static com.android.os.AtomsProto.PackageNotificationChannelPreferences.CHANNEL_NAME_FIELD_NUMBER; import static com.android.os.AtomsProto.PackageNotificationChannelPreferences.IMPORTANCE_FIELD_NUMBER; @@ -48,7 +51,6 @@ import static com.android.server.notification.PreferencesHelper.DEFAULT_BUBBLE_P import static com.android.server.notification.PreferencesHelper.NOTIFICATION_CHANNEL_COUNT_LIMIT; import static com.android.server.notification.PreferencesHelper.NOTIFICATION_CHANNEL_GROUP_COUNT_LIMIT; import static com.android.server.notification.PreferencesHelper.UNKNOWN_UID; - import static com.google.common.truth.Truth.assertThat; import static junit.framework.Assert.assertNull; @@ -102,6 +104,7 @@ import android.os.RemoteCallback; import android.os.RemoteException; import android.os.UserHandle; import android.os.UserManager; +import android.permission.PermissionManager; import android.provider.Settings; import android.provider.Settings.Global; import android.provider.Settings.Secure; @@ -185,6 +188,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { @Mock Context mContext; @Mock ZenModeHelper mMockZenModeHelper; @Mock AppOpsManager mAppOpsManager; + @Mock PermissionManager mPermissionManager; private NotificationManager.Policy mTestNotificationPolicy; @@ -218,6 +222,8 @@ public class PreferencesHelperTest extends UiServiceTestCase { InstrumentationRegistry.getContext().getResources()); when(mContext.getContentResolver()).thenReturn( InstrumentationRegistry.getContext().getContentResolver()); + when(mPm.getPermissionFlags(any(), any(), any())) + .thenReturn(PackageManager.FLAG_PERMISSION_USER_SET); when(mContext.getPackageManager()).thenReturn(mPm); when(mContext.getApplicationInfo()).thenReturn(legacy); // most tests assume badging is enabled @@ -303,7 +309,8 @@ public class PreferencesHelperTest extends UiServiceTestCase { mStatsEventBuilderFactory = new WrappedSysUiStatsEvent.WrappedBuilderFactory(); mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, - mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false); + mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, + mStatsEventBuilderFactory, false); resetZenModeHelper(); mAudioAttributes = new AudioAttributes.Builder() @@ -645,7 +652,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { @Test public void testReadXml_oldXml_migrates() throws Exception { mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, - mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory, true); + mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mStatsEventBuilderFactory, true); String xml = "<ranking version=\"2\">\n" + "<package name=\"" + PKG_N_MR1 + "\" uid=\"" + UID_N_MR1 @@ -711,7 +718,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { @Test public void testReadXml_oldXml_backup_migratesWhenPkgInstalled() throws Exception { mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, - mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false); + mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false); when(mPm.getPackageUidAsUser("pkg1", USER_SYSTEM)).thenReturn(UNKNOWN_UID); when(mPm.getPackageUidAsUser("pkg2", USER_SYSTEM)).thenReturn(UNKNOWN_UID); @@ -789,7 +796,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { @Test public void testReadXml_newXml_noMigration_showPermissionNotification() throws Exception { mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, - mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory, true); + mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mStatsEventBuilderFactory, true); String xml = "<ranking version=\"3\">\n" + "<package name=\"" + PKG_N_MR1 + "\" show_badge=\"true\">\n" @@ -846,7 +853,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { @Test public void testReadXml_newXml_permissionNotificationOff() throws Exception { mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, - mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false); + mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false); String xml = "<ranking version=\"3\">\n" + "<package name=\"" + PKG_N_MR1 + "\" show_badge=\"true\">\n" @@ -903,7 +910,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { @Test public void testReadXml_newXml_noMigration_noPermissionNotification() throws Exception { mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, - mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory, true); + mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mStatsEventBuilderFactory, true); String xml = "<ranking version=\"4\">\n" + "<package name=\"" + PKG_N_MR1 + "\" show_badge=\"true\">\n" @@ -959,7 +966,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { @Test public void testReadXml_oldXml_migration_NoUid() throws Exception { mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, - mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false); + mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false); when(mPm.getPackageUidAsUser("something", USER_SYSTEM)).thenReturn(UNKNOWN_UID); String xml = "<ranking version=\"2\">\n" @@ -992,7 +999,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { @Test public void testReadXml_newXml_noMigration_NoUid() throws Exception { mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, - mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false); + mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false); when(mPm.getPackageUidAsUser("something", USER_SYSTEM)).thenReturn(UNKNOWN_UID); String xml = "<ranking version=\"3\">\n" @@ -1024,7 +1031,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { @Test public void testChannelXmlForNonBackup_postMigration() throws Exception { mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, - mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false); + mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false); ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> appPermissions = new ArrayMap<>(); appPermissions.put(new Pair<>(1, "first"), new Pair<>(true, false)); @@ -1110,7 +1117,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { @Test public void testChannelXmlForBackup_postMigration() throws Exception { mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, - mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false); + mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false); ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> appPermissions = new ArrayMap<>(); appPermissions.put(new Pair<>(1, "first"), new Pair<>(true, false)); @@ -1202,7 +1209,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { @Test public void testChannelXmlForBackup_postMigration_noExternal() throws Exception { mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, - mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false); + mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false); ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> appPermissions = new ArrayMap<>(); appPermissions.put(new Pair<>(UID_P, PKG_P), new Pair<>(true, false)); @@ -1287,7 +1294,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { @Test public void testChannelXmlForBackup_postMigration_noLocalSettings() throws Exception { mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, - mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false); + mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false); ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> appPermissions = new ArrayMap<>(); appPermissions.put(new Pair<>(1, "first"), new Pair<>(true, false)); @@ -1498,7 +1505,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { new FileNotFoundException("")).thenReturn(resId); mHelper = new PreferencesHelper(mContext, mPm, mHandler, mMockZenModeHelper, - mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false); + mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false); NotificationChannel channel = new NotificationChannel("id", "name", IMPORTANCE_LOW); @@ -2443,7 +2450,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND, 0); when(mMockZenModeHelper.getNotificationPolicy()).thenReturn(mTestNotificationPolicy); mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, - mPermissionHelper, mLogger, + mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false); @@ -2474,7 +2481,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND, 0); when(mMockZenModeHelper.getNotificationPolicy()).thenReturn(mTestNotificationPolicy); mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, - mPermissionHelper, mLogger, + mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false); // create notification channel that can bypass dnd, but app is blocked @@ -2498,7 +2505,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND, 0); when(mMockZenModeHelper.getNotificationPolicy()).thenReturn(mTestNotificationPolicy); mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, - mPermissionHelper, mLogger, + mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false); // create notification channel that can bypass dnd, but app is blocked @@ -2552,7 +2559,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND, 0); when(mMockZenModeHelper.getNotificationPolicy()).thenReturn(mTestNotificationPolicy); mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, - mPermissionHelper, mLogger, + mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false); assertFalse(mHelper.areChannelsBypassingDnd()); verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any(), anyInt(), anyBoolean()); @@ -2565,7 +2572,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { mTestNotificationPolicy = new NotificationManager.Policy(0, 0, 0, 0, 0, 0); when(mMockZenModeHelper.getNotificationPolicy()).thenReturn(mTestNotificationPolicy); mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, - mPermissionHelper, mLogger, + mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false); assertFalse(mHelper.areChannelsBypassingDnd()); verify(mMockZenModeHelper, never()).setNotificationPolicy(any(), anyInt(), anyBoolean()); @@ -3668,7 +3675,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { + "</package>\n" + "</ranking>\n"; mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, - mPermissionHelper, mLogger, + mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false); loadByteArrayXml(preQXml.getBytes(), true, USER_SYSTEM); @@ -3682,7 +3689,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL); mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, - mPermissionHelper, mLogger, + mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false); loadStreamXml(baos, false, UserHandle.USER_ALL); @@ -3752,7 +3759,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL); mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, - mPermissionHelper, mLogger, + mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false); loadStreamXml(baos, false, UserHandle.USER_ALL); @@ -3765,7 +3772,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL); mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, - mPermissionHelper, mLogger, + mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false); loadStreamXml(baos, false, UserHandle.USER_ALL); @@ -3779,7 +3786,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL); mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, - mPermissionHelper, mLogger, + mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false); loadStreamXml(baos, false, UserHandle.USER_ALL); @@ -3795,7 +3802,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL); mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, - mPermissionHelper, mLogger, + mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false); loadStreamXml(baos, false, UserHandle.USER_ALL); @@ -3851,7 +3858,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL); mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, - mPermissionHelper, mLogger, + mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false); loadStreamXml(baos, false, UserHandle.USER_ALL); @@ -3889,7 +3896,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL); mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, - mPermissionHelper, mLogger, + mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false); loadStreamXml(baos, false, UserHandle.USER_ALL); @@ -4547,7 +4554,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { @Test public void testPlaceholderConversationId_shortcutRequired() throws Exception { mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, - mPermissionHelper, mLogger, + mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false); final String xml = "<ranking version=\"1\">\n" @@ -4567,7 +4574,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { @Test public void testNormalConversationId_shortcutRequired() throws Exception { mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, - mPermissionHelper, mLogger, + mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false); final String xml = "<ranking version=\"1\">\n" @@ -4587,7 +4594,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { @Test public void testNoConversationId_shortcutRequired() throws Exception { mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, - mPermissionHelper, mLogger, + mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false); final String xml = "<ranking version=\"1\">\n" @@ -4607,7 +4614,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { @Test public void testDeleted_noTime() throws Exception { mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, - mPermissionHelper, mLogger, + mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false); final String xml = "<ranking version=\"1\">\n" @@ -4627,7 +4634,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { @Test public void testDeleted_twice() throws Exception { mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, - mPermissionHelper, mLogger, + mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false); mHelper.createNotificationChannel( @@ -4642,7 +4649,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { @Test public void testDeleted_recentTime() throws Exception { mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, - mPermissionHelper, mLogger, + mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false); mHelper.createNotificationChannel( @@ -4661,7 +4668,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { null); parser.nextTag(); mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, - mPermissionHelper, mLogger, + mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false); mHelper.readXml(parser, true, USER_SYSTEM); @@ -4673,7 +4680,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { @Test public void testUnDelete_time() throws Exception { mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, - mPermissionHelper, mLogger, + mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false); mHelper.createNotificationChannel( @@ -4695,7 +4702,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { @Test public void testDeleted_longTime() throws Exception { mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, - mPermissionHelper, mLogger, + mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false); long time = System.currentTimeMillis() - (DateUtils.DAY_IN_MILLIS * 30); @@ -5464,4 +5471,93 @@ public class PreferencesHelperTest extends UiServiceTestCase { verifyZeroInteractions(mHandler); } + + @Test + public void testGetFsiState_flagDisabled_zero() { + final int fsiState = mHelper.getFsiState("pkg", /* uid= */ 0, + /* requestedFsiPermission */ true, /* isFlagEnabled= */ false); + + assertEquals(0, fsiState); + } + + @Test + public void testGetFsiState_appDidNotRequest_enumNotRequested() { + final int fsiState = mHelper.getFsiState("pkg", /* uid= */ 0, + /* requestedFsiPermission */ false, /* isFlagEnabled= */ true); + + assertEquals(PACKAGE_NOTIFICATION_PREFERENCES__FSI_STATE__NOT_REQUESTED, fsiState); + } + + @Test + public void testGetFsiState_permissionGranted_enumGranted() { + when(mPermissionManager.checkPermissionForPreflight(any(), any())) + .thenReturn(PermissionManager.PERMISSION_GRANTED); + + final int fsiState = mHelper.getFsiState("pkg", /* uid= */ 0, + /* requestedFsiPermission= */ true, /* isFlagEnabled= */ true); + + assertEquals(PACKAGE_NOTIFICATION_PREFERENCES__FSI_STATE__GRANTED, fsiState); + } + + @Test + public void testGetFsiState_permissionSoftDenied_enumDenied() { + when(mPermissionManager.checkPermissionForPreflight(any(), any())) + .thenReturn(PermissionManager.PERMISSION_SOFT_DENIED); + + final int fsiState = mHelper.getFsiState("pkg", /* uid= */ 0, + /* requestedFsiPermission = */ true, /* isFlagEnabled= */ true); + + assertEquals(PACKAGE_NOTIFICATION_PREFERENCES__FSI_STATE__DENIED, fsiState); + } + + @Test + public void testGetFsiState_permissionHardDenied_enumDenied() { + when(mPermissionManager.checkPermissionForPreflight(any(), any())) + .thenReturn(PermissionManager.PERMISSION_HARD_DENIED); + + final int fsiState = mHelper.getFsiState("pkg", /* uid= */ 0, + /* requestedFsiPermission = */ true, /* isFlagEnabled= */ true); + + assertEquals(PACKAGE_NOTIFICATION_PREFERENCES__FSI_STATE__DENIED, fsiState); + } + + @Test + public void testIsFsiPermissionUserSet_appDidNotRequest_false() { + final boolean isUserSet = mHelper.isFsiPermissionUserSet("pkg", /* uid= */ 0, + /* fsiState = */ PACKAGE_NOTIFICATION_PREFERENCES__FSI_STATE__NOT_REQUESTED, + /* currentPermissionFlags= */ PackageManager.FLAG_PERMISSION_USER_SET, + /* isStickyHunFlagEnabled= */ true); + + assertFalse(isUserSet); + } + + @Test + public void testIsFsiPermissionUserSet_flagDisabled_false() { + final boolean isUserSet = mHelper.isFsiPermissionUserSet("pkg", /* uid= */ 0, + /* fsiState = */ PACKAGE_NOTIFICATION_PREFERENCES__FSI_STATE__GRANTED, + /* currentPermissionFlags= */ PackageManager.FLAG_PERMISSION_USER_SET, + /* isStickyHunFlagEnabled= */ false); + + assertFalse(isUserSet); + } + + @Test + public void testIsFsiPermissionUserSet_userSet_true() { + final boolean isUserSet = mHelper.isFsiPermissionUserSet("pkg", /* uid= */ 0, + /* fsiState = */ PACKAGE_NOTIFICATION_PREFERENCES__FSI_STATE__GRANTED, + /* currentPermissionFlags= */ PackageManager.FLAG_PERMISSION_USER_SET, + /* isStickyHunFlagEnabled= */ true); + + assertTrue(isUserSet); + } + + @Test + public void testIsFsiPermissionUserSet_notUserSet_false() { + final boolean isUserSet = mHelper.isFsiPermissionUserSet("pkg", /* uid= */ 0, + /* fsiState = */ PACKAGE_NOTIFICATION_PREFERENCES__FSI_STATE__GRANTED, + /* currentPermissionFlags= */ ~PackageManager.FLAG_PERMISSION_USER_SET, + /* isStickyHunFlagEnabled= */ true); + + assertFalse(isUserSet); + } } diff --git a/services/tests/uiservicestests/src/com/android/server/notification/RateEstimatorTest.java b/services/tests/uiservicestests/src/com/android/server/notification/RateEstimatorTest.java index 68aa1dc1bf3b..131aaa0d4ea7 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/RateEstimatorTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/RateEstimatorTest.java @@ -15,8 +15,9 @@ */ package com.android.server.notification; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static com.google.common.truth.Truth.assertThat; + +import static java.util.concurrent.TimeUnit.HOURS; import android.test.suitebuilder.annotation.SmallTest; @@ -42,110 +43,120 @@ public class RateEstimatorTest extends UiServiceTestCase { @Test public void testRunningTimeBackwardDoesntExplodeUpdate() throws Exception { - assertUpdateTime(mTestStartTime); - assertUpdateTime(mTestStartTime - 1000L); + updateAndVerifyRate(mTestStartTime); + updateAndVerifyRate(mTestStartTime - 1000L); } @Test public void testRunningTimeBackwardDoesntExplodeGet() throws Exception { - assertUpdateTime(mTestStartTime); + updateAndVerifyRate(mTestStartTime); final float rate = mEstimator.getRate(mTestStartTime - 1000L); - assertFalse(Float.isInfinite(rate)); - assertFalse(Float.isNaN(rate)); + assertThat(rate).isFinite(); } @Test public void testInstantaneousEventsDontExplodeUpdate() throws Exception { - assertUpdateTime(mTestStartTime); - assertUpdateTime(mTestStartTime); + updateAndVerifyRate(mTestStartTime); + updateAndVerifyRate(mTestStartTime); } @Test public void testInstantaneousEventsDontExplodeGet() throws Exception { - assertUpdateTime(mTestStartTime); - assertUpdateTime(mTestStartTime); + updateAndVerifyRate(mTestStartTime); + updateAndVerifyRate(mTestStartTime); final float rate = mEstimator.getRate(mTestStartTime); - assertFalse(Float.isInfinite(rate)); - assertFalse(Float.isNaN(rate)); + assertThat(rate).isFinite(); } @Test public void testInstantaneousBurstIsEstimatedUnderTwoPercent() throws Exception { - assertUpdateTime(mTestStartTime); + updateAndVerifyRate(mTestStartTime); long eventStart = mTestStartTime + 1000; // start event a long time after initialization long nextEventTime = postEvents(eventStart, 0, 5); // five events at \inf final float rate = mEstimator.getRate(nextEventTime); - assertLessThan("Rate", rate, 20f); + assertThat(rate).isLessThan(20f); } @Test public void testCompactBurstIsEstimatedUnderTwoPercent() throws Exception { - assertUpdateTime(mTestStartTime); + updateAndVerifyRate(mTestStartTime); long eventStart = mTestStartTime + 1000; // start event a long time after initialization long nextEventTime = postEvents(eventStart, 1, 5); // five events at 1000Hz final float rate = mEstimator.getRate(nextEventTime); - assertLessThan("Rate", rate, 20f); + assertThat(rate).isLessThan(20f); } @Test public void testSustained1000HzBurstIsEstimatedOverNinetyPercent() throws Exception { - assertUpdateTime(mTestStartTime); + updateAndVerifyRate(mTestStartTime); long eventStart = mTestStartTime + 1000; // start event a long time after initialization long nextEventTime = postEvents(eventStart, 1, 100); // one hundred events at 1000Hz final float rate = mEstimator.getRate(nextEventTime); - assertGreaterThan("Rate", rate, 900f); + assertThat(rate).isGreaterThan(900f); } @Test public void testSustained100HzBurstIsEstimatedOverNinetyPercent() throws Exception { - assertUpdateTime(mTestStartTime); + updateAndVerifyRate(mTestStartTime); long eventStart = mTestStartTime + 1000; // start event a long time after initialization long nextEventTime = postEvents(eventStart, 10, 100); // one hundred events at 100Hz final float rate = mEstimator.getRate(nextEventTime); - assertGreaterThan("Rate", rate, 90f); + assertThat(rate).isGreaterThan(90f); } @Test public void testRecoverQuicklyAfterSustainedBurst() throws Exception { - assertUpdateTime(mTestStartTime); + updateAndVerifyRate(mTestStartTime); long eventStart = mTestStartTime + 1000; // start event a long time after initialization - long nextEventTime = postEvents(eventStart, 10, 1000); // one hundred events at 100Hz - final float rate = mEstimator.getRate(nextEventTime + 5000L); // two seconds later - assertLessThan("Rate", rate, 2f); + long nextEventTime = postEvents(eventStart, 10, 1000); // one thousand events at 100Hz + final float rate = mEstimator.getRate(nextEventTime + 5000L); // five seconds later + assertThat(rate).isLessThan(2f); } @Test public void testEstimateShouldNotOvershoot() throws Exception { - assertUpdateTime(mTestStartTime); + updateAndVerifyRate(mTestStartTime); long eventStart = mTestStartTime + 1000; // start event a long time after initialization - long nextEventTime = postEvents(eventStart, 1, 1000); // one thousand events at 1000Hz + long nextEventTime = postEvents(eventStart, 1, 5000); // five thousand events at 1000Hz final float rate = mEstimator.getRate(nextEventTime); - assertLessThan("Rate", rate, 1000f); + assertThat(rate).isAtMost(1000f); } @Test public void testGetRateWithoutUpdate() throws Exception { final float rate = mEstimator.getRate(mTestStartTime); - assertLessThan("Rate", rate, 0.1f); + assertThat(rate).isLessThan(0.1f); } @Test public void testGetRateWithOneUpdate() throws Exception { - assertUpdateTime(mTestStartTime); + updateAndVerifyRate(mTestStartTime); final float rate = mEstimator.getRate(mTestStartTime+1); - assertLessThan("Rate", rate, 1f); + assertThat(rate).isLessThan(1f); } - private void assertLessThan(String label, float a, float b) { - assertTrue(String.format("%s was %f, but should be less than %f", label, a, b), a <= b); - } + @Test + public void testEstimateCatchesUpQuickly() { + long nextEventTime = postEvents(mTestStartTime, 100, 30); // 30 events at 10Hz + + final float firstBurstRate = mEstimator.getRate(nextEventTime); + assertThat(firstBurstRate).isWithin(2f).of(10); + + nextEventTime += HOURS.toMillis(3); // 3 hours later... + nextEventTime = postEvents(nextEventTime, 100, 30); // same burst of 30 events at 10Hz + + // Catching up. Rate is not yet 10, since we had a long period of inactivity... + float secondBurstRate = mEstimator.getRate(nextEventTime); + assertThat(secondBurstRate).isWithin(1f).of(6); - private void assertGreaterThan(String label, float a, float b) { - assertTrue(String.format("%s was %f, but should be more than %f", label, a, b), a >= b); + // ... but after a few more events, we are there. + nextEventTime = postEvents(nextEventTime, 100, 10); // 10 more events at 10Hz + secondBurstRate = mEstimator.getRate(nextEventTime); + assertThat(secondBurstRate).isWithin(1f).of(10); } - /** @returns the next event time. */ + /** @return the next event time. */ private long postEvents(long start, long dt, int num) { long time = start; for (int i = 0; i < num; i++) { @@ -155,9 +166,8 @@ public class RateEstimatorTest extends UiServiceTestCase { return time; } - private void assertUpdateTime(long time) { - final float rate = mEstimator.update(time); - assertFalse(Float.isInfinite(rate)); - assertFalse(Float.isNaN(rate)); + private void updateAndVerifyRate(long time) { + mEstimator.update(time); + assertThat(mEstimator.getRate(time)).isFinite(); } -} +}
\ No newline at end of file diff --git a/services/tests/wmtests/Android.bp b/services/tests/wmtests/Android.bp index e537197584cb..5f48f3ce1d68 100644 --- a/services/tests/wmtests/Android.bp +++ b/services/tests/wmtests/Android.bp @@ -82,7 +82,10 @@ android_test { ], platform_apis: true, - test_suites: ["device-tests"], + test_suites: [ + "device-tests", + "automotive-tests", + ], certificate: "platform", diff --git a/services/tests/wmtests/AndroidManifest.xml b/services/tests/wmtests/AndroidManifest.xml index 37e5da5f5eaf..9c793752bdf9 100644 --- a/services/tests/wmtests/AndroidManifest.xml +++ b/services/tests/wmtests/AndroidManifest.xml @@ -96,6 +96,19 @@ android:theme="@style/WhiteBackgroundTheme" android:exported="true"/> + <activity android:name="com.android.server.wm.TrustedPresentationCallbackTest$TestActivity" + android:exported="true" + android:showWhenLocked="true" + android:turnScreenOn="true" /> + + <activity + android:name="androidx.test.core.app.InstrumentationActivityInvoker$EmptyActivity" + android:exported="true"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + </intent-filter> + </activity> + <service android:name="android.view.cts.surfacevalidator.LocalMediaProjectionService" android:foregroundServiceType="mediaProjection" android:enabled="true"> diff --git a/services/tests/wmtests/src/com/android/server/policy/PowerKeyGestureTests.java b/services/tests/wmtests/src/com/android/server/policy/PowerKeyGestureTests.java index 5863e9d9243a..6a9f283c9c4c 100644 --- a/services/tests/wmtests/src/com/android/server/policy/PowerKeyGestureTests.java +++ b/services/tests/wmtests/src/com/android/server/policy/PowerKeyGestureTests.java @@ -58,9 +58,20 @@ public class PowerKeyGestureTests extends ShortcutKeyTestBase { mPhoneWindowManager.overrideCanStartDreaming(true); sendKey(KEYCODE_POWER); mPhoneWindowManager.assertDreamRequest(); + mPhoneWindowManager.overrideIsDreaming(true); mPhoneWindowManager.assertLockedAfterAppTransitionFinished(); } + @Test + public void testAppTransitionFinishedCalledAfterDreamStoppedWillNotLockAgain() { + mPhoneWindowManager.overrideShortPressOnPower(SHORT_PRESS_POWER_DREAM_OR_SLEEP); + mPhoneWindowManager.overrideCanStartDreaming(true); + sendKey(KEYCODE_POWER); + mPhoneWindowManager.assertDreamRequest(); + mPhoneWindowManager.overrideIsDreaming(false); + mPhoneWindowManager.assertDidNotLockAfterAppTransitionFinished(); + } + /** * Power double-press to launch camera does not lock device when the single press behavior is to * dream. @@ -72,7 +83,7 @@ public class PowerKeyGestureTests extends ShortcutKeyTestBase { sendKey(KEYCODE_POWER); sendKey(KEYCODE_POWER); mPhoneWindowManager.assertCameraLaunch(); - mPhoneWindowManager.assertWillNotLockAfterAppTransitionFinished(); + mPhoneWindowManager.assertDidNotLockAfterAppTransitionFinished(); } /** diff --git a/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java b/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java index 676bfb00c60c..2015ae9b8081 100644 --- a/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java +++ b/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java @@ -39,6 +39,7 @@ import static android.view.KeyEvent.META_SHIFT_RIGHT_ON; import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy; import static com.android.server.policy.WindowManagerPolicy.ACTION_PASS_TO_USER; import static java.util.Collections.unmodifiableMap; @@ -59,7 +60,7 @@ import java.util.Map; class ShortcutKeyTestBase { TestPhoneWindowManager mPhoneWindowManager; - final Context mContext = getInstrumentation().getTargetContext(); + final Context mContext = spy(getInstrumentation().getTargetContext()); /** Modifier key to meta state */ private static final Map<Integer, Integer> MODIFIER; diff --git a/services/tests/wmtests/src/com/android/server/policy/StemKeyGestureTests.java b/services/tests/wmtests/src/com/android/server/policy/StemKeyGestureTests.java new file mode 100644 index 000000000000..fe8017e5f513 --- /dev/null +++ b/services/tests/wmtests/src/com/android/server/policy/StemKeyGestureTests.java @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.policy; + +import static android.view.KeyEvent.KEYCODE_STEM_PRIMARY; + +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; +import static com.android.server.policy.PhoneWindowManager.SHORT_PRESS_PRIMARY_LAUNCH_ALL_APPS; + +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; + +import android.content.Context; +import android.content.res.Resources; + +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.Mockito; + +/** + * Test class for stem key gesture. + * + * Build/Install/Run: + * atest WmTests:StemKeyGestureTests + */ +public class StemKeyGestureTests extends ShortcutKeyTestBase { + @Mock private Resources mResources; + + /** + * Stem single key should not launch behavior during set up. + */ + @Test + public void stemSingleKey_duringSetup_doNothing() { + stemKeySetup( + () -> overrideBehavior( + com.android.internal.R.integer.config_shortPressOnStemPrimaryBehavior, + SHORT_PRESS_PRIMARY_LAUNCH_ALL_APPS)); + mPhoneWindowManager.setKeyguardServiceDelegateIsShowing(false); + mPhoneWindowManager.overrideIsUserSetupComplete(false); + + sendKey(KEYCODE_STEM_PRIMARY); + + mPhoneWindowManager.assertNotOpenAllAppView(); + } + + /** + * Stem single key should launch all app after set up. + */ + @Test + public void stemSingleKey_AfterSetup_openAllApp() { + stemKeySetup( + () -> overrideBehavior( + com.android.internal.R.integer.config_shortPressOnStemPrimaryBehavior, + SHORT_PRESS_PRIMARY_LAUNCH_ALL_APPS)); + mPhoneWindowManager.setKeyguardServiceDelegateIsShowing(false); + mPhoneWindowManager.overrideIsUserSetupComplete(true); + + sendKey(KEYCODE_STEM_PRIMARY); + + mPhoneWindowManager.assertOpenAllAppView(); + } + + private void stemKeySetup(Runnable behaviorOverrideRunnable) { + super.tearDown(); + setupResourcesMock(); + behaviorOverrideRunnable.run(); + super.setUp(); + } + + private void setupResourcesMock() { + Resources realResources = mContext.getResources(); + + mResources = Mockito.mock(Resources.class); + doReturn(mResources).when(mContext).getResources(); + + doAnswer(invocation -> realResources.getXml((Integer) invocation.getArguments()[0])) + .when(mResources).getXml(anyInt()); + doAnswer(invocation -> realResources.getString((Integer) invocation.getArguments()[0])) + .when(mResources).getString(anyInt()); + doAnswer(invocation -> realResources.getBoolean((Integer) invocation.getArguments()[0])) + .when(mResources).getBoolean(anyInt()); + } + + private void overrideBehavior(int resId, int expectedBehavior) { + doReturn(expectedBehavior).when(mResources).getInteger(eq(resId)); + } +} diff --git a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java index d38302429c02..766a88f6476c 100644 --- a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java +++ b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java @@ -16,6 +16,7 @@ package com.android.server.policy; +import static android.os.Build.HW_TIMEOUT_MULTIPLIER; import static android.provider.Settings.Secure.VOLUME_HUSH_MUTE; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.Display.STATE_ON; @@ -43,8 +44,11 @@ import static com.android.server.policy.PhoneWindowManager.LONG_PRESS_POWER_SHUT import static com.android.server.policy.PhoneWindowManager.POWER_VOLUME_UP_BEHAVIOR_MUTE; import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.Mockito.CALLS_REAL_METHODS; +import static org.mockito.Mockito.after; import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mockingDetails; import static org.mockito.Mockito.timeout; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.withSettings; @@ -61,9 +65,11 @@ import android.hardware.display.DisplayManagerInternal; import android.media.AudioManagerInternal; import android.os.Handler; import android.os.HandlerThread; +import android.os.IBinder; import android.os.PowerManager; import android.os.PowerManagerInternal; import android.os.RemoteException; +import android.os.UserHandle; import android.os.Vibrator; import android.service.dreams.DreamManagerInternal; import android.telecom.TelecomManager; @@ -79,6 +85,7 @@ import com.android.server.LocalServices; import com.android.server.input.InputManagerInternal; import com.android.server.inputmethod.InputMethodManagerInternal; import com.android.server.pm.UserManagerInternal; +import com.android.server.policy.keyguard.KeyguardServiceDelegate; import com.android.server.statusbar.StatusBarManagerInternal; import com.android.server.vr.VrManagerInternal; import com.android.server.wm.ActivityTaskManagerInternal; @@ -100,6 +107,8 @@ import java.util.function.Supplier; class TestPhoneWindowManager { private static final long SHORTCUT_KEY_DELAY_MILLIS = 150; + private static final long TEST_SINGLE_KEY_DELAY_MILLIS + = SingleKeyGestureDetector.MULTI_PRESS_TIMEOUT + 1000L * HW_TIMEOUT_MULTIPLIER; private PhoneWindowManager mPhoneWindowManager; private Context mContext; @@ -134,6 +143,8 @@ class TestPhoneWindowManager { @Mock private StatusBarManagerInternal mStatusBarManagerInternal; + @Mock private KeyguardServiceDelegate mKeyguardServiceDelegate; + private StaticMockitoSession mMockitoSession; private HandlerThread mHandlerThread; private Handler mHandler; @@ -151,6 +162,10 @@ class TestPhoneWindowManager { Supplier<GlobalActions> getGlobalActionsFactory() { return () -> mGlobalActions; } + + KeyguardServiceDelegate getKeyguardServiceDelegate() { + return mKeyguardServiceDelegate; + } } TestPhoneWindowManager(Context context) { @@ -158,12 +173,12 @@ class TestPhoneWindowManager { mHandlerThread = new HandlerThread("fake window manager"); mHandlerThread.start(); mHandler = new Handler(mHandlerThread.getLooper()); - mHandler.runWithScissors(()-> setUp(context), 0 /* timeout */); + mContext = mockingDetails(context).isSpy() ? context : spy(context); + mHandler.runWithScissors(this::setUp, 0 /* timeout */); } - private void setUp(Context context) { + private void setUp() { mPhoneWindowManager = spy(new PhoneWindowManager()); - mContext = spy(context); // Use stubOnly() to reduce memory usage if it doesn't need verification. final MockSettings spyStubOnly = withSettings().stubOnly() @@ -251,6 +266,7 @@ class TestPhoneWindowManager { overrideLaunchAccessibility(); doReturn(false).when(mPhoneWindowManager).keyguardOn(); doNothing().when(mContext).startActivityAsUser(any(), any()); + doNothing().when(mContext).startActivityAsUser(any(), any(), any()); Mockito.reset(mContext); } @@ -329,6 +345,10 @@ class TestPhoneWindowManager { doReturn(canDream).when(mDreamManagerInternal).canStartDreaming(anyBoolean()); } + void overrideIsDreaming(boolean isDreaming) { + doReturn(isDreaming).when(mDreamManagerInternal).isDreaming(); + } + void overrideDisplayState(int state) { doReturn(state).when(mDisplay).getState(); doReturn(state == STATE_ON).when(mDisplayPolicy).isAwake(); @@ -381,6 +401,14 @@ class TestPhoneWindowManager { doNothing().when(mPhoneWindowManager).launchHomeFromHotKey(anyInt()); } + void overrideIsUserSetupComplete(boolean isCompleted) { + doReturn(isCompleted).when(mPhoneWindowManager).isUserSetupComplete(); + } + + void setKeyguardServiceDelegateIsShowing(boolean isShowing) { + doReturn(isShowing).when(mKeyguardServiceDelegate).isShowing(); + } + /** * Below functions will check the policy behavior could be invoked. */ @@ -497,21 +525,42 @@ class TestPhoneWindowManager { verify(mInputManagerInternal).toggleCapsLock(anyInt()); } - void assertWillNotLockAfterAppTransitionFinished() { - Assert.assertFalse(mPhoneWindowManager.mLockAfterAppTransitionFinished); - } - void assertLockedAfterAppTransitionFinished() { ArgumentCaptor<AppTransitionListener> transitionCaptor = ArgumentCaptor.forClass(AppTransitionListener.class); verify(mWindowManagerInternal).registerAppTransitionListener( transitionCaptor.capture()); - transitionCaptor.getValue().onAppTransitionFinishedLocked(any()); + final IBinder token = mock(IBinder.class); + transitionCaptor.getValue().onAppTransitionFinishedLocked(token); verify(mPhoneWindowManager).lockNow(null); } + void assertDidNotLockAfterAppTransitionFinished() { + ArgumentCaptor<AppTransitionListener> transitionCaptor = + ArgumentCaptor.forClass(AppTransitionListener.class); + verify(mWindowManagerInternal).registerAppTransitionListener( + transitionCaptor.capture()); + final IBinder token = mock(IBinder.class); + transitionCaptor.getValue().onAppTransitionFinishedLocked(token); + verify(mPhoneWindowManager, never()).lockNow(null); + } + void assertGoToHomescreen() { waitForIdle(); verify(mPhoneWindowManager).launchHomeFromHotKey(anyInt()); } + + void assertOpenAllAppView() { + waitForIdle(); + ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class); + verify(mContext, timeout(TEST_SINGLE_KEY_DELAY_MILLIS)) + .startActivityAsUser(intentCaptor.capture(), isNull(), any(UserHandle.class)); + Assert.assertEquals(Intent.ACTION_ALL_APPS, intentCaptor.getValue().getAction()); + } + + void assertNotOpenAllAppView() { + waitForIdle(); + verify(mContext, after(TEST_SINGLE_KEY_DELAY_MILLIS).never()) + .startActivityAsUser(any(Intent.class), any(), any(UserHandle.class)); + } } diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java index e881039db1df..3db53eb08ea1 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java @@ -3334,7 +3334,7 @@ public class ActivityRecordTests extends WindowTestsBase { // app1 requests IME visible. app1.setRequestedVisibleTypes(ime(), ime()); - mDisplayContent.getInsetsStateController().onInsetsModified(app1); + mDisplayContent.getInsetsStateController().onRequestedVisibleTypesChanged(app1); // Verify app1's IME insets is visible and app2's IME insets frozen flag set. assertTrue(app1.getInsetsState().peekSource(ID_IME).isVisible()); @@ -3403,7 +3403,7 @@ public class ActivityRecordTests extends WindowTestsBase { assertFalse(activity2.mImeInsetsFrozenUntilStartInput); app1.setRequestedVisibleTypes(ime()); - controller.onInsetsModified(app1); + controller.onRequestedVisibleTypesChanged(app1); // Expect all activities in split-screen will get IME insets visible state assertTrue(app1.getInsetsState().peekSource(ID_IME).isVisible()); diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java index 2b589bf59682..d179338bf947 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java @@ -1702,6 +1702,9 @@ public class ActivityStarterTests extends WindowTestsBase { @Test public void testRecordActivityMovementBeforeDeliverToTop() { + // Mock recents as task is only marked to be in recents + mAtm.mTaskSupervisor.setRecentTasks(mock(RecentTasks.class)); + final Task task = new TaskBuilder(mAtm.mTaskSupervisor).build(); final ActivityRecord activityBot = new ActivityBuilder(mAtm).setTask(task).build(); final ActivityRecord activityTop = new ActivityBuilder(mAtm).setTask(task).build(); diff --git a/services/tests/wmtests/src/com/android/server/wm/DesktopModeLaunchParamsModifierTests.java b/services/tests/wmtests/src/com/android/server/wm/DesktopModeLaunchParamsModifierTests.java index 3a456fb9366c..dc4e47dfea30 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DesktopModeLaunchParamsModifierTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DesktopModeLaunchParamsModifierTests.java @@ -18,6 +18,7 @@ package com.android.server.wm; import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.util.DisplayMetrics.DENSITY_DEFAULT; @@ -80,13 +81,27 @@ public class DesktopModeLaunchParamsModifierTests extends WindowTestsBase { } @Test - public void testReturnsSkipIfTaskNotUsingActivityTypeStandard() { + public void testReturnsSkipIfTaskNotUsingActivityTypeStandardOrUndefined() { final Task task = new TaskBuilder(mSupervisor).setActivityType( ACTIVITY_TYPE_ASSISTANT).build(); assertEquals(RESULT_SKIP, new CalculateRequestBuilder().setTask(task).calculate()); } @Test + public void testReturnsDoneIfTaskUsingActivityTypeStandard() { + final Task task = new TaskBuilder(mSupervisor).setActivityType( + ACTIVITY_TYPE_STANDARD).build(); + assertEquals(RESULT_DONE, new CalculateRequestBuilder().setTask(task).calculate()); + } + + @Test + public void testReturnsDoneIfTaskUsingActivityTypeUndefined() { + final Task task = new TaskBuilder(mSupervisor).setActivityType( + ACTIVITY_TYPE_UNDEFINED).build(); + assertEquals(RESULT_DONE, new CalculateRequestBuilder().setTask(task).calculate()); + } + + @Test public void testReturnsSkipIfCurrentParamsHasBounds() { final Task task = new TaskBuilder(mSupervisor).setActivityType( ACTIVITY_TYPE_STANDARD).build(); diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java index b0609ddfddfd..dd90e0450280 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java @@ -182,6 +182,44 @@ public class DisplayPolicyTests extends WindowTestsBase { dimmingNonImTarget, imeNonDrawNavBar, NAV_BAR_BOTTOM)); } + @Test + public void testChooseNavigationBackgroundWindow() { + final WindowState drawBarWin = createOpaqueFullscreen(false); + final WindowState nonDrawBarWin = createDimmingDialogWindow(true); + + final WindowState visibleIme = createInputMethodWindow(true, true, false); + final WindowState invisibleIme = createInputMethodWindow(false, true, false); + final WindowState nonDrawBarIme = createInputMethodWindow(true, false, false); + + assertEquals(drawBarWin, DisplayPolicy.chooseNavigationBackgroundWindow( + drawBarWin, null, NAV_BAR_BOTTOM)); + assertNull(DisplayPolicy.chooseNavigationBackgroundWindow( + null, null, NAV_BAR_BOTTOM)); + assertNull(DisplayPolicy.chooseNavigationBackgroundWindow( + nonDrawBarWin, null, NAV_BAR_BOTTOM)); + + assertEquals(visibleIme, DisplayPolicy.chooseNavigationBackgroundWindow( + drawBarWin, visibleIme, NAV_BAR_BOTTOM)); + assertEquals(visibleIme, DisplayPolicy.chooseNavigationBackgroundWindow( + null, visibleIme, NAV_BAR_BOTTOM)); + assertEquals(visibleIme, DisplayPolicy.chooseNavigationBackgroundWindow( + nonDrawBarWin, visibleIme, NAV_BAR_BOTTOM)); + + assertEquals(drawBarWin, DisplayPolicy.chooseNavigationBackgroundWindow( + drawBarWin, invisibleIme, NAV_BAR_BOTTOM)); + assertNull(DisplayPolicy.chooseNavigationBackgroundWindow( + null, invisibleIme, NAV_BAR_BOTTOM)); + assertNull(DisplayPolicy.chooseNavigationBackgroundWindow( + nonDrawBarWin, invisibleIme, NAV_BAR_BOTTOM)); + + assertEquals(drawBarWin, DisplayPolicy.chooseNavigationBackgroundWindow( + drawBarWin, nonDrawBarIme, NAV_BAR_BOTTOM)); + assertNull(DisplayPolicy.chooseNavigationBackgroundWindow( + null, nonDrawBarIme, NAV_BAR_BOTTOM)); + assertNull(DisplayPolicy.chooseNavigationBackgroundWindow( + nonDrawBarWin, nonDrawBarIme, NAV_BAR_BOTTOM)); + } + @SetupWindows(addWindows = W_NAVIGATION_BAR) @Test public void testUpdateLightNavigationBarLw() { diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationCompatPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationCompatPolicyTests.java index 8e015d4d228d..769a309cf5a7 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationCompatPolicyTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationCompatPolicyTests.java @@ -103,8 +103,7 @@ public final class DisplayRotationCompatPolicyTests extends WindowTestsBase { public void setUp() throws Exception { mLetterboxConfiguration = mDisplayContent.mWmService.mLetterboxConfiguration; spyOn(mLetterboxConfiguration); - when(mLetterboxConfiguration.isCameraCompatTreatmentEnabled( - /* checkDeviceConfig */ anyBoolean())) + when(mLetterboxConfiguration.isCameraCompatTreatmentEnabled()) .thenReturn(true); when(mLetterboxConfiguration.isCameraCompatRefreshEnabled()) .thenReturn(true); @@ -177,8 +176,7 @@ public final class DisplayRotationCompatPolicyTests extends WindowTestsBase { @Test public void testOnScreenRotationAnimationFinished_treatmentNotEnabled_doNotShowToast() { - when(mLetterboxConfiguration.isCameraCompatTreatmentEnabled( - /* checkDeviceConfig */ anyBoolean())) + when(mLetterboxConfiguration.isCameraCompatTreatmentEnabled()) .thenReturn(false); spyOn(mDisplayRotationCompatPolicy); @@ -238,8 +236,7 @@ public final class DisplayRotationCompatPolicyTests extends WindowTestsBase { @Test public void testTreatmentNotEnabled_noForceRotationOrRefresh() throws Exception { - when(mLetterboxConfiguration.isCameraCompatTreatmentEnabled( - /* checkDeviceConfig */ anyBoolean())) + when(mLetterboxConfiguration.isCameraCompatTreatmentEnabled()) .thenReturn(false); configureActivity(SCREEN_ORIENTATION_PORTRAIT); @@ -253,8 +250,7 @@ public final class DisplayRotationCompatPolicyTests extends WindowTestsBase { @Test public void testTreatmentDisabledViaDeviceConfig_noForceRotationOrRefresh() throws Exception { - when(mLetterboxConfiguration.isCameraCompatTreatmentEnabled( - /* checkDeviceConfig */ true)) + when(mLetterboxConfiguration.isCameraCompatTreatmentEnabled()) .thenReturn(false); configureActivity(SCREEN_ORIENTATION_PORTRAIT); diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationImmersiveAppCompatPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationImmersiveAppCompatPolicyTests.java index d29b18f89f77..b1057032eb36 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationImmersiveAppCompatPolicyTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationImmersiveAppCompatPolicyTests.java @@ -31,7 +31,6 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.when; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.anyBoolean; import android.platform.test.annotations.Presubmit; import android.view.Surface; @@ -79,8 +78,11 @@ public class DisplayRotationImmersiveAppCompatPolicyTests extends WindowTestsBas when(mDisplayContent.getIgnoreOrientationRequest()).thenReturn(true); mMockLetterboxConfiguration = mock(LetterboxConfiguration.class); - when(mMockLetterboxConfiguration.isDisplayRotationImmersiveAppCompatPolicyEnabled( - /* checkDeviceConfig */ anyBoolean())).thenReturn(true); + when(mMockLetterboxConfiguration.isDisplayRotationImmersiveAppCompatPolicyEnabled()) + .thenReturn(true); + when(mMockLetterboxConfiguration + .isDisplayRotationImmersiveAppCompatPolicyEnabledAtBuildTime()) + .thenReturn(true); mPolicy = DisplayRotationImmersiveAppCompatPolicy.createIfNeeded( mMockLetterboxConfiguration, createDisplayRotationMock(), @@ -204,8 +206,8 @@ public class DisplayRotationImmersiveAppCompatPolicyTests extends WindowTestsBas @Test public void testRotationChoiceEnforcedOnly_featureFlagDisabled_lockNotEnforced() { - when(mMockLetterboxConfiguration.isDisplayRotationImmersiveAppCompatPolicyEnabled( - /* checkDeviceConfig */ true)).thenReturn(false); + when(mMockLetterboxConfiguration.isDisplayRotationImmersiveAppCompatPolicyEnabled()) + .thenReturn(false); assertIsRotationLockEnforcedReturnsFalseForAllRotations(); } diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java index a8fc25fc4477..204cbf79dba9 100644 --- a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java @@ -370,7 +370,7 @@ public class InsetsPolicyTest extends WindowTestsBase { mAppWindow.setRequestedVisibleTypes( navigationBars() | statusBars(), navigationBars() | statusBars()); - policy.onInsetsModified(mAppWindow); + policy.onRequestedVisibleTypesChanged(mAppWindow); waitUntilWindowAnimatorIdle(); controls = mDisplayContent.getInsetsStateController().getControlsForDispatch(mAppWindow); diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java index d7e736df064b..114796d17ef1 100644 --- a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java @@ -192,17 +192,16 @@ public class InsetsStateControllerTest extends WindowTestsBase { // This can be the IME z-order target while app cannot be the IME z-order target. // This is also the only IME control target in this test, so IME won't be invisible caused // by the control-target change. - mDisplayContent.updateImeInputAndControlTarget( - createWindow(null, TYPE_APPLICATION, "base")); + final WindowState base = createWindow(null, TYPE_APPLICATION, "base"); + mDisplayContent.updateImeInputAndControlTarget(base); // Make IME and stay visible during the test. mImeWindow.setHasSurface(true); getController().getOrCreateSourceProvider(ID_IME, ime()) .setWindowContainer(mImeWindow, null, null); - getController().onImeControlTargetChanged( - mDisplayContent.getImeInputTarget().getWindowState()); - mDisplayContent.getImeInputTarget().getWindowState().setRequestedVisibleTypes(ime(), ime()); - getController().onInsetsModified(mDisplayContent.getImeInputTarget().getWindowState()); + getController().onImeControlTargetChanged(base); + base.setRequestedVisibleTypes(ime(), ime()); + getController().onRequestedVisibleTypesChanged(base); // Send our spy window (app) into the system so that we can detect the invocation. final WindowState win = createWindow(null, TYPE_APPLICATION, "app"); @@ -485,7 +484,7 @@ public class InsetsStateControllerTest extends WindowTestsBase { mDisplayContent.updateImeInputAndControlTarget(app); app.setRequestedVisibleTypes(ime(), ime()); - getController().onInsetsModified(app); + getController().onRequestedVisibleTypesChanged(app); assertTrue(ime.getControllableInsetProvider().getSource().isVisible()); getController().updateAboveInsetsState(true /* notifyInsetsChange */); diff --git a/services/tests/wmtests/src/com/android/server/wm/LetterboxConfigurationDeviceConfigTests.java b/services/tests/wmtests/src/com/android/server/wm/LetterboxConfigurationDeviceConfigTests.java deleted file mode 100644 index 2b7a06bd35f3..000000000000 --- a/services/tests/wmtests/src/com/android/server/wm/LetterboxConfigurationDeviceConfigTests.java +++ /dev/null @@ -1,109 +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.server.wm; - -import static com.android.server.wm.LetterboxConfigurationDeviceConfig.sKeyToDefaultValueMap; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -import android.platform.test.annotations.Presubmit; -import android.provider.DeviceConfig; - -import androidx.test.filters.SmallTest; - -import com.android.modules.utils.testing.TestableDeviceConfig; - -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; - -import java.util.Map; - -/** - * Test class for {@link LetterboxConfigurationDeviceConfig}. - * - * atest WmTests:LetterboxConfigurationDeviceConfigTests - */ -@SmallTest -@Presubmit -public class LetterboxConfigurationDeviceConfigTests { - - private LetterboxConfigurationDeviceConfig mDeviceConfig; - - @Rule - public final TestableDeviceConfig.TestableDeviceConfigRule - mDeviceConfigRule = new TestableDeviceConfig.TestableDeviceConfigRule(); - - @Before - public void setUp() { - mDeviceConfig = new LetterboxConfigurationDeviceConfig(/* executor */ Runnable::run); - } - - @Test - public void testGetFlag_flagIsActive_flagChanges() throws Throwable { - for (Map.Entry<String, Boolean> entry : sKeyToDefaultValueMap.entrySet()) { - testGetFlagForKey_flagIsActive_flagChanges(entry.getKey(), entry.getValue()); - } - } - - private void testGetFlagForKey_flagIsActive_flagChanges(final String key, boolean defaultValue) - throws InterruptedException { - mDeviceConfig.updateFlagActiveStatus(/* isActive */ true, key); - - assertEquals("Unexpected default value for " + key, - mDeviceConfig.getFlag(key), defaultValue); - - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_WINDOW_MANAGER, key, - /* value */ Boolean.TRUE.toString(), /* makeDefault */ false); - - assertTrue("Flag " + key + "is not true after change", mDeviceConfig.getFlag(key)); - - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_WINDOW_MANAGER, key, - /* value */ Boolean.FALSE.toString(), /* makeDefault */ false); - - assertFalse("Flag " + key + "is not false after change", mDeviceConfig.getFlag(key)); - } - - @Test - public void testGetFlag_flagIsNotActive_alwaysReturnDefaultValue() throws Throwable { - for (Map.Entry<String, Boolean> entry : sKeyToDefaultValueMap.entrySet()) { - testGetFlagForKey_flagIsNotActive_alwaysReturnDefaultValue( - entry.getKey(), entry.getValue()); - } - } - - private void testGetFlagForKey_flagIsNotActive_alwaysReturnDefaultValue(final String key, - boolean defaultValue) throws InterruptedException { - assertEquals("Unexpected default value for " + key, - mDeviceConfig.getFlag(key), defaultValue); - - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_WINDOW_MANAGER, key, - /* value */ Boolean.TRUE.toString(), /* makeDefault */ false); - - assertEquals("Flag " + key + "is not set to default after change", - mDeviceConfig.getFlag(key), defaultValue); - - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_WINDOW_MANAGER, key, - /* value */ Boolean.FALSE.toString(), /* makeDefault */ false); - - assertEquals("Flag " + key + "is not set to default after change", - mDeviceConfig.getFlag(key), defaultValue); - } - -} diff --git a/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java index 7d507e9150e8..81a37943505f 100644 --- a/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java @@ -38,6 +38,7 @@ import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCA import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; import static android.content.res.Configuration.ORIENTATION_PORTRAIT; +import static android.view.InsetsSource.FLAG_INSETS_ROUNDED_CORNER; import static android.view.WindowManager.PROPERTY_CAMERA_COMPAT_ALLOW_FORCE_ROTATION; import static android.view.WindowManager.PROPERTY_CAMERA_COMPAT_ALLOW_REFRESH; import static android.view.WindowManager.PROPERTY_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE; @@ -62,7 +63,6 @@ import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.verify; @@ -146,7 +146,9 @@ public class LetterboxUiControllerTest extends WindowTestsBase { @Test @EnableCompatChanges({OVERRIDE_ENABLE_COMPAT_IGNORE_REQUESTED_ORIENTATION}) public void testShouldIgnoreRequestedOrientation_cameraCompatTreatment_returnsTrue() { - doReturn(true).when(mLetterboxConfiguration).isCameraCompatTreatmentEnabled(anyBoolean()); + doReturn(true).when(mLetterboxConfiguration).isCameraCompatTreatmentEnabled(); + doReturn(true).when(mLetterboxConfiguration) + .isCameraCompatTreatmentEnabledAtBuildTime(); // Recreate DisplayContent with DisplayRotationCompatPolicy mActivity = setUpActivityWithComponent(); @@ -305,7 +307,7 @@ public class LetterboxUiControllerTest extends WindowTestsBase { @Test public void testShouldRefreshActivityForCameraCompat_flagIsDisabled_returnsFalse() { doReturn(false).when(mLetterboxConfiguration) - .isCameraCompatTreatmentEnabled(/* checkDeviceConfig */ true); + .isCameraCompatTreatmentEnabled(); assertFalse(mController.shouldRefreshActivityForCameraCompat()); } @@ -314,7 +316,7 @@ public class LetterboxUiControllerTest extends WindowTestsBase { @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_DISABLE_REFRESH}) public void testShouldRefreshActivityForCameraCompat_overrideEnabled_returnsFalse() { doReturn(true).when(mLetterboxConfiguration) - .isCameraCompatTreatmentEnabled(/* checkDeviceConfig */ true); + .isCameraCompatTreatmentEnabled(); assertFalse(mController.shouldRefreshActivityForCameraCompat()); } @@ -324,7 +326,7 @@ public class LetterboxUiControllerTest extends WindowTestsBase { public void testShouldRefreshActivityForCameraCompat_propertyIsTrueAndOverride_returnsFalse() throws Exception { doReturn(true).when(mLetterboxConfiguration) - .isCameraCompatTreatmentEnabled(/* checkDeviceConfig */ true); + .isCameraCompatTreatmentEnabled(); mockThatProperty(PROPERTY_CAMERA_COMPAT_ALLOW_REFRESH, /* value */ true); mController = new LetterboxUiController(mWm, mActivity); @@ -336,7 +338,7 @@ public class LetterboxUiControllerTest extends WindowTestsBase { public void testShouldRefreshActivityForCameraCompat_propertyIsFalse_returnsFalse() throws Exception { doReturn(true).when(mLetterboxConfiguration) - .isCameraCompatTreatmentEnabled(/* checkDeviceConfig */ true); + .isCameraCompatTreatmentEnabled(); mockThatProperty(PROPERTY_CAMERA_COMPAT_ALLOW_REFRESH, /* value */ false); mController = new LetterboxUiController(mWm, mActivity); @@ -348,7 +350,7 @@ public class LetterboxUiControllerTest extends WindowTestsBase { public void testShouldRefreshActivityForCameraCompat_propertyIsTrue_returnsTrue() throws Exception { doReturn(true).when(mLetterboxConfiguration) - .isCameraCompatTreatmentEnabled(/* checkDeviceConfig */ true); + .isCameraCompatTreatmentEnabled(); mockThatProperty(PROPERTY_CAMERA_COMPAT_ALLOW_REFRESH, /* value */ true); mController = new LetterboxUiController(mWm, mActivity); @@ -362,7 +364,7 @@ public class LetterboxUiControllerTest extends WindowTestsBase { @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE}) public void testShouldRefreshActivityViaPauseForCameraCompat_flagIsDisabled_returnsFalse() { doReturn(false).when(mLetterboxConfiguration) - .isCameraCompatTreatmentEnabled(/* checkDeviceConfig */ true); + .isCameraCompatTreatmentEnabled(); assertFalse(mController.shouldRefreshActivityViaPauseForCameraCompat()); } @@ -371,7 +373,7 @@ public class LetterboxUiControllerTest extends WindowTestsBase { @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE}) public void testShouldRefreshActivityViaPauseForCameraCompat_overrideEnabled_returnsTrue() { doReturn(true).when(mLetterboxConfiguration) - .isCameraCompatTreatmentEnabled(/* checkDeviceConfig */ true); + .isCameraCompatTreatmentEnabled(); assertTrue(mController.shouldRefreshActivityViaPauseForCameraCompat()); } @@ -381,7 +383,7 @@ public class LetterboxUiControllerTest extends WindowTestsBase { public void testShouldRefreshActivityViaPauseForCameraCompat_propertyIsFalseAndOverride_returnFalse() throws Exception { doReturn(true).when(mLetterboxConfiguration) - .isCameraCompatTreatmentEnabled(/* checkDeviceConfig */ true); + .isCameraCompatTreatmentEnabled(); mockThatProperty(PROPERTY_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE, /* value */ false); mController = new LetterboxUiController(mWm, mActivity); @@ -393,7 +395,7 @@ public class LetterboxUiControllerTest extends WindowTestsBase { public void testShouldRefreshActivityViaPauseForCameraCompat_propertyIsTrue_returnsTrue() throws Exception { doReturn(true).when(mLetterboxConfiguration) - .isCameraCompatTreatmentEnabled(/* checkDeviceConfig */ true); + .isCameraCompatTreatmentEnabled(); mockThatProperty(PROPERTY_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE, /* value */ true); mController = new LetterboxUiController(mWm, mActivity); @@ -406,7 +408,7 @@ public class LetterboxUiControllerTest extends WindowTestsBase { @Test public void testShouldForceRotateForCameraCompat_flagIsDisabled_returnsFalse() { doReturn(false).when(mLetterboxConfiguration) - .isCameraCompatTreatmentEnabled(/* checkDeviceConfig */ true); + .isCameraCompatTreatmentEnabled(); assertFalse(mController.shouldForceRotateForCameraCompat()); } @@ -415,7 +417,7 @@ public class LetterboxUiControllerTest extends WindowTestsBase { @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_DISABLE_FORCE_ROTATION}) public void testShouldForceRotateForCameraCompat_overrideEnabled_returnsFalse() { doReturn(true).when(mLetterboxConfiguration) - .isCameraCompatTreatmentEnabled(/* checkDeviceConfig */ true); + .isCameraCompatTreatmentEnabled(); assertFalse(mController.shouldForceRotateForCameraCompat()); } @@ -425,7 +427,7 @@ public class LetterboxUiControllerTest extends WindowTestsBase { public void testShouldForceRotateForCameraCompat_propertyIsTrueAndOverride_returnsFalse() throws Exception { doReturn(true).when(mLetterboxConfiguration) - .isCameraCompatTreatmentEnabled(/* checkDeviceConfig */ true); + .isCameraCompatTreatmentEnabled(); mockThatProperty(PROPERTY_CAMERA_COMPAT_ALLOW_FORCE_ROTATION, /* value */ true); mController = new LetterboxUiController(mWm, mActivity); @@ -437,7 +439,7 @@ public class LetterboxUiControllerTest extends WindowTestsBase { public void testShouldForceRotateForCameraCompat_propertyIsFalse_returnsFalse() throws Exception { doReturn(true).when(mLetterboxConfiguration) - .isCameraCompatTreatmentEnabled(/* checkDeviceConfig */ true); + .isCameraCompatTreatmentEnabled(); mockThatProperty(PROPERTY_CAMERA_COMPAT_ALLOW_FORCE_ROTATION, /* value */ false); mController = new LetterboxUiController(mWm, mActivity); @@ -449,7 +451,7 @@ public class LetterboxUiControllerTest extends WindowTestsBase { public void testShouldForceRotateForCameraCompat_propertyIsTrue_returnsTrue() throws Exception { doReturn(true).when(mLetterboxConfiguration) - .isCameraCompatTreatmentEnabled(/* checkDeviceConfig */ true); + .isCameraCompatTreatmentEnabled(); mockThatProperty(PROPERTY_CAMERA_COMPAT_ALLOW_FORCE_ROTATION, /* value */ true); mController = new LetterboxUiController(mWm, mActivity); @@ -461,7 +463,7 @@ public class LetterboxUiControllerTest extends WindowTestsBase { public void testGetCropBoundsIfNeeded_handleCropForTransparentActivityBasedOnOpaqueBounds() { final InsetsSource taskbar = new InsetsSource(/*id=*/ 0, WindowInsets.Type.navigationBars()); - taskbar.setInsetsRoundedCornerFrame(true); + taskbar.setFlags(FLAG_INSETS_ROUNDED_CORNER, FLAG_INSETS_ROUNDED_CORNER); final WindowState mainWindow = mockForGetCropBoundsAndRoundedCorners(taskbar); final Rect opaqueBounds = new Rect(0, 0, 500, 300); doReturn(opaqueBounds).when(mActivity).getBounds(); @@ -505,7 +507,7 @@ public class LetterboxUiControllerTest extends WindowTestsBase { public void testGetCropBoundsIfNeeded_appliesCrop() { final InsetsSource taskbar = new InsetsSource(/*id=*/ 0, WindowInsets.Type.navigationBars()); - taskbar.setInsetsRoundedCornerFrame(true); + taskbar.setFlags(FLAG_INSETS_ROUNDED_CORNER, FLAG_INSETS_ROUNDED_CORNER); final WindowState mainWindow = mockForGetCropBoundsAndRoundedCorners(taskbar); // Apply crop if taskbar is expanded @@ -528,7 +530,7 @@ public class LetterboxUiControllerTest extends WindowTestsBase { public void testGetCropBoundsIfNeeded_appliesCropWithSizeCompatScaling() { final InsetsSource taskbar = new InsetsSource(/*id=*/ 0, WindowInsets.Type.navigationBars()); - taskbar.setInsetsRoundedCornerFrame(true); + taskbar.setFlags(FLAG_INSETS_ROUNDED_CORNER, FLAG_INSETS_ROUNDED_CORNER); final WindowState mainWindow = mockForGetCropBoundsAndRoundedCorners(taskbar); final float scaling = 2.0f; @@ -740,7 +742,9 @@ public class LetterboxUiControllerTest extends WindowTestsBase { @EnableCompatChanges({OVERRIDE_UNDEFINED_ORIENTATION_TO_PORTRAIT, OVERRIDE_ORIENTATION_ONLY_FOR_CAMERA}) public void testOverrideOrientationIfNeeded_whenCameraNotActive_returnsUnchanged() { - doReturn(true).when(mLetterboxConfiguration).isCameraCompatTreatmentEnabled(anyBoolean()); + doReturn(true).when(mLetterboxConfiguration).isCameraCompatTreatmentEnabled(); + doReturn(true).when(mLetterboxConfiguration) + .isCameraCompatTreatmentEnabledAtBuildTime(); // Recreate DisplayContent with DisplayRotationCompatPolicy mActivity = setUpActivityWithComponent(); @@ -758,7 +762,9 @@ public class LetterboxUiControllerTest extends WindowTestsBase { @EnableCompatChanges({OVERRIDE_UNDEFINED_ORIENTATION_TO_PORTRAIT, OVERRIDE_ORIENTATION_ONLY_FOR_CAMERA}) public void testOverrideOrientationIfNeeded_whenCameraActive_returnsPortrait() { - doReturn(true).when(mLetterboxConfiguration).isCameraCompatTreatmentEnabled(anyBoolean()); + doReturn(true).when(mLetterboxConfiguration).isCameraCompatTreatmentEnabled(); + doReturn(true).when(mLetterboxConfiguration) + .isCameraCompatTreatmentEnabledAtBuildTime(); // Recreate DisplayContent with DisplayRotationCompatPolicy mActivity = setUpActivityWithComponent(); @@ -1059,7 +1065,9 @@ public class LetterboxUiControllerTest extends WindowTestsBase { @Test public void testgetFixedOrientationLetterboxAspectRatio_splitScreenAspectEnabled() { doReturn(true).when(mActivity.mWmService.mLetterboxConfiguration) - .isCameraCompatTreatmentEnabled(anyBoolean()); + .isCameraCompatTreatmentEnabled(); + doReturn(true).when(mActivity.mWmService.mLetterboxConfiguration) + .isCameraCompatTreatmentEnabledAtBuildTime(); doReturn(true).when(mActivity.mWmService.mLetterboxConfiguration) .isCameraCompatSplitScreenAspectRatioEnabled(); doReturn(false).when(mActivity.mWmService.mLetterboxConfiguration) diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java index 0ccb0d0b2ef5..b02b774da86a 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java @@ -49,10 +49,12 @@ import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; import static java.lang.Integer.MAX_VALUE; @@ -1139,6 +1141,40 @@ public class RecentTasksTest extends WindowTestsBase { } @Test + public void addTask_taskAlreadyInRecentsMovedToTop_callsTaskNotificationController() { + final Task firstTask = createTaskBuilder(".Task").build(); + final Task secondTask = createTaskBuilder(".Task2").build(); + + mRecentTasks.add(firstTask); + mRecentTasks.add(secondTask); + + TaskChangeNotificationController controller = + mAtm.getTaskChangeNotificationController(); + clearInvocations(controller); + + // Add firstTask back to top + mRecentTasks.add(firstTask); + verify(controller).notifyTaskListUpdated(); + } + + @Test + public void addTask_taskAlreadyInRecentsOnTop_doesNotNotify() { + final Task firstTask = createTaskBuilder(".Task").build(); + final Task secondTask = createTaskBuilder(".Task2").build(); + + mRecentTasks.add(firstTask); + mRecentTasks.add(secondTask); + + TaskChangeNotificationController controller = + mAtm.getTaskChangeNotificationController(); + clearInvocations(controller); + + // Add secondTask to top again + mRecentTasks.add(secondTask); + verifyZeroInteractions(controller); + } + + @Test public void removeTask_callsTaskNotificationController() { final Task task = createTaskBuilder(".Task").build(); 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 6a89b55b80cc..3908947804cd 100644 --- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java @@ -30,6 +30,7 @@ import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; import static android.content.res.Configuration.ORIENTATION_PORTRAIT; import static android.provider.DeviceConfig.NAMESPACE_CONSTRAIN_DISPLAY_APIS; +import static android.view.InsetsSource.FLAG_INSETS_ROUNDED_CORNER; import static android.view.Surface.ROTATION_0; import static android.view.Surface.ROTATION_180; import static android.view.Surface.ROTATION_270; @@ -3548,7 +3549,7 @@ public class SizeCompatTests extends WindowTestsBase { final InsetsSource navSource = new InsetsSource( InsetsSource.createId(null, 0, navigationBars()), navigationBars()); - navSource.setInsetsRoundedCornerFrame(true); + navSource.setFlags(FLAG_INSETS_ROUNDED_CORNER, FLAG_INSETS_ROUNDED_CORNER); navSource.setFrame(new Rect(0, screenHeight - taskbarHeight, screenWidth, screenHeight)); mActivity.mWmService.mLetterboxConfiguration.setLetterboxActivityCornersRadius(15); @@ -3596,7 +3597,7 @@ public class SizeCompatTests extends WindowTestsBase { final InsetsSource navSource = new InsetsSource( InsetsSource.createId(null, 0, navigationBars()), navigationBars()); - navSource.setInsetsRoundedCornerFrame(true); + navSource.setFlags(FLAG_INSETS_ROUNDED_CORNER, FLAG_INSETS_ROUNDED_CORNER); // Immersive activity has transient navbar navSource.setVisible(!immersive); navSource.setFrame(new Rect(0, screenHeight - taskbarHeight, screenWidth, screenHeight)); diff --git a/services/tests/wmtests/src/com/android/server/wm/SynchedDeviceConfigTests.java b/services/tests/wmtests/src/com/android/server/wm/SynchedDeviceConfigTests.java new file mode 100644 index 000000000000..ecab62f72f69 --- /dev/null +++ b/services/tests/wmtests/src/com/android/server/wm/SynchedDeviceConfigTests.java @@ -0,0 +1,194 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wm; + +import static com.google.common.truth.Truth.assertThat; + +import static org.junit.Assert.assertEquals; + +import android.app.ActivityThread; +import android.platform.test.annotations.Presubmit; +import android.provider.DeviceConfig; + +import androidx.test.filters.SmallTest; + +import com.android.modules.utils.testing.TestableDeviceConfig; + +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; + +import java.util.Objects; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Executor; +import java.util.concurrent.TimeUnit; + +/** + * Test class for {@link SynchedDeviceConfig}. + * + * atest WmTests:SynchedDeviceConfigTests + */ +@SmallTest +@Presubmit +public class SynchedDeviceConfigTests { + + private static final long WAIT_FOR_PROPERTY_CHANGE_TIMEOUT_MILLIS = 2000; // 2 sec + private static final String NAMESPACE_FOR_TEST = "TestingNameSpace"; + + private SynchedDeviceConfig mDeviceConfig; + + private Executor mExecutor; + + @Rule + public final TestableDeviceConfig.TestableDeviceConfigRule + mDeviceConfigRule = new TestableDeviceConfig.TestableDeviceConfigRule(); + + @Before + public void setUp() { + mExecutor = Objects.requireNonNull(ActivityThread.currentApplication()).getMainExecutor(); + mDeviceConfig = SynchedDeviceConfig + .builder(/* nameSpace */ NAMESPACE_FOR_TEST, /* executor */ mExecutor) + .addDeviceConfigEntry(/* key */ "key1", /* default */ true, /* enabled */ true) + .addDeviceConfigEntry(/* key */ "key2", /* default */ false, /* enabled */ true) + .addDeviceConfigEntry(/* key */ "key3", /* default */ true, /* enabled */ false) + .addDeviceConfigEntry(/* key */ "key4", /* default */ false, /* enabled */ false) + .addDeviceConfigEntry(/* key */ "key5", /* default */ true, /* enabled */ false) + .addDeviceConfigEntry(/* key */ "key6", /* default */ false, /* enabled */ false) + .build(); + } + + @After + public void tearDown() { + DeviceConfig.removeOnPropertiesChangedListener(mDeviceConfig); + } + + @Test + public void testWhenStarted_initialValuesAreDefaultOrFalseIfDisabled() { + assertFlagValue(/* key */ "key1", /* expected */ true); // enabled + assertFlagValue(/* key */ "key2", /* expected */ false); // enabled + assertFlagValue(/* key */ "key3", /* expected */ false); // disabled + assertFlagValue(/* key */ "key4", /* expected */ false); // disabled + assertFlagValue(/* key */ "key5", /* expected */ false); // disabled + assertFlagValue(/* key */ "key6", /* expected */ false); // disabled + } + + @Test + public void testIsEnabled() { + assertFlagEnabled(/* key */ "key1", /* expected */ true); + assertFlagEnabled(/* key */ "key2", /* expected */ true); + assertFlagEnabled(/* key */ "key3", /* expected */ false); + assertFlagEnabled(/* key */ "key4", /* expected */ false); + assertFlagEnabled(/* key */ "key5", /* expected */ false); + assertFlagEnabled(/* key */ "key6", /* expected */ false); + } + + @Test + public void testWhenUpdated_onlyEnabledChanges() { + final CountDownLatch countDownLatch = new CountDownLatch(4); + final DeviceConfig.OnPropertiesChangedListener countDownLatchListener = + properties -> countDownLatch.countDown(); + DeviceConfig.addOnPropertiesChangedListener(NAMESPACE_FOR_TEST, mExecutor, + countDownLatchListener); + + try { + // We update all the keys + updateProperty(/* key */ "key1", /* value */ false); + updateProperty(/* key */ "key2", /* value */ true); + updateProperty(/* key */ "key3", /* value */ false); + updateProperty(/* key */ "key4", /* value */ true); + + assertThat(countDownLatch.await( + WAIT_FOR_PROPERTY_CHANGE_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)).isTrue(); + + // We update all the flags but only the enabled ones change + assertFlagValue(/* key */ "key1", /* expected */ false); // changes + assertFlagValue(/* key */ "key2", /* expected */ true); // changes + assertFlagValue(/* key */ "key3", /* expected */ false); // disabled + assertFlagValue(/* key */ "key4", /* expected */ false); // disabled + } catch (InterruptedException e) { + Assert.fail(e.getMessage()); + } finally { + DeviceConfig.removeOnPropertiesChangedListener(countDownLatchListener); + } + } + + @Test + public void testWhenEnabled_updatesAreUsed() { + final CountDownLatch countDownLatchBefore = new CountDownLatch(2); + final CountDownLatch countDownLatchAfter = new CountDownLatch(2); + final DeviceConfig.OnPropertiesChangedListener countDownLatchBeforeListener = + properties -> countDownLatchBefore.countDown(); + final DeviceConfig.OnPropertiesChangedListener countDownLatchAfterListener = + properties -> countDownLatchAfter.countDown(); + DeviceConfig.addOnPropertiesChangedListener(NAMESPACE_FOR_TEST, mExecutor, + countDownLatchBeforeListener); + + try { + // We update disabled values + updateProperty(/* key */ "key3", /* value */ false); + updateProperty(/* key */ "key4", /* value */ true); + + assertThat(countDownLatchBefore.await( + WAIT_FOR_PROPERTY_CHANGE_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)).isTrue(); + + // We check they haven't been updated + assertFlagValue(/* key */ "key3", /* expected */ false); + assertFlagValue(/* key */ "key4", /* expected */ false); + + + DeviceConfig.removeOnPropertiesChangedListener(countDownLatchBeforeListener); + DeviceConfig.addOnPropertiesChangedListener(NAMESPACE_FOR_TEST, mExecutor, + countDownLatchAfterListener); + + // We update enabled flags + updateProperty(/* key */ "key1", /* value */ false); + updateProperty(/* key */ "key2", /* value */ true); + + assertThat(countDownLatchAfter.await( + WAIT_FOR_PROPERTY_CHANGE_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)).isTrue(); + + // Value have been updated + assertFlagValue(/* key */ "key1", /* expected */ false); + assertFlagValue(/* key */ "key2", /* expected */ true); + + } catch (InterruptedException e) { + Assert.fail(e.getMessage()); + } finally { + DeviceConfig.removeOnPropertiesChangedListener(countDownLatchAfterListener); + } + } + + + private void assertFlagValue(String key, boolean expectedValue) { + assertEquals(/* message */"Flag " + key + " value is not " + expectedValue, /* expected */ + expectedValue, /* actual */ mDeviceConfig.getFlagValue(key)); + } + + + private void assertFlagEnabled(String key, boolean expectedValue) { + assertEquals(/* message */ + "Flag " + key + " enabled is not " + expectedValue, /* expected */ + expectedValue, /* actual */ mDeviceConfig.isBuildTimeFlagEnabled(key)); + } + + private void updateProperty(String key, Boolean value) { + DeviceConfig.setProperty(NAMESPACE_FOR_TEST, key, /* value */ + value.toString(), /* makeDefault */ false); + } +} diff --git a/services/tests/wmtests/src/com/android/server/wm/TrustedPresentationCallbackTest.java b/services/tests/wmtests/src/com/android/server/wm/TrustedPresentationCallbackTest.java new file mode 100644 index 000000000000..df11a44bb1b5 --- /dev/null +++ b/services/tests/wmtests/src/com/android/server/wm/TrustedPresentationCallbackTest.java @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wm; + +import static android.server.wm.ActivityManagerTestBase.createFullscreenActivityScenarioRule; +import static android.server.wm.BuildUtils.HW_TIMEOUT_MULTIPLIER; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import android.app.Activity; +import android.platform.test.annotations.Presubmit; +import android.util.Log; +import android.view.SurfaceControl; +import android.view.SurfaceControl.TrustedPresentationThresholds; + +import androidx.test.ext.junit.rules.ActivityScenarioRule; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.function.Consumer; + +/** + * TODO (b/287076178): Move these tests to + * {@link android.view.surfacecontrol.cts.TrustedPresentationCallbackTest} when API is made public + */ +@Presubmit +public class TrustedPresentationCallbackTest { + private static final String TAG = "TrustedPresentationCallbackTest"; + private static final int STABILITY_REQUIREMENT_MS = 500; + private static final long WAIT_TIME_MS = HW_TIMEOUT_MULTIPLIER * 2000L; + + private static final float FRACTION_VISIBLE = 0.1f; + + @Rule + public ActivityScenarioRule<TestActivity> mActivityRule = createFullscreenActivityScenarioRule( + TestActivity.class); + + private TestActivity mActivity; + + @Before + public void setup() { + mActivityRule.getScenario().onActivity(activity -> mActivity = activity); + } + + @Test + public void testAddTrustedPresentationListenerOnWindow() throws InterruptedException { + boolean[] results = new boolean[1]; + CountDownLatch receivedResults = new CountDownLatch(1); + TrustedPresentationThresholds thresholds = new TrustedPresentationThresholds( + 1 /* minAlpha */, FRACTION_VISIBLE, STABILITY_REQUIREMENT_MS); + SurfaceControl.Transaction t = new SurfaceControl.Transaction(); + mActivity.getWindow().getRootSurfaceControl().addTrustedPresentationCallback(t, thresholds, + Runnable::run, inTrustedPresentationState -> { + Log.d(TAG, "onTrustedPresentationChanged " + inTrustedPresentationState); + results[0] = inTrustedPresentationState; + receivedResults.countDown(); + }); + t.apply(); + + assertTrue("Timed out waiting for results", + receivedResults.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS)); + assertTrue(results[0]); + } + + @Test + public void testRemoveTrustedPresentationListenerOnWindow() throws InterruptedException { + final Object resultsLock = new Object(); + boolean[] results = new boolean[1]; + boolean[] receivedResults = new boolean[1]; + TrustedPresentationThresholds thresholds = new TrustedPresentationThresholds( + 1 /* minAlpha */, FRACTION_VISIBLE, STABILITY_REQUIREMENT_MS); + Consumer<Boolean> trustedPresentationCallback = inTrustedPresentationState -> { + synchronized (resultsLock) { + results[0] = inTrustedPresentationState; + receivedResults[0] = true; + resultsLock.notify(); + } + }; + SurfaceControl.Transaction t = new SurfaceControl.Transaction(); + mActivity.getWindow().getRootSurfaceControl().addTrustedPresentationCallback(t, thresholds, + Runnable::run, trustedPresentationCallback); + t.apply(); + + synchronized (resultsLock) { + if (!receivedResults[0]) { + resultsLock.wait(WAIT_TIME_MS); + } + // Make sure we received the results and not just timed out + assertTrue("Timed out waiting for results", receivedResults[0]); + assertTrue(results[0]); + + // reset the state + receivedResults[0] = false; + } + + mActivity.getWindow().getRootSurfaceControl().removeTrustedPresentationCallback(t, + trustedPresentationCallback); + t.apply(); + + synchronized (resultsLock) { + if (!receivedResults[0]) { + resultsLock.wait(WAIT_TIME_MS); + } + // Ensure we waited the full time and never received a notify on the result from the + // callback. + assertFalse("Should never have received a callback", receivedResults[0]); + // results shouldn't have changed. + assertTrue(results[0]); + } + } + + public static class TestActivity extends Activity { + } +} diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java index d3f68185a269..197ee92aa7eb 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java @@ -52,6 +52,7 @@ import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; @@ -99,6 +100,7 @@ import androidx.test.filters.SmallTest; import androidx.test.platform.app.InstrumentationRegistry; import com.android.compatibility.common.util.AdoptShellPermissionsRule; +import com.android.internal.os.IResultReceiver; import org.junit.Rule; import org.junit.Test; @@ -905,6 +907,56 @@ public class WindowManagerServiceTests extends WindowTestsBase { argThat(h -> (h.inputConfig & InputConfig.SPY) == InputConfig.SPY)); } + @Test + public void testRequestKeyboardShortcuts_noWindow() { + doNothing().when(mWm.mContext).enforceCallingOrSelfPermission(anyString(), anyString()); + doReturn(null).when(mWm).getFocusedWindowLocked(); + doReturn(null).when(mWm.mRoot).getCurrentInputMethodWindow(); + + TestResultReceiver receiver = new TestResultReceiver(); + mWm.requestAppKeyboardShortcuts(receiver, 0); + assertNotNull(receiver.resultData); + assertTrue(receiver.resultData.isEmpty()); + + receiver = new TestResultReceiver(); + mWm.requestImeKeyboardShortcuts(receiver, 0); + assertNotNull(receiver.resultData); + assertTrue(receiver.resultData.isEmpty()); + } + + @Test + public void testRequestKeyboardShortcuts() throws RemoteException { + final IWindow window = mock(IWindow.class); + final IBinder binder = mock(IBinder.class); + doReturn(binder).when(window).asBinder(); + final WindowState windowState = + createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "appWin", window); + doNothing().when(mWm.mContext).enforceCallingOrSelfPermission(anyString(), anyString()); + doReturn(windowState).when(mWm).getFocusedWindowLocked(); + doReturn(windowState).when(mWm.mRoot).getCurrentInputMethodWindow(); + + TestResultReceiver receiver = new TestResultReceiver(); + mWm.requestAppKeyboardShortcuts(receiver, 0); + mWm.requestImeKeyboardShortcuts(receiver, 0); + verify(window, times(2)).requestAppKeyboardShortcuts(receiver, 0); + } + + class TestResultReceiver implements IResultReceiver { + public android.os.Bundle resultData; + private final IBinder mBinder = mock(IBinder.class); + + @Override + public void send(int resultCode, android.os.Bundle resultData) + throws android.os.RemoteException { + this.resultData = resultData; + } + + @Override + public android.os.IBinder asBinder() { + return mBinder; + } + } + private void setupActivityWithLaunchCookie(IBinder launchCookie, WindowContainerToken wct) { final WindowContainer.RemoteToken remoteToken = mock(WindowContainer.RemoteToken.class); when(remoteToken.toWindowContainerToken()).thenReturn(wct); diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index 5b1c6b1c88b8..5b946e8540d1 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -9730,6 +9730,27 @@ public class CarrierConfigManager { public static final String KEY_IWLAN_HANDOVER_POLICY_STRING_ARRAY = "iwlan_handover_policy_string_array"; + /** + * Score table for {@link TelephonyManager#MOBILE_DATA_POLICY_AUTO_DATA_SWITCH}. The score is + * used in conjunction with a tolerance value defined in resource config + * {@code auto_data_switch_score_tolerance}, greater than which device will switch to the sub + * with higher score. + * Possible keys are network type name string(also see {@link #KEY_BANDWIDTH_STRING_ARRAY}). + * Value should be "score_level_0, score_level_1, score_level_2, score_level_3,score_level_4". + * Each network type must have 5 scores correspond to {@link CellSignalStrength}, where score is + * a non-negative integer. A score of 0 is treated the same as data out of service. + * + * For NR (5G), the following network names should be used: + * - NR_NSA: NR NSA, sub-6 frequencies + * - NR_NSA_MMWAVE: NR NSA, mmwave frequencies + * - NR_SA: NR SA, sub-6 frequencies + * - NR_SA_MMWAVE: NR SA, mmwave frequencies + * + * @hide + */ + public static final String KEY_AUTO_DATA_SWITCH_RAT_SIGNAL_SCORE_BUNDLE = + "auto_data_switch_rat_signal_score_string_bundle"; + /** The default value for every variable. */ private static final PersistableBundle sDefaults; @@ -10404,6 +10425,51 @@ public class CarrierConfigManager { sDefaults.putStringArray(KEY_IWLAN_HANDOVER_POLICY_STRING_ARRAY, new String[]{ "source=GERAN|UTRAN|EUTRAN|NGRAN|IWLAN, " + "target=GERAN|UTRAN|EUTRAN|NGRAN|IWLAN, type=allowed"}); + PersistableBundle auto_data_switch_rat_signal_score_string_bundle = new PersistableBundle(); + auto_data_switch_rat_signal_score_string_bundle.putIntArray( + "NR_SA_MMWAVE", new int[]{10000, 13227, 16000, 18488, 20017}); + auto_data_switch_rat_signal_score_string_bundle.putIntArray( + "NR_NSA_MMWAVE", new int[]{8000, 10227, 12488, 15017, 15278}); + auto_data_switch_rat_signal_score_string_bundle.putIntArray( + "LTE", new int[]{3731, 5965, 8618, 11179, 13384}); + auto_data_switch_rat_signal_score_string_bundle.putIntArray( + "NR_SA", new int[]{5288, 6795, 6955, 7562, 9713}); + auto_data_switch_rat_signal_score_string_bundle.putIntArray( + "NR_NSA", new int[]{5463, 6827, 8029, 9007, 9428}); + auto_data_switch_rat_signal_score_string_bundle.putIntArray( + "UMTS", new int[]{100, 169, 183, 192, 300}); + auto_data_switch_rat_signal_score_string_bundle.putIntArray( + "eHRPD", new int[]{10, 400, 600, 800, 1000}); + auto_data_switch_rat_signal_score_string_bundle.putIntArray( + "TD_SCDMA", new int[]{1, 100, 500, 1000}); + auto_data_switch_rat_signal_score_string_bundle.putIntArray( + "iDEN", new int[]{1, 2, 10, 50, 100}); + auto_data_switch_rat_signal_score_string_bundle.putIntArray( + "EvDo_B", new int[]{1000, 1495, 2186, 2532, 2600}); + auto_data_switch_rat_signal_score_string_bundle.putIntArray( + "HSPA+", new int[]{1619, 2500, 3393, 4129, 4212}); + auto_data_switch_rat_signal_score_string_bundle.putIntArray( + "HSPA", new int[]{1000, 1495, 2186, 2532, 2544}); + auto_data_switch_rat_signal_score_string_bundle.putIntArray( + "HSUPA", new int[]{1500, 1919, 2132, 2362, 2704}); + auto_data_switch_rat_signal_score_string_bundle.putIntArray( + "HSDPA", new int[]{1500, 1732, 4000, 7000, 8000}); + auto_data_switch_rat_signal_score_string_bundle.putIntArray( + "EvDo_A", new int[]{600, 840, 1200, 1300, 1400}); + auto_data_switch_rat_signal_score_string_bundle.putIntArray( + "EvDo_0", new int[]{300, 600, 1000, 1500, 2000}); + auto_data_switch_rat_signal_score_string_bundle.putIntArray( + "1xRTT", new int[]{50, 60, 70, 80}); + auto_data_switch_rat_signal_score_string_bundle.putIntArray( + "EDGE", new int[]{154, 169, 183, 192, 267}); + auto_data_switch_rat_signal_score_string_bundle.putIntArray( + "GPRS", new int[]{15, 30, 40, 45, 50}); + auto_data_switch_rat_signal_score_string_bundle.putIntArray( + "CDMA", new int[]{1, 50, 100, 300, 2000}); + auto_data_switch_rat_signal_score_string_bundle.putIntArray( + "GSM", new int[]{1, 2, 10, 50, 100}); + sDefaults.putPersistableBundle(KEY_AUTO_DATA_SWITCH_RAT_SIGNAL_SCORE_BUNDLE, + auto_data_switch_rat_signal_score_string_bundle); sDefaults.putInt(KEY_CELLULAR_USAGE_SETTING_INT, SubscriptionManager.USAGE_SETTING_UNKNOWN); // Default data stall recovery configurations. diff --git a/tests/FlickerTests/Android.bp b/tests/FlickerTests/Android.bp index 4ba538ed9d45..a996fa100da9 100644 --- a/tests/FlickerTests/Android.bp +++ b/tests/FlickerTests/Android.bp @@ -23,14 +23,49 @@ package { default_applicable_licenses: ["frameworks_base_license"], } -android_test { - name: "FlickerTests", +filegroup { + name: "FlickerTestsBase-src", + srcs: ["src/com/android/server/wm/flicker/*.kt"], +} + +filegroup { + name: "FlickerTestsAppClose-src", + srcs: ["src/**/close/*.kt"], +} + +filegroup { + name: "FlickerTestsActivityEmbedding-src", srcs: [ - "src/**/*.java", - "src/**/*.kt", + "src/**/activityembedding/*.kt", + "src/**/activityembedding/close/*.kt", + "src/**/activityembedding/rotation/*.kt", ], - manifest: "AndroidManifest.xml", - test_config: "AndroidTest.xml", +} + +filegroup { + name: "FlickerTestsIme-src", + srcs: ["src/**/ime/*.kt"], +} + +filegroup { + name: "FlickerTestsAppLaunch-src", + srcs: ["src/**/launch/*.kt"], +} + +filegroup { + name: "FlickerTestsQuickswitch-src", + srcs: ["src/**/quickswitch/*.kt"], +} + +filegroup { + name: "FlickerTestsRotation-src", + srcs: ["src/**/rotation/*.kt"], +} + +java_defaults { + name: "FlickerTestsDefault", + manifest: "manifests/AndroidManifest.xml", + test_config_template: "AndroidTestTemplate.xml", platform_apis: true, certificate: "platform", optimize: { @@ -42,16 +77,98 @@ android_test { "androidx.test.ext.junit", "flickertestapplib", "flickerlib", - "flickerlib-apphelpers", "flickerlib-helpers", - "truth-prebuilt", - "launcher-helper-lib", - "launcher-aosp-tapl", "platform-test-annotations", - "wm-flicker-window-extensions", + "wm-flicker-common-app-helpers", ], data: [ ":FlickerTestApp", + "trace_config/*", + ], +} + +android_test { + name: "FlickerTestsOther", + defaults: ["FlickerTestsDefault"], + additional_manifests: ["manifests/AndroidManifestOther.xml"], + package_name: "com.android.server.wm.flicker", + instrumentation_target_package: "com.android.server.wm.flicker", + srcs: [ + "src/**/*.java", + "src/**/*.kt", + ], + exclude_srcs: [ + ":FlickerTestsAppClose-src", + ":FlickerTestsIme-src", + ":FlickerTestsAppLaunch-src", + ":FlickerTestsQuickswitch-src", + ":FlickerTestsRotation-src", + ], +} + +android_test { + name: "FlickerTestsAppClose", + defaults: ["FlickerTestsDefault"], + additional_manifests: ["manifests/AndroidManifestAppClose.xml"], + package_name: "com.android.server.wm.flicker.close", + instrumentation_target_package: "com.android.server.wm.flicker.close", + srcs: [ + ":FlickerTestsBase-src", + ":FlickerTestsAppClose-src", + ], + exclude_srcs: [ + ":FlickerTestsActivityEmbedding-src", + ], +} + +android_test { + name: "FlickerTestsIme", + defaults: ["FlickerTestsDefault"], + additional_manifests: ["manifests/AndroidManifestIme.xml"], + package_name: "com.android.server.wm.flicker.ime", + instrumentation_target_package: "com.android.server.wm.flicker.ime", + srcs: [ + ":FlickerTestsBase-src", + ":FlickerTestsIme-src", + ], +} + +android_test { + name: "FlickerTestsAppLaunch", + defaults: ["FlickerTestsDefault"], + additional_manifests: ["manifests/AndroidManifestAppLaunch.xml"], + package_name: "com.android.server.wm.flicker.launch", + instrumentation_target_package: "com.android.server.wm.flicker.launch", + srcs: [ + ":FlickerTestsBase-src", + ":FlickerTestsAppLaunch-src", + ], +} + +android_test { + name: "FlickerTestsQuickswitch", + defaults: ["FlickerTestsDefault"], + additional_manifests: ["manifests/AndroidManifestQuickswitch.xml"], + package_name: "com.android.server.wm.flicker.quickswitch", + instrumentation_target_package: "com.android.server.wm.flicker.quickswitch", + srcs: [ + ":FlickerTestsBase-src", + ":FlickerTestsQuickswitch-src", + ], +} + +android_test { + name: "FlickerTestsRotation", + defaults: ["FlickerTestsDefault"], + additional_manifests: ["manifests/AndroidManifestRotation.xml"], + package_name: "com.android.server.wm.flicker.rotation", + instrumentation_target_package: "com.android.server.wm.flicker.rotation", + srcs: [ + ":FlickerTestsBase-src", + ":FlickerTestsRotation-src", + ], + exclude_srcs: [ + ":FlickerTestsActivityEmbedding-src", ], } diff --git a/tests/FlickerTests/AndroidTest.xml b/tests/FlickerTests/AndroidTest.xml deleted file mode 100644 index 32ff243921ec..000000000000 --- a/tests/FlickerTests/AndroidTest.xml +++ /dev/null @@ -1,53 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - * Copyright 2018 Google Inc. All Rights Reserved. - --> -<configuration description="Runs WindowManager Flicker Tests"> - <option name="test-tag" value="FlickerTests" /> - <target_preparer class="com.android.tradefed.targetprep.DeviceSetup"> - <!-- keeps the screen on during tests --> - <option name="screen-always-on" value="on" /> - <!-- prevents the phone from restarting --> - <option name="force-skip-system-props" value="true" /> - <!-- set WM tracing verbose level to all --> - <option name="run-command" value="cmd window tracing level all" /> - <!-- set WM tracing to frame (avoid incomplete states) --> - <option name="run-command" value="cmd window tracing frame" /> - <!-- ensure lock screen mode is swipe --> - <option name="run-command" value="locksettings set-disabled false" /> - <!-- disable betterbug as it's log collection dialogues cause flakes in e2e tests --> - <option name="run-command" value="pm disable com.google.android.internal.betterbug" /> - <!-- restart launcher to activate TAPL --> - <option name="run-command" value="setprop ro.test_harness 1 ; am force-stop com.google.android.apps.nexuslauncher" /> - <!-- Ensure output directory is empty at the start --> - <option name="run-command" value="rm -rf /sdcard/flicker" /> - <!-- Increase trace size: 20mb for WM and 80mb for SF --> - <option name="run-command" value="cmd window tracing size 20480" /> - <option name="run-command" value="su root service call SurfaceFlinger 1029 i32 81920" /> - </target_preparer> - <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer"> - <option name="run-command" value="settings put secure show_ime_with_hard_keyboard 1" /> - <option name="run-command" value="settings put system show_touches 1" /> - <option name="run-command" value="settings put system pointer_location 1" /> - <option name="teardown-command" value="settings delete secure show_ime_with_hard_keyboard" /> - <option name="teardown-command" value="settings delete system show_touches" /> - <option name="teardown-command" value="settings delete system pointer_location" /> - <option name="teardown-command" value="cmd overlay enable com.android.internal.systemui.navbar.gestural" /> - </target_preparer> - <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> - <option name="cleanup-apks" value="true"/> - <option name="test-file-name" value="FlickerTests.apk"/> - <option name="test-file-name" value="FlickerTestApp.apk" /> - </target_preparer> - <test class="com.android.tradefed.testtype.AndroidJUnitTest"> - <option name="package" value="com.android.server.wm.flicker"/> - <option name="shell-timeout" value="6600s" /> - <option name="test-timeout" value="6600s" /> - <option name="hidden-api-checks" value="false" /> - </test> - <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector"> - <option name="directory-keys" value="/sdcard/flicker" /> - <option name="collect-on-run-ended-only" value="true" /> - <option name="clean-up" value="true" /> - </metrics_collector> -</configuration> diff --git a/tests/FlickerTests/AndroidTestTemplate.xml b/tests/FlickerTests/AndroidTestTemplate.xml new file mode 100644 index 000000000000..1ede943a9fa2 --- /dev/null +++ b/tests/FlickerTests/AndroidTestTemplate.xml @@ -0,0 +1,91 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + * Copyright 2018 Google Inc. All Rights Reserved. + --> +<configuration description="Runs WindowManager {MODULE}"> + <option name="test-tag" value="FlickerTests"/> + <!-- Needed for storing the perfetto trace files in the sdcard/test_results--> + <option name="isolated-storage" value="false"/> + + <target_preparer class="com.android.tradefed.targetprep.DeviceSetup"> + <!-- keeps the screen on during tests --> + <option name="screen-always-on" value="on"/> + <!-- prevents the phone from restarting --> + <option name="force-skip-system-props" value="true"/> + <!-- set WM tracing verbose level to all --> + <option name="run-command" value="cmd window tracing level all"/> + <!-- set WM tracing to frame (avoid incomplete states) --> + <option name="run-command" value="cmd window tracing frame"/> + <!-- ensure lock screen mode is swipe --> + <option name="run-command" value="locksettings set-disabled false"/> + <!-- disable betterbug as it's log collection dialogues cause flakes in e2e tests --> + <option name="run-command" value="pm disable com.google.android.internal.betterbug"/> + <!-- restart launcher to activate TAPL --> + <option name="run-command" + value="setprop ro.test_harness 1 ; am force-stop com.google.android.apps.nexuslauncher"/> + <!-- Increase trace size: 20mb for WM and 80mb for SF --> + <option name="run-command" value="cmd window tracing size 20480"/> + <option name="run-command" value="su root service call SurfaceFlinger 1029 i32 81920"/> + </target_preparer> + <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer"> + <option name="test-user-token" value="%TEST_USER%"/> + <option name="run-command" value="rm -rf /data/user/%TEST_USER%/files/*"/> + <option name="run-command" value="settings put secure show_ime_with_hard_keyboard 1"/> + <option name="run-command" value="settings put system show_touches 1"/> + <option name="run-command" value="settings put system pointer_location 1"/> + <option name="teardown-command" + value="settings delete secure show_ime_with_hard_keyboard"/> + <option name="teardown-command" value="settings delete system show_touches"/> + <option name="teardown-command" value="settings delete system pointer_location"/> + <option name="teardown-command" + value="cmd overlay enable com.android.internal.systemui.navbar.gestural"/> + </target_preparer> + <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> + <option name="cleanup-apks" value="true"/> + <option name="test-file-name" value="{MODULE}.apk"/> + <option name="test-file-name" value="FlickerTestApp.apk"/> + </target_preparer> + <!-- Needed for pushing the trace config file --> + <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/> + <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer"> + <option name="push-file" + key="trace_config.textproto" + value="/data/misc/perfetto-traces/trace_config.textproto" + /> + <!--Install the content provider automatically when we push some file in sdcard folder.--> + <!--Needed to avoid the installation during the test suite.--> + <option name="push-file" key="trace_config.textproto" value="/sdcard/sample.textproto"/> + </target_preparer> + <test class="com.android.tradefed.testtype.AndroidJUnitTest"> + <option name="package" value="{PACKAGE}"/> + <option name="shell-timeout" value="6600s"/> + <option name="test-timeout" value="6600s"/> + <option name="hidden-api-checks" value="false"/> + <option name="device-listeners" value="android.device.collectors.PerfettoListener"/> + <!-- PerfettoListener related arguments --> + <option name="instrumentation-arg" key="perfetto_config_text_proto" value="true"/> + <option name="instrumentation-arg" + key="perfetto_config_file" + value="trace_config.textproto" + /> + <option name="instrumentation-arg" key="per_run" value="true"/> + </test> + <!-- Needed for pulling the collected trace config on to the host --> + <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector"> + <option name="pull-pattern-keys" value="perfetto_file_path"/> + <option name="directory-keys" + value="/data/user/0/com.android.server.wm.flicker/files"/> + <option name="directory-keys" + value="/data/user/0/com.android.server.wm.flicker.close/files"/> + <option name="directory-keys" + value="/data/user/0/com.android.server.wm.flicker.ime/files"/> + <option name="directory-keys" + value="/data/user/0/com.android.server.wm.flicker.launch/files"/> + <option name="directory-keys" + value="/data/user/0/com.android.server.wm.flicker.quickswitch/files"/> + <option name="directory-keys" + value="/data/user/0/com.android.server.wm.flicker.rotation/files"/> + <option name="collect-on-run-ended-only" value="true"/> + <option name="clean-up" value="true"/> + </metrics_collector> +</configuration> diff --git a/tests/FlickerTests/AndroidManifest.xml b/tests/FlickerTests/manifests/AndroidManifest.xml index 462f91bc081c..de8a3c680768 100644 --- a/tests/FlickerTests/AndroidManifest.xml +++ b/tests/FlickerTests/manifests/AndroidManifest.xml @@ -1,18 +1,19 @@ <?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2018 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> +<!-- + ~ Copyright (C) 2023 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.server.wm.flicker"> @@ -47,9 +48,4 @@ <uses-library android:name="android.test.runner"/> <uses-library android:name="androidx.window.extensions" android:required="false"/> </application> - - <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" - android:targetPackage="com.android.server.wm.flicker" - android:label="WindowManager Flicker Tests"> - </instrumentation> </manifest> diff --git a/tests/FlickerTests/manifests/AndroidManifestAppClose.xml b/tests/FlickerTests/manifests/AndroidManifestAppClose.xml new file mode 100644 index 000000000000..4cdcb903b498 --- /dev/null +++ b/tests/FlickerTests/manifests/AndroidManifestAppClose.xml @@ -0,0 +1,24 @@ +<!-- + ~ Copyright (C) 2023 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.server.wm.flicker.close"> + + <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" + android:targetPackage="com.android.server.wm.flicker.close" + android:label="WindowManager Flicker Tests"> + </instrumentation> +</manifest> diff --git a/tests/FlickerTests/manifests/AndroidManifestAppLaunch.xml b/tests/FlickerTests/manifests/AndroidManifestAppLaunch.xml new file mode 100644 index 000000000000..659a745ba480 --- /dev/null +++ b/tests/FlickerTests/manifests/AndroidManifestAppLaunch.xml @@ -0,0 +1,24 @@ +<!-- + ~ Copyright (C) 2023 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.server.wm.flicker.launch"> + + <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" + android:targetPackage="com.android.server.wm.flicker.launch" + android:label="WindowManager Flicker Tests"> + </instrumentation> +</manifest> diff --git a/tests/FlickerTests/manifests/AndroidManifestIme.xml b/tests/FlickerTests/manifests/AndroidManifestIme.xml new file mode 100644 index 000000000000..abd03af4888a --- /dev/null +++ b/tests/FlickerTests/manifests/AndroidManifestIme.xml @@ -0,0 +1,24 @@ +<!-- + ~ Copyright (C) 2023 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.server.wm.flicker.ime"> + + <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" + android:targetPackage="com.android.server.wm.flicker.ime" + android:label="WindowManager Flicker Tests"> + </instrumentation> +</manifest> diff --git a/tests/FlickerTests/manifests/AndroidManifestOther.xml b/tests/FlickerTests/manifests/AndroidManifestOther.xml new file mode 100644 index 000000000000..47749b8133b1 --- /dev/null +++ b/tests/FlickerTests/manifests/AndroidManifestOther.xml @@ -0,0 +1,24 @@ +<!-- + ~ Copyright (C) 2023 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.server.wm.flicker"> + + <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" + android:targetPackage="com.android.server.wm.flicker" + android:label="WindowManager Flicker Tests"> + </instrumentation> +</manifest> diff --git a/tests/FlickerTests/manifests/AndroidManifestQuickswitch.xml b/tests/FlickerTests/manifests/AndroidManifestQuickswitch.xml new file mode 100644 index 000000000000..203035d30584 --- /dev/null +++ b/tests/FlickerTests/manifests/AndroidManifestQuickswitch.xml @@ -0,0 +1,24 @@ +<!-- + ~ Copyright (C) 2023 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.server.wm.flicker.quickswitch"> + + <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" + android:targetPackage="com.android.server.wm.flicker.quickswitch" + android:label="WindowManager Flicker Tests"> + </instrumentation> +</manifest> diff --git a/tests/FlickerTests/manifests/AndroidManifestRotation.xml b/tests/FlickerTests/manifests/AndroidManifestRotation.xml new file mode 100644 index 000000000000..2852cf23a35b --- /dev/null +++ b/tests/FlickerTests/manifests/AndroidManifestRotation.xml @@ -0,0 +1,24 @@ +<!-- + ~ Copyright (C) 2023 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.server.wm.flicker.rotation"> + + <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" + android:targetPackage="com.android.server.wm.flicker.rotation" + android:label="WindowManager Flicker Tests"> + </instrumentation> +</manifest> diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/RTLStartSecondaryWithPlaceholderTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/RTLStartSecondaryWithPlaceholderTest.kt new file mode 100644 index 000000000000..236c44e89f41 --- /dev/null +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/RTLStartSecondaryWithPlaceholderTest.kt @@ -0,0 +1,211 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wm.flicker.activityembedding + +import android.platform.test.annotations.Presubmit +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerBuilder +import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.FlickerTestFactory +import androidx.test.filters.RequiresDevice +import com.android.server.wm.flicker.helpers.ActivityEmbeddingAppHelper +import org.junit.FixMethodOrder +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.MethodSorters +import org.junit.runners.Parameterized + +/** + * Test launching a placeholder split over a normal split, both splits are configured in RTL. + * + * Setup: From A launch a split in RTL - resulting in B|A. + * Transitions: + * From A start PlaceholderPrimary, which is configured to launch with PlaceholderSecondary in RTL. + * Expect split PlaceholderSecondary|PlaceholderPrimary covering split B|A. + * + * To run this test: `atest FlickerTests:RTLStartSecondaryWithPlaceholderTest` + */ +@RequiresDevice +@RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +class RTLStartSecondaryWithPlaceholderTest(flicker: FlickerTest) : + ActivityEmbeddingTestBase(flicker) { + + /** {@inheritDoc} */ + override val transition: FlickerBuilder.() -> Unit = { + setup { + tapl.setExpectedRotationCheckEnabled(false) + testApp.launchViaIntent(wmHelper) + testApp.launchSecondaryActivityRTL(wmHelper) + } + transitions { + testApp.launchPlaceholderSplitRTL(wmHelper) + } + teardown { + tapl.goHome() + testApp.exit(wmHelper) + } + } + + /** + * Main activity and Secondary activity will become invisible because they are covered by + * PlaceholderPrimary activity and PlaceholderSecondary activity. + */ + @Presubmit + @Test + fun assertWindowVisibilities() { + flicker.assertWm { + isAppWindowVisible(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT) + .then() + .isAppWindowInvisible(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT) + } + flicker.assertWm { + isAppWindowVisible(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT) + .then() + .isAppWindowInvisible(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT) + } + flicker.assertWm { + isAppWindowInvisible(ActivityEmbeddingAppHelper.PLACEHOLDER_PRIMARY_COMPONENT) + .then() + .isAppWindowVisible(ActivityEmbeddingAppHelper.PLACEHOLDER_PRIMARY_COMPONENT) + } + flicker.assertWm { + isAppWindowInvisible(ActivityEmbeddingAppHelper.PLACEHOLDER_SECONDARY_COMPONENT) + .then() + .isAppWindowVisible(ActivityEmbeddingAppHelper.PLACEHOLDER_SECONDARY_COMPONENT) + } + } + + /** + * Main activity and Secondary activity will become invisible because they are covered by + * PlaceholderPrimary activity and PlaceholderSecondary activity. + */ + @Presubmit + @Test + fun assertLayerVisibilities() { + flicker.assertLayers { + this.isVisible(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT) + .then() + .isInvisible(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT) + } + flicker.assertLayers { + this.isVisible(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT) + .then() + .isInvisible(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT) + } + flicker.assertLayers { + isInvisible(ActivityEmbeddingAppHelper.PLACEHOLDER_PRIMARY_COMPONENT) + .then() + .isVisible(ActivityEmbeddingAppHelper.PLACEHOLDER_PRIMARY_COMPONENT) + } + flicker.assertLayers { + isInvisible(ActivityEmbeddingAppHelper.PLACEHOLDER_SECONDARY_COMPONENT) + .then() + .isVisible(ActivityEmbeddingAppHelper.PLACEHOLDER_SECONDARY_COMPONENT) + } + } + + /** Main activity and Secondary activity split is in right-to-left layout direction. */ + @Presubmit + @Test + fun assertWMRTLBeforeTransition() { + flicker.assertWmStart { + val mainActivityRegion = + this.visibleRegion(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT) + val secondaryActivityRegion = + this.visibleRegion(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT) + mainActivityRegion.notOverlaps(secondaryActivityRegion.region) + // secondary activity is on the left, main activity is on the right. + check { "isRTLBeforeTransition" } + .that(mainActivityRegion.region.bounds.left) + .isEqual(secondaryActivityRegion.region.bounds.right) + } + } + + /** Main activity and Secondary activity split is in right-to-left layout direction. */ + @Presubmit + @Test + fun assertLayerRTLBeforeTransition() { + flicker.assertLayersStart { + val mainActivityRegion = + this.visibleRegion(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT) + val secondaryActivityRegion = + this.visibleRegion(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT) + mainActivityRegion.notOverlaps(secondaryActivityRegion.region) + // secondary activity is on the left, main activity is on the right. + check { "isRTLBeforeTransition" } + .that(mainActivityRegion.region.bounds.left) + .isEqual(secondaryActivityRegion.region.bounds.right) + } + } + + /** + * PlaceholderPrimary activity and PlaceholderSecondary activity split are in right-to-left + * layout direction. + */ + @Presubmit + @Test + fun assertWMRTLAfterTransition() { + flicker.assertWmEnd { + val mainActivityRegion = + this.visibleRegion(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT) + val secondaryActivityRegion = + this.visibleRegion(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT) + mainActivityRegion.notOverlaps(secondaryActivityRegion.region) + // secondary activity is on the left, main activity is on the right. + check { "isRTLBeforeTransition" } + .that(mainActivityRegion.region.bounds.left) + .isEqual(secondaryActivityRegion.region.bounds.right) + } + } + + /** + * PlaceholderPrimary activity and PlaceholderSecondary activity split are in right-to-left + * layout direction. + */ + @Presubmit + @Test + fun assertLayerRTLAfterTransition() { + flicker.assertLayersEnd { + val mainActivityRegion = + this.visibleRegion(ActivityEmbeddingAppHelper.PLACEHOLDER_PRIMARY_COMPONENT) + val secondaryActivityRegion = + this.visibleRegion(ActivityEmbeddingAppHelper.PLACEHOLDER_SECONDARY_COMPONENT) + mainActivityRegion.notOverlaps(secondaryActivityRegion.region) + // Placeholder secondary activity is on the left, placeholder primary activity is on the + // right. + check { "isRTLAfterTransition" } + .that(mainActivityRegion.region.bounds.left) + .isEqual(secondaryActivityRegion.region.bounds.right) + } + } + + companion object { + /** + * Creates the test configurations. + * + * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and + * navigation modes. + */ + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams(): Collection<FlickerTest> { + return FlickerTestFactory.nonRotationTests() + } + } +} diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/close/CloseSecondaryActivityInSplitTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/close/CloseSecondaryActivityInSplitTest.kt new file mode 100644 index 000000000000..c0c738b16c2a --- /dev/null +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/close/CloseSecondaryActivityInSplitTest.kt @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wm.flicker.activityembedding + +import android.platform.test.annotations.Presubmit +import android.tools.common.datatypes.Rect +import android.tools.common.traces.component.ComponentNameMatcher +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerBuilder +import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.FlickerTestFactory +import androidx.test.filters.RequiresDevice +import com.android.server.wm.flicker.helpers.ActivityEmbeddingAppHelper +import org.junit.FixMethodOrder +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.MethodSorters +import org.junit.runners.Parameterized + +/** + * Test closing a secondary activity in a split. + * + * Setup: Launch A|B in split with B being the secondary activity. + * Transitions: Finish B and expect A to become fullscreen. + * + * To run this test: `atest FlickerTests:CloseSecondaryActivityInSplitTest` + */ +@RequiresDevice +@RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +class CloseSecondaryActivityInSplitTest(flicker: FlickerTest) : + ActivityEmbeddingTestBase(flicker) { + + override val transition: FlickerBuilder.() -> Unit = { + setup { + tapl.setExpectedRotationCheckEnabled(false) + // Launches fullscreen A. + testApp.launchViaIntent(wmHelper) + // Launches a split A|B and waits for both activities to show. + testApp.launchSecondaryActivity(wmHelper) + // Get fullscreen bounds + startDisplayBounds = + wmHelper.currentState.layerState.physicalDisplayBounds ?: + error("Can't get display bounds") + } + transitions { + // Finish secondary activity B. + testApp.finishSecondaryActivity(wmHelper) + // Expect the main activity A to expand into fullscreen. + wmHelper.StateSyncBuilder().withFullScreenApp(testApp).waitForAndVerify() + } + teardown { + tapl.goHome() + testApp.exit(wmHelper) + } + } + + /** Main activity is always visible and becomes fullscreen in the end. */ + @Presubmit + @Test + fun mainActivityWindowBecomesFullScreen() { + flicker.assertWm { isAppWindowVisible(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT) } + flicker.assertWmEnd { + this.visibleRegion(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT) + .coversExactly(startDisplayBounds) + } + } + + /** Main activity surface is animated from split to fullscreen. */ + @Presubmit + @Test + fun mainActivityLayerIsAlwaysVisible() { + flicker.assertLayers { + isVisible( + ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT.or( + ComponentNameMatcher.TRANSITION_SNAPSHOT + ) + ) + } + flicker.assertLayersEnd { + isVisible(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT) + .isInvisible(ComponentNameMatcher.TRANSITION_SNAPSHOT) + } + } + + /** Secondary activity should destroy and become invisible. */ + @Presubmit + @Test + fun secondaryActivityWindowFinishes() { + flicker.assertWm { + contains(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT) + .then() + .notContains(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT) + } + } + + @Presubmit + @Test + fun secondaryActivityLayerFinishes() { + flicker.assertLayers { + isVisible(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT) + .then() + .isInvisible(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT) + } + } + + companion object { + /** {@inheritDoc} */ + private var startDisplayBounds = Rect.EMPTY + /** + * Creates the test configurations. + * + * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and + * navigation modes. + */ + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams(): Collection<FlickerTest> { + return FlickerTestFactory.nonRotationTests() + } + } + }
\ No newline at end of file diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/MainActivityStartsSecondaryWithAlwaysExpandTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/MainActivityStartsSecondaryWithAlwaysExpandTest.kt new file mode 100644 index 000000000000..00316ea249b7 --- /dev/null +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/MainActivityStartsSecondaryWithAlwaysExpandTest.kt @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wm.flicker.activityembedding + +import android.platform.test.annotations.Presubmit +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerBuilder +import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.FlickerTestFactory +import androidx.test.filters.RequiresDevice +import com.android.server.wm.flicker.helpers.ActivityEmbeddingAppHelper +import org.junit.FixMethodOrder +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.MethodSorters +import org.junit.runners.Parameterized +import android.tools.common.datatypes.Rect + +/** + * Test launching an activity with AlwaysExpand rule. + * + * Setup: Launch A|B in split with B being the secondary activity. + * Transitions: + * A start C with alwaysExpand=true, expect C to launch in fullscreen and cover split A|B. + * + * To run this test: `atest FlickerTests:MainActivityStartsSecondaryWithAlwaysExpandTest` + */ +@RequiresDevice +@RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +class MainActivityStartsSecondaryWithAlwaysExpandTest(flicker: FlickerTest) : + ActivityEmbeddingTestBase(flicker) { + + /** {@inheritDoc} */ + override val transition: FlickerBuilder.() -> Unit = { + setup { + tapl.setExpectedRotationCheckEnabled(false) + // Launch a split + testApp.launchViaIntent(wmHelper) + testApp.launchSecondaryActivity(wmHelper) + startDisplayBounds = + wmHelper.currentState.layerState.physicalDisplayBounds ?: error("Display not found") + } + transitions { + // Launch C with alwaysExpand + testApp.launchAlwaysExpandActivity(wmHelper) + } + teardown { + tapl.goHome() + testApp.exit(wmHelper) + } + } + + /** Transition begins with a split. */ + @Presubmit + @Test + fun startsWithSplit() { + flicker.assertWmStart { + this.isAppWindowVisible(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT) + } + flicker.assertWmStart { + this.isAppWindowVisible(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT) + } + } + + + /** Main activity should become invisible after being covered by always expand activity. */ + @Presubmit + @Test + fun mainActivityLayerBecomesInvisible() { + flicker.assertLayers { + isVisible(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT) + .then() + .isInvisible(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT) + } + } + + /** Secondary activity should become invisible after being covered by always expand activity. */ + @Presubmit + @Test + fun secondaryActivityLayerBecomesInvisible() { + flicker.assertLayers { + isVisible(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT) + .then() + .isInvisible(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT) + } + } + + /** At the end of transition always expand activity is in fullscreen. */ + @Presubmit + @Test + fun endsWithAlwaysExpandActivityCoveringFullScreen() { + flicker.assertWmEnd { + this.visibleRegion(ActivityEmbeddingAppHelper.ALWAYS_EXPAND_ACTIVITY_COMPONENT) + .coversExactly(startDisplayBounds) + } + } + + /** Always expand activity is on top of the split. */ + @Presubmit + @Test + fun endsWithAlwaysExpandActivityOnTop() { + flicker.assertWmEnd { + this.isAppWindowOnTop( + ActivityEmbeddingAppHelper.ALWAYS_EXPAND_ACTIVITY_COMPONENT) + } + } + + companion object { + /** {@inheritDoc} */ + private var startDisplayBounds = Rect.EMPTY + /** + * Creates the test configurations. + * + * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and + * navigation modes. + */ + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams(): Collection<FlickerTest> { + return FlickerTestFactory.nonRotationTests() + } + } +} + diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/OpenActivityEmbeddingPlaceholderSplitTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenActivityEmbeddingPlaceholderSplitTest.kt index ed17059e79e7..ed17059e79e7 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/OpenActivityEmbeddingPlaceholderSplitTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenActivityEmbeddingPlaceholderSplitTest.kt diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/OpenActivityEmbeddingSecondaryToSplitTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenActivityEmbeddingSecondaryToSplitTest.kt index 863828881d36..863828881d36 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/OpenActivityEmbeddingSecondaryToSplitTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenActivityEmbeddingSecondaryToSplitTest.kt diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/rotation/RotateSplitNoChangeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/rotation/RotateSplitNoChangeTest.kt new file mode 100644 index 000000000000..39ae8e2d9799 --- /dev/null +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/rotation/RotateSplitNoChangeTest.kt @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wm.flicker.activityembedding + +import android.platform.test.annotations.Presubmit +import android.tools.common.traces.component.ComponentNameMatcher +import com.android.server.wm.flicker.rotation.RotationTransition +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerBuilder +import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.FlickerTestFactory +import androidx.test.filters.RequiresDevice +import com.android.server.wm.flicker.helpers.ActivityEmbeddingAppHelper +import org.junit.FixMethodOrder +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.MethodSorters +import org.junit.runners.Parameterized + +/** + * Tests rotating two activities in an Activity Embedding split. + * + * Setup: Launch A|B in split with B being the secondary activity. + * Transitions: Rotate display, and expect A and B to split evenly in new rotation. + * + * To run this test: `atest FlickerTests:RotateSplitNoChangeTest` + */ +@RequiresDevice +@RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +open class RotateSplitNoChangeTest(flicker: FlickerTest) : RotationTransition(flicker) { + + override val testApp = ActivityEmbeddingAppHelper(instrumentation) + override val transition: FlickerBuilder.() -> Unit + get() = { + super.transition(this) + setup { + testApp.launchViaIntent(wmHelper) + testApp.launchSecondaryActivity(wmHelper) + } + } + + /** + * Checks that the [ComponentNameMatcher.ROTATION] layer appears during the transition, doesn't + * flicker, and disappears before the transition is complete + */ + @Presubmit + @Test + fun rotationLayerAppearsAndVanishes() { + flicker.assertLayers { + this.isVisible(testApp) + .then() + .isVisible(ComponentNameMatcher.ROTATION) + .then() + .isVisible(testApp) + .isInvisible(ComponentNameMatcher.ROTATION) + } + } + + /** + * Overrides inherited assertion because in AE Split, the main and secondary activity are separate + * layers, each covering up exactly half of the display. + */ + @Presubmit + @Test + override fun appLayerRotates_StartingPos() { + flicker.assertLayersStart { + this.entry.displays.map { display -> + val leftLayerRegion = this.visibleRegion(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT) + val rightLayerRegion = + this.visibleRegion(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT) + // Compare dimensions of two splits, given we're using default split attributes, + // both activities take up the same visible size on the display. + check{"height"}.that(leftLayerRegion.region.height).isEqual(rightLayerRegion.region.height) + check{"width"}.that(leftLayerRegion.region.width).isEqual(rightLayerRegion.region.width) + leftLayerRegion.notOverlaps(rightLayerRegion.region) + // Layers of two activities sum to be fullscreen size on display. + leftLayerRegion.plus(rightLayerRegion.region).coversExactly(display.layerStackSpace) + } + } + } + + /** + * Verifies dimensions of both split activities hold their invariance after transition too. + */ + @Presubmit + @Test + override fun appLayerRotates_EndingPos() { + flicker.assertLayersEnd { + this.entry.displays.map { display -> + val leftLayerRegion = this.visibleRegion(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT) + val rightLayerRegion = + this.visibleRegion(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT) + check{"height"}.that(leftLayerRegion.region.height).isEqual(rightLayerRegion.region.height) + check{"width"}.that(leftLayerRegion.region.width).isEqual(rightLayerRegion.region.width) + leftLayerRegion.notOverlaps(rightLayerRegion.region) + leftLayerRegion.plus(rightLayerRegion.region).coversExactly(display.layerStackSpace) + } + } + } + + /** Both activities in split should remain visible during rotation. */ + @Presubmit + @Test + fun bothActivitiesAreAlwaysVisible() { + flicker.assertWm { + isAppWindowVisible(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT) + } + flicker.assertWm { + isAppWindowVisible(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT) + } + } + + companion object { + /** + * Creates the test configurations. + * + * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and + * navigation modes. + */ + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams(): Collection<FlickerTest> { + return FlickerTestFactory.rotationTests() + } + } +}
\ No newline at end of file diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ActivityEmbeddingAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ActivityEmbeddingAppHelper.kt index daecfe7e4c4d..793c68ea376d 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ActivityEmbeddingAppHelper.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ActivityEmbeddingAppHelper.kt @@ -60,6 +60,70 @@ constructor( } /** + * Clicks the button to finishes the secondary activity launched through + * [launchSecondaryActivity], waits for the main activity to resume. + */ + fun finishSecondaryActivity(wmHelper: WindowManagerStateHelper) { + val finishButton = + uiDevice.wait( + Until.findObject(By.res(getPackage(), "finish_secondary_activity_button")), + FIND_TIMEOUT + ) + require(finishButton != null) { "Can't find finish secondary activity button on screen." } + finishButton.click() + wmHelper + .StateSyncBuilder() + .withActivityRemoved(SECONDARY_ACTIVITY_COMPONENT) + .waitForAndVerify() + } + + /** + * Clicks the button to launch a secondary activity with alwaysExpand enabled, which will launch + * a fullscreen window on top of the visible region. + */ + fun launchAlwaysExpandActivity(wmHelper: WindowManagerStateHelper) { + val launchButton = + uiDevice.wait( + Until.findObject( + By.res(getPackage(), + "launch_always_expand_activity_button")), + FIND_TIMEOUT + ) + require(launchButton != null) { + "Can't find launch always expand activity button on screen." + } + launchButton.click() + wmHelper + .StateSyncBuilder() + .withActivityState(ALWAYS_EXPAND_ACTIVITY_COMPONENT, PlatformConsts.STATE_RESUMED) + .withActivityState(MAIN_ACTIVITY_COMPONENT, PlatformConsts.STATE_PAUSED) + .waitForAndVerify() + } + + /** + * Clicks the button to launch the secondary activity in RTL, which should split with the main + * activity based on the split pair rule. + */ + fun launchSecondaryActivityRTL(wmHelper: WindowManagerStateHelper) { + val launchButton = + uiDevice.wait( + Until.findObject( + By.res(getPackage(), + "launch_secondary_activity_rtl_button")), + FIND_TIMEOUT + ) + require(launchButton != null) { + "Can't find launch secondary activity rtl button on screen." + } + launchButton.click() + wmHelper + .StateSyncBuilder() + .withActivityState(SECONDARY_ACTIVITY_COMPONENT, PlatformConsts.STATE_RESUMED) + .withActivityState(MAIN_ACTIVITY_COMPONENT, PlatformConsts.STATE_RESUMED) + .waitForAndVerify() + } + + /** * Clicks the button to launch the placeholder primary activity, which should launch the * placeholder secondary activity based on the placeholder rule. */ @@ -78,6 +142,29 @@ constructor( .waitForAndVerify() } + /** + * Clicks the button to launch the placeholder primary activity in RTL, which should launch the + * placeholder secondary activity based on the placeholder rule. + */ + fun launchPlaceholderSplitRTL(wmHelper: WindowManagerStateHelper) { + val launchButton = + uiDevice.wait( + Until.findObject( + By.res(getPackage(), + "launch_placeholder_split_rtl_button")), + FIND_TIMEOUT + ) + require(launchButton != null) { + "Can't find launch placeholder split button on screen." + } + launchButton.click() + wmHelper + .StateSyncBuilder() + .withActivityState(PLACEHOLDER_PRIMARY_COMPONENT, PlatformConsts.STATE_RESUMED) + .withActivityState(PLACEHOLDER_SECONDARY_COMPONENT, PlatformConsts.STATE_RESUMED) + .waitForAndVerify() + } + companion object { private const val TAG = "ActivityEmbeddingAppHelper" @@ -87,6 +174,9 @@ constructor( val SECONDARY_ACTIVITY_COMPONENT = ActivityOptions.ActivityEmbedding.SecondaryActivity.COMPONENT.toFlickerComponent() + val ALWAYS_EXPAND_ACTIVITY_COMPONENT = + ActivityOptions.ActivityEmbedding.AlwaysExpandActivity.COMPONENT.toFlickerComponent() + val PLACEHOLDER_PRIMARY_COMPONENT = ActivityOptions.ActivityEmbedding.PlaceholderPrimaryActivity.COMPONENT .toFlickerComponent() diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/GestureHelper.java b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/GestureHelper.java index a8f1b3de564e..eeee7b4dfc6b 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/GestureHelper.java +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/GestureHelper.java @@ -62,6 +62,33 @@ public class GestureHelper { } /** + * Injects a series of {@link MotionEvent}s to simulate tapping. + * + * @param point coordinates of pointer to tap + * @param times the number of times to tap + */ + public boolean tap(@NonNull Tuple point, int times) throws InterruptedException { + PointerProperties ptrProp = getPointerProp(0, MotionEvent.TOOL_TYPE_FINGER); + PointerCoords ptrCoord = getPointerCoord(point.x, point.y, 1, 1); + + for (int i = 0; i <= times; i++) { + // If already tapped, inject delay in between movements + if (times > 0) { + SystemClock.sleep(50L); + } + if (!primaryPointerDown(ptrProp, ptrCoord, SystemClock.uptimeMillis())) { + return false; + } + // Delay before releasing tap + SystemClock.sleep(100L); + if (!primaryPointerUp(ptrProp, ptrCoord, SystemClock.uptimeMillis())) { + return false; + } + } + return true; + } + + /** * Injects a series of {@link MotionEvent}s to simulate a drag gesture without pointer release. * * Simulates a drag gesture without releasing the primary pointer. The primary pointer info diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/LetterboxAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/LetterboxAppHelper.kt index a670d68cabd9..34581bbb4516 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/LetterboxAppHelper.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/LetterboxAppHelper.kt @@ -17,6 +17,8 @@ package com.android.server.wm.flicker.helpers import android.app.Instrumentation +import android.tools.common.datatypes.Region +import android.tools.common.datatypes.Rect import android.tools.common.traces.component.ComponentNameMatcher import android.tools.device.apphelpers.StandardAppHelper import android.tools.device.helpers.FIND_TIMEOUT @@ -36,6 +38,8 @@ constructor( ActivityOptions.NonResizeablePortraitActivity.COMPONENT.toFlickerComponent() ) : StandardAppHelper(instr, launcherName, component) { + private val gestureHelper: GestureHelper = GestureHelper(mInstrumentation) + fun clickRestart(wmHelper: WindowManagerStateHelper) { val restartButton = uiDevice.wait( @@ -56,4 +60,64 @@ constructor( ?: error("Restart dialog button not found") wmHelper.StateSyncBuilder().withAppTransitionIdle().waitForAndVerify() } + + fun repositionHorizontally(displayBounds: Rect, right: Boolean) { + val x = if (right) displayBounds.right - BOUNDS_OFFSET else BOUNDS_OFFSET + reposition(x.toFloat(), displayBounds.centerY().toFloat()) + } + + fun repositionVertically(displayBounds: Rect, bottom: Boolean) { + val y = if (bottom) displayBounds.bottom - BOUNDS_OFFSET else BOUNDS_OFFSET + reposition(displayBounds.centerX().toFloat(), y.toFloat()) + } + + private fun reposition(x: Float, y: Float) { + val coords = GestureHelper.Tuple(x, y) + require(gestureHelper.tap(coords, 2)) { "Failed to reposition letterbox app" } + } + + fun waitForAppToMoveHorizontallyTo( + wmHelper: WindowManagerStateHelper, + displayBounds: Rect, + right: Boolean + ) { + wmHelper.StateSyncBuilder().add("letterboxAppRepositioned") { + val letterboxAppWindow = getWindowRegion(wmHelper) + val appRegionBounds = letterboxAppWindow.bounds + val appWidth = appRegionBounds.width + return@add if (right) appRegionBounds.left == displayBounds.right - appWidth && + appRegionBounds.right == displayBounds.right + else appRegionBounds.left == displayBounds.left && + appRegionBounds.right == displayBounds.left + appWidth + }.waitForAndVerify() + } + + fun waitForAppToMoveVerticallyTo( + wmHelper: WindowManagerStateHelper, + displayBounds: Rect, + navBarHeight: Int, + bottom: Boolean + ) { + wmHelper.StateSyncBuilder().add("letterboxAppRepositioned") { + val letterboxAppWindow = getWindowRegion(wmHelper) + val appRegionBounds = letterboxAppWindow.bounds + val appHeight = appRegionBounds.height + return@add if (bottom) appRegionBounds.bottom == displayBounds.bottom && + appRegionBounds.top == (displayBounds.bottom - appHeight + navBarHeight) + else appRegionBounds.top == displayBounds.top && + appRegionBounds.bottom == displayBounds.top + appHeight + }.waitForAndVerify() + } + + private fun getWindowRegion(wmHelper: WindowManagerStateHelper): Region { + val windowRegion = wmHelper.getWindowRegion(this) + require(!windowRegion.isEmpty) { + "Unable to find letterbox app window in the current state" + } + return windowRegion + } + + companion object { + private const val BOUNDS_OFFSET: Int = 100 + } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivityTransitionTest.kt index e6594c969373..a87fae857509 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivityTransitionTest.kt @@ -57,7 +57,7 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class ActivitiesTransitionTest(flicker: FlickerTest) : BaseTest(flicker) { +open class ActivityTransitionTest(flicker: FlickerTest) : BaseTest(flicker) { private val testApp: TwoActivitiesAppHelper = TwoActivitiesAppHelper(instrumentation) /** {@inheritDoc} */ diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppAfterCameraTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivityTransitionTestCfArm.kt index ac05c7687311..85344a156d7a 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppAfterCameraTestCfArm.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivityTransitionTestCfArm.kt @@ -27,7 +27,7 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class OpenAppAfterCameraTestCfArm(flicker: FlickerTest) : OpenAppAfterCameraTest(flicker) { +class ActivityTransitionTestCfArm(flicker: FlickerTest) : ActivityTransitionTest(flicker) { companion object { /** * Creates the test configurations. diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdFromIcon.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIconColdTest.kt index 3a80c6649833..575206591e59 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdFromIcon.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIconColdTest.kt @@ -55,7 +55,7 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class OpenAppColdFromIcon(flicker: FlickerTest) : OpenAppFromLauncherTransition(flicker) { +open class OpenAppFromIconColdTest(flicker: FlickerTest) : OpenAppFromLauncherTransition(flicker) { /** {@inheritDoc} */ override val transition: FlickerBuilder.() -> Unit get() = { diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdFromIconCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIconColdTestCfArm.kt index d33a2724ca44..d453c1ae908a 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdFromIconCfArm.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIconColdTestCfArm.kt @@ -32,7 +32,7 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class OpenAppColdFromIconCfArm(flicker: FlickerTest) : OpenAppColdFromIcon(flicker) { +class OpenAppFromIconColdTestCfArm(flicker: FlickerTest) : OpenAppFromIconColdTest(flicker) { @Test @FlakyTest override fun visibleLayersShownMoreThanOneConsecutiveEntry() { diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppAfterCameraTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdAfterCameraTest.kt index 549183f407e2..e74731555642 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppAfterCameraTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdAfterCameraTest.kt @@ -38,7 +38,8 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class OpenAppAfterCameraTest(flicker: FlickerTest) : OpenAppFromLauncherTransition(flicker) { +open class OpenAppFromIntentColdAfterCameraTest(flicker: FlickerTest) : + OpenAppFromLauncherTransition(flicker) { private val cameraApp = CameraAppHelper(instrumentation) /** {@inheritDoc} */ override val transition: FlickerBuilder.() -> Unit diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdAfterCameraTestCfArm.kt index 8b89a8b4c40d..177ad7dc09d1 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTestCfArm.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdAfterCameraTestCfArm.kt @@ -27,7 +27,8 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class ActivitiesTransitionTestCfArm(flicker: FlickerTest) : ActivitiesTransitionTest(flicker) { +class OpenAppFromIntentColdAfterCameraTestCfArm(flicker: FlickerTest) : + OpenAppFromIntentColdAfterCameraTest(flicker) { companion object { /** * Creates the test configurations. diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdTest.kt index 26f88d23cda0..f45f728664cf 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdTest.kt @@ -58,7 +58,8 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class OpenAppColdTest(flicker: FlickerTest) : OpenAppFromLauncherTransition(flicker) { +open class OpenAppFromIntentColdTest(flicker: FlickerTest) : + OpenAppFromLauncherTransition(flicker) { /** {@inheritDoc} */ override val transition: FlickerBuilder.() -> Unit get() = { diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdTestCfArm.kt index d9a99dadbd3d..0d695f306c00 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTestCfArm.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdTestCfArm.kt @@ -31,7 +31,7 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class OpenAppColdTestCfArm(flicker: FlickerTest) : OpenAppColdTest(flicker) { +class OpenAppFromIntentColdTestCfArm(flicker: FlickerTest) : OpenAppFromIntentColdTest(flicker) { @FlakyTest(bugId = 273696733) @Test override fun appLayerReplacesLauncher() = super.appLayerReplacesLauncher() diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentWarmTest.kt index 3385830ee77f..a42bff5bf170 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentWarmTest.kt @@ -58,7 +58,8 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class OpenAppWarmTest(flicker: FlickerTest) : OpenAppFromLauncherTransition(flicker) { +open class OpenAppFromIntentWarmTest(flicker: FlickerTest) : + OpenAppFromLauncherTransition(flicker) { /** Defines the transition used to run the test */ override val transition: FlickerBuilder.() -> Unit get() = { diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentWarmTestCfArm.kt index d8b38b30cf13..b6ffcb3df9f3 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTestCfArm.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentWarmTestCfArm.kt @@ -29,7 +29,7 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class OpenAppWarmTestCfArm(flicker: FlickerTest) : OpenAppWarmTest(flicker) { +class OpenAppFromIntentWarmTestCfArm(flicker: FlickerTest) : OpenAppFromIntentWarmTest(flicker) { companion object { /** * Creates the test configurations. diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationCold.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenNotificationColdTest.kt index b21777b30b21..fd4272600d55 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationCold.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenNotificationColdTest.kt @@ -44,8 +44,8 @@ import org.junit.runners.Parameterized @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) @Postsubmit -open class OpenAppFromLockNotificationCold(flicker: FlickerTest) : - OpenAppFromNotificationCold(flicker) { +open class OpenAppFromLockscreenNotificationColdTest(flicker: FlickerTest) : + OpenAppFromNotificationColdTest(flicker) { override val openingNotificationsFromLockScreen = true diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWarm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenNotificationWarmTest.kt index ec92ca65f80a..fd051d50d032 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWarm.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenNotificationWarmTest.kt @@ -46,7 +46,8 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class OpenAppFromLockNotificationWarm(flicker: FlickerTest) : OpenAppFromNotificationWarm(flicker) { +class OpenAppFromLockscreenNotificationWarmTest(flicker: FlickerTest) : + OpenAppFromNotificationWarmTest(flicker) { override val openingNotificationsFromLockScreen = true diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWithLockOverlayApp.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenNotificationWithOverlayAppTest.kt index 009d61797fe0..37afa8d0caba 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWithLockOverlayApp.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenNotificationWithOverlayAppTest.kt @@ -37,6 +37,8 @@ import org.junit.runners.Parameterized * Test cold launching an app from a notification from the lock screen when there is an app overlaid * on the lock screen. * + * This test assumes the device doesn't have AOD enabled + * * To run this test: `atest FlickerTests:OpenAppFromLockNotificationWithLockOverlayApp` */ @RequiresDevice @@ -44,8 +46,8 @@ import org.junit.runners.Parameterized @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) @Postsubmit -class OpenAppFromLockNotificationWithLockOverlayApp(flicker: FlickerTest) : - OpenAppFromLockNotificationCold(flicker) { +class OpenAppFromLockscreenNotificationWithOverlayAppTest(flicker: FlickerTest) : + OpenAppFromLockscreenNotificationColdTest(flicker) { private val showWhenLockedApp = ShowWhenLockedAppHelper(instrumentation) // Although we are technically still locked here, the overlay app means we should open the diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenTransition.kt index eae9ca10c711..30c3ec205bb1 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockTransition.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenTransition.kt @@ -28,7 +28,7 @@ import org.junit.Ignore import org.junit.Test /** Base class for app launch tests from lock screen */ -abstract class OpenAppFromLockTransition(flicker: FlickerTest) : OpenAppTransition(flicker) { +abstract class OpenAppFromLockscreenTransition(flicker: FlickerTest) : OpenAppTransition(flicker) { /** Defines the transition used to run the test */ override val transition: FlickerBuilder.() -> Unit = { diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenViaIntentTest.kt index 1383ae39f760..924d03f6d302 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenViaIntentTest.kt @@ -63,7 +63,8 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class OpenAppNonResizeableTest(flicker: FlickerTest) : OpenAppFromLockTransition(flicker) { +open class OpenAppFromLockscreenViaIntentTest(flicker: FlickerTest) : + OpenAppFromLockscreenTransition(flicker) { override val testApp = NonResizeableAppHelper(instrumentation) /** diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationCold.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationColdTest.kt index 7bcb91070ecf..d873ec5e83e2 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationCold.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationColdTest.kt @@ -35,8 +35,6 @@ import org.junit.runners.Parameterized /** * Test cold launching an app from a notification. * - * This test assumes the device doesn't have AOD enabled - * * To run this test: `atest FlickerTests:OpenAppFromNotificationCold` */ @RequiresDevice @@ -44,8 +42,8 @@ import org.junit.runners.Parameterized @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) @Postsubmit -open class OpenAppFromNotificationCold(flicker: FlickerTest) : - OpenAppFromNotificationWarm(flicker) { +open class OpenAppFromNotificationColdTest(flicker: FlickerTest) : + OpenAppFromNotificationWarmTest(flicker) { /** {@inheritDoc} */ override val transition: FlickerBuilder.() -> Unit get() = { diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationColdCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationColdTestCfArm.kt index 8b4a613305c0..fb2a48c2ad7f 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationColdCfArm.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationColdTestCfArm.kt @@ -29,8 +29,8 @@ import org.junit.runners.Parameterized @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) @Postsubmit -class OpenAppFromNotificationColdCfArm(flicker: FlickerTest) : - OpenAppFromNotificationCold(flicker) { +class OpenAppFromNotificationColdTestCfArm(flicker: FlickerTest) : + OpenAppFromNotificationColdTest(flicker) { companion object { /** * Creates the test configurations. diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarmTest.kt index 425e674dec3a..99668ecd0b68 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarm.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarmTest.kt @@ -47,15 +47,13 @@ import org.junit.runners.Parameterized /** * Test cold launching an app from a notification. * - * This test assumes the device doesn't have AOD enabled - * * To run this test: `atest FlickerTests:OpenAppFromNotificationWarm` */ @RequiresDevice @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class OpenAppFromNotificationWarm(flicker: FlickerTest) : OpenAppTransition(flicker) { +open class OpenAppFromNotificationWarmTest(flicker: FlickerTest) : OpenAppTransition(flicker) { override val testApp: NotificationAppHelper = NotificationAppHelper(instrumentation) open val openingNotificationsFromLockScreen = false diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarmCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarmTestCfArm.kt index 43d28fa60e51..2a2597e1ebe8 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarmCfArm.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarmTestCfArm.kt @@ -27,8 +27,8 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class OpenAppFromNotificationWarmCfArm(flicker: FlickerTest) : - OpenAppFromNotificationWarm(flicker) { +class OpenAppFromNotificationWarmTestCfArm(flicker: FlickerTest) : + OpenAppFromNotificationWarmTest(flicker) { companion object { /** * Creates the test configurations. diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenCameraOnDoubleClickPowerButton.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenCameraFromHomeOnDoubleClickPowerButtonTest.kt index ae9ca8007dc8..6ee8ae69924a 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenCameraOnDoubleClickPowerButton.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenCameraFromHomeOnDoubleClickPowerButtonTest.kt @@ -60,7 +60,7 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class OpenCameraOnDoubleClickPowerButton(flicker: FlickerTest) : +class OpenCameraFromHomeOnDoubleClickPowerButtonTest(flicker: FlickerTest) : OpenAppFromLauncherTransition(flicker) { private val cameraApp = CameraAppHelper(instrumentation) override val testApp: StandardAppHelper diff --git a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml index 1ec9ec9b0eda..64302831202e 100644 --- a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml +++ b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml @@ -22,7 +22,9 @@ <application android:allowBackup="false" android:supportsRtl="true"> <uses-library android:name="androidx.window.extensions" android:required="false"/> - + <property + android:name="android.window.PROPERTY_ACTIVITY_EMBEDDING_SPLITS_ENABLED" + android:value="true" /> <activity android:name=".SimpleActivity" android:taskAffinity="com.android.server.wm.flicker.testapp.SimpleActivity" android:theme="@style/CutoutShortEdges" @@ -198,6 +200,13 @@ android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout" android:exported="false"/> <activity + android:name=".ActivityEmbeddingAlwaysExpandActivity" + android:label="ActivityEmbedding AlwaysExpand" + android:taskAffinity="com.android.server.wm.flicker.testapp.ActivityEmbedding" + android:theme="@style/CutoutShortEdges" + android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout" + android:exported="false"/> + <activity android:name=".ActivityEmbeddingPlaceholderPrimaryActivity" android:label="ActivityEmbedding Placeholder Primary" android:taskAffinity="com.android.server.wm.flicker.testapp.ActivityEmbedding" diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_embedding_base_layout.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_embedding_base_layout.xml index 3a02cadc90dd..f0dfdfce035f 100644 --- a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_embedding_base_layout.xml +++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_embedding_base_layout.xml @@ -20,5 +20,4 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> - </LinearLayout> diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_embedding_main_layout.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_embedding_main_layout.xml index d78b9a836a37..b9d789b73732 100644 --- a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_embedding_main_layout.xml +++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_embedding_main_layout.xml @@ -25,16 +25,39 @@ android:id="@+id/launch_secondary_activity_button" android:layout_width="wrap_content" android:layout_height="48dp" - android:layout_centerHorizontal="true" android:onClick="launchSecondaryActivity" + android:tag="LEFT_TO_RIGHT" android:text="Launch Secondary Activity" /> <Button + android:id="@+id/launch_secondary_activity_rtl_button" + android:layout_width="wrap_content" + android:layout_height="48dp" + android:onClick="launchSecondaryActivity" + android:tag="RIGHT_TO_LEFT" + android:text="Launch Secondary Activity in RTL" /> + + <Button android:id="@+id/launch_placeholder_split_button" android:layout_width="wrap_content" android:layout_height="48dp" - android:layout_centerHorizontal="true" android:onClick="launchPlaceholderSplit" + android:tag="LEFT_TO_RIGHT" android:text="Launch Placeholder Split" /> + <Button + android:id="@+id/launch_always_expand_activity_button" + android:layout_width="wrap_content" + android:layout_height="48dp" + android:onClick="launchAlwaysExpandActivity" + android:text="Launch Always Expand Activity" /> + + <Button + android:id="@+id/launch_placeholder_split_rtl_button" + android:layout_width="wrap_content" + android:layout_height="48dp" + android:onClick="launchPlaceholderSplit" + android:tag="RIGHT_TO_LEFT" + android:text="Launch Placeholder Split in RTL" /> + </LinearLayout> diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_embedding_secondary_activity_layout.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_embedding_secondary_activity_layout.xml new file mode 100644 index 000000000000..239aba59f4a7 --- /dev/null +++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_embedding_secondary_activity_layout.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright 2023 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/secondary_activity_layout" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical"> + + <Button + android:id="@+id/finish_secondary_activity_button" + android:layout_width="wrap_content" + android:layout_height="48dp" + android:text="Finish" /> + +</LinearLayout>
\ No newline at end of file diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingAlwaysExpandActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingAlwaysExpandActivity.java new file mode 100644 index 000000000000..d9b24ed23424 --- /dev/null +++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingAlwaysExpandActivity.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.server.wm.flicker.testapp; + +import android.app.Activity; +import android.graphics.Color; +import android.os.Bundle; + +/** + * Activity with alwaysExpand=true (launched via R.id.launch_always_expand_activity_button) + */ +public class ActivityEmbeddingAlwaysExpandActivity extends Activity { + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_embedding_base_layout); + findViewById(R.id.root_activity_layout).setBackgroundColor(Color.GREEN); + } + +} diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingMainActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingMainActivity.java index 6a7a2ccd3378..817c79c9831f 100644 --- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingMainActivity.java +++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingMainActivity.java @@ -16,87 +16,138 @@ package com.android.server.wm.flicker.testapp; + import android.app.Activity; import android.content.Intent; import android.os.Bundle; -import android.util.ArraySet; -import android.util.Log; import android.view.View; -import androidx.window.extensions.embedding.ActivityEmbeddingComponent; -import androidx.window.extensions.embedding.EmbeddingRule; -import androidx.window.extensions.embedding.SplitPairRule; -import androidx.window.extensions.embedding.SplitPlaceholderRule; - import com.android.server.wm.flicker.helpers.ActivityEmbeddingAppHelper; +import androidx.annotation.NonNull; +import androidx.window.embedding.ActivityFilter; +import androidx.window.embedding.ActivityRule; +import androidx.window.embedding.EmbeddingAspectRatio; +import androidx.window.embedding.RuleController; +import androidx.window.embedding.SplitAttributes; +import androidx.window.embedding.SplitAttributes.LayoutDirection; +import androidx.window.embedding.SplitController; +import androidx.window.embedding.SplitPairFilter; +import androidx.window.embedding.SplitPairRule; +import androidx.window.embedding.SplitPlaceholderRule; +import androidx.window.embedding.SplitRule; +import java.util.HashSet; import java.util.Set; /** Main activity of the ActivityEmbedding test app to launch other embedding activities. */ public class ActivityEmbeddingMainActivity extends Activity { private static final String TAG = "ActivityEmbeddingMainActivity"; private static final float DEFAULT_SPLIT_RATIO = 0.5f; + private RuleController mRuleController; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_embedding_main_layout); + final SplitController.SplitSupportStatus status = SplitController.getInstance( + this).getSplitSupportStatus(); + if (status != SplitController.SplitSupportStatus.SPLIT_AVAILABLE) { + throw new RuntimeException( + "Unable to initiate SplitController in ActivityEmbeddingMainActivity, " + + "splitSupportStatus = " + status); + } + mRuleController = RuleController.getInstance(this); } /** R.id.launch_secondary_activity_button onClick */ public void launchSecondaryActivity(View view) { - initializeSplitRules(createSplitPairRules()); + final String layoutDirection = view.getTag().toString(); + mRuleController.clearRules(); + mRuleController.addRule(createSplitPairRules(layoutDirection)); startActivity(new Intent().setComponent( ActivityOptions.ActivityEmbedding.SecondaryActivity.COMPONENT)); } + /** R.id.launch_always_expand_activity_button onClick */ + public void launchAlwaysExpandActivity(View view) { + final Set<ActivityFilter> activityFilters = new HashSet<>(); + activityFilters.add( + new ActivityFilter(ActivityOptions.ActivityEmbedding.AlwaysExpandActivity.COMPONENT, + null)); + final ActivityRule activityRule = new ActivityRule.Builder(activityFilters) + .setAlwaysExpand(true) + .build(); + + RuleController rc = RuleController.getInstance(this); + + rc.addRule(activityRule); + startActivity(new Intent().setComponent( + ActivityOptions.ActivityEmbedding.AlwaysExpandActivity.COMPONENT)); + } + /** R.id.launch_placeholder_split_button onClick */ public void launchPlaceholderSplit(View view) { - initializeSplitRules(createSplitPlaceholderRules()); + final String layoutDirection = view.getTag().toString(); + mRuleController.clearRules(); + mRuleController.addRule(createSplitPlaceholderRules(layoutDirection)); startActivity(new Intent().setComponent( ActivityOptions.ActivityEmbedding.PlaceholderPrimaryActivity.COMPONENT)); } - private void initializeSplitRules(Set<EmbeddingRule> rules) { - ActivityEmbeddingComponent embeddingComponent = - ActivityEmbeddingAppHelper.getActivityEmbeddingComponent(); - if (embeddingComponent == null) { - // Embedding not supported - Log.d(TAG, "ActivityEmbedding is not supported on this device"); - finish(); - return; - } - embeddingComponent.setEmbeddingRules(rules); + private static SplitPairRule createSplitPairRules(@NonNull String layoutDirection) { + final Set<SplitPairFilter> pairFilters = new HashSet<>(); + final SplitPairFilter activitiesPair = new SplitPairFilter( + ActivityOptions.ActivityEmbedding.MainActivity.COMPONENT, + ActivityOptions.ActivityEmbedding.SecondaryActivity.COMPONENT, + null /* secondaryActivityIntentAction */); + pairFilters.add(activitiesPair); + final SplitAttributes splitAttributes = new SplitAttributes.Builder() + .setSplitType(SplitAttributes.SplitType.SPLIT_TYPE_EQUAL) + .setLayoutDirection(parseLayoutDirection(layoutDirection)) + .build(); + // Setting thresholds to ALWAYS_ALLOW values to make it easy for running on all devices. + final SplitPairRule rule = new SplitPairRule.Builder(pairFilters) + .setDefaultSplitAttributes(splitAttributes) + .setMinWidthDp(SplitRule.SPLIT_MIN_DIMENSION_ALWAYS_ALLOW) + .setMinHeightDp(SplitRule.SPLIT_MIN_DIMENSION_ALWAYS_ALLOW) + .setMinSmallestWidthDp(SplitRule.SPLIT_MIN_DIMENSION_ALWAYS_ALLOW) + .setMaxAspectRatioInPortrait(EmbeddingAspectRatio.ALWAYS_ALLOW) + .setMaxAspectRatioInLandscape(EmbeddingAspectRatio.ALWAYS_ALLOW) + .build(); + return rule; } - private Set<EmbeddingRule> createSplitPairRules() { - final Set<EmbeddingRule> rules = new ArraySet<>(); - final SplitPairRule rule = new SplitPairRule.Builder( - activitiesPair -> activitiesPair.first instanceof ActivityEmbeddingMainActivity - && activitiesPair.second instanceof ActivityEmbeddingSecondaryActivity, - activityIntentPair -> - activityIntentPair.first instanceof ActivityEmbeddingMainActivity - && activityIntentPair.second.getComponent().equals(ActivityOptions - .ActivityEmbedding.SecondaryActivity.COMPONENT), - windowMetrics -> true) - .setSplitRatio(DEFAULT_SPLIT_RATIO) + private static SplitPlaceholderRule createSplitPlaceholderRules( + @NonNull String layoutDirection) { + final Set<ActivityFilter> activityFilters = new HashSet<>(); + activityFilters.add(new ActivityFilter( + ActivityOptions.ActivityEmbedding.PlaceholderPrimaryActivity.COMPONENT, + null /* intentAction */)); + final Intent intent = new Intent(); + intent.setComponent( + ActivityOptions.ActivityEmbedding.PlaceholderSecondaryActivity.COMPONENT); + final SplitAttributes splitAttributes = new SplitAttributes.Builder() + .setSplitType(SplitAttributes.SplitType.SPLIT_TYPE_EQUAL) + .setLayoutDirection(parseLayoutDirection(layoutDirection)) .build(); - rules.add(rule); - return rules; + final SplitPlaceholderRule rule = new SplitPlaceholderRule.Builder(activityFilters, intent) + .setDefaultSplitAttributes(splitAttributes) + .setMinWidthDp(SplitRule.SPLIT_MIN_DIMENSION_ALWAYS_ALLOW) + .setMinHeightDp(SplitRule.SPLIT_MIN_DIMENSION_ALWAYS_ALLOW) + .setMinSmallestWidthDp(SplitRule.SPLIT_MIN_DIMENSION_ALWAYS_ALLOW) + .setMaxAspectRatioInPortrait(EmbeddingAspectRatio.ALWAYS_ALLOW) + .setMaxAspectRatioInLandscape(EmbeddingAspectRatio.ALWAYS_ALLOW) + .build(); + return rule; } - private Set<EmbeddingRule> createSplitPlaceholderRules() { - final Set<EmbeddingRule> rules = new ArraySet<>(); - final SplitPlaceholderRule rule = new SplitPlaceholderRule.Builder( - new Intent().setComponent( - ActivityOptions.ActivityEmbedding.PlaceholderSecondaryActivity.COMPONENT), - activity -> activity instanceof ActivityEmbeddingPlaceholderPrimaryActivity, - intent -> intent.getComponent().equals( - ActivityOptions.ActivityEmbedding.PlaceholderPrimaryActivity.COMPONENT), - windowMetrics -> true) - .setSplitRatio(DEFAULT_SPLIT_RATIO) - .build(); - rules.add(rule); - return rules; + private static LayoutDirection parseLayoutDirection(@NonNull String layoutDirectionStr) { + if (layoutDirectionStr.equals(LayoutDirection.LEFT_TO_RIGHT.toString())) { + return LayoutDirection.LEFT_TO_RIGHT; + } + if (layoutDirectionStr.equals(LayoutDirection.RIGHT_TO_LEFT.toString())) { + return LayoutDirection.RIGHT_TO_LEFT; + } + return LayoutDirection.LOCALE; } } diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingSecondaryActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingSecondaryActivity.java index 00f4c2576eb1..6e78750cdeee 100644 --- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingSecondaryActivity.java +++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingSecondaryActivity.java @@ -16,15 +16,28 @@ package com.android.server.wm.flicker.testapp; +import android.app.Activity; import android.graphics.Color; +import android.os.Bundle; +import android.view.View; /** * Activity to be used as the secondary activity to split with * {@link ActivityEmbeddingMainActivity}. */ -public class ActivityEmbeddingSecondaryActivity extends ActivityEmbeddingBaseActivity { +public class ActivityEmbeddingSecondaryActivity extends Activity { + @Override - int getBackgroundColor() { - return Color.YELLOW; + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_embedding_secondary_activity_layout); + findViewById(R.id.secondary_activity_layout).setBackgroundColor(Color.YELLOW); + findViewById(R.id.finish_secondary_activity_button).setOnClickListener( + new View.OnClickListener() { + @Override + public void onClick(View v) { + finish(); + } + }); } } diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java index 9c3226b5292c..0f5c003f12fd 100644 --- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java +++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java @@ -99,6 +99,12 @@ public class ActivityOptions { FLICKER_APP_PACKAGE + ".ActivityEmbeddingSecondaryActivity"); } + public static class AlwaysExpandActivity { + public static final String LABEL = "ActivityEmbeddingAlwaysExpandActivity"; + public static final ComponentName COMPONENT = new ComponentName(FLICKER_APP_PACKAGE, + FLICKER_APP_PACKAGE + ".ActivityEmbeddingAlwaysExpandActivity"); + } + public static class PlaceholderPrimaryActivity { public static final String LABEL = "ActivityEmbeddingPlaceholderPrimaryActivity"; public static final ComponentName COMPONENT = new ComponentName(FLICKER_APP_PACKAGE, diff --git a/tests/FlickerTests/trace_config/trace_config.textproto b/tests/FlickerTests/trace_config/trace_config.textproto new file mode 100644 index 000000000000..c9a35aca9085 --- /dev/null +++ b/tests/FlickerTests/trace_config/trace_config.textproto @@ -0,0 +1,77 @@ +# Copyright (C) 2023 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# proto-message: TraceConfig + +# Enable periodic flushing of the trace buffer into the output file. +write_into_file: true + +# Writes the userspace buffer into the file every 1s. +file_write_period_ms: 2500 + +# See b/126487238 - we need to guarantee ordering of events. +flush_period_ms: 30000 + +# The trace buffers needs to be big enough to hold |file_write_period_ms| of +# trace data. The trace buffer sizing depends on the number of trace categories +# enabled and the device activity. + +# RSS events +buffers: { + size_kb: 63488 + fill_policy: RING_BUFFER +} + +data_sources { + config { + name: "linux.process_stats" + target_buffer: 0 + # polled per-process memory counters and process/thread names. + # If you don't want the polled counters, remove the "process_stats_config" + # section, but keep the data source itself as it still provides on-demand + # thread/process naming for ftrace data below. + process_stats_config { + scan_all_processes_on_start: true + } + } +} + +data_sources: { + config { + name: "linux.ftrace" + ftrace_config { + ftrace_events: "ftrace/print" + ftrace_events: "task/task_newtask" + ftrace_events: "task/task_rename" + atrace_categories: "ss" + atrace_categories: "wm" + atrace_categories: "am" + atrace_categories: "aidl" + atrace_categories: "input" + atrace_categories: "binder_driver" + atrace_categories: "sched_process_exit" + atrace_apps: "com.android.server.wm.flicker" + atrace_apps: "com.android.server.wm.flicker.other" + atrace_apps: "com.android.server.wm.flicker.close" + atrace_apps: "com.android.server.wm.flicker.ime" + atrace_apps: "com.android.server.wm.flicker.launch" + atrace_apps: "com.android.server.wm.flicker.quickswitch" + atrace_apps: "com.android.server.wm.flicker.rotation" + atrace_apps: "com.android.server.wm.flicker.testapp" + atrace_apps: "com.android.systemui" + atrace_apps: "com.google.android.apps.nexuslauncher" + } + } +} + diff --git a/tools/protologtool/Android.bp b/tools/protologtool/Android.bp index 039bb4e2788c..46745e995f64 100644 --- a/tools/protologtool/Android.bp +++ b/tools/protologtool/Android.bp @@ -11,7 +11,7 @@ java_library_host { name: "protologtool-lib", srcs: [ "src/com/android/protolog/tool/**/*.kt", - ":protolog-common-src", + ":protolog-common-no-android-src", ], static_libs: [ "javaparser", diff --git a/wifi/java/src/android/net/wifi/nl80211/InstantWifi.java b/wifi/java/src/android/net/wifi/nl80211/InstantWifi.java deleted file mode 100644 index 433e88c58851..000000000000 --- a/wifi/java/src/android/net/wifi/nl80211/InstantWifi.java +++ /dev/null @@ -1,318 +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 android.net.wifi.nl80211; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.app.AlarmManager; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.net.ConnectivityManager; -import android.net.ConnectivityManager.NetworkCallback; -import android.net.Network; -import android.net.NetworkCapabilities; -import android.net.NetworkRequest; -import android.net.wifi.WifiConfiguration; -import android.net.wifi.WifiInfo; -import android.net.wifi.WifiManager; -import android.os.Handler; -import android.os.PowerManager; -import android.os.SystemClock; -import android.util.Log; - -import com.android.internal.annotations.VisibleForTesting; - -import java.util.ArrayList; -import java.util.HashSet; -import java.util.Set; - -/** - * @hide - */ -public class InstantWifi { - private static final String INSTANT_WIFI_TAG = "InstantWifi"; - private static final int OVERRIDED_SCAN_CONNECTION_TIMEOUT_MS = 1000; - private static final int WIFI_NETWORK_EXPIRED_MS = 7 * 24 * 60 * 60 * 1000; // a week - private static final String NO_CONNECTION_TIMEOUT_ALARM_TAG = - INSTANT_WIFI_TAG + " No Connection Timeout"; - - private Context mContext; - private AlarmManager mAlarmManager; - private Handler mEventHandler; - private ConnectivityManager mConnectivityManager; - private WifiManager mWifiManager; - private PowerManager mPowerManager; - private long mLastWifiOnSinceBootMs; - private long mLastScreenOnSinceBootMs; - private boolean mIsWifiConnected = false; - private boolean mScreenOn = false; - private boolean mWifiEnabled = false; - private boolean mIsNoConnectionAlarmSet = false; - private ArrayList<WifiNetwork> mConnectedWifiNetworkList = new ArrayList<>(); - private AlarmManager.OnAlarmListener mNoConnectionTimeoutCallback = - new AlarmManager.OnAlarmListener() { - public void onAlarm() { - Log.i(INSTANT_WIFI_TAG, "Timed out waiting for wifi connection"); - mIsNoConnectionAlarmSet = false; - mWifiManager.startScan(); - } - }; - - public InstantWifi(Context context, AlarmManager alarmManager, Handler eventHandler) { - mContext = context; - mAlarmManager = alarmManager; - mEventHandler = eventHandler; - mWifiManager = mContext.getSystemService(WifiManager.class); - mConnectivityManager = mContext.getSystemService(ConnectivityManager.class); - mConnectivityManager.registerNetworkCallback( - new NetworkRequest.Builder() - .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) - .addTransportType(NetworkCapabilities.TRANSPORT_WIFI) - .build(), new WifiNetworkCallback()); - // System power service was initialized before wifi nl80211 service. - mPowerManager = mContext.getSystemService(PowerManager.class); - IntentFilter screenEventfilter = new IntentFilter(); - screenEventfilter.addAction(Intent.ACTION_SCREEN_ON); - screenEventfilter.addAction(Intent.ACTION_SCREEN_OFF); - mContext.registerReceiver( - new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - String action = intent.getAction(); - if (action.equals(Intent.ACTION_SCREEN_ON)) { - if (!mScreenOn) { - mLastScreenOnSinceBootMs = getMockableElapsedRealtime(); - } - mScreenOn = true; - } else if (action.equals(Intent.ACTION_SCREEN_OFF)) { - mScreenOn = false; - } - Log.d(INSTANT_WIFI_TAG, "mScreenOn is changed to " + mScreenOn); - } - }, screenEventfilter, null, mEventHandler); - mScreenOn = mPowerManager.isInteractive(); - mContext.registerReceiver( - new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - int state = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, - WifiManager.WIFI_STATE_UNKNOWN); - mWifiEnabled = state == WifiManager.WIFI_STATE_ENABLED; - if (mWifiEnabled) { - mLastWifiOnSinceBootMs = getMockableElapsedRealtime(); - } - Log.d(INSTANT_WIFI_TAG, "mWifiEnabled is changed to " + mWifiEnabled); - } - }, - new IntentFilter(WifiManager.WIFI_STATE_CHANGED_ACTION), - null, mEventHandler); - } - - @VisibleForTesting - protected long getMockableElapsedRealtime() { - return SystemClock.elapsedRealtime(); - } - - private class WifiNetwork { - private final int mNetId; - private Set<Integer> mConnectedFrequencies = new HashSet<Integer>(); - private int[] mLastTwoConnectedFrequencies = new int[2]; - private long mLastConnectedTimeMillis; - WifiNetwork(int netId) { - mNetId = netId; - } - - public int getNetId() { - return mNetId; - } - - public boolean addConnectedFrequency(int channelFrequency) { - mLastConnectedTimeMillis = getMockableElapsedRealtime(); - if (mLastTwoConnectedFrequencies[0] != channelFrequency - && mLastTwoConnectedFrequencies[1] != channelFrequency) { - mLastTwoConnectedFrequencies[0] = mLastTwoConnectedFrequencies[1]; - mLastTwoConnectedFrequencies[1] = channelFrequency; - } - return mConnectedFrequencies.add(channelFrequency); - } - - public Set<Integer> getConnectedFrequencies() { - return mConnectedFrequencies; - } - - public int[] getLastTwoConnectedFrequencies() { - if ((getMockableElapsedRealtime() - mLastConnectedTimeMillis) - > WIFI_NETWORK_EXPIRED_MS) { - return new int[0]; - } - return mLastTwoConnectedFrequencies; - } - - public long getLastConnectedTimeMillis() { - return mLastConnectedTimeMillis; - } - } - - private class WifiNetworkCallback extends NetworkCallback { - @Override - public void onAvailable(@NonNull Network network) { - } - - @Override - public void onCapabilitiesChanged(Network network, - NetworkCapabilities networkCapabilities) { - if (networkCapabilities != null && network != null) { - WifiInfo wifiInfo = (WifiInfo) networkCapabilities.getTransportInfo(); - if (wifiInfo == null || mWifiManager == null) { - return; - } - WifiConfiguration config = mWifiManager.getPrivilegedConnectedNetwork(); - if (config == null) { - return; - } - final int currentNetworkId = config.networkId; - final int connectecFrequency = wifiInfo.getFrequency(); - if (connectecFrequency < 0 || currentNetworkId < 0) { - return; - } - mIsWifiConnected = true; - if (mIsNoConnectionAlarmSet) { - mAlarmManager.cancel(mNoConnectionTimeoutCallback); - } - Log.d(INSTANT_WIFI_TAG, "Receive Wifi is connected, freq = " + connectecFrequency - + " and currentNetworkId : " + currentNetworkId - + ", wifiinfo = " + wifiInfo); - boolean isExist = false; - for (WifiNetwork wifiNetwork : mConnectedWifiNetworkList) { - if (wifiNetwork.getNetId() == currentNetworkId) { - if (wifiNetwork.addConnectedFrequency(connectecFrequency)) { - Log.d(INSTANT_WIFI_TAG, "Update connected frequency: " - + connectecFrequency + " to Network currentNetworkId : " - + currentNetworkId); - } - isExist = true; - } - } - if (!isExist) { - WifiNetwork currentNetwork = new WifiNetwork(currentNetworkId); - currentNetwork.addConnectedFrequency(connectecFrequency); - if (mConnectedWifiNetworkList.size() < 5) { - mConnectedWifiNetworkList.add(currentNetwork); - } else { - ArrayList<WifiNetwork> lastConnectedWifiNetworkList = new ArrayList<>(); - WifiNetwork legacyNetwork = mConnectedWifiNetworkList.get(0); - for (WifiNetwork connectedNetwork : mConnectedWifiNetworkList) { - if (connectedNetwork.getNetId() == legacyNetwork.getNetId()) { - continue; - } - // Keep the used recently network in the last connected list - if (connectedNetwork.getLastConnectedTimeMillis() - > legacyNetwork.getLastConnectedTimeMillis()) { - lastConnectedWifiNetworkList.add(connectedNetwork); - } else { - lastConnectedWifiNetworkList.add(legacyNetwork); - legacyNetwork = connectedNetwork; - } - } - mConnectedWifiNetworkList = lastConnectedWifiNetworkList; - } - } - } - } - - @Override - public void onLost(@NonNull Network network) { - mIsWifiConnected = false; - } - } - - /** - * Returns whether or not the scan freqs should be overrided by using predicted channels. - */ - public boolean isUsePredictedScanningChannels() { - if (mIsWifiConnected || mConnectedWifiNetworkList.size() == 0 - || !mWifiManager.isWifiEnabled() || !mPowerManager.isInteractive()) { - return false; - } - if (!mWifiEnabled || !mScreenOn) { - Log.d(INSTANT_WIFI_TAG, "WiFi/Screen State mis-match, run instant Wifi anyway!"); - return true; - } - return (((getMockableElapsedRealtime() - mLastWifiOnSinceBootMs) - < OVERRIDED_SCAN_CONNECTION_TIMEOUT_MS) - || ((getMockableElapsedRealtime() - mLastScreenOnSinceBootMs) - < OVERRIDED_SCAN_CONNECTION_TIMEOUT_MS)); - } - - /** - * Overrides the frequenies in SingleScanSetting - * - * @param settings the SingleScanSettings will be overrided. - * @param freqs new frequencies of SingleScanSettings - */ - @Nullable - public void overrideFreqsForSingleScanSettingsIfNecessary( - @Nullable SingleScanSettings settings, @Nullable Set<Integer> freqs) { - if (!isUsePredictedScanningChannels() || settings == null || freqs == null - || freqs.size() == 0) { - return; - } - if (settings.channelSettings == null) { - settings.channelSettings = new ArrayList<>(); - } else { - settings.channelSettings.clear(); - } - for (int freq : freqs) { - if (freq > 0) { - ChannelSettings channel = new ChannelSettings(); - channel.frequency = freq; - settings.channelSettings.add(channel); - } - } - // Monitor connection after last override scan request. - if (mIsNoConnectionAlarmSet) { - mAlarmManager.cancel(mNoConnectionTimeoutCallback); - } - mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, - getMockableElapsedRealtime() + OVERRIDED_SCAN_CONNECTION_TIMEOUT_MS, - NO_CONNECTION_TIMEOUT_ALARM_TAG, mNoConnectionTimeoutCallback, mEventHandler); - mIsNoConnectionAlarmSet = true; - } - - /** - * Returns the predicted scanning chcnnels set. - */ - @NonNull - public Set<Integer> getPredictedScanningChannels() { - Set<Integer> predictedScanChannels = new HashSet<>(); - if (!isUsePredictedScanningChannels()) { - Log.d(INSTANT_WIFI_TAG, "Drop, size: " + mConnectedWifiNetworkList.size()); - return predictedScanChannels; - } - for (WifiNetwork network : mConnectedWifiNetworkList) { - for (int connectedFrequency : network.getLastTwoConnectedFrequencies()) { - if (connectedFrequency > 0) { - predictedScanChannels.add(connectedFrequency); - Log.d(INSTANT_WIFI_TAG, "Add channel: " + connectedFrequency - + " to predicted channel"); - } - } - } - return predictedScanChannels; - } -} diff --git a/wifi/java/src/android/net/wifi/nl80211/WifiNl80211Manager.java b/wifi/java/src/android/net/wifi/nl80211/WifiNl80211Manager.java index ca12c4cb2020..2a199d27a60e 100644 --- a/wifi/java/src/android/net/wifi/nl80211/WifiNl80211Manager.java +++ b/wifi/java/src/android/net/wifi/nl80211/WifiNl80211Manager.java @@ -105,8 +105,6 @@ public class WifiNl80211Manager { // Cached wificond binder handlers. private IWificond mWificond; - private Context mContext; - private InstantWifi mInstantWifi; private WificondEventHandler mWificondEventHandler = new WificondEventHandler(); private HashMap<String, IClientInterface> mClientInterfaces = new HashMap<>(); private HashMap<String, IApInterface> mApInterfaces = new HashMap<>(); @@ -176,12 +174,6 @@ public class WifiNl80211Manager { /** @hide */ @VisibleForTesting - protected InstantWifi getInstantWifiMockable() { - return mInstantWifi; - } - - /** @hide */ - @VisibleForTesting public class WificondEventHandler extends IWificondEventCallback.Stub { private Map<CountryCodeChangedListener, Executor> mCountryCodeChangedListenerHolder = new HashMap<>(); @@ -427,7 +419,6 @@ public class WifiNl80211Manager { public WifiNl80211Manager(Context context) { mAlarmManager = context.getSystemService(AlarmManager.class); mEventHandler = new Handler(context.getMainLooper()); - mContext = context; } /** @@ -443,7 +434,6 @@ public class WifiNl80211Manager { if (mWificond == null) { Log.e(TAG, "Failed to get reference to wificond"); } - mContext = context; } /** @hide */ @@ -451,7 +441,6 @@ public class WifiNl80211Manager { public WifiNl80211Manager(Context context, IWificond wificond) { this(context); mWificond = wificond; - mContext = context; } /** @hide */ @@ -755,9 +744,6 @@ public class WifiNl80211Manager { Log.e(TAG, "Failed to refresh wificond scanner due to remote exception"); } - if (getInstantWifiMockable() == null) { - mInstantWifi = new InstantWifi(mContext, mAlarmManager, mEventHandler); - } return true; } @@ -1085,10 +1071,6 @@ public class WifiNl80211Manager { if (settings == null) { return false; } - if (getInstantWifiMockable() != null) { - getInstantWifiMockable().overrideFreqsForSingleScanSettingsIfNecessary(settings, - getInstantWifiMockable().getPredictedScanningChannels()); - } try { return scannerImpl.scan(settings); } catch (RemoteException e1) { @@ -1133,10 +1115,6 @@ public class WifiNl80211Manager { if (settings == null) { return WifiScanner.REASON_INVALID_ARGS; } - if (getInstantWifiMockable() != null) { - getInstantWifiMockable().overrideFreqsForSingleScanSettingsIfNecessary(settings, - getInstantWifiMockable().getPredictedScanningChannels()); - } try { int status = scannerImpl.scanRequest(settings); return toFrameworkScanStatusCode(status); diff --git a/wifi/tests/src/android/net/wifi/nl80211/InstantWifiTest.java b/wifi/tests/src/android/net/wifi/nl80211/InstantWifiTest.java deleted file mode 100644 index ebff0e28082e..000000000000 --- a/wifi/tests/src/android/net/wifi/nl80211/InstantWifiTest.java +++ /dev/null @@ -1,256 +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 android.net.wifi.nl80211; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.argThat; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.anyInt; -import static org.mockito.Mockito.anyLong; -import static org.mockito.Mockito.doNothing; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.app.AlarmManager; -import android.app.test.TestAlarmManager; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.net.ConnectivityManager; -import android.net.ConnectivityManager.NetworkCallback; -import android.net.Network; -import android.net.NetworkCapabilities; -import android.net.wifi.WifiConfiguration; -import android.net.wifi.WifiInfo; -import android.net.wifi.WifiManager; -import android.os.Handler; -import android.os.IPowerManager; -import android.os.IThermalService; -import android.os.PowerManager; -import android.os.test.TestLooper; - -import androidx.test.filters.SmallTest; - -import org.junit.Before; -import org.junit.Test; -import org.mockito.ArgumentCaptor; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -import java.util.HashSet; -import java.util.Set; - -/** - * Unit tests for {@link android.net.wifi.nl80211.InstantWifi}. - */ -@SmallTest -public class InstantWifiTest { - @Mock private Context mContext; - @Mock private ConnectivityManager mMockConnectivityManager; - @Mock private WifiManager mMockWifiManager; - @Mock private Network mMockWifiNetwork; - @Mock private WifiInfo mMockWifiInfo; - @Mock private WifiConfiguration mMockWifiConfiguration; - @Mock private IPowerManager mPowerManagerService; - private InstantWifi mInstantWifi; - private TestLooper mLooper; - private Handler mHandler; - private TestAlarmManager mTestAlarmManager; - private AlarmManager mAlarmManager; - private PowerManager mMockPowerManager; - - private final ArgumentCaptor<NetworkCallback> mWifiNetworkCallbackCaptor = - ArgumentCaptor.forClass(NetworkCallback.class); - private final ArgumentCaptor<BroadcastReceiver> mScreenBroadcastReceiverCaptor = - ArgumentCaptor.forClass(BroadcastReceiver.class); - private final ArgumentCaptor<BroadcastReceiver> mWifiStateBroadcastReceiverCaptor = - ArgumentCaptor.forClass(BroadcastReceiver.class); - - private static final int TEST_NETWORK_ID = 1; - private static final int TEST_24G_FREQUENCY = 2412; - private static final int TEST_5G_FREQUENCY = 5745; - private long mTimeOffsetMs = 0; - - private class InstantWifiSpy extends InstantWifi { - InstantWifiSpy(Context context, AlarmManager alarmManager, Handler handler) { - super(context, alarmManager, handler); - } - - @Override - protected long getMockableElapsedRealtime() { - return mTimeOffsetMs; - } - } - - @Before - public void setUp() throws Exception { - MockitoAnnotations.initMocks(this); - - mLooper = new TestLooper(); - mHandler = new Handler(mLooper.getLooper()); - - mTestAlarmManager = new TestAlarmManager(); - mAlarmManager = mTestAlarmManager.getAlarmManager(); - when(mContext.getSystemServiceName(AlarmManager.class)).thenReturn(Context.ALARM_SERVICE); - when(mContext.getSystemService(AlarmManager.class)).thenReturn(mAlarmManager); - when(mContext.getSystemServiceName(WifiManager.class)).thenReturn(Context.WIFI_SERVICE); - when(mContext.getSystemService(WifiManager.class)).thenReturn(mMockWifiManager); - when(mContext.getSystemServiceName(ConnectivityManager.class)) - .thenReturn(Context.CONNECTIVITY_SERVICE); - when(mContext.getSystemService(ConnectivityManager.class)) - .thenReturn(mMockConnectivityManager); - mMockPowerManager = new PowerManager(mContext, mPowerManagerService, - mock(IThermalService.class), mHandler); - when(mContext.getSystemServiceName(PowerManager.class)).thenReturn(Context.POWER_SERVICE); - when(mContext.getSystemService(PowerManager.class)).thenReturn(mMockPowerManager); - when(mPowerManagerService.isInteractive()).thenReturn(true); - - doReturn(mMockWifiInfo).when(mMockWifiInfo).makeCopy(anyLong()); - mTimeOffsetMs = 0; - mInstantWifi = new InstantWifiSpy(mContext, mAlarmManager, mHandler); - verifyInstantWifiInitialization(); - } - - private void verifyInstantWifiInitialization() { - verify(mMockConnectivityManager).registerNetworkCallback(any(), - mWifiNetworkCallbackCaptor.capture()); - verify(mContext).registerReceiver(mScreenBroadcastReceiverCaptor.capture(), - argThat((IntentFilter filter) -> - filter.hasAction(Intent.ACTION_SCREEN_ON) - && filter.hasAction(Intent.ACTION_SCREEN_OFF)), eq(null), any()); - - verify(mContext).registerReceiver(mWifiStateBroadcastReceiverCaptor.capture(), - argThat((IntentFilter filter) -> - filter.hasAction(WifiManager.WIFI_STATE_CHANGED_ACTION)), eq(null), any()); - } - - private void mockWifiConnectedEvent(int networkId, int connectedFrequency) { - // Send wifi connected event - NetworkCapabilities mockWifiNetworkCapabilities = - new NetworkCapabilities.Builder().setTransportInfo(mMockWifiInfo).build(); - mMockWifiConfiguration.networkId = networkId; - when(mMockWifiManager.getPrivilegedConnectedNetwork()).thenReturn(mMockWifiConfiguration); - when(mMockWifiInfo.getFrequency()).thenReturn(connectedFrequency); - mWifiNetworkCallbackCaptor.getValue().onCapabilitiesChanged(mMockWifiNetwork, - mockWifiNetworkCapabilities); - mLooper.dispatchAll(); - } - - private void mockWifiOnScreenOnBroadcast(boolean isWifiOn, boolean isScreenOn) - throws Exception { - // Send Wifi On broadcast - Intent wifiOnIntent = new Intent(WifiManager.WIFI_STATE_CHANGED_ACTION); - wifiOnIntent.putExtra(WifiManager.EXTRA_WIFI_STATE, - isWifiOn ? WifiManager.WIFI_STATE_ENABLED : WifiManager.WIFI_STATE_DISABLED); - mWifiStateBroadcastReceiverCaptor.getValue().onReceive(mContext, wifiOnIntent); - mLooper.dispatchAll(); - // Send Screen On broadcast - Intent screenOnIntent = - new Intent(isScreenOn ? Intent.ACTION_SCREEN_ON : Intent.ACTION_SCREEN_OFF); - mScreenBroadcastReceiverCaptor.getValue().onReceive(mContext, screenOnIntent); - mLooper.dispatchAll(); - when(mMockWifiManager.isWifiEnabled()).thenReturn(isWifiOn); - when(mPowerManagerService.isInteractive()).thenReturn(isScreenOn); - } - - @Test - public void testisUsePredictedScanningChannels() throws Exception { - assertFalse(mInstantWifi.isUsePredictedScanningChannels()); - mockWifiOnScreenOnBroadcast(true /* isWifiOn */, false /* isScreenOn */); - assertFalse(mInstantWifi.isUsePredictedScanningChannels()); - mockWifiOnScreenOnBroadcast(false /* isWifiOn */, true /* isScreenOn */); - assertFalse(mInstantWifi.isUsePredictedScanningChannels()); - mockWifiOnScreenOnBroadcast(true /* isWifiOn */, true /* isScreenOn */); - assertFalse(mInstantWifi.isUsePredictedScanningChannels()); - // Send wifi connected event - mockWifiConnectedEvent(TEST_NETWORK_ID, TEST_24G_FREQUENCY); - assertFalse(mInstantWifi.isUsePredictedScanningChannels()); - // Send wifi disconnect - mWifiNetworkCallbackCaptor.getValue().onLost(mMockWifiNetwork); - assertTrue(mInstantWifi.isUsePredictedScanningChannels()); - // Shift time to make it expired - mTimeOffsetMs = 1100; - assertFalse(mInstantWifi.isUsePredictedScanningChannels()); - } - - @Test - public void testGetPredictedScanningChannels() throws Exception { - mockWifiOnScreenOnBroadcast(true /* isWifiOn */, true /* isScreenOn */); - // Send wifi connected event on T0 - mockWifiConnectedEvent(TEST_NETWORK_ID, TEST_24G_FREQUENCY); - // Send wifi disconnect - mWifiNetworkCallbackCaptor.getValue().onLost(mMockWifiNetwork); - assertTrue(mInstantWifi.isUsePredictedScanningChannels()); - assertTrue(mInstantWifi.getPredictedScanningChannels().contains(TEST_24G_FREQUENCY)); - mTimeOffsetMs += 1000; // T1 = 1000 ms - // Send wifi connected event - mockWifiConnectedEvent(TEST_NETWORK_ID + 1, TEST_5G_FREQUENCY); - // Send wifi disconnect - mWifiNetworkCallbackCaptor.getValue().onLost(mMockWifiNetwork); - // isUsePredictedScanningChannels is false since wifi on & screen on is expired - assertFalse(mInstantWifi.isUsePredictedScanningChannels()); - // Override the Wifi On & Screen on time - mockWifiOnScreenOnBroadcast(true /* isWifiOn */, true /* isScreenOn */); - assertTrue(mInstantWifi.getPredictedScanningChannels().contains(TEST_5G_FREQUENCY)); - mTimeOffsetMs += 7 * 24 * 60 * 60 * 1000; // Make T0 expired - // Override the Wifi On & Screen on time - mockWifiOnScreenOnBroadcast(true /* isWifiOn */, true /* isScreenOn */); - assertFalse(mInstantWifi.getPredictedScanningChannels().contains(TEST_24G_FREQUENCY)); - assertTrue(mInstantWifi.getPredictedScanningChannels().contains(TEST_5G_FREQUENCY)); - } - - @Test - public void testOverrideFreqsForSingleScanSettings() throws Exception { - mockWifiOnScreenOnBroadcast(true /* isWifiOn */, true /* isScreenOn */); - // Send wifi connected event - mockWifiConnectedEvent(TEST_NETWORK_ID, TEST_24G_FREQUENCY); - assertFalse(mInstantWifi.isUsePredictedScanningChannels()); - // Send wifi disconnect - mWifiNetworkCallbackCaptor.getValue().onLost(mMockWifiNetwork); - assertTrue(mInstantWifi.isUsePredictedScanningChannels()); - - final ArgumentCaptor<AlarmManager.OnAlarmListener> alarmListenerCaptor = - ArgumentCaptor.forClass(AlarmManager.OnAlarmListener.class); - doNothing().when(mAlarmManager).set(anyInt(), anyLong(), any(), - alarmListenerCaptor.capture(), any()); - Set<Integer> testFreqs = Set.of( - TEST_24G_FREQUENCY, TEST_5G_FREQUENCY); - SingleScanSettings testSingleScanSettings = new SingleScanSettings(); - mInstantWifi.overrideFreqsForSingleScanSettingsIfNecessary( - testSingleScanSettings, new HashSet<Integer>()); - mInstantWifi.overrideFreqsForSingleScanSettingsIfNecessary( - testSingleScanSettings, null); - mInstantWifi.overrideFreqsForSingleScanSettingsIfNecessary(null, null); - verify(mAlarmManager, never()).set(anyInt(), anyLong(), any(), any(), any()); - mInstantWifi.overrideFreqsForSingleScanSettingsIfNecessary(testSingleScanSettings, - testFreqs); - verify(mAlarmManager).set(anyInt(), anyLong(), any(), any(), any()); - Set<Integer> overridedFreqs = new HashSet<Integer>(); - for (ChannelSettings channel : testSingleScanSettings.channelSettings) { - overridedFreqs.add(channel.frequency); - } - assertEquals(testFreqs, overridedFreqs); - alarmListenerCaptor.getValue().onAlarm(); - verify(mMockWifiManager).startScan(); - } -} diff --git a/wifi/tests/src/android/net/wifi/nl80211/WifiNl80211ManagerTest.java b/wifi/tests/src/android/net/wifi/nl80211/WifiNl80211ManagerTest.java index f12818fa64f9..362eb1425a30 100644 --- a/wifi/tests/src/android/net/wifi/nl80211/WifiNl80211ManagerTest.java +++ b/wifi/tests/src/android/net/wifi/nl80211/WifiNl80211ManagerTest.java @@ -18,7 +18,6 @@ package android.net.wifi.nl80211; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; @@ -27,7 +26,6 @@ import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Matchers.argThat; import static org.mockito.Mockito.any; import static org.mockito.Mockito.anyLong; -import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; @@ -39,7 +37,6 @@ import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; import android.app.AlarmManager; -import android.app.test.MockAnswerUtil.AnswerWithArguments; import android.app.test.TestAlarmManager; import android.content.Context; import android.net.MacAddress; @@ -107,9 +104,6 @@ public class WifiNl80211ManagerTest { private WifiNl80211Manager.CountryCodeChangedListener mCountryCodeChangedListener2; @Mock private Context mContext; - @Mock - private InstantWifi mMockInstantWifi; - private TestLooper mLooper; private TestAlarmManager mTestAlarmManager; private AlarmManager mAlarmManager; @@ -173,17 +167,6 @@ public class WifiNl80211ManagerTest { 0x00, 0x00 }; - private class WifiNl80211ManagerSpy extends WifiNl80211Manager { - WifiNl80211ManagerSpy(Context context, IWificond wificond) { - super(context, wificond); - } - - @Override - protected InstantWifi getInstantWifiMockable() { - return mMockInstantWifi; - } - } - @Before public void setUp() throws Exception { // Setup mocks for successful WificondControl operation. Failure case mocks should be @@ -198,8 +181,6 @@ public class WifiNl80211ManagerTest { mLooper = new TestLooper(); when(mContext.getMainLooper()).thenReturn(mLooper.getLooper()); - doNothing().when(mMockInstantWifi).overrideFreqsForSingleScanSettingsIfNecessary( - any(), any()); when(mWificond.asBinder()).thenReturn(mWifiCondBinder); when(mClientInterface.getWifiScannerImpl()).thenReturn(mWifiScannerImpl); when(mWificond.createClientInterface(any())).thenReturn(mClientInterface); @@ -208,7 +189,7 @@ public class WifiNl80211ManagerTest { when(mWificond.tearDownApInterface(any())).thenReturn(true); when(mClientInterface.getWifiScannerImpl()).thenReturn(mWifiScannerImpl); when(mClientInterface.getInterfaceName()).thenReturn(TEST_INTERFACE_NAME); - mWificondControl = new WifiNl80211ManagerSpy(mContext, mWificond); + mWificondControl = new WifiNl80211Manager(mContext, mWificond); mWificondEventHandler = mWificondControl.getWificondEventHandler(); assertEquals(true, mWificondControl.setupInterfaceForClientMode(TEST_INTERFACE_NAME, Runnable::run, @@ -1178,40 +1159,6 @@ public class WifiNl80211ManagerTest { verify(mWificond).notifyCountryCodeChanged(); } - @Test - public void testInstantWifi() throws Exception { - doAnswer(new AnswerWithArguments() { - public void answer(SingleScanSettings settings, Set<Integer> freqs) { - if (settings.channelSettings == null) { - settings.channelSettings = new ArrayList<>(); - } else { - settings.channelSettings.clear(); - } - for (int freq : freqs) { - if (freq > 0) { - ChannelSettings channel = new ChannelSettings(); - channel.frequency = freq; - settings.channelSettings.add(channel); - } - } - } - }).when(mMockInstantWifi).overrideFreqsForSingleScanSettingsIfNecessary( - any(), any()); - Set<Integer> testPredictedChannelsSet = Set.of(2412, 5745); - assertNotEquals(testPredictedChannelsSet, SCAN_FREQ_SET); - when(mWifiScannerImpl.scan(any(SingleScanSettings.class))).thenReturn(true); - when(mMockInstantWifi.getPredictedScanningChannels()).thenReturn(testPredictedChannelsSet); - - // Trigger scan to check scan settings are changed - assertTrue(mWificondControl.startScan( - TEST_INTERFACE_NAME, WifiScanner.SCAN_TYPE_LOW_POWER, - SCAN_FREQ_SET, SCAN_HIDDEN_NETWORK_SSID_LIST)); - verify(mMockInstantWifi).getPredictedScanningChannels(); - verify(mWifiScannerImpl).scan(argThat(new ScanMatcher( - IWifiScannerImpl.SCAN_TYPE_LOW_POWER, - testPredictedChannelsSet, SCAN_HIDDEN_NETWORK_SSID_LIST, false, null))); - } - // Create a ArgumentMatcher which captures a SingleScanSettings parameter and checks if it // matches the provided frequency set and ssid set. private class ScanMatcher implements ArgumentMatcher<SingleScanSettings> { |