[automerger skipped] Merge "Import translations. DO NOT MERGE ANYWHERE" into tm-qpr-dev am: 8064923875 -s ours am: da4bb792ae -s ours am: cd7cbaf416 -s ours
am skip reason: subject contains skip directive
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/23789021
Change-Id: I8404b6f824e68515be7284cf727d03a3c59461d7
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/apct-tests/perftests/surfaceflinger/AndroidTest.xml b/apct-tests/perftests/surfaceflinger/AndroidTest.xml
index 11d1106..58cf58b 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 0c9569f..250a1d1 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -2958,6 +2958,10 @@
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 02b14ad..f8f4cc3 100644
--- a/core/java/Android.bp
+++ b/core/java/Android.bp
@@ -510,6 +510,17 @@
],
}
+// 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 8021ce0..6e0fc4f 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -1084,6 +1084,16 @@
}
/**
+ * 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 @@
* 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 @@
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 @@
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 b5ee895..ff0f437 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -81,6 +81,7 @@
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 @@
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 @@
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 @@
@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 @@
@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 @@
*/
@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 @@
*/
@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 @@
@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 @@
*/
@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 @@
mColorBackground = colorBackground;
mStatusBarColor = statusBarColor;
mNavigationBarColor = navigationBarColor;
+ mStatusBarAppearance = statusBarAppearance;
mEnsureStatusBarContrastWhenTransparent = ensureStatusBarContrastWhenTransparent;
mEnsureNavigationBarContrastWhenTransparent =
ensureNavigationBarContrastWhenTransparent;
@@ -1789,6 +1794,7 @@
mColorBackground = other.mColorBackground;
mStatusBarColor = other.mStatusBarColor;
mNavigationBarColor = other.mNavigationBarColor;
+ mStatusBarAppearance = other.mStatusBarAppearance;
mEnsureStatusBarContrastWhenTransparent = other.mEnsureStatusBarContrastWhenTransparent;
mEnsureNavigationBarContrastWhenTransparent =
other.mEnsureNavigationBarContrastWhenTransparent;
@@ -1818,6 +1824,9 @@
if (other.mNavigationBarColor != 0) {
mNavigationBarColor = other.mNavigationBarColor;
}
+ if (other.mStatusBarAppearance != 0) {
+ mStatusBarAppearance = other.mStatusBarAppearance;
+ }
mEnsureStatusBarContrastWhenTransparent = other.mEnsureStatusBarContrastWhenTransparent;
mEnsureNavigationBarContrastWhenTransparent =
@@ -2089,6 +2098,14 @@
/**
* @hide
*/
+ @Appearance
+ public int getStatusBarAppearance() {
+ return mStatusBarAppearance;
+ }
+
+ /**
+ * @hide
+ */
public void setEnsureStatusBarContrastWhenTransparent(
boolean ensureStatusBarContrastWhenTransparent) {
mEnsureStatusBarContrastWhenTransparent = ensureStatusBarContrastWhenTransparent;
@@ -2097,6 +2114,13 @@
/**
* @hide
*/
+ public void setStatusBarAppearance(@Appearance int statusBarAppearance) {
+ mStatusBarAppearance = statusBarAppearance;
+ }
+
+ /**
+ * @hide
+ */
public boolean getEnsureNavigationBarContrastWhenTransparent() {
return mEnsureNavigationBarContrastWhenTransparent;
}
@@ -2218,6 +2242,7 @@
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 @@
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 @@
&& 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 411d157..4851279 100644
--- a/core/java/android/app/Dialog.java
+++ b/core/java/android/app/Dialog.java
@@ -694,12 +694,22 @@
*/
@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 09450f5..e7a2b61e 100644
--- a/core/java/android/app/KeyguardManager.java
+++ b/core/java/android/app/KeyguardManager.java
@@ -117,6 +117,26 @@
"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 2eb6ca7..8d2394b 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -2833,12 +2833,14 @@
}
/**
- * 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 @@
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 @@
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 @@
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 @@
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 @@
}
/**
+ * 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 97a794d..18fc0ce 100644
--- a/core/java/android/app/Person.java
+++ b/core/java/android/app/Person.java
@@ -24,6 +24,7 @@
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 @@
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 b710644..5f5a7df 100644
--- a/core/java/android/app/WallpaperColors.java
+++ b/core/java/android/app/WallpaperColors.java
@@ -28,6 +28,7 @@
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 @@
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 @@
bitmap.recycle();
drawable.setBounds(initialBounds);
+ Trace.endSection();
return colors;
}
@@ -195,7 +198,7 @@
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 @@
bitmap.recycle();
}
+ Trace.endSection();
return new WallpaperColors(populationByColor, HINT_FROM_BITMAP | hints);
}
@@ -462,7 +466,7 @@
* 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 @@
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 @@
" 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 ee888ed..1c5332a 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -853,7 +853,7 @@
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 @@
}
/**
- * 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 @@
/**
* 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 @@
/**
* 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 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,22 +2913,63 @@
}
}
- // 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.
*
* @return true on success; false on error.
diff --git a/core/java/android/app/servertransaction/TransactionExecutorHelper.java b/core/java/android/app/servertransaction/TransactionExecutorHelper.java
index 5311b09..baf2a47 100644
--- a/core/java/android/app/servertransaction/TransactionExecutorHelper.java
+++ b/core/java/android/app/servertransaction/TransactionExecutorHelper.java
@@ -193,8 +193,8 @@
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 856bde8..36e0529 100644
--- a/core/java/android/content/ContentCaptureOptions.java
+++ b/core/java/android/content/ContentCaptureOptions.java
@@ -76,6 +76,20 @@
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 @@
* 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 @@
this.textChangeFlushingFrequencyMs = textChangeFlushingFrequencyMs;
this.logHistorySize = logHistorySize;
this.disableFlushForViewTreeAppearing = disableFlushForViewTreeAppearing;
+ this.enableReceiver = enableReceiver;
+ this.contentProtectionOptions = contentProtectionOptions;
this.whitelistedComponents = whitelistedComponents;
}
@@ -191,12 +254,22 @@
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 @@
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 @@
parcel.writeInt(textChangeFlushingFrequencyMs);
parcel.writeInt(logHistorySize);
parcel.writeBoolean(disableFlushForViewTreeAppearing);
+ parcel.writeBoolean(enableReceiver);
+ contentProtectionOptions.writeToParcel(parcel);
parcel.writeArraySet(whitelistedComponents);
}
@@ -254,12 +339,22 @@
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 @@
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 d6df033..94bff89 100644
--- a/core/java/android/hardware/display/DisplayManagerInternal.java
+++ b/core/java/android/hardware/display/DisplayManagerInternal.java
@@ -413,6 +413,15 @@
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 @@
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 01977f6..6195443 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -103,6 +103,7 @@
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 @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 @@
/**
* 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 @@
* @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 @@
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 @@
}
if (mEnrollmentCallback != null) {
- mEnrollmentCallback.onPointerDown(sensorId);
+ mEnrollmentCallback.onUdfpsPointerDown(sensorId);
}
}
@@ -1500,7 +1526,7 @@
mAuthenticationCallback.onUdfpsPointerUp(sensorId);
}
if (mEnrollmentCallback != null) {
- mEnrollmentCallback.onPointerUp(sensorId);
+ mEnrollmentCallback.onUdfpsPointerUp(sensorId);
}
}
@@ -1512,6 +1538,12 @@
}
}
+ private void sendUdfpsOverlayShown() {
+ if (mEnrollmentCallback != null) {
+ mEnrollmentCallback.onUdfpsOverlayShown();
+ }
+ }
+
/**
* @hide
*/
@@ -1787,6 +1819,11 @@
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 a9779b5..89d710d 100644
--- a/core/java/android/hardware/fingerprint/FingerprintServiceReceiver.java
+++ b/core/java/android/hardware/fingerprint/FingerprintServiceReceiver.java
@@ -75,4 +75,9 @@
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 ec5749e..ff2f313 100644
--- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl
+++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
@@ -193,7 +193,7 @@
// 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 9cea1fe..91a32d7 100644
--- a/core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl
+++ b/core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl
@@ -32,4 +32,5 @@
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 17bbe14..33960c0 100644
--- a/core/java/android/hardware/input/InputSettings.java
+++ b/core/java/android/hardware/input/InputSettings.java
@@ -266,7 +266,7 @@
*/
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 0311da4..4403251 100644
--- a/core/java/android/hardware/input/KeyboardLayout.java
+++ b/core/java/android/hardware/input/KeyboardLayout.java
@@ -73,7 +73,7 @@
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 @@
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 @@
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 1df9b75..18d0b09 100644
--- a/core/java/android/hardware/lights/Light.java
+++ b/core/java/android/hardware/lights/Light.java
@@ -110,6 +110,8 @@
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 @@
* @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 @@
* @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 @@
mOrdinal = in.readInt();
mType = in.readInt();
mCapabilities = in.readInt();
+ mPreferredBrightnessLevels = in.createIntArray();
}
/** Implement the Parcelable interface */
@@ -149,6 +163,7 @@
dest.writeInt(mOrdinal);
dest.writeInt(mType);
dest.writeInt(mCapabilities);
+ dest.writeIntArray(mPreferredBrightnessLevels);
}
/** Implement the Parcelable interface */
@@ -252,4 +267,17 @@
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 a9c4818..2f9c207 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -52,8 +52,6 @@
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.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 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
+ */
+ @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
*/
public static final int IME_ACTIVE = 0x1;
/**
- * @hide
* The IME is perceptibly visible to the user.
+ * @hide
*/
public static final int IME_VISIBLE = 0x2;
/**
- * @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;
-
- /**
- * @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 @@
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 @@
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 @@
* @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 @@
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 @@
};
}
+ @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 94971b8..c9073fa 100644
--- a/core/java/android/os/GraphicsEnvironment.java
+++ b/core/java/android/os/GraphicsEnvironment.java
@@ -123,6 +123,7 @@
private int mAngleOptInIndex = -1;
private boolean mEnabledByGameMode = false;
+ private boolean mShouldUseAngle = false;
/**
* Set up GraphicsEnvironment
@@ -141,19 +142,16 @@
// 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 @@
}
/**
- * 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) && setupAndUseAngle(context, packageName)) {
- final Intent intent = new Intent(ACTION_ANGLE_FOR_ANDROID_TOAST_MESSAGE);
- 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);
-
- 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);
+ if (!shouldShowAngleInUseDialogBox(context)) {
+ return;
}
+
+ if (!mShouldUseAngle) {
+ return;
+ }
+
+ 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) {
+ final Bundle results = getResultExtras(true);
+
+ 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 @@
private static native void setDriverPathAndSphalLibraries(String path, String sphalLibraries);
private static native void setGpuStats(String driverPackageName, String driverVersionName,
long driverVersionCode, long driverBuildTime, String appPackageName, int vulkanVersion);
- private static native void setAngleInfo(String path, String appPackage,
+ private static native void setAngleInfo(String path, String packageName,
String devOptIn, String[] features);
- private static native boolean getShouldUseAngle(String packageName);
private static native boolean setInjectLayersPrSetDumpable();
private static native void nativeToggleAngleAsSystemDriver(boolean enabled);
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 84f1880..2d21718 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -1764,10 +1764,19 @@
/**
* Specifies if a user is not allowed to use 2g networks.
*
+ * <p> This is a security feature. 2g has no mutual authentication between a device and
+ * cellular base station and downgrading a device's connection to 2g is a common tactic for
+ * several types of privacy and security compromising attacks that could allow an adversary
+ * to intercept, inject, or modify cellular communications.
+ *
* <p>This restriction can only be set by a device owner or a profile owner of an
* organization-owned managed profile on the parent profile.
- * In all cases, the setting applies globally on the device and will prevent the device from
- * scanning for or connecting to 2g networks, except in the case of an emergency.
+ * In all cases, the setting applies globally on the device.
+ *
+ * <p> Cellular connectivity loss (where a device would have otherwise successfully
+ * connected to a 2g network) occurs if the device is in an area where only 2g networks are
+ * available. Emergency calls are an exception and are never impacted. The device will still
+ * scan for and connect to a 2g network for emergency calls.
*
* <p>Holders of the permission
* {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_MOBILE_NETWORK}
diff --git a/core/java/android/os/image/DynamicSystemClient.java b/core/java/android/os/image/DynamicSystemClient.java
index 218ecc8..88096ab 100644
--- a/core/java/android/os/image/DynamicSystemClient.java
+++ b/core/java/android/os/image/DynamicSystemClient.java
@@ -209,6 +209,13 @@
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 9610b16..536795b 100644
--- a/core/java/android/os/image/DynamicSystemManager.java
+++ b/core/java/android/os/image/DynamicSystemManager.java
@@ -285,4 +285,16 @@
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 755368a..0280ebb 100644
--- a/core/java/android/os/image/IDynamicSystemService.aidl
+++ b/core/java/android/os/image/IDynamicSystemService.aidl
@@ -145,4 +145,10 @@
*/
@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 d695c0c..d425bf8 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -7486,6 +7486,14 @@
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 @@
"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 737c95f..7fa0ac8 100644
--- a/core/java/android/service/contentcapture/ContentCaptureService.java
+++ b/core/java/android/service/contentcapture/ContentCaptureService.java
@@ -37,6 +37,7 @@
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 @@
"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 @@
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 @@
}
};
- /**
- * 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 @@
@Override
public final IBinder onBind(Intent intent) {
if (SERVICE_INTERFACE.equals(intent.getAction())) {
- return mServerInterface.asBinder();
+ return mContentCaptureServerInterface.asBinder();
}
- Log.w(TAG, "Tried to bind to wrong intent (should be " + SERVICE_INTERFACE + ": " + intent);
+ 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 @@
} else {
stateFlags |= ContentCaptureSession.STATE_DISABLED;
}
- setClientState(clientReceiver, stateFlags, mClientInterface.asBinder());
+ setClientState(clientReceiver, stateFlags, mContentCaptureClientInterface.asBinder());
}
private void handleSendEvents(int uid,
@@ -524,6 +560,18 @@
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 fb60619..4aa5c00 100644
--- a/core/java/android/service/contentcapture/ContentCaptureServiceInfo.java
+++ b/core/java/android/service/contentcapture/ContentCaptureServiceInfo.java
@@ -48,7 +48,7 @@
*
* @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/core/java/android/service/contentcapture/IContentProtectionService.aidl b/core/java/android/service/contentcapture/IContentProtectionService.aidl
new file mode 100644
index 0000000..4a13c3f
--- /dev/null
+++ b/core/java/android/service/contentcapture/IContentProtectionService.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 android.service.contentcapture;
+
+import android.content.pm.ParceledListSlice;
+import android.view.contentcapture.ContentCaptureEvent;
+
+/**
+ * Interface from the system server to the content protection service.
+ *
+ * @hide
+ */
+oneway interface IContentProtectionService {
+
+ void onLoginDetected(in ParceledListSlice events);
+}
diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java
index cd57de5..5e7f5d6 100644
--- a/core/java/android/service/dreams/DreamService.java
+++ b/core/java/android/service/dreams/DreamService.java
@@ -16,6 +16,8 @@
package android.service.dreams;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+
import android.annotation.IdRes;
import android.annotation.LayoutRes;
import android.annotation.NonNull;
@@ -630,7 +632,7 @@
}
/**
- * Marks this dream as windowless. Only available to doze dreams.
+ * Marks this dream as windowless. It should be called in {@link #onCreate} method.
*
* @hide
*
@@ -640,7 +642,7 @@
}
/**
- * Returns whether this dream is windowless. Only available to doze dreams.
+ * Returns whether this dream is windowless.
*
* @hide
*/
@@ -1230,8 +1232,10 @@
mDreamToken = dreamToken;
mCanDoze = canDoze;
- if (mWindowless && !mCanDoze) {
- throw new IllegalStateException("Only doze dreams can be windowless");
+ // This is not a security check to prevent malicious dreams but a guard rail to stop
+ // third-party dreams from being windowless and not working well as a result.
+ if (mWindowless && !mCanDoze && !isCallerSystemUi()) {
+ throw new IllegalStateException("Only doze or SystemUI dreams can be windowless.");
}
mDispatchAfterOnAttachedToWindow = () -> {
@@ -1366,6 +1370,11 @@
}
}
+ private boolean isCallerSystemUi() {
+ return checkCallingOrSelfPermission(android.Manifest.permission.STATUS_BAR_SERVICE)
+ == PERMISSION_GRANTED;
+ }
+
private int applyFlags(int oldFlags, int flags, int mask) {
return (oldFlags&~mask) | (flags&mask);
}
diff --git a/core/java/android/service/notification/NotificationRankingUpdate.java b/core/java/android/service/notification/NotificationRankingUpdate.java
index a853714..75640bd 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 @@
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 dc06671..1ed5d3f 100644
--- a/core/java/android/view/AttachedSurfaceControl.java
+++ b/core/java/android/view/AttachedSurfaceControl.java
@@ -23,6 +23,9 @@
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 @@
*/
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 15008ae..fd4b329 100644
--- a/core/java/android/view/ISurfaceControlViewHost.aidl
+++ b/core/java/android/view/ISurfaceControlViewHost.aidl
@@ -19,6 +19,7 @@
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 @@
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 0000000..f42e001
--- /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 209729b..6d96bb94 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -471,10 +471,23 @@
* 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 37b6c77..83bdb08 100644
--- a/core/java/android/view/InsetsFrameProvider.java
+++ b/core/java/android/view/InsetsFrameProvider.java
@@ -164,6 +164,10 @@
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 9fc42ff..e101849 100644
--- a/core/java/android/view/InsetsSource.java
+++ b/core/java/android/view/InsetsSource.java
@@ -58,9 +58,20 @@
*/
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 @@
private @Nullable Rect mVisibleFrame;
private boolean mVisible;
- private boolean mInsetsRoundedCornerFrame;
private final Rect mTmpFrame = new Rect();
@@ -98,7 +108,6 @@
? new Rect(other.mVisibleFrame)
: null;
mFlags = other.mFlags;
- mInsetsRoundedCornerFrame = other.mInsetsRoundedCornerFrame;
}
public void set(InsetsSource other) {
@@ -108,7 +117,6 @@
? 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 @@
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 @@
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 @@
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 @@
}
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 @@
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 @@
}
mVisible = in.readBoolean();
mFlags = in.readInt();
- mInsetsRoundedCornerFrame = in.readBoolean();
}
@Override
@@ -414,7 +421,6 @@
}
dest.writeBoolean(mVisible);
dest.writeInt(mFlags);
- dest.writeBoolean(mInsetsRoundedCornerFrame);
}
@Override
@@ -424,7 +430,6 @@
+ " 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 b55d25a..dceae90 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 @@
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 1af8ca2..c703af5 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -1285,6 +1285,8 @@
* </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 @@
* </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 @@
* </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 @@
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 c8cf7d9..effc127 100644
--- a/core/java/android/view/SurfaceControlViewHost.java
+++ b/core/java/android/view/SurfaceControlViewHost.java
@@ -54,7 +54,7 @@
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 @@
return;
}
mViewRoot.mHandler.post(() -> {
- if (mWm != null) {
- mWm.setConfiguration(configuration);
- }
+ mWm.setConfiguration(configuration);
if (mViewRoot != null) {
mViewRoot.forceWmRelayout();
}
@@ -116,6 +114,11 @@
}
return null;
}
+
+ @Override
+ public void attachParentInterface(@Nullable ISurfaceControlViewHostParent parentInterface) {
+ mViewRoot.mHandler.post(() -> mWm.setParentInterface(parentInterface));
+ }
}
private ISurfaceControlViewHost mRemoteInterface = new ISurfaceControlViewHostImpl();
@@ -148,10 +151,11 @@
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 @@
/**
* @hide
*/
+ @NonNull
public ISurfaceControlViewHost getRemoteInterface() {
return mRemoteInterface;
}
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 0e4cf89..1e268be 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.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 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 @@
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 @@
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 @@
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 @@
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 d457847..01a99b9 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -2043,7 +2043,10 @@
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 a28be4b..9316dbf 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -1900,9 +1900,10 @@
&& !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;
}
@@ -3819,7 +3820,7 @@
}
mPendingTransitions.clear();
}
- if (!performDraw() && mActiveSurfaceSyncGroup != null) {
+ if (!performDraw(mActiveSurfaceSyncGroup) && mActiveSurfaceSyncGroup != null) {
mActiveSurfaceSyncGroup.markSyncReady();
}
}
@@ -4582,6 +4583,10 @@
});
}
+ /**
+ * These callbacks check if the draw failed for any reason and apply
+ * those transactions directly so they don't get stuck forever.
+ */
private void registerCallbackForPendingTransactions() {
Transaction t = new Transaction();
t.merge(mPendingTransaction);
@@ -4610,7 +4615,7 @@
});
}
- private boolean performDraw() {
+ private boolean performDraw(@Nullable SurfaceSyncGroup surfaceSyncGroup) {
mLastPerformDrawSkippedReason = null;
if (mAttachInfo.mDisplayState == Display.STATE_OFF && !mReportNextDraw) {
mLastPerformDrawSkippedReason = "screen_off";
@@ -4620,7 +4625,7 @@
return false;
}
- final boolean fullRedrawNeeded = mFullRedrawNeeded || mActiveSurfaceSyncGroup != null;
+ final boolean fullRedrawNeeded = mFullRedrawNeeded || surfaceSyncGroup != null;
mFullRedrawNeeded = false;
mIsDrawing = true;
@@ -4628,22 +4633,12 @@
addFrameCommitCallbackIfNeeded();
- boolean usingAsyncReport = isHardwareEnabled() && mActiveSurfaceSyncGroup != null;
- if (usingAsyncReport) {
- registerCallbacksForSync(mSyncBuffer, mActiveSurfaceSyncGroup);
- } else if (mHasPendingTransactions) {
- // These callbacks are only needed if there's no sync involved and there were calls to
- // applyTransactionOnDraw. These callbacks check if the draw failed for any reason and
- // apply those transactions directly so they don't get stuck forever.
- registerCallbackForPendingTransactions();
- }
- mHasPendingTransactions = false;
+ boolean usingAsyncReport;
try {
- boolean canUseAsync = draw(fullRedrawNeeded, usingAsyncReport && mSyncBuffer);
- if (usingAsyncReport && !canUseAsync) {
+ usingAsyncReport = draw(fullRedrawNeeded, surfaceSyncGroup, mSyncBuffer);
+ if (mAttachInfo.mThreadedRenderer != null && !usingAsyncReport) {
mAttachInfo.mThreadedRenderer.setFrameCallback(null);
- usingAsyncReport = false;
}
} finally {
mIsDrawing = false;
@@ -4681,10 +4676,12 @@
}
if (mSurfaceHolder != null && mSurface.isValid()) {
- final SurfaceSyncGroup surfaceSyncGroup = mActiveSurfaceSyncGroup;
- SurfaceCallbackHelper sch = new SurfaceCallbackHelper(() ->
- mHandler.post(() -> surfaceSyncGroup.markSyncReady()));
- mActiveSurfaceSyncGroup = null;
+ usingAsyncReport = true;
+ SurfaceCallbackHelper sch = new SurfaceCallbackHelper(() -> {
+ if (surfaceSyncGroup != null) {
+ surfaceSyncGroup.markSyncReady();
+ }
+ });
SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();
@@ -4695,8 +4692,9 @@
}
}
}
- if (mActiveSurfaceSyncGroup != null && !usingAsyncReport) {
- mActiveSurfaceSyncGroup.markSyncReady();
+
+ if (surfaceSyncGroup != null && !usingAsyncReport) {
+ surfaceSyncGroup.markSyncReady();
}
if (mPerformContentCapture) {
performContentCaptureInitialReport();
@@ -4789,7 +4787,8 @@
}
}
- private boolean draw(boolean fullRedrawNeeded, boolean forceDraw) {
+ private boolean draw(boolean fullRedrawNeeded,
+ @Nullable SurfaceSyncGroup activeSyncGroup, boolean syncBuffer) {
Surface surface = mSurface;
if (!surface.isValid()) {
return false;
@@ -4933,9 +4932,19 @@
mAttachInfo.mThreadedRenderer.setTargetHdrSdrRatio(mRenderHdrSdrRatio);
}
- if (forceDraw) {
- mAttachInfo.mThreadedRenderer.forceDrawNextFrame();
+ if (activeSyncGroup != null) {
+ registerCallbacksForSync(syncBuffer, activeSyncGroup);
+ if (syncBuffer) {
+ mAttachInfo.mThreadedRenderer.forceDrawNextFrame();
+ }
+ } else if (mHasPendingTransactions) {
+ // Register a calback if there's no sync involved but there were calls to
+ // applyTransactionOnDraw. If there is a sync involved, the sync callback will
+ // handle merging the pending transaction.
+ registerCallbackForPendingTransactions();
}
+ mHasPendingTransactions = false;
+
mAttachInfo.mThreadedRenderer.draw(mView, mAttachInfo, this);
} else {
// If we get here with a disabled & requested hardware renderer, something went
@@ -11553,4 +11562,17 @@
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 21fe87f..2f04b0c 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -658,6 +658,12 @@
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 @@
if (mDecorCallback != null) {
mDecorCallback.onSystemBarAppearanceChanged(appearance);
}
+ if (mWindowControllerCallback != null) {
+ mWindowControllerCallback.updateStatusBarAppearance(appearance);
+ }
}
/** @hide */
@@ -1482,6 +1491,11 @@
}
/** @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 a917815..d702367 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -1384,15 +1384,28 @@
"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 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 @@
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 @@
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 @@
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 @@
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 df3e0bb..b57163c 100644
--- a/core/java/android/view/WindowManagerImpl.java
+++ b/core/java/android/view/WindowManagerImpl.java
@@ -215,14 +215,36 @@
@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 96bfb2d..7d3d283 100644
--- a/core/java/android/view/WindowlessWindowManager.java
+++ b/core/java/android/view/WindowlessWindowManager.java
@@ -49,7 +49,8 @@
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 @@
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 @@
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 @@
outInsetsState.set(mInsetsState);
}
+ sendLayoutParamsToParent();
return 0;
}
@@ -645,4 +650,45 @@
" 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 668351b..c9afdc0 100644
--- a/core/java/android/view/contentcapture/ContentCaptureManager.java
+++ b/core/java/android/view/contentcapture/ContentCaptureManager.java
@@ -52,6 +52,7 @@
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 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 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 @@
@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 @@
}
/** @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 @@
return mContext;
}
+ @NonNull
public String getPackageName() {
return mPackageName;
}
@@ -469,6 +509,16 @@
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 @@
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 62044aa..dc3d323 100644
--- a/core/java/android/view/contentcapture/ContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/ContentCaptureSession.java
@@ -191,20 +191,22 @@
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 @@
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 a641110..1487997 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 @@
*/
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 efd50e7..2241fd5 100644
--- a/core/java/android/view/contentcapture/MainContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/MainContentCaptureSession.java
@@ -52,8 +52,10 @@
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 @@
/**
* 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 @@
@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 @@
@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 @@
}
}
- 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 @@
}
/**
- * 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 @@
}
}
+ // 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 @@
}
}
+ /** @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 @@
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 @@
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 @@
return;
}
+ if (!isContentCaptureReceiverEnabled()) {
+ return;
+ }
+
if (mDirectServiceInterface == null) {
if (sVerbose) {
Log.v(TAG, "handleForceFlush(" + getDebugState(reason) + "): hold your horses, "
@@ -607,8 +669,10 @@
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 @@
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 @@
}
}
mDirectServiceInterface = null;
+ mContentProtectionEventProcessor = null;
mHandler.removeMessages(MSG_FLUSH);
}
@@ -878,4 +946,14 @@
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 044a31f..f218995 100644
--- a/core/java/android/view/contentcapture/ViewNode.java
+++ b/core/java/android/view/contentcapture/ViewNode.java
@@ -480,6 +480,11 @@
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 0000000..b44abf3
--- /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 0000000..9abf6f1
--- /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 41ef44e..40b060a 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -4361,15 +4361,14 @@
* @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 05717dd..7eee33f 100644
--- a/core/java/android/view/inputmethod/TextAppearanceInfo.java
+++ b/core/java/android/view/inputmethod/TextAppearanceInfo.java
@@ -238,7 +238,10 @@
.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 adeb889..6ad1960 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -4703,7 +4703,7 @@
@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 634cbe3..405099d 100644
--- a/core/java/android/widget/Button.java
+++ b/core/java/android/widget/Button.java
@@ -18,6 +18,7 @@
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 @@
@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 2dbff58..d37c37a 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -8112,16 +8112,10 @@
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 e1b0c91..b6c5396c 100644
--- a/core/java/android/widget/ImageButton.java
+++ b/core/java/android/widget/ImageButton.java
@@ -18,6 +18,7 @@
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 @@
@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 f3600b0..edf0f48 100644
--- a/core/java/android/widget/RadialTimePickerView.java
+++ b/core/java/android/widget/RadialTimePickerView.java
@@ -38,6 +38,7 @@
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 @@
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 7f96266..3be8c3d 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -232,6 +232,7 @@
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 @@
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 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 6c53a44..1317b51 100644
--- a/core/java/android/widget/SimpleMonthView.java
+++ b/core/java/android/widget/SimpleMonthView.java
@@ -39,6 +39,7 @@
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 @@
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 ad431ef..ecc41a5 100644
--- a/core/java/android/widget/Spinner.java
+++ b/core/java/android/widget/Spinner.java
@@ -38,6 +38,7 @@
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 @@
@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 db7d484..7e1e52d 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -9223,18 +9223,20 @@
@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 (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);
+ 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 25bf85c..7fc55a7 100644
--- a/core/java/android/window/WindowInfosListenerForTest.java
+++ b/core/java/android/window/WindowInfosListenerForTest.java
@@ -87,6 +87,14 @@
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 94c230b..95c3419 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 @@
* 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 57cc38c..0ea8014 100644
--- a/core/java/com/android/internal/app/AssistUtils.java
+++ b/core/java/com/android/internal/app/AssistUtils.java
@@ -59,6 +59,8 @@
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 dd52de4..9ffccb3 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 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 c9e7600..6b8ae94 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 @@
/** 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 66e3333..1a38049 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.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 @@
*
* @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 e530aec..92427ec 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_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;
@@ -68,6 +69,7 @@
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH_FROM_SETTINGS_BUTTON;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_CLEAR_ALL;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_DIALOG_OPEN;
+import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_EXPAND_FROM_STATUS_BAR;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_HEADS_UP_APPEAR;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_HEADS_UP_DISAPPEAR;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_NOTIFICATION_ADD;
@@ -258,8 +260,17 @@
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;
+ public static final int CUJ_SHADE_EXPAND_FROM_STATUS_BAR = 79;
+
+ private static final int LAST_CUJ = CUJ_SHADE_EXPAND_FROM_STATUS_BAR;
private static final int NO_STATSD_LOGGING = -1;
// Used to convert CujType to InteractionType enum value for statsd logging.
@@ -340,6 +351,15 @@
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;
+ CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_SHADE_EXPAND_FROM_STATUS_BAR] = UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_EXPAND_FROM_STATUS_BAR;
}
private static class InstanceHolder {
@@ -439,6 +459,8 @@
CUJ_IME_INSETS_ANIMATION,
CUJ_LOCKSCREEN_CLOCK_MOVE_ANIMATION,
CUJ_LAUNCHER_OPEN_SEARCH_RESULT,
+ CUJ_LAUNCHER_APP_CLOSE_TO_HOME_FALLBACK,
+ CUJ_SHADE_EXPAND_FROM_STATUS_BAR,
})
@Retention(RetentionPolicy.SOURCE)
public @interface CujType {
@@ -1050,6 +1072,10 @@
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";
+ case CUJ_SHADE_EXPAND_FROM_STATUS_BAR:
+ return "SHADE_EXPAND_FROM_STATUS_BAR";
}
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 93765cd..8870096 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 013d0d3..d6ab8bc 100644
--- a/core/java/com/android/internal/statusbar/IAppClipsService.aidl
+++ b/core/java/com/android/internal/statusbar/IAppClipsService.aidl
@@ -23,4 +23,6 @@
*/
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 4f827cd..8b9a991 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 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 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 fcc77bd4..c23e902 100644
--- a/core/java/com/android/internal/util/TraceBuffer.java
+++ b/core/java/com/android/internal/util/TraceBuffer.java
@@ -18,6 +18,7 @@
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 @@
* {@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 @@
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 @@
* @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 @@
/**
* 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 @@
/**
* 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 92cfa67..361bdce 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.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 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 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 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 @@
@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 @@
@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 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 @@
}
}
+ /** 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 @@
}
@Override
public boolean shouldBypassCache(Integer userHandle) {
- return userHandle == USER_FRP;
+ return isSpecialUserId(userHandle);
}
};
@@ -1105,9 +1132,10 @@
@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 @@
}
/**
+ * 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 f55d15d..e65b4b6 100644
--- a/core/java/com/android/internal/widget/PointerLocationView.java
+++ b/core/java/com/android/internal/widget/PointerLocationView.java
@@ -163,6 +163,8 @@
@UnsupportedAppUsage
private boolean mPrintCoords = true;
+ private float mDensity;
+
public PointerLocationView(Context c) {
super(c);
setFocusableInTouchMode(true);
@@ -365,11 +367,8 @@
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 @@
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 @@
// 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_hardware_input_InputWindowHandle.cpp b/core/jni/android_hardware_input_InputWindowHandle.cpp
index 416d991..eb5f297 100644
--- a/core/jni/android_hardware_input_InputWindowHandle.cpp
+++ b/core/jni/android_hardware_input_InputWindowHandle.cpp
@@ -163,10 +163,9 @@
mInfo.touchOcclusionMode = static_cast<TouchOcclusionMode>(
env->GetIntField(obj, gInputWindowHandleClassInfo.touchOcclusionMode));
- mInfo.ownerPid = env->GetIntField(obj,
- gInputWindowHandleClassInfo.ownerPid);
- mInfo.ownerUid = env->GetIntField(obj,
- gInputWindowHandleClassInfo.ownerUid);
+ mInfo.ownerPid = gui::Pid{env->GetIntField(obj, gInputWindowHandleClassInfo.ownerPid)};
+ mInfo.ownerUid = gui::Uid{
+ static_cast<uid_t>(env->GetIntField(obj, gInputWindowHandleClassInfo.ownerUid))};
mInfo.packageName = getStringField(env, obj, gInputWindowHandleClassInfo.packageName, "<null>");
mInfo.displayId = env->GetIntField(obj,
gInputWindowHandleClassInfo.displayId);
@@ -308,8 +307,10 @@
env->SetIntField(inputWindowHandle, gInputWindowHandleClassInfo.touchOcclusionMode,
static_cast<int32_t>(windowInfo.touchOcclusionMode));
- env->SetIntField(inputWindowHandle, gInputWindowHandleClassInfo.ownerPid, windowInfo.ownerPid);
- env->SetIntField(inputWindowHandle, gInputWindowHandleClassInfo.ownerUid, windowInfo.ownerUid);
+ env->SetIntField(inputWindowHandle, gInputWindowHandleClassInfo.ownerPid,
+ windowInfo.ownerPid.val());
+ env->SetIntField(inputWindowHandle, gInputWindowHandleClassInfo.ownerUid,
+ windowInfo.ownerUid.val());
ScopedLocalRef<jstring> packageName(env, env->NewStringUTF(windowInfo.packageName.data()));
env->SetObjectField(inputWindowHandle, gInputWindowHandleClassInfo.packageName,
packageName.get());
diff --git a/core/jni/android_os_GraphicsEnvironment.cpp b/core/jni/android_os_GraphicsEnvironment.cpp
index 01dbceb..d94b982 100644
--- a/core/jni/android_os_GraphicsEnvironment.cpp
+++ b/core/jni/android_os_GraphicsEnvironment.cpp
@@ -49,10 +49,10 @@
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 @@
}
}
- 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 @@
{"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 0000000..1699008
--- /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 6acfd57..0000000
--- 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 ee8c0f8..f27535c 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>
- <!-- 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.
- The brightness values must be between 0 and 255 and be non-decreasing.
+ <!-- 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 -->
- <integer-array name="config_autoBrightnessKeyboardBacklightValues">
+ <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_autoKeyboardBacklight(Increase/Decrease)LuxThreshold arrays.
+ The brightness values must be between 0 and 255 and be non-decreasing.
+
+ 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
@@ -5643,6 +5702,9 @@
TODO(b/255532890) Enable when ignoreOrientationRequest is set -->
<bool name="config_letterboxIsEnabledForTranslucentActivities">false</bool>
+ <!-- Whether per-app user aspect ratio override settings is enabled -->
+ <bool name="config_appCompatUserAppAspectRatioSettingsIsEnabled">false</bool>
+
<!-- Whether sending compat fake focus for split screen resumed activities is enabled.
Needed because some game engines wait to get focus before drawing the content of
the app which isn't guaranteed by default in multi-window modes. -->
@@ -6185,7 +6247,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 +6508,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 4ae54a0..08c40ba 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 80a9977..2e5da33 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" />
@@ -4425,9 +4430,6 @@
<!-- Set to true to make assistant show in front of the dream/screensaver. -->
<java-symbol type="bool" name="config_assistantOnTopOfDream"/>
- <!-- Set to true to enable letterboxing on translucent activities. -->
- <java-symbol type="bool" name="config_letterboxIsEnabledForTranslucentActivities" />
-
<java-symbol type="string" name="config_overrideComponentUiPackage" />
<java-symbol type="string" name="notification_channel_network_status" />
@@ -4523,6 +4525,12 @@
<java-symbol type="dimen" name="config_letterboxDefaultMinAspectRatioForUnresizableApps" />
<java-symbol type="bool" name="config_letterboxIsSplitScreenAspectRatioForUnresizableAppsEnabled" />
<java-symbol type="bool" name="config_letterboxIsDisplayAspectRatioForFixedOrientationLetterboxEnabled" />
+ <!-- Set to true to enable letterboxing on translucent activities. -->
+ <java-symbol type="bool" name="config_letterboxIsEnabledForTranslucentActivities" />
+
+ <!-- Whether per-app user aspect ratio override settings is enabled -->
+ <java-symbol type="bool" name="config_appCompatUserAppAspectRatioSettingsIsEnabled" />
+
<java-symbol type="bool" name="config_isCompatFakeFocusEnabled" />
<java-symbol type="bool" name="config_isWindowManagerCameraCompatTreatmentEnabled" />
<java-symbol type="bool" name="config_isWindowManagerCameraCompatSplitScreenAspectRatioEnabled" />
@@ -4913,6 +4921,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 436f058..85d54e0 100644
--- a/core/tests/BroadcastRadioTests/Android.bp
+++ b/core/tests/BroadcastRadioTests/Android.bp
@@ -45,6 +45,7 @@
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 c1deba3..129de64 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 0000000..a2a6447
--- /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 7ea1848..6a50e9a 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 81eb213..5ac99db 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 @@
0x222222, // colorBackground
0x333333, // statusBarColor
0x444444, // navigationBarColor
+ 0, // statusBarAppearance
true, // ensureStatusBarContrastWhenTransparent
true, // ensureNavigationBarContrastWhenTransparent
RESIZE_MODE_RESIZEABLE, // resizeMode
@@ -152,6 +153,7 @@
0x222222, // colorBackground
0x333333, // statusBarColor
0x444444, // navigationBarColor
+ 0, // statusBarAppearance
false, // ensureStatusBarContrastWhenTransparent
false, // ensureNavigationBarContrastWhenTransparent
RESIZE_MODE_UNRESIZEABLE, // resizeMode
@@ -167,6 +169,7 @@
0x2222222, // colorBackground
0x3333332, // statusBarColor
0x4444442, // navigationBarColor
+ 0, // statusBarAppearance
true, // ensureStatusBarContrastWhenTransparent
true, // ensureNavigationBarContrastWhenTransparent
RESIZE_MODE_RESIZEABLE, // resizeMode
@@ -197,6 +200,7 @@
0x222222, // colorBackground
0x333333, // statusBarColor
0x444444, // navigationBarColor
+ 0, // statusBarAppearance
false, // ensureStatusBarContrastWhenTransparent
false, // ensureNavigationBarContrastWhenTransparent
RESIZE_MODE_UNRESIZEABLE, // resizeMode
@@ -219,6 +223,7 @@
0x222222, // colorBackground
0x333333, // statusBarColor
0x444444, // navigationBarColor
+ 0, // statusBarAppearance
false, // ensureStatusBarContrastWhenTransparent
false, // ensureNavigationBarContrastWhenTransparent
RESIZE_MODE_UNRESIZEABLE, // resizeMode
@@ -250,6 +255,7 @@
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 c6f4fa2..f8348d2 100644
--- a/core/tests/coretests/src/android/content/ContentCaptureOptionsTest.java
+++ b/core/tests/coretests/src/android/content/ContentCaptureOptionsTest.java
@@ -22,6 +22,7 @@
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 @@
@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 @@
@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 @@
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/content/res/FontScaleConverterActivityTest.java b/core/tests/coretests/src/android/content/res/FontScaleConverterActivityTest.java
index df4fb44..0941a2b 100644
--- a/core/tests/coretests/src/android/content/res/FontScaleConverterActivityTest.java
+++ b/core/tests/coretests/src/android/content/res/FontScaleConverterActivityTest.java
@@ -137,7 +137,7 @@
);
});
- PollingCheck.waitFor(/* timeout= */ 5000, () -> {
+ PollingCheck.waitFor(/* timeout= */ 7000, () -> {
AtomicBoolean isActivityAtCorrectScale = new AtomicBoolean(false);
rule.getScenario().onActivity(activity ->
isActivityAtCorrectScale.set(
@@ -146,12 +146,7 @@
.fontScale == fontScale
)
);
- return isActivityAtCorrectScale.get() && InstrumentationRegistry
- .getInstrumentation()
- .getContext()
- .getResources()
- .getConfiguration()
- .fontScale == fontScale;
+ return isActivityAtCorrectScale.get();
});
}
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 0000000..a84ac55
--- /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 17ed4c4..101f7c2 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 @@
@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 @@
}
@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 @@
@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 @@
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 27d58b8..23b9b9b 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.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.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
+import java.util.Map;
+
/**
* Unit tests for {@link ContentCaptureSession}.
*
@@ -126,6 +131,7 @@
() -> 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 @@
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 @@
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 0000000..3373b8b
--- /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 93315f1..a4e77f5 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 @@
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);
- ViewStructureImpl structure = new ViewStructureImpl(view);
- ViewNode node = structure.getNode();
+ mViewStructure.addChildCount(1);
+ assertThat(mViewNode.getChildCount()).isEqualTo(0);
- structure.setChildCount(1);
- assertThat(node.getChildCount()).isEqualTo(0);
+ assertThat(mViewStructure.newChild(0)).isNull();
+ assertThat(mViewNode.getChildCount()).isEqualTo(0);
- structure.addChildCount(1);
- assertThat(node.getChildCount()).isEqualTo(0);
+ assertThat(mViewStructure.asyncNewChild(0)).isNull();
+ assertThat(mViewNode.getChildCount()).isEqualTo(0);
- assertThat(structure.newChild(0)).isNull();
- assertThat(node.getChildCount()).isEqualTo(0);
+ mViewStructure.asyncCommit();
+ assertThat(mViewNode.getChildCount()).isEqualTo(0);
- assertThat(structure.asyncNewChild(0)).isNull();
- assertThat(node.getChildCount()).isEqualTo(0);
+ mViewStructure.setWebDomain("Y U NO SET?");
+ assertThat(mViewNode.getWebDomain()).isNull();
- structure.asyncCommit();
- assertThat(node.getChildCount()).isEqualTo(0);
+ assertThat(mViewStructure.newHtmlInfoBuilder("WHATEVER")).isNull();
- structure.setWebDomain("Y U NO SET?");
- assertThat(node.getWebDomain()).isNull();
+ mViewStructure.setHtmlInfo(mHtmlInfoMock);
+ assertThat(mViewNode.getHtmlInfo()).isNull();
- assertThat(structure.newHtmlInfoBuilder("WHATEVER")).isNull();
+ mViewStructure.setDataIsSensitive(true);
- structure.setHtmlInfo(mHtmlInfoMock);
- assertThat(node.getHtmlInfo()).isNull();
-
- structure.setDataIsSensitive(true);
-
- assertThat(structure.getTempRect()).isNull();
+ assertThat(mViewStructure.getTempRect()).isNull();
// 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();
+ 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();
+ }
+
+ @Test
+ public void testGetSet_textIdEntry() {
+ assertThat(mViewNode.getTextIdEntry()).isNull();
+
+ String expected = "TEXT_ID_ENTRY";
+ mViewNode.setTextIdEntry(expected);
+
+ 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 0000000..39a2e0e
--- /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 0000000..1459799
--- /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 f93cd18..0750cf1 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.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 @@
@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 @@
@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 @@
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 @@
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 0000000..8e9e1a5
--- /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 0000000..d491fb0
--- /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 6167c4b..1a668f7 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 @@
}
@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 96811be..741e535 100644
--- a/core/tests/mockingcoretests/Android.bp
+++ b/core/tests/mockingcoretests/Android.bp
@@ -56,7 +56,10 @@
],
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 be2c27d..3e0e36d 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 @@
// 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 93e44f1..94e23e7 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 89f4890..4cedd41 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 @@
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 @@
@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 @@
// 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 @@
// 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 @@
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 @@
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 @@
}
}
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 @@
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 @@
// 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 @@
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 @@
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 @@
/** 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 @@
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 @@
@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 @@
@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 @@
@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 @@
}
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 4b15bb1..4580c98 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 @@
/** 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 @@
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 60be9d1..61df335 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 @@
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 ff08782..9e26472 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 @@
// 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 @@
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 @@
// 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
+ "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 13e7092..11af1d1 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 @@
assertFalse(taskContainer.isEmpty());
taskContainer.mFinishedContainer.add(tf.getTaskFragmentToken());
- taskContainer.mContainers.clear();
+ taskContainer.clearTaskFragmentContainer();
assertFalse(taskContainer.isEmpty());
}
@@ -152,13 +152,13 @@
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 54978bd..7159893 100644
--- a/libs/WindowManager/Shell/Android.bp
+++ b/libs/WindowManager/Shell/Android.bp
@@ -42,16 +42,19 @@
filegroup {
name: "wm_shell_util-sources",
srcs: [
- "src/com/android/wm/shell/util/**/*.java",
- "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/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/common/TransactionPool.java",
+ "src/com/android/wm/shell/common/TriangleShape.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 0000000..d99d64d
--- /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 5d77713..ce24275 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 0000000..f450846
--- /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 0000000..ddcd5c6
--- /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 0000000..82e5aee
--- /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 1d6864c..0ca912e 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 171a6b2..f2a0785 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 2be34c9..2e3f604 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 102f2cb..7e09c98 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
@@ -422,6 +422,7 @@
}
if (mBubbleBarExpandedView != null) {
mBubbleBarExpandedView.cleanUpExpandedState();
+ mBubbleBarExpandedView = null;
}
if (mIntent != null) {
mIntent.unregisterCancelListener(mIntentCancelListener);
@@ -549,10 +550,10 @@
/**
* Set visibility of bubble in the expanded state.
*
- * @param visibility {@code true} if the expanded bubble should be visible on the screen.
- *
- * Note that this contents visibility doesn't affect visibility at {@link android.view.View},
+ * <p>Note that this contents visibility doesn't affect visibility at {@link android.view.View},
* and setting {@code false} actually means rendering the expanded view in transparent.
+ *
+ * @param visibility {@code true} if the expanded bubble should be visible on the screen.
*/
@Override
public void setTaskViewVisibility(boolean visibility) {
@@ -855,7 +856,8 @@
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 0fdfbb8..6880237 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
@@ -64,7 +64,6 @@
import android.os.Handler;
import android.os.RemoteException;
import android.os.ServiceManager;
-import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
import android.service.notification.NotificationListenerService;
@@ -92,6 +91,7 @@
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.WindowManagerShellWrapper;
import com.android.wm.shell.bubbles.bar.BubbleBarLayerView;
+import com.android.wm.shell.bubbles.properties.BubbleProperties;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.ExternalInterfaceBinder;
import com.android.wm.shell.common.FloatingContentCoordinator;
@@ -143,16 +143,6 @@
private static final String SYSTEM_DIALOG_REASON_KEY = "reason";
private static final String SYSTEM_DIALOG_REASON_GESTURE_NAV = "gestureNav";
- // TODO(b/256873975) Should use proper flag when available to shell/launcher
- /**
- * Whether bubbles are showing in the bubble bar from launcher. This is only available
- * on large screens and {@link BubbleController#isShowingAsBubbleBar()} should be used
- * to check all conditions that indicate if the bubble bar is in use.
- */
- private static final boolean BUBBLE_BAR_ENABLED =
- SystemProperties.getBoolean("persist.wm.debug.bubble_bar", false);
-
-
/**
* Common interface to send updates to bubble views.
*/
@@ -195,6 +185,7 @@
private final ShellController mShellController;
private final ShellCommandHandler mShellCommandHandler;
private final IWindowManager mWmService;
+ private final BubbleProperties mBubbleProperties;
// Used to post to main UI thread
private final ShellExecutor mMainExecutor;
@@ -291,7 +282,8 @@
@ShellBackgroundThread ShellExecutor bgExecutor,
TaskViewTransitions taskViewTransitions,
SyncTransactionQueue syncQueue,
- IWindowManager wmService) {
+ IWindowManager wmService,
+ BubbleProperties bubbleProperties) {
mContext = context;
mShellCommandHandler = shellCommandHandler;
mShellController = shellController;
@@ -328,6 +320,7 @@
mDragAndDropController = dragAndDropController;
mSyncQueue = syncQueue;
mWmService = wmService;
+ mBubbleProperties = bubbleProperties;
shellInit.addInitCallback(this::onInit, this);
}
@@ -518,11 +511,14 @@
/**
* Sets a listener to be notified of bubble updates. This is used by launcher so that
* it may render bubbles in itself. Only one listener is supported.
+ *
+ * <p>If bubble bar is supported, bubble views will be updated to switch to bar mode.
*/
public void registerBubbleStateListener(Bubbles.BubbleStateListener listener) {
- if (isShowingAsBubbleBar()) {
- // Only set the listener if bubble bar is showing.
+ if (canShowAsBubbleBar() && listener != null) {
+ // Only set the listener if we can show the bubble bar.
mBubbleStateListener = listener;
+ setUpBubbleViewsForMode();
sendInitialListenerUpdate();
} else {
mBubbleStateListener = null;
@@ -531,9 +527,15 @@
/**
* Unregisters the {@link Bubbles.BubbleStateListener}.
+ *
+ * <p>If there's an existing listener, then we're switching back to stack mode and bubble views
+ * will be updated accordingly.
*/
public void unregisterBubbleStateListener() {
- mBubbleStateListener = null;
+ if (mBubbleStateListener != null) {
+ mBubbleStateListener = null;
+ setUpBubbleViewsForMode();
+ }
}
/**
@@ -645,8 +647,12 @@
/** Whether bubbles are showing in the bubble bar. */
public boolean isShowingAsBubbleBar() {
- // TODO(b/269670598): should also check that we're in gesture nav
- return BUBBLE_BAR_ENABLED && mBubblePositioner.isLargeScreen();
+ return canShowAsBubbleBar() && mBubbleStateListener != null;
+ }
+
+ /** Whether the current configuration supports showing as bubble bar. */
+ private boolean canShowAsBubbleBar() {
+ return mBubbleProperties.isBubbleBarEnabled() && mBubblePositioner.isLargeScreen();
}
/** Whether this userId belongs to the current user. */
@@ -719,6 +725,7 @@
// 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) {
@@ -773,12 +780,12 @@
try {
mAddedToWindowManager = true;
registerBroadcastReceiver();
- mBubbleData.getOverflow().initialize(this);
+ mBubbleData.getOverflow().initialize(this, isShowingAsBubbleBar());
// (TODO: b/273314541) some duplication in the inset listener
if (isShowingAsBubbleBar()) {
mWindowManager.addView(mLayerView, mWmLayoutParams);
mLayerView.setOnApplyWindowInsetsListener((view, windowInsets) -> {
- if (!windowInsets.equals(mWindowInsets)) {
+ if (!windowInsets.equals(mWindowInsets) && mLayerView != null) {
mWindowInsets = windowInsets;
mBubblePositioner.update();
mLayerView.onDisplaySizeChanged();
@@ -788,7 +795,7 @@
} else {
mWindowManager.addView(mStackView, mWmLayoutParams);
mStackView.setOnApplyWindowInsetsListener((view, windowInsets) -> {
- if (!windowInsets.equals(mWindowInsets)) {
+ if (!windowInsets.equals(mWindowInsets) && mStackView != null) {
mWindowInsets = windowInsets;
mBubblePositioner.update();
mStackView.onDisplaySizeChanged();
@@ -1065,9 +1072,18 @@
* Expands and selects the provided bubble as long as it already exists in the stack or the
* overflow.
*
- * This is used by external callers (launcher).
+ * <p>This is used by external callers (launcher).
*/
- public void expandStackAndSelectBubbleFromLauncher(String key) {
+ @VisibleForTesting
+ public void expandStackAndSelectBubbleFromLauncher(String key, boolean onLauncherHome) {
+ mBubblePositioner.setShowingInBubbleBar(onLauncherHome);
+
+ if (BubbleOverflow.KEY.equals(key)) {
+ mBubbleData.setSelectedBubbleFromLauncher(mBubbleData.getOverflow());
+ mLayerView.showExpandedView(mBubbleData.getOverflow());
+ return;
+ }
+
Bubble b = mBubbleData.getAnyBubbleWithkey(key);
if (b == null) {
return;
@@ -1220,6 +1236,13 @@
}
/**
+ * 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.
@@ -1259,7 +1282,9 @@
return;
}
mOverflowDataLoadNeeded = false;
- mDataRepository.loadBubbles(mCurrentUserId, (bubbles) -> {
+ List<UserInfo> users = mUserManager.getAliveUsers();
+ List<Integer> userIds = users.stream().map(userInfo -> userInfo.id).toList();
+ mDataRepository.loadBubbles(mCurrentUserId, userIds, (bubbles) -> {
bubbles.forEach(bubble -> {
if (mBubbleData.hasAnyBubbleWithKey(bubble.getKey())) {
// if the bubble is already active, there's no need to push it to overflow
@@ -1278,6 +1303,50 @@
});
}
+ void setUpBubbleViewsForMode() {
+ mBubbleViewCallback = isShowingAsBubbleBar()
+ ? mBubbleBarViewCallback
+ : mBubbleStackViewCallback;
+
+ // reset the overflow so that it can be re-added later if needed.
+ if (mStackView != null) {
+ mStackView.resetOverflowView();
+ mStackView.removeAllViews();
+ }
+ // cleanup existing bubble views so they can be recreated later if needed.
+ mBubbleData.getBubbles().forEach(Bubble::cleanupViews);
+
+ // remove the current bubble container from window manager, null it out, and create a new
+ // container based on the current mode.
+ removeFromWindowManagerMaybe();
+ mLayerView = null;
+ mStackView = null;
+ ensureBubbleViewsAndWindowCreated();
+
+ // inflate bubble views
+ BubbleViewInfoTask.Callback callback = null;
+ if (!isShowingAsBubbleBar()) {
+ callback = b -> {
+ if (mStackView != null) {
+ mStackView.addBubble(b);
+ mStackView.setSelectedBubble(b);
+ } else {
+ Log.w(TAG, "Tried to add a bubble to the stack but the stack is null");
+ }
+ };
+ }
+ for (int i = mBubbleData.getBubbles().size() - 1; i >= 0; i--) {
+ Bubble bubble = mBubbleData.getBubbles().get(i);
+ bubble.inflate(callback,
+ mContext,
+ this,
+ mStackView,
+ mLayerView,
+ mBubbleIconFactory,
+ false /* skipInflation */);
+ }
+ }
+
/**
* Adds or updates a bubble associated with the provided notification entry.
*
@@ -1738,7 +1807,7 @@
// Update the cached state for queries from SysUI
mImpl.mCachedState.update(update);
- if (isShowingAsBubbleBar() && mBubbleStateListener != null) {
+ if (isShowingAsBubbleBar()) {
BubbleBarUpdate bubbleBarUpdate = update.toBubbleBarUpdate();
// Some updates aren't relevant to the bubble bar so check first.
if (bubbleBarUpdate.anythingChanged()) {
@@ -1846,7 +1915,7 @@
if (mStackView != null) {
mStackView.setVisibility(VISIBLE);
}
- if (mLayerView != null && isStackExpanded()) {
+ if (mLayerView != null) {
mLayerView.setVisibility(VISIBLE);
}
}
@@ -1860,10 +1929,17 @@
}
@VisibleForTesting
+ @Nullable
public BubbleStackView getStackView() {
return mStackView;
}
+ @VisibleForTesting
+ @Nullable
+ public BubbleBarLayerView getLayerView() {
+ return mLayerView;
+ }
+
/**
* Check if notification panel is in an expanded state.
* Makes a call to System UI process and delivers the result via {@code callback} on the
@@ -2002,22 +2078,18 @@
@Override
public void registerBubbleListener(IBubblesListener listener) {
- mMainExecutor.execute(() -> {
- mListener.register(listener);
- });
+ mMainExecutor.execute(() -> mListener.register(listener));
}
@Override
public void unregisterBubbleListener(IBubblesListener listener) {
- mMainExecutor.execute(() -> mListener.unregister());
+ mMainExecutor.execute(mListener::unregister);
}
@Override
public void showBubble(String key, boolean onLauncherHome) {
- mMainExecutor.execute(() -> {
- mBubblePositioner.setShowingInBubbleBar(onLauncherHome);
- mController.expandStackAndSelectBubbleFromLauncher(key);
- });
+ mMainExecutor.execute(
+ () -> mController.expandStackAndSelectBubbleFromLauncher(key, onLauncherHome));
}
@Override
@@ -2029,11 +2101,6 @@
public void collapseBubbles() {
mMainExecutor.execute(() -> mController.collapseStack());
}
-
- @Override
- public void onTaskbarStateChanged(int newState) {
- // TODO (b/269670598)
- }
}
private class BubblesImpl implements Bubbles {
@@ -2144,7 +2211,7 @@
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/BubbleDataRepository.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleDataRepository.kt
index e37c785..896a334 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleDataRepository.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleDataRepository.kt
@@ -17,7 +17,6 @@
import android.annotation.SuppressLint
import android.annotation.UserIdInt
-import android.content.Context
import android.content.pm.LauncherApps
import android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_CACHED
import android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_DYNAMIC
@@ -25,6 +24,8 @@
import android.content.pm.UserInfo
import android.os.UserHandle
import android.util.Log
+import android.util.SparseArray
+import com.android.internal.annotations.VisibleForTesting
import com.android.wm.shell.bubbles.Bubbles.BubbleMetadataFlagListener
import com.android.wm.shell.bubbles.storage.BubbleEntity
import com.android.wm.shell.bubbles.storage.BubblePersistentRepository
@@ -33,19 +34,19 @@
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
+import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.cancelAndJoin
import kotlinx.coroutines.launch
import kotlinx.coroutines.yield
-internal class BubbleDataRepository(
- context: Context,
+class BubbleDataRepository(
private val launcherApps: LauncherApps,
- private val mainExecutor: ShellExecutor
+ private val mainExecutor: ShellExecutor,
+ private val persistentRepository: BubblePersistentRepository,
) {
private val volatileRepository = BubbleVolatileRepository(launcherApps)
- private val persistentRepository = BubblePersistentRepository(context)
- private val ioScope = CoroutineScope(Dispatchers.IO)
+ private val coroutineScope = CoroutineScope(Dispatchers.IO + SupervisorJob())
private var job: Job? = null
// For use in Bubble construction.
@@ -98,6 +99,43 @@
if (volatileRepository.sanitizeBubbles(userIds)) persistToDisk()
}
+ /**
+ * Removes all entities that don't have a user in the activeUsers list, if any entities were
+ * removed it persists the new list to disk.
+ */
+ private fun filterForActiveUsersAndPersist(
+ activeUsers: List<Int>,
+ entitiesByUser: SparseArray<List<BubbleEntity>>
+ ): SparseArray<List<BubbleEntity>> {
+ val validEntitiesByUser = SparseArray<List<BubbleEntity>>()
+ var entitiesChanged = false
+ for (i in 0 until entitiesByUser.size()) {
+ val parentUserId = entitiesByUser.keyAt(i)
+ if (activeUsers.contains(parentUserId)) {
+ val validEntities = mutableListOf<BubbleEntity>()
+ // Check if each of the bubbles in the top-level user still has a valid user
+ // as it could belong to a profile and have a different id from the parent.
+ for (entity in entitiesByUser.get(parentUserId)) {
+ if (activeUsers.contains(entity.userId)) {
+ validEntities.add(entity)
+ } else {
+ entitiesChanged = true
+ }
+ }
+ if (validEntities.isNotEmpty()) {
+ validEntitiesByUser.put(parentUserId, validEntities)
+ }
+ } else {
+ entitiesChanged = true
+ }
+ }
+ if (entitiesChanged) {
+ persistToDisk(validEntitiesByUser)
+ return validEntitiesByUser
+ }
+ return entitiesByUser
+ }
+
private fun transform(bubbles: List<Bubble>): List<BubbleEntity> {
return bubbles.mapNotNull { b ->
BubbleEntity(
@@ -129,15 +167,17 @@
* Job C resumes and reaches yield() and is then cancelled
* Job D resumes and performs another blocking I/O
*/
- private fun persistToDisk() {
+ private fun persistToDisk(
+ entitiesByUser: SparseArray<List<BubbleEntity>> = volatileRepository.bubbles
+ ) {
val prev = job
- job = ioScope.launch {
+ job = coroutineScope.launch {
// if there was an ongoing disk I/O operation, they can be cancelled
prev?.cancelAndJoin()
// check for cancellation before disk I/O
yield()
// save to disk
- persistentRepository.persistsToDisk(volatileRepository.bubbles)
+ persistentRepository.persistsToDisk(entitiesByUser)
}
}
@@ -148,7 +188,12 @@
* bubbles.
*/
@SuppressLint("WrongConstant")
- fun loadBubbles(userId: Int, cb: (List<Bubble>) -> Unit) = ioScope.launch {
+ @VisibleForTesting
+ fun loadBubbles(
+ userId: Int,
+ currentUsers: List<Int>,
+ cb: (List<Bubble>) -> Unit
+ ) = coroutineScope.launch {
/**
* Load BubbleEntity from disk.
* e.g.
@@ -159,7 +204,12 @@
* ]
*/
val entitiesByUser = persistentRepository.readFromDisk()
- val entities = entitiesByUser.get(userId) ?: return@launch
+
+ // Before doing anything, validate that the entities we loaded are valid & have an existing
+ // user.
+ val validEntitiesByUser = filterForActiveUsersAndPersist(currentUsers, entitiesByUser)
+
+ val entities = validEntitiesByUser.get(userId) ?: return@launch
volatileRepository.addBubbles(userId, entities)
/**
* Extract userId/packageName from these entities.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
index e1a3f3a..6718565 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
@@ -948,9 +948,9 @@
mTaskView.onLocationChanged();
}
if (mIsOverflow) {
- post(() -> {
- mOverflowView.show();
- });
+ // post this to the looper so that the view has a chance to be laid out before it can
+ // calculate row and column sizes correctly.
+ post(() -> mOverflowView.show());
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt
index df7f5b4..df19757 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt
@@ -44,6 +44,7 @@
private val inflater: LayoutInflater = LayoutInflater.from(context)
private var expandedView: BubbleExpandedView?
+ private var bubbleBarExpandedView: BubbleBarExpandedView? = null
private var overflowBtn: BadgedImageView?
init {
@@ -53,19 +54,26 @@
}
/** Call before use and again if cleanUpExpandedState was called. */
- fun initialize(controller: BubbleController) {
- createExpandedView()
- getExpandedView()?.initialize(controller, controller.stackView, true /* isOverflow */)
+ fun initialize(controller: BubbleController, forBubbleBar: Boolean) {
+ if (forBubbleBar) {
+ createBubbleBarExpandedView().initialize(controller, true /* isOverflow */)
+ } else {
+ createExpandedView()
+ .initialize(controller, controller.stackView, true /* isOverflow */)
+ }
}
fun cleanUpExpandedState() {
expandedView?.cleanUpExpandedState()
expandedView = null
+ bubbleBarExpandedView?.cleanUpExpandedState()
+ bubbleBarExpandedView = null
}
fun update() {
updateResources()
getExpandedView()?.applyThemeAttrs()
+ getBubbleBarExpandedView()?.applyThemeAttrs()
// Apply inset and new style to fresh icon drawable.
getIconView()?.setIconImageResource(R.drawable.bubble_ic_overflow_button)
updateBtnTheme()
@@ -151,26 +159,39 @@
overflowBtn?.updateDotVisibility(true /* animate */)
}
- fun createExpandedView(): BubbleExpandedView? {
- expandedView =
+ /** Creates the expanded view for bubbles showing in the stack view. */
+ private fun createExpandedView(): BubbleExpandedView {
+ val view =
inflater.inflate(
R.layout.bubble_expanded_view,
null /* root */,
false /* attachToRoot */
) as BubbleExpandedView
- expandedView?.applyThemeAttrs()
+ view.applyThemeAttrs()
+ expandedView = view
updateResources()
- return expandedView
+ return view
}
override fun getExpandedView(): BubbleExpandedView? {
return expandedView
}
- override fun getBubbleBarExpandedView(): BubbleBarExpandedView? {
- return null
+ /** Creates the expanded view for bubbles showing in the bubble bar. */
+ private fun createBubbleBarExpandedView(): BubbleBarExpandedView {
+ val view =
+ inflater.inflate(
+ R.layout.bubble_bar_expanded_view,
+ null, /* root */
+ false /* attachToRoot*/
+ ) as BubbleBarExpandedView
+ view.applyThemeAttrs()
+ bubbleBarExpandedView = view
+ return view
}
+ override fun getBubbleBarExpandedView(): BubbleBarExpandedView? = bubbleBarExpandedView
+
override fun getDotColor(): Int {
return dotColor
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
index d101b0c..cb08f93 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
@@ -732,17 +732,25 @@
/**
* How wide the expanded view should be when showing from the bubble bar.
*/
- public int getExpandedViewWidthForBubbleBar() {
- return mExpandedViewLargeScreenWidth;
+ public int getExpandedViewWidthForBubbleBar(boolean isOverflow) {
+ return isOverflow ? mOverflowWidth : mExpandedViewLargeScreenWidth;
}
/**
* How tall the expanded view should be when showing from the bubble bar.
*/
- public int getExpandedViewHeightForBubbleBar() {
+ public int getExpandedViewHeightForBubbleBar(boolean isOverflow) {
+ return isOverflow
+ ? mOverflowHeight
+ : getExpandedViewBottomForBubbleBar() - mInsets.top - mExpandedViewPadding;
+ }
+
+ /** The bottom position of the expanded view when showing above the bubble bar. */
+ public int getExpandedViewBottomForBubbleBar() {
return getAvailableRect().height()
+ + mInsets.top
- mBubbleBarSize
- - mExpandedViewPadding * 2
+ - mExpandedViewPadding
- getBubbleBarHomeAdjustment();
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
index 68fea41..282db9e 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.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;
@@ -307,6 +309,7 @@
String bubblesOnScreen = BubbleDebugConfig.formatBubblesString(
getBubblesOnScreen(), getExpandedBubble());
+ pw.print(" stack visibility : "); pw.println(getVisibility());
pw.print(" bubbles on screen: "); pw.println(bubblesOnScreen);
pw.print(" gestureInProgress: "); pw.println(mIsGestureInProgress);
pw.print(" showingDismiss: "); pw.println(mDismissView.isShowing());
@@ -968,6 +971,8 @@
mBubbleContainer.bringToFront();
mBubbleOverflow = mBubbleData.getOverflow();
+
+ resetOverflowView();
mBubbleContainer.addView(mBubbleOverflow.getIconView(),
mBubbleContainer.getChildCount() /* index */,
new FrameLayout.LayoutParams(mPositioner.getBubbleSize(),
@@ -1179,6 +1184,7 @@
removeView(mDismissView);
}
mDismissView = new DismissView(getContext());
+ DismissViewUtils.setup(mDismissView);
int elevation = getResources().getDimensionPixelSize(R.dimen.bubble_elevation);
addView(mDismissView);
@@ -1494,9 +1500,6 @@
getViewTreeObserver().removeOnPreDrawListener(mViewUpdater);
getViewTreeObserver().removeOnDrawListener(mSystemGestureExcludeUpdater);
getViewTreeObserver().removeOnComputeInternalInsetsListener(this);
- if (mBubbleOverflow != null) {
- mBubbleOverflow.cleanUpExpandedState();
- }
}
@Override
@@ -3411,6 +3414,19 @@
}
/**
+ * Removes the overflow view from the stack. This allows for re-adding it later to a new stack.
+ */
+ void resetOverflowView() {
+ BadgedImageView overflowIcon = mBubbleOverflow.getIconView();
+ if (overflowIcon != null) {
+ PhysicsAnimationLayout parent = (PhysicsAnimationLayout) overflowIcon.getParent();
+ if (parent != null) {
+ parent.removeViewNoAnimation(overflowIcon);
+ }
+ }
+ }
+
+ /**
* Holds some commonly queried information about the stack.
*/
public static class StackViewState {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewHelper.java
index 7a58159..da4a989 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 @@
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 @@
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 8ab9841..66e6930 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 @@
@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 @@
if (isCancelled() || viewInfo == null) {
return;
}
+
mMainExecutor.execute(() -> {
+ if (!verifyState()) {
+ return;
+ }
mBubble.setViewInfo(viewInfo);
if (mCallback != null) {
mCallback.onBubbleViewsReady(mBubble);
@@ -126,6 +134,14 @@
});
}
+ private boolean verifyState() {
+ if (mController.get().isShowingAsBubbleBar()) {
+ return mLayerView.get() != null;
+ } else {
+ return mStackView.get() != null;
+ }
+ }
+
/**
* Info necessary to render a bubble.
*/
@@ -160,39 +176,14 @@
LayoutInflater inflater = LayoutInflater.from(c);
info.bubbleBarExpandedView = (BubbleBarExpandedView) inflater.inflate(
R.layout.bubble_bar_expanded_view, layerView, false /* attachToRoot */);
- info.bubbleBarExpandedView.initialize(controller);
+ info.bubbleBarExpandedView.initialize(controller, false /* isOverflow */);
}
- if (b.getShortcutInfo() != null) {
- info.shortcutInfo = b.getShortcutInfo();
- }
-
- // App name & app icon
- PackageManager pm = BubbleController.getPackageManagerForUser(c,
- b.getUser().getIdentifier());
- ApplicationInfo appInfo;
- Drawable badgedIcon;
- Drawable appIcon;
- try {
- appInfo = pm.getApplicationInfo(
- b.getPackageName(),
- PackageManager.MATCH_UNINSTALLED_PACKAGES
- | PackageManager.MATCH_DISABLED_COMPONENTS
- | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
- | PackageManager.MATCH_DIRECT_BOOT_AWARE);
- if (appInfo != null) {
- info.appName = String.valueOf(pm.getApplicationLabel(appInfo));
- }
- appIcon = pm.getApplicationIcon(b.getPackageName());
- badgedIcon = pm.getUserBadgedIcon(appIcon, b.getUser());
- } catch (PackageManager.NameNotFoundException exception) {
- // If we can't find package... don't think we should show the bubble.
- Log.w(TAG, "Unable to find package: " + b.getPackageName());
+ if (!populateCommonInfo(info, c, b, iconFactory)) {
+ // if we failed to update common fields return null
return null;
}
- info.rawBadgeBitmap = iconFactory.getBadgeBitmap(badgedIcon, false).icon;
-
return info;
}
@@ -215,66 +206,11 @@
info.expandedView.initialize(controller, stackView, false /* isOverflow */);
}
- if (b.getShortcutInfo() != null) {
- info.shortcutInfo = b.getShortcutInfo();
- }
-
- // App name & app icon
- PackageManager pm = BubbleController.getPackageManagerForUser(c,
- b.getUser().getIdentifier());
- ApplicationInfo appInfo;
- Drawable badgedIcon;
- Drawable appIcon;
- try {
- appInfo = pm.getApplicationInfo(
- b.getPackageName(),
- PackageManager.MATCH_UNINSTALLED_PACKAGES
- | PackageManager.MATCH_DISABLED_COMPONENTS
- | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
- | PackageManager.MATCH_DIRECT_BOOT_AWARE);
- if (appInfo != null) {
- info.appName = String.valueOf(pm.getApplicationLabel(appInfo));
- }
- appIcon = pm.getApplicationIcon(b.getPackageName());
- badgedIcon = pm.getUserBadgedIcon(appIcon, b.getUser());
- } catch (PackageManager.NameNotFoundException exception) {
- // If we can't find package... don't think we should show the bubble.
- Log.w(TAG, "Unable to find package: " + b.getPackageName());
+ if (!populateCommonInfo(info, c, b, iconFactory)) {
+ // if we failed to update common fields return null
return null;
}
- // Badged bubble image
- Drawable bubbleDrawable = iconFactory.getBubbleDrawable(c, info.shortcutInfo,
- b.getIcon());
- if (bubbleDrawable == null) {
- // Default to app icon
- bubbleDrawable = appIcon;
- }
-
- BitmapInfo badgeBitmapInfo = iconFactory.getBadgeBitmap(badgedIcon,
- b.isImportantConversation());
- info.badgeBitmap = badgeBitmapInfo.icon;
- // Raw badge bitmap never includes the important conversation ring
- info.rawBadgeBitmap = b.isImportantConversation()
- ? iconFactory.getBadgeBitmap(badgedIcon, false).icon
- : badgeBitmapInfo.icon;
-
- float[] bubbleBitmapScale = new float[1];
- info.bubbleBitmap = iconFactory.getBubbleBitmap(bubbleDrawable, bubbleBitmapScale);
-
- // Dot color & placement
- Path iconPath = PathParser.createPathFromPathData(
- c.getResources().getString(com.android.internal.R.string.config_icon_mask));
- Matrix matrix = new Matrix();
- float scale = bubbleBitmapScale[0];
- float radius = DEFAULT_PATH_SIZE / 2f;
- matrix.setScale(scale /* x scale */, scale /* y scale */, radius /* pivot x */,
- radius /* pivot y */);
- iconPath.transform(matrix);
- info.dotPath = iconPath;
- info.dotColor = ColorUtils.blendARGB(badgeBitmapInfo.color,
- Color.WHITE, WHITE_SCRIM_ALPHA);
-
// Flyout
info.flyoutMessage = b.getFlyoutMessage();
if (info.flyoutMessage != null) {
@@ -285,6 +221,75 @@
}
}
+ /**
+ * Modifies the given {@code info} object and populates common fields in it.
+ *
+ * <p>This method returns {@code true} if the update was successful and {@code false} otherwise.
+ * Callers should assume that the info object is unusable if the update was unsuccessful.
+ */
+ private static boolean populateCommonInfo(
+ BubbleViewInfo info, Context c, Bubble b, BubbleIconFactory iconFactory) {
+ if (b.getShortcutInfo() != null) {
+ info.shortcutInfo = b.getShortcutInfo();
+ }
+
+ // App name & app icon
+ PackageManager pm = BubbleController.getPackageManagerForUser(c,
+ b.getUser().getIdentifier());
+ ApplicationInfo appInfo;
+ Drawable badgedIcon;
+ Drawable appIcon;
+ try {
+ appInfo = pm.getApplicationInfo(
+ b.getPackageName(),
+ PackageManager.MATCH_UNINSTALLED_PACKAGES
+ | PackageManager.MATCH_DISABLED_COMPONENTS
+ | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
+ | PackageManager.MATCH_DIRECT_BOOT_AWARE);
+ if (appInfo != null) {
+ info.appName = String.valueOf(pm.getApplicationLabel(appInfo));
+ }
+ appIcon = pm.getApplicationIcon(b.getPackageName());
+ badgedIcon = pm.getUserBadgedIcon(appIcon, b.getUser());
+ } catch (PackageManager.NameNotFoundException exception) {
+ // If we can't find package... don't think we should show the bubble.
+ Log.w(TAG, "Unable to find package: " + b.getPackageName());
+ return false;
+ }
+
+ // Badged bubble image
+ Drawable bubbleDrawable = iconFactory.getBubbleDrawable(c, info.shortcutInfo, b.getIcon());
+ if (bubbleDrawable == null) {
+ // Default to app icon
+ bubbleDrawable = appIcon;
+ }
+
+ BitmapInfo badgeBitmapInfo = iconFactory.getBadgeBitmap(badgedIcon,
+ b.isImportantConversation());
+ info.badgeBitmap = badgeBitmapInfo.icon;
+ // Raw badge bitmap never includes the important conversation ring
+ info.rawBadgeBitmap = b.isImportantConversation() // is this needed for bar?
+ ? iconFactory.getBadgeBitmap(badgedIcon, false).icon
+ : badgeBitmapInfo.icon;
+
+ float[] bubbleBitmapScale = new float[1];
+ info.bubbleBitmap = iconFactory.getBubbleBitmap(bubbleDrawable, bubbleBitmapScale);
+
+ // Dot color & placement
+ Path iconPath = PathParser.createPathFromPathData(
+ c.getResources().getString(com.android.internal.R.string.config_icon_mask));
+ Matrix matrix = new Matrix();
+ float scale = bubbleBitmapScale[0];
+ float radius = DEFAULT_PATH_SIZE / 2f;
+ matrix.setScale(scale /* x scale */, scale /* y scale */, radius /* pivot x */,
+ radius /* pivot y */);
+ iconPath.transform(matrix);
+ info.dotPath = iconPath;
+ info.dotColor = ColorUtils.blendARGB(badgeBitmapInfo.color,
+ Color.WHITE, WHITE_SCRIM_ALPHA);
+ return true;
+ }
+
@Nullable
static Drawable loadSenderAvatar(@NonNull final Context context, @Nullable final Icon icon) {
Objects.requireNonNull(context);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
index 4d329dd..759246e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
@@ -60,9 +60,11 @@
DISMISS_NOTIF_CANCEL, DISMISS_ACCESSIBILITY_ACTION, DISMISS_NO_LONGER_BUBBLE,
DISMISS_USER_CHANGED, DISMISS_GROUP_CANCELLED, DISMISS_INVALID_INTENT,
DISMISS_OVERFLOW_MAX_REACHED, DISMISS_SHORTCUT_REMOVED, DISMISS_PACKAGE_REMOVED,
- DISMISS_NO_BUBBLE_UP, DISMISS_RELOAD_FROM_DISK, DISMISS_USER_REMOVED})
+ DISMISS_NO_BUBBLE_UP, DISMISS_RELOAD_FROM_DISK, DISMISS_USER_REMOVED,
+ DISMISS_SWITCH_TO_STACK})
@Target({FIELD, LOCAL_VARIABLE, PARAMETER})
- @interface DismissReason {}
+ @interface DismissReason {
+ }
int DISMISS_USER_GESTURE = 1;
int DISMISS_AGED = 2;
@@ -80,6 +82,7 @@
int DISMISS_NO_BUBBLE_UP = 14;
int DISMISS_RELOAD_FROM_DISK = 15;
int DISMISS_USER_REMOVED = 16;
+ int DISMISS_SWITCH_TO_STACK = 17;
/** Returns a binder that can be passed to an external process to manipulate Bubbles. */
default IBubbles createExternalInterface() {
@@ -120,8 +123,8 @@
/**
* This method has different behavior depending on:
- * - if an app bubble exists
- * - if an app bubble is expanded
+ * - if an app bubble exists
+ * - if an app bubble is expanded
*
* If no app bubble exists, this will add and expand a bubble with the provided intent. The
* intent must be explicit (i.e. include a package name or fully qualified component class name)
@@ -135,13 +138,13 @@
* the bubble or bubble stack.
*
* Some notes:
- * - Only one app bubble is supported at a time, regardless of users. Multi-users support is
- * tracked in b/273533235.
- * - Calling this method with a different intent than the existing app bubble will do nothing
+ * - Only one app bubble is supported at a time, regardless of users. Multi-users support is
+ * tracked in b/273533235.
+ * - Calling this method with a different intent than the existing app bubble will do nothing
*
* @param intent the intent to display in the bubble expanded view.
- * @param user the {@link UserHandle} of the user to start this activity for.
- * @param icon the {@link Icon} to use for the bubble view.
+ * @param user the {@link UserHandle} of the user to start this activity for.
+ * @param icon the {@link Icon} to use for the bubble view.
*/
void showOrHideAppBubble(Intent intent, UserHandle user, @Nullable Icon icon);
@@ -172,13 +175,12 @@
* {@link Bubble#setSuppressNotification}. For the case of suppressed summaries, we also add
* {@link BubbleData#addSummaryToSuppress}.
*
- * @param entry the notification of the BubbleEntry should be removed.
- * @param children the list of child notification of the BubbleEntry from 1st param entry,
- * this will be null if entry does have no children.
+ * @param entry the notification of the BubbleEntry should be removed.
+ * @param children the list of child notification of the BubbleEntry from 1st param entry,
+ * this will be null if entry does have no children.
* @param removeCallback the remove callback for SystemUI side to remove notification, the int
* number should be list position of children list and use -1 for
* removing the parent notification.
- *
* @return true if we want to intercept the dismissal of the entry, else false.
*/
boolean handleDismissalInterception(BubbleEntry entry, @Nullable List<BubbleEntry> children,
@@ -200,9 +202,9 @@
/**
* Called when new notification entry updated.
*
- * @param entry the {@link BubbleEntry} by the notification.
+ * @param entry the {@link BubbleEntry} by the notification.
* @param shouldBubbleUp {@code true} if this notification should bubble up.
- * @param fromSystem {@code true} if this update is from NotificationManagerService.
+ * @param fromSystem {@code true} if this update is from NotificationManagerService.
*/
void onEntryUpdated(BubbleEntry entry, boolean shouldBubbleUp, boolean fromSystem);
@@ -218,7 +220,7 @@
* filtering and sorting. This is used to dismiss or create bubbles based on changes in
* permissions on the notification channel or the global setting.
*
- * @param rankingMap the updated ranking map from NotificationListenerService
+ * @param rankingMap the updated ranking map from NotificationListenerService
* @param entryDataByKey a map of ranking key to bubble entry and whether the entry should
* bubble up
*/
@@ -230,9 +232,9 @@
* Called when a notification channel is modified, in response to
* {@link NotificationListenerService#onNotificationChannelModified}.
*
- * @param pkg the package the notification channel belongs to.
- * @param user the user the notification channel belongs to.
- * @param channel the channel being modified.
+ * @param pkg the package the notification channel belongs to.
+ * @param user the user the notification channel belongs to.
+ * @param channel the channel being modified.
* @param modificationType the type of modification that occurred to the channel.
*/
void onNotificationChannelModified(
@@ -300,7 +302,7 @@
* Called when the expansion state of the bubble stack changes.
*
* @param isExpanding whether it's expanding or collapsing
- * @param key the notification key associated with bubble being expanded
+ * @param key the notification key associated with bubble being expanded
*/
void onBubbleExpandChanged(boolean isExpanding, String key);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/DismissView.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/DismissView.kt
deleted file mode 100644
index 67ecb91..0000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/DismissView.kt
+++ /dev/null
@@ -1,159 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.bubbles
-
-import android.animation.ObjectAnimator
-import android.content.Context
-import android.graphics.Color
-import android.graphics.drawable.GradientDrawable
-import android.util.IntProperty
-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.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.
- */
-class DismissView(context: Context) : FrameLayout(context) {
-
- var circle = DismissCircleView(context)
- var isShowing = false
- var targetSizeResId: Int
-
- 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 val GRADIENT_ALPHA: IntProperty<GradientDrawable> =
- object : IntProperty<GradientDrawable>("alpha") {
- override fun setValue(d: GradientDrawable, percent: Int) {
- d.alpha = percent
- }
- override fun get(d: GradientDrawable): Int {
- return d.alpha
- }
- }
-
- 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)
- 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())
- }
-
- /**
- * Animates this view in.
- */
- fun show() {
- if (isShowing) return
- isShowing = true
- setVisibility(View.VISIBLE)
- val alphaAnim = ObjectAnimator.ofInt(gradientDrawable, GRADIENT_ALPHA,
- gradientDrawable.alpha, 255)
- alphaAnim.setDuration(DISMISS_SCRIM_FADE_MS)
- alphaAnim.start()
-
- animator.cancel()
- animator
- .spring(DynamicAnimation.TRANSLATION_Y, 0f, spring)
- .start()
- }
-
- /**
- * Animates this view out, as well as the circle that encircles the bubbles, if they
- * were dragged into the target and encircled.
- */
- fun hide() {
- if (!isShowing) return
- isShowing = false
- val alphaAnim = ObjectAnimator.ofInt(gradientDrawable, GRADIENT_ALPHA,
- gradientDrawable.alpha, 0)
- alphaAnim.setDuration(DISMISS_SCRIM_FADE_MS)
- alphaAnim.start()
- animator
- .spring(DynamicAnimation.TRANSLATION_Y, height.toFloat(),
- spring)
- .withEndActions({ setVisibility(View.INVISIBLE) })
- .start()
- }
-
- /**
- * Cancels the animator for the dismiss target.
- */
- fun cancelAnimators() {
- animator.cancel()
- }
-
- fun updateResources() {
- updatePadding()
- layoutParams.height = resources.getDimensionPixelSize(
- R.dimen.floating_dismiss_gradient_height)
-
- val targetSize = resources.getDimensionPixelSize(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)
- val alpha = 0.7f * 255
- val gradientColorWithAlpha = Color.argb(alpha.toInt(),
- Color.red(gradientColor),
- Color.green(gradientColor),
- Color.blue(gradientColor))
- val gd = GradientDrawable(
- GradientDrawable.Orientation.BOTTOM_TOP,
- intArrayOf(gradientColorWithAlpha, Color.TRANSPARENT))
- gd.setDither(true)
- gd.setAlpha(0)
- return gd
- }
-
- private fun updatePadding() {
- 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))
- }
-}
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 0000000..ed36240
--- /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/IBubbles.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl
index 862e818..20ae846 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl
@@ -35,6 +35,4 @@
oneway void collapseBubbles() = 5;
- oneway void onTaskbarStateChanged(in int newState) = 6;
-
}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/PhysicsAnimationLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/PhysicsAnimationLayout.java
index beb1c5f..f3cc514 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/PhysicsAnimationLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/PhysicsAnimationLayout.java
@@ -327,6 +327,12 @@
addViewInternal(child, index, params, false /* isReorder */);
}
+ /** Removes the child view immediately. */
+ public void removeViewNoAnimation(View view) {
+ super.removeView(view);
+ view.setTag(R.id.physics_animator_tag, null);
+ }
+
@Override
public void removeView(View view) {
if (mController != null) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java
index 23f65f9..f729d02 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java
@@ -27,6 +27,7 @@
import com.android.wm.shell.animation.Interpolators;
import com.android.wm.shell.animation.PhysicsAnimator;
+import com.android.wm.shell.bubbles.BubbleOverflow;
import com.android.wm.shell.bubbles.BubblePositioner;
import com.android.wm.shell.bubbles.BubbleViewProvider;
import com.android.wm.shell.bubbles.animation.AnimatableScaleMatrix;
@@ -215,9 +216,10 @@
}
BubbleBarExpandedView bbev = mExpandedBubble.getBubbleBarExpandedView();
+ boolean isOverflowExpanded = mExpandedBubble.getKey().equals(BubbleOverflow.KEY);
final int padding = mPositioner.getBubbleBarExpandedViewPadding();
- final int width = mPositioner.getExpandedViewWidthForBubbleBar();
- final int height = mPositioner.getExpandedViewHeightForBubbleBar();
+ final int width = mPositioner.getExpandedViewWidthForBubbleBar(isOverflowExpanded);
+ final int height = mPositioner.getExpandedViewHeightForBubbleBar(isOverflowExpanded);
FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) bbev.getLayoutParams();
lp.width = width;
lp.height = height;
@@ -227,7 +229,8 @@
} else {
bbev.setX(mPositioner.getAvailableRect().width() - width - padding);
}
- bbev.setY(mPositioner.getInsets().top + padding);
+ bbev.setY(mPositioner.getExpandedViewBottomForBubbleBar() - height);
bbev.updateLocation();
+ bbev.maybeShowOverflow();
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java
index b8f049b..396aa0e 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,12 +16,16 @@
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.LayoutInflater;
import android.view.View;
import android.view.ViewOutlineProvider;
import android.widget.FrameLayout;
@@ -30,9 +34,14 @@
import com.android.wm.shell.R;
import com.android.wm.shell.bubbles.Bubble;
import com.android.wm.shell.bubbles.BubbleController;
+import com.android.wm.shell.bubbles.BubbleOverflowContainerView;
import com.android.wm.shell.bubbles.BubbleTaskViewHelper;
+import com.android.wm.shell.bubbles.Bubbles;
import com.android.wm.shell.taskview.TaskView;
+import java.util.function.Consumer;
+import java.util.function.Supplier;
+
/**
* Expanded view of a bubble when it's part of the bubble bar.
*
@@ -44,12 +53,17 @@
private static final int INVALID_TASK_ID = -1;
private BubbleController mController;
+ private boolean mIsOverflow;
private BubbleTaskViewHelper mBubbleTaskViewHelper;
+ private BubbleBarMenuViewController mMenuViewController;
+ private @Nullable Supplier<Rect> mLayerBoundsSupplier;
+ private @Nullable Consumer<String> mUnBubbleConversationCallback;
- private HandleView mMenuView;
- private TaskView mTaskView;
+ private BubbleBarHandleView mHandleView = new BubbleBarHandleView(getContext());
+ private @Nullable TaskView mTaskView;
+ private @Nullable BubbleOverflowContainerView mOverflowView;
- private int mMenuHeight;
+ private int mHandleHeight;
private int mBackgroundColor;
private float mCornerRadius = 0f;
@@ -83,11 +97,9 @@
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,23 +110,71 @@
});
}
+ @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) {
+ public void initialize(BubbleController controller, boolean isOverflow) {
mController = controller;
- mBubbleTaskViewHelper = new BubbleTaskViewHelper(mContext, mController,
- /* listener= */ this,
- /* viewParent= */ this);
- mTaskView = mBubbleTaskViewHelper.getTaskView();
- addView(mTaskView);
- mTaskView.setEnableSurfaceClipping(true);
- mTaskView.setCornerRadius(mCornerRadius);
+ mIsOverflow = isOverflow;
+
+ if (mIsOverflow) {
+ mOverflowView = (BubbleOverflowContainerView) LayoutInflater.from(getContext()).inflate(
+ R.layout.bubble_overflow_container, null /* root */);
+ mOverflowView.setBubbleController(mController);
+ addView(mOverflowView);
+ } else {
+
+ mBubbleTaskViewHelper = new BubbleTaskViewHelper(mContext, mController,
+ /* listener= */ this,
+ /* viewParent= */ this);
+ mTaskView = mBubbleTaskViewHelper.getTaskView();
+ addView(mTaskView);
+ mTaskView.setEnableSurfaceClipping(true);
+ mTaskView.setCornerRadius(mCornerRadius);
+ }
+ mMenuViewController = new BubbleBarMenuViewController(mContext, this);
+ mMenuViewController.setListener(new BubbleBarMenuViewController.Listener() {
+ @Override
+ 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() {
+ /** Updates the view based on the current theme. */
+ public 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 +183,12 @@
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 +196,8 @@
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 +209,12 @@
@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 +222,7 @@
@Override
public void onTaskCreated() {
setContentVisibility(true);
- updateMenuColor();
+ updateHandleAndBackgroundColor(false /* animated */);
}
@Override
@@ -187,11 +243,13 @@
}
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,12 +257,33 @@
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.
*/
public void updateLocation() {
- if (mTaskView == null) return;
- mTaskView.onLocationChanged();
+ if (mTaskView != null) {
+ mTaskView.onLocationChanged();
+ }
+ }
+
+ /** Shows the expanded view for the overflow if it exists. */
+ void maybeShowOverflow() {
+ if (mOverflowView != null) {
+ // post this to the looper so that the view has a chance to be laid out before it can
+ // calculate row and column sizes correctly.
+ post(() -> mOverflowView.show());
+ }
}
/** Sets the alpha of the task view. */
@@ -218,16 +297,33 @@
}
}
- /** 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 0000000..ce26bc0
--- /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 b1a725b..d20b33e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java
@@ -29,9 +29,12 @@
import android.widget.FrameLayout;
import com.android.wm.shell.bubbles.BubbleController;
+import com.android.wm.shell.bubbles.BubbleOverflow;
import com.android.wm.shell.bubbles.BubblePositioner;
import com.android.wm.shell.bubbles.BubbleViewProvider;
+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 +56,7 @@
@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. */
@@ -143,9 +147,18 @@
if (mExpandedView == null) {
mExpandedBubble = b;
mExpandedView = expandedView;
- final int width = mPositioner.getExpandedViewWidthForBubbleBar();
- final int height = mPositioner.getExpandedViewHeightForBubbleBar();
+ boolean isOverflowExpanded = b.getKey().equals(BubbleOverflow.KEY);
+ final int width = mPositioner.getExpandedViewWidthForBubbleBar(isOverflowExpanded);
+ final int height = mPositioner.getExpandedViewHeightForBubbleBar(isOverflowExpanded);
mExpandedView.setVisibility(GONE);
+ mExpandedView.setUnBubbleConversationCallback(mUnBubbleConversationCallback);
+ mExpandedView.setLayerBoundsSupplier(() -> new Rect(0, 0, getWidth(), getHeight()));
+ mExpandedView.setUnBubbleConversationCallback(bubbleKey -> {
+ if (mUnBubbleConversationCallback != null) {
+ mUnBubbleConversationCallback.accept(bubbleKey);
+ }
+ });
+ mExpandedView.setY(mPositioner.getExpandedViewBottomForBubbleBar() - height);
addView(mExpandedView, new FrameLayout.LayoutParams(width, height));
}
@@ -165,12 +178,19 @@
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;
+ boolean isOverflowExpanded = mExpandedBubble.getKey().equals(BubbleOverflow.KEY);
final int padding = mPositioner.getBubbleBarExpandedViewPadding();
- final int width = mPositioner.getExpandedViewWidthForBubbleBar();
- final int height = mPositioner.getExpandedViewHeightForBubbleBar();
+ final int width = mPositioner.getExpandedViewWidthForBubbleBar(isOverflowExpanded);
+ final int height = mPositioner.getExpandedViewHeightForBubbleBar(isOverflowExpanded);
FrameLayout.LayoutParams lp = (LayoutParams) mExpandedView.getLayoutParams();
lp.width = width;
lp.height = height;
@@ -180,7 +200,7 @@
} else {
mExpandedView.setX(mPositioner.getAvailableRect().width() - width - padding);
}
- mExpandedView.setY(mPositioner.getInsets().top + padding);
+ mExpandedView.setY(mPositioner.getExpandedViewBottomForBubbleBar() - height);
mExpandedView.updateLocation();
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuItemView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuItemView.java
new file mode 100644
index 0000000..00b9777
--- /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 0000000..211fe0d
--- /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 0000000..8be140c
--- /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 9ee8a9d..0000000
--- 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/bubbles/properties/BubbleProperties.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/properties/BubbleProperties.kt
new file mode 100644
index 0000000..85aaa8e
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/properties/BubbleProperties.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.bubbles.properties
+
+/**
+ * An interface for exposing bubble properties via flags which can be controlled easily in tests.
+ */
+interface BubbleProperties {
+ /**
+ * Whether bubble bar is enabled.
+ *
+ * When this is `true`, depending on additional factors, such as screen size and taskbar state,
+ * bubbles will be displayed in the bubble bar instead of floating.
+ *
+ * When this is `false`, bubbles will be floating.
+ */
+ val isBubbleBarEnabled: Boolean
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/properties/ProdBubbleProperties.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/properties/ProdBubbleProperties.kt
new file mode 100644
index 0000000..9d8b9a6
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/properties/ProdBubbleProperties.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.bubbles.properties
+
+import android.os.SystemProperties
+
+/** Provides bubble properties in production. */
+object ProdBubbleProperties : BubbleProperties {
+
+ // TODO(b/256873975) Should use proper flag when available to shell/launcher
+ override val isBubbleBarEnabled =
+ SystemProperties.getBoolean("persist.wm.debug.bubble_bar", false)
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DismissCircleView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DismissCircleView.java
deleted file mode 100644
index e0c782d..0000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DismissCircleView.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.common;
-
-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;
-
-/**
- * Circular view with a semitransparent, circular background with an 'X' inside it.
- *
- * This is used by both Bubbles and PIP as the dismiss target.
- */
-public class DismissCircleView extends FrameLayout {
-
- 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));
- 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);
- mIconView.setLayoutParams(
- new FrameLayout.LayoutParams(iconSize, iconSize, Gravity.CENTER));
- }
-}
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 0000000..81592c3
--- /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/OWNERS b/libs/WindowManager/Shell/src/com/android/wm/shell/common/OWNERS
new file mode 100644
index 0000000..7af0389
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/OWNERS
@@ -0,0 +1 @@
+madym@google.com
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/DismissCircleView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/DismissCircleView.java
new file mode 100644
index 0000000..7c5bb21
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/DismissCircleView.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.common.bubbles;
+
+import android.content.Context;
+import android.content.res.Configuration;
+import android.view.Gravity;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+
+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.
+ *
+ * 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);
+ addView(mIconView);
+ }
+
+ @Override
+ protected void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ 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 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/common/bubbles/DismissView.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/DismissView.kt
new file mode 100644
index 0000000..d275a0b
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/DismissView.kt
@@ -0,0 +1,222 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.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.animation.PhysicsAnimator
+
+/**
+ * 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 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: GradientDrawable? = null
+
+ private val GRADIENT_ALPHA: IntProperty<GradientDrawable> =
+ object : IntProperty<GradientDrawable>("alpha") {
+ override fun setValue(d: GradientDrawable, percent: Int) {
+ d.alpha = percent
+ }
+ override fun get(d: GradientDrawable): Int {
+ return d.alpha
+ }
+ }
+
+ init {
+ 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)
+
+ // 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()
+ }
+
+ /**
+ * Animates this view in.
+ */
+ fun show() {
+ if (isShowing) return
+ val gradientDrawable = checkExists(gradientDrawable) ?: return
+ isShowing = true
+ setVisibility(View.VISIBLE)
+ val alphaAnim = ObjectAnimator.ofInt(gradientDrawable, GRADIENT_ALPHA,
+ gradientDrawable.alpha, 255)
+ alphaAnim.setDuration(DISMISS_SCRIM_FADE_MS)
+ alphaAnim.start()
+
+ animator.cancel()
+ animator
+ .spring(DynamicAnimation.TRANSLATION_Y, 0f, spring)
+ .start()
+ }
+
+ /**
+ * Animates this view out, as well as the circle that encircles the bubbles, if they
+ * were dragged into the target and encircled.
+ */
+ fun hide() {
+ if (!isShowing) return
+ val gradientDrawable = checkExists(gradientDrawable) ?: return
+ isShowing = false
+ val alphaAnim = ObjectAnimator.ofInt(gradientDrawable, GRADIENT_ALPHA,
+ gradientDrawable.alpha, 0)
+ alphaAnim.setDuration(DISMISS_SCRIM_FADE_MS)
+ alphaAnim.start()
+ animator
+ .spring(DynamicAnimation.TRANSLATION_Y, height.toFloat(),
+ spring)
+ .withEndActions({ setVisibility(View.INVISIBLE) })
+ .start()
+ }
+
+ /**
+ * Cancels the animator for the dismiss target.
+ */
+ fun cancelAnimators() {
+ animator.cancel()
+ }
+
+ fun updateResources() {
+ val config = checkExists(config) ?: return
+ updatePadding()
+ 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(@ColorRes color: Int): GradientDrawable {
+ val gradientColor = ContextCompat.getColor(context, color)
+ val alpha = 0.7f * 255
+ val gradientColorWithAlpha = Color.argb(alpha.toInt(),
+ Color.red(gradientColor),
+ Color.green(gradientColor),
+ Color.blue(gradientColor))
+ val gd = GradientDrawable(
+ GradientDrawable.Orientation.BOTTOM_TOP,
+ intArrayOf(gradientColorWithAlpha, Color.TRANSPARENT))
+ gd.setDither(true)
+ gd.setAlpha(0)
+ return gd
+ }
+
+ 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(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
similarity index 98%
rename from libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/RelativeTouchListener.kt
rename to libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/RelativeTouchListener.kt
index ea9d065..cc37bd3a4 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 c76937d..ec26800 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 @@
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 @@
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 @@
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 69f0bad..262d487 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.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 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 @@
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 @@
}
@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 @@
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 12d51f5..47d58af 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.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 @@
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 c491fed..34a6e0a 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.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 @@
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 cff3172..20c3bd2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -36,17 +36,21 @@
import com.android.wm.shell.bubbles.BubbleDataRepository;
import com.android.wm.shell.bubbles.BubbleLogger;
import com.android.wm.shell.bubbles.BubblePositioner;
+import com.android.wm.shell.bubbles.properties.ProdBubbleProperties;
+import com.android.wm.shell.bubbles.storage.BubblePersistentRepository;
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.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 +59,7 @@
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;
@@ -180,11 +185,12 @@
IWindowManager wmService) {
return new BubbleController(context, shellInit, shellCommandHandler, shellController, data,
null /* synchronizer */, floatingContentCoordinator,
- new BubbleDataRepository(context, launcherApps, mainExecutor),
+ new BubbleDataRepository(launcherApps, mainExecutor,
+ new BubblePersistentRepository(context)),
statusBarService, windowManager, windowManagerShellWrapper, userManager,
launcherApps, logger, taskStackListener, organizer, positioner, displayController,
oneHandedOptional, dragAndDropController, mainExecutor, mainHandler, bgExecutor,
- taskViewTransitions, syncQueue, wmService);
+ taskViewTransitions, syncQueue, wmService, ProdBubbleProperties.INSTANCE);
}
//
@@ -202,8 +208,7 @@
SyncTransactionQueue syncQueue,
Transitions transitions,
Optional<DesktopModeController> desktopModeController,
- Optional<DesktopTasksController> desktopTasksController,
- Optional<SplitScreenController> splitScreenController) {
+ Optional<DesktopTasksController> desktopTasksController) {
if (DesktopModeStatus.isAnyEnabled()) {
return new DesktopModeWindowDecorViewModel(
context,
@@ -214,16 +219,15 @@
syncQueue,
transitions,
desktopModeController,
- desktopTasksController,
- splitScreenController);
+ desktopTasksController);
}
return new CaptionWindowDecorViewModel(
- context,
- mainHandler,
- mainChoreographer,
- taskOrganizer,
- displayController,
- syncQueue);
+ context,
+ mainHandler,
+ mainChoreographer,
+ taskOrganizer,
+ displayController,
+ syncQueue);
}
//
@@ -263,8 +267,13 @@
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 +336,14 @@
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 +547,13 @@
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
@@ -573,13 +587,13 @@
animators.add(fullscreenAnimator);
return new UnfoldAnimationController(
- shellInit,
- transactionPool,
- progressProvider.get(),
- animators,
- unfoldTransitionHandler,
- mainExecutor
- );
+ shellInit,
+ transactionPool,
+ progressProvider.get(),
+ animators,
+ unfoldTransitionHandler,
+ mainExecutor
+ );
}
@Provides
@@ -671,6 +685,7 @@
static DesktopTasksController provideDesktopTasksController(
Context context,
ShellInit shellInit,
+ ShellCommandHandler shellCommandHandler,
ShellController shellController,
DisplayController displayController,
ShellTaskOrganizer shellTaskOrganizer,
@@ -679,13 +694,16 @@
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 +715,13 @@
@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 b9d2be2..db6c258 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 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 @@
}
/**
+ * 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 @@
}
@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 @@
);
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 3ab175d..711df0d 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.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 @@
*/
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 @@
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 @@
}
/**
+ * 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 @@
*/
@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 91bb155..65a35b2 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,19 +25,21 @@
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.SurfaceControl.Transaction
import android.view.WindowManager.TRANSIT_CHANGE
import android.view.WindowManager.TRANSIT_NONE
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 +47,23 @@
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 +71,7 @@
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 +80,10 @@
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 +94,11 @@
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 +109,14 @@
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 +137,58 @@
}
}
+ /**
+ * 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 +208,7 @@
)
val wct = WindowContainerTransaction()
moveHomeTaskToFront(wct)
- addMoveToDesktopChanges(wct, taskInfo.getToken())
+ addMoveToDesktopChanges(wct, taskInfo)
wct.setBounds(taskInfo.token, startBounds)
if (Transitions.ENABLE_SHELL_TRANSITIONS) {
@@ -178,7 +228,7 @@
)
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 +254,7 @@
)
val wct = WindowContainerTransaction()
- addMoveToFullscreenChanges(wct, task.token)
+ addMoveToFullscreenChanges(wct, task)
if (Transitions.ENABLE_SHELL_TRANSITIONS) {
transitions.startTransition(TRANSIT_CHANGE, wct, null /* handler */)
} else {
@@ -218,16 +268,24 @@
*/
fun cancelMoveToFreeform(task: RunningTaskInfo, position: Point) {
KtProtoLog.v(
- WM_SHELL_DESKTOP_MODE,
- "DesktopTasksController: cancelMoveToFreeform taskId=%d",
- task.taskId
+ WM_SHELL_DESKTOP_MODE,
+ "DesktopTasksController: cancelMoveToFreeform taskId=%d",
+ task.taskId
)
val wct = WindowContainerTransaction()
- addMoveToFullscreenChanges(wct, task.token)
+ wct.setBounds(task.token, null)
+
if (Transitions.ENABLE_SHELL_TRANSITIONS) {
- enterDesktopTaskTransitionHandler.startCancelMoveToDesktopMode(wct, position,
- mOnAnimationFinishedCallback)
+ enterDesktopTaskTransitionHandler.startCancelMoveToDesktopMode(
+ wct, position) { t ->
+ val callbackWCT = WindowContainerTransaction()
+ visualIndicator?.releaseVisualIndicator(t)
+ visualIndicator = null
+ addMoveToFullscreenChanges(callbackWCT, task)
+ shellTaskOrganizer.applyTransaction(callbackWCT)
+ }
} else {
+ addMoveToFullscreenChanges(wct, task)
shellTaskOrganizer.applyTransaction(wct)
releaseVisualIndicator()
}
@@ -240,7 +298,7 @@
task.taskId
)
val wct = WindowContainerTransaction()
- addMoveToFullscreenChanges(wct, task.token)
+ addMoveToFullscreenChanges(wct, task)
if (Transitions.ENABLE_SHELL_TRANSITIONS) {
exitDesktopTaskTransitionHandler.startTransition(
@@ -252,6 +310,11 @@
}
/** 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 +394,49 @@
}
}
+ /** 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 +503,169 @@
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(
- WM_SHELL_DESKTOP_MODE,
- "DesktopTasksController: switch fullscreen task to freeform on transition" +
- " taskId=%d",
- task.taskId
- )
- return WindowContainerTransaction().also { wct ->
- addMoveToDesktopChanges(wct, task.token)
- }
+ 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
+ }
- // 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(
+ /**
+ * 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 freeform task to fullscreen oon transition" +
- " taskId=%d",
+ " taskId=%d",
task.taskId
- )
- return WindowContainerTransaction().also { wct ->
- addMoveToFullscreenChanges(wct, task.token)
- }
+ )
+ return WindowContainerTransaction().also { wct ->
+ addMoveToFullscreenChanges(wct, task)
}
}
return null
}
+ 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 fullscreen task to freeform on transition" +
+ " taskId=%d",
+ task.taskId
+ )
+ 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 +716,19 @@
* 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 +829,12 @@
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 +861,51 @@
@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 +916,27 @@
) { 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 +947,33 @@
)
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/EnterDesktopTaskTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/EnterDesktopTaskTransitionHandler.java
index 3733b91..3e175f3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/EnterDesktopTaskTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/EnterDesktopTaskTransitionHandler.java
@@ -17,7 +17,6 @@
package com.android.wm.shell.desktopmode;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -200,7 +199,7 @@
}
if (type == Transitions.TRANSIT_CANCEL_ENTERING_DESKTOP_MODE
- && taskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN
+ && taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM
&& mPosition != null) {
// This Transition animates a task to fullscreen after being dragged from the status
// bar and then released back into the status bar area
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl
index 899d672..ee3a080 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 @@
/** 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 0000000..39128a8
--- /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 0000000..94788e4
--- /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 99922fb..f9ea1d4 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 @@
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 04fc79a..55e34fe 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 @@
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 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.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 @@
@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 @@
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 @@
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 9729a40..da455f8 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 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 @@
}
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 2a61445..b0fa993 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 @@
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 89538cb..e52235f 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 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 @@
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 8639b36..9903113 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 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 @@
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 e7a367f..e294229 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.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.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 @@
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 @@
TransactionPool transactionPool,
IconProvider iconProvider,
Optional<RecentTasksController> recentTasks,
+ LaunchAdjacentController launchAdjacentController,
+ Optional<WindowDecorViewModel> windowDecorViewModel,
ShellExecutor mainExecutor) {
mShellCommandHandler = shellCommandHandler;
mShellController = shellController;
@@ -213,6 +219,8 @@
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 @@
TransactionPool transactionPool,
IconProvider iconProvider,
RecentTasksController recentTasks,
+ LaunchAdjacentController launchAdjacentController,
+ WindowDecorViewModel windowDecorViewModel,
ShellExecutor mainExecutor,
StageCoordinator stageCoordinator) {
mShellCommandHandler = shellCommandHandler;
@@ -259,6 +269,8 @@
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 @@
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 01d8967..acc1c5e 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.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.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 @@
// 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 @@
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 @@
mLogger = new SplitscreenEventLogger();
mMainExecutor = mainExecutor;
mRecentTasks = recentTasks;
+ mLaunchAdjacentController = launchAdjacentController;
+ mWindowDecorViewModel = windowDecorViewModel;
taskOrganizer.createRootTask(displayId, WINDOWING_MODE_FULLSCREEN, this /* listener */);
@@ -288,7 +296,8 @@
mMainStageListener,
mSyncQueue,
mSurfaceSession,
- iconProvider);
+ iconProvider,
+ mWindowDecorViewModel);
mSideStage = new SideStage(
mContext,
mTaskOrganizer,
@@ -296,7 +305,8 @@
mSideStageListener,
mSyncQueue,
mSurfaceSession,
- iconProvider);
+ iconProvider,
+ mWindowDecorViewModel);
mDisplayController = displayController;
mDisplayImeController = displayImeController;
mDisplayInsetsController = displayInsetsController;
@@ -323,7 +333,9 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
}
}
+
+ 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 a01eddb..3ef4f02 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.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 @@
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 @@
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 @@
@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 27d520d..a2301b1 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.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 @@
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 @@
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 @@
mTransactionPool = transactionPool;
mIconProvider = iconProvider;
mRecentTasksOptional = recentTasks;
+ mLaunchAdjacentController = launchAdjacentController;
mMainHandler = mainHandler;
mSystemWindows = systemWindows;
@@ -111,7 +116,7 @@
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 4d563fb..7947691 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.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 @@
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 e83780a..a28ce55 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 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 @@
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 @@
/** 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 @@
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 @@
if (mRecentsHandler != null) {
mRecentsHandler.addMixer(this);
}
+ mDesktopModeController = desktopModeControllerOptional.orElse(null);
+ mDesktopTasksController = desktopTasksControllerOptional.orElse(null);
mUnfoldHandler = unfoldHandler.orElse(null);
}, this);
}
@@ -238,7 +250,8 @@
@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 @@
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 @@
} 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 @@
}
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 @@
}
/**
+ * 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 @@
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 @@
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 @@
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 @@
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 2327d86..d8a8877 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 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 f81fc6f..6bba0d1 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 @@
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 a4cf149..21994a9 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.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 @@
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 39fb793..92c2a7c 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.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 @@
}
@Override
+ public void setSplitScreenController(SplitScreenController splitScreenController) {}
+
+ @Override
public boolean onTaskOpening(
RunningTaskInfo taskInfo,
SurfaceControl taskSurface,
@@ -254,7 +260,7 @@
* @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 @@
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 @@
}
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 116af70..b217bd3 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 @@
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 @@
mHandler,
mChoreographer,
mDisplay.getDisplayId(),
+ 0 /* taskCornerRadius */,
mDecorationContainerSurface,
mDragPositioningCallback);
}
@@ -221,4 +222,9 @@
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 9fd57d7..331835c 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 @@
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.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.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 @@
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 @@
SyncTransactionQueue syncQueue,
Transitions transitions,
Optional<DesktopModeController> desktopModeController,
- Optional<DesktopTasksController> desktopTasksController,
- Optional<SplitScreenController> splitScreenController) {
+ Optional<DesktopTasksController> desktopTasksController) {
this(
context,
mainHandler,
@@ -133,7 +136,6 @@
transitions,
desktopModeController,
desktopTasksController,
- splitScreenController,
new DesktopModeWindowDecoration.Factory(),
new InputMonitorFactory(),
SurfaceControl.Transaction::new);
@@ -150,7 +152,6 @@
Transitions transitions,
Optional<DesktopModeController> desktopModeController,
Optional<DesktopTasksController> desktopTasksController,
- Optional<SplitScreenController> splitScreenController,
DesktopModeWindowDecoration.Factory desktopModeWindowDecorFactory,
InputMonitorFactory inputMonitorFactory,
Supplier<SurfaceControl.Transaction> transactionFactory) {
@@ -160,7 +161,6 @@
mActivityTaskManager = mContext.getSystemService(ActivityTaskManager.class);
mTaskOrganizer = taskOrganizer;
mDisplayController = displayController;
- mSplitScreenController = splitScreenController;
mSyncQueue = syncQueue;
mTransitions = transitions;
mDesktopModeController = desktopModeController;
@@ -177,6 +177,11 @@
}
@Override
+ public void setSplitScreenController(SplitScreenController splitScreenController) {
+ mSplitScreenController = splitScreenController;
+ }
+
+ @Override
public boolean onTaskOpening(
ActivityManager.RunningTaskInfo taskInfo,
SurfaceControl taskSurface,
@@ -193,7 +198,10 @@
@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 @@
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 @@
}
}
- 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 @@
mTaskToken = taskInfo.token;
mDragPositioningCallback = dragPositioningCallback;
mDragDetector = new DragDetector(this);
+ mGestureDetector = new GestureDetector(mContext, this);
}
@Override
@@ -301,14 +311,14 @@
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 @@
}
} 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 @@
return false;
}
moveTaskToFront(mTaskOrganizer.getRunningTaskInfo(mTaskId));
- return mDragDetector.onMotionEvent(e);
+ return mDragDetector.onMotionEvent(v, e);
}
private void moveTaskToFront(RunningTaskInfo taskInfo) {
@@ -362,7 +378,7 @@
* @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 @@
== 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 @@
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 @@
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 @@
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 @@
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 @@
@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 @@
@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 @@
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 @@
mMainChoreographer,
mSyncQueue);
mWindowDecorByTaskId.put(taskInfo.taskId, windowDecoration);
+ windowDecoration.createResizeVeil();
final DragPositioningCallback dragPositioningCallback = createDragPositioningCallback(
windowDecoration, taskInfo);
@@ -814,14 +861,18 @@
@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 ce11b26..bc89385 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.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.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 @@
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 @@
mHandler,
mChoreographer,
mDisplay.getDisplayId(),
+ mRelayoutParams.mCornerRadius,
mDecorationContainerSurface,
mDragPositioningCallback);
}
@@ -294,23 +292,37 @@
}
/**
- * 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 @@
}
}
+ @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 65b5a7a..da26898 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 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 @@
*
* @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 @@
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 @@
// 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 @@
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 @@
}
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 287d861..e5fc66a 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.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 @@
*/
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 @@
private int mTaskHeight;
private int mResizeHandleThickness;
private int mCornerSize;
+ private int mTaskCornerRadius;
private Rect mLeftTopCornerBounds;
private Rect mRightTopCornerBounds;
@@ -87,12 +88,14 @@
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 @@
}
/**
- * 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 @@
}
@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 @@
@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 8277109..bfce72b 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 @@
}
/**
- * 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 @@
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 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 58c78e6..39b9021 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 @@
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 @@
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 9f03d9a..ae1a3d9 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.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 @@
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 ac5ff20..ddc7fef 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 @@
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 @@
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 @@
.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 @@
}
}
+ 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 @@
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 @@
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 514ea52..d293cf7 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 b6696c7..e382a0f 100644
--- a/libs/WindowManager/Shell/tests/flicker/Android.bp
+++ b/libs/WindowManager/Shell/tests/flicker/Android.bp
@@ -23,14 +23,33 @@
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 @@
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 b5937ae..0000000
--- 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 0000000..991d7b5
--- /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
similarity index 90%
rename from libs/WindowManager/Shell/tests/flicker/AndroidManifest.xml
rename to libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifest.xml
index 4721741..6a87de4 100644
--- a/libs/WindowManager/Shell/tests/flicker/AndroidManifest.xml
+++ b/libs/WindowManager/Shell/tests/flicker/manifests/AndroidManifest.xml
@@ -15,6 +15,7 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
package="com.android.wm.shell.flicker">
<uses-sdk android:minSdkVersion="29" android:targetSdkVersion="29"/>
@@ -57,10 +58,11 @@
<action android:name="android.service.notification.NotificationListenerService" />
</intent-filter>
</service>
- </application>
- <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
- android:targetPackage="com.android.wm.shell.flicker"
- android:label="WindowManager Shell Flicker Tests">
- </instrumentation>
+ <!-- (b/197936012) Remove startup provider due to test timeout issue -->
+ <provider
+ android:name="androidx.startup.InitializationProvider"
+ android:authorities="${applicationId}.androidx-startup"
+ tools:node="remove" />
+ </application>
</manifest>
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 0000000..437871f
--- /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 0000000..cf642f6
--- /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 0000000..5a8155a
--- /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 0000000..887d8db
--- /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/BaseBenchmarkTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/BaseBenchmarkTest.kt
index e06e074..0f3e0f5 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/BaseBenchmarkTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/BaseBenchmarkTest.kt
@@ -19,14 +19,14 @@
import android.app.Instrumentation
import android.tools.device.flicker.junit.FlickerBuilderProvider
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTest
import androidx.test.platform.app.InstrumentationRegistry
import com.android.launcher3.tapl.LauncherInstrumentation
abstract class BaseBenchmarkTest
@JvmOverloads
constructor(
- protected open val flicker: FlickerTest,
+ protected open val flicker: LegacyFlickerTest,
protected val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation(),
protected val tapl: LauncherInstrumentation = LauncherInstrumentation()
) {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/BaseTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/BaseTest.kt
index c98c5a0..d2fe9fe 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/BaseTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/BaseTest.kt
@@ -18,7 +18,7 @@
import android.app.Instrumentation
import android.tools.common.traces.component.ComponentNameMatcher
-import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTest
import androidx.test.platform.app.InstrumentationRegistry
import com.android.launcher3.tapl.LauncherInstrumentation
@@ -30,7 +30,7 @@
abstract class BaseTest
@JvmOverloads
constructor(
- override val flicker: FlickerTest,
+ override val flicker: LegacyFlickerTest,
instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation(),
tapl: LauncherInstrumentation = LauncherInstrumentation()
) : BaseBenchmarkTest(flicker, instrumentation, tapl), ICommonAssertions
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt
index 798cc95..9cc03a5 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt
@@ -23,18 +23,18 @@
import android.tools.common.flicker.subject.layers.LayerTraceEntrySubject
import android.tools.common.flicker.subject.layers.LayersTraceSubject
import android.tools.common.traces.component.IComponentMatcher
-import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.helpers.WindowUtils
-fun FlickerTest.appPairsDividerIsVisibleAtEnd() {
+fun LegacyFlickerTest.appPairsDividerIsVisibleAtEnd() {
assertLayersEnd { this.isVisible(APP_PAIR_SPLIT_DIVIDER_COMPONENT) }
}
-fun FlickerTest.appPairsDividerIsInvisibleAtEnd() {
+fun LegacyFlickerTest.appPairsDividerIsInvisibleAtEnd() {
assertLayersEnd { this.notContains(APP_PAIR_SPLIT_DIVIDER_COMPONENT) }
}
-fun FlickerTest.appPairsDividerBecomesVisible() {
+fun LegacyFlickerTest.appPairsDividerBecomesVisible() {
assertLayers {
this.isInvisible(DOCKED_STACK_DIVIDER_COMPONENT)
.then()
@@ -42,7 +42,7 @@
}
}
-fun FlickerTest.splitScreenEntered(
+fun LegacyFlickerTest.splitScreenEntered(
component1: IComponentMatcher,
component2: IComponentMatcher,
fromOtherApp: Boolean,
@@ -69,7 +69,7 @@
splitScreenDividerIsVisibleAtEnd()
}
-fun FlickerTest.splitScreenDismissed(
+fun LegacyFlickerTest.splitScreenDismissed(
component1: IComponentMatcher,
component2: IComponentMatcher,
toHome: Boolean
@@ -87,27 +87,27 @@
splitScreenDividerIsInvisibleAtEnd()
}
-fun FlickerTest.splitScreenDividerIsVisibleAtStart() {
+fun LegacyFlickerTest.splitScreenDividerIsVisibleAtStart() {
assertLayersStart { this.isVisible(SPLIT_SCREEN_DIVIDER_COMPONENT) }
}
-fun FlickerTest.splitScreenDividerIsVisibleAtEnd() {
+fun LegacyFlickerTest.splitScreenDividerIsVisibleAtEnd() {
assertLayersEnd { this.isVisible(SPLIT_SCREEN_DIVIDER_COMPONENT) }
}
-fun FlickerTest.splitScreenDividerIsInvisibleAtStart() {
+fun LegacyFlickerTest.splitScreenDividerIsInvisibleAtStart() {
assertLayersStart { this.isInvisible(SPLIT_SCREEN_DIVIDER_COMPONENT) }
}
-fun FlickerTest.splitScreenDividerIsInvisibleAtEnd() {
+fun LegacyFlickerTest.splitScreenDividerIsInvisibleAtEnd() {
assertLayersEnd { this.isInvisible(SPLIT_SCREEN_DIVIDER_COMPONENT) }
}
-fun FlickerTest.splitScreenDividerBecomesVisible() {
+fun LegacyFlickerTest.splitScreenDividerBecomesVisible() {
layerBecomesVisible(SPLIT_SCREEN_DIVIDER_COMPONENT)
}
-fun FlickerTest.splitScreenDividerBecomesInvisible() {
+fun LegacyFlickerTest.splitScreenDividerBecomesInvisible() {
assertLayers {
this.isVisible(SPLIT_SCREEN_DIVIDER_COMPONENT)
.then()
@@ -115,23 +115,23 @@
}
}
-fun FlickerTest.layerBecomesVisible(component: IComponentMatcher) {
+fun LegacyFlickerTest.layerBecomesVisible(component: IComponentMatcher) {
assertLayers { this.isInvisible(component).then().isVisible(component) }
}
-fun FlickerTest.layerBecomesInvisible(component: IComponentMatcher) {
+fun LegacyFlickerTest.layerBecomesInvisible(component: IComponentMatcher) {
assertLayers { this.isVisible(component).then().isInvisible(component) }
}
-fun FlickerTest.layerIsVisibleAtEnd(component: IComponentMatcher) {
+fun LegacyFlickerTest.layerIsVisibleAtEnd(component: IComponentMatcher) {
assertLayersEnd { this.isVisible(component) }
}
-fun FlickerTest.layerKeepVisible(component: IComponentMatcher) {
+fun LegacyFlickerTest.layerKeepVisible(component: IComponentMatcher) {
assertLayers { this.isVisible(component) }
}
-fun FlickerTest.splitAppLayerBoundsBecomesVisible(
+fun LegacyFlickerTest.splitAppLayerBoundsBecomesVisible(
component: IComponentMatcher,
landscapePosLeft: Boolean,
portraitPosTop: Boolean
@@ -150,7 +150,7 @@
}
}
-fun FlickerTest.splitAppLayerBoundsBecomesVisibleByDrag(component: IComponentMatcher) {
+fun LegacyFlickerTest.splitAppLayerBoundsBecomesVisibleByDrag(component: IComponentMatcher) {
assertLayers {
this.notContains(SPLIT_SCREEN_DIVIDER_COMPONENT.or(component), isOptional = true)
.then()
@@ -161,7 +161,7 @@
}
}
-fun FlickerTest.splitAppLayerBoundsBecomesInvisible(
+fun LegacyFlickerTest.splitAppLayerBoundsBecomesInvisible(
component: IComponentMatcher,
landscapePosLeft: Boolean,
portraitPosTop: Boolean
@@ -180,7 +180,7 @@
}
}
-fun FlickerTest.splitAppLayerBoundsIsVisibleAtEnd(
+fun LegacyFlickerTest.splitAppLayerBoundsIsVisibleAtEnd(
component: IComponentMatcher,
landscapePosLeft: Boolean,
portraitPosTop: Boolean
@@ -195,7 +195,7 @@
}
}
-fun FlickerTest.splitAppLayerBoundsKeepVisible(
+fun LegacyFlickerTest.splitAppLayerBoundsKeepVisible(
component: IComponentMatcher,
landscapePosLeft: Boolean,
portraitPosTop: Boolean
@@ -210,7 +210,7 @@
}
}
-fun FlickerTest.splitAppLayerBoundsChanges(
+fun LegacyFlickerTest.splitAppLayerBoundsChanges(
component: IComponentMatcher,
landscapePosLeft: Boolean,
portraitPosTop: Boolean
@@ -304,7 +304,7 @@
}
}
-fun FlickerTest.appWindowBecomesVisible(component: IComponentMatcher) {
+fun LegacyFlickerTest.appWindowBecomesVisible(component: IComponentMatcher) {
assertWm {
this.isAppWindowInvisible(component)
.then()
@@ -316,39 +316,39 @@
}
}
-fun FlickerTest.appWindowBecomesInvisible(component: IComponentMatcher) {
+fun LegacyFlickerTest.appWindowBecomesInvisible(component: IComponentMatcher) {
assertWm { this.isAppWindowVisible(component).then().isAppWindowInvisible(component) }
}
-fun FlickerTest.appWindowIsVisibleAtStart(component: IComponentMatcher) {
+fun LegacyFlickerTest.appWindowIsVisibleAtStart(component: IComponentMatcher) {
assertWmStart { this.isAppWindowVisible(component) }
}
-fun FlickerTest.appWindowIsVisibleAtEnd(component: IComponentMatcher) {
+fun LegacyFlickerTest.appWindowIsVisibleAtEnd(component: IComponentMatcher) {
assertWmEnd { this.isAppWindowVisible(component) }
}
-fun FlickerTest.appWindowIsInvisibleAtStart(component: IComponentMatcher) {
+fun LegacyFlickerTest.appWindowIsInvisibleAtStart(component: IComponentMatcher) {
assertWmStart { this.isAppWindowInvisible(component) }
}
-fun FlickerTest.appWindowIsInvisibleAtEnd(component: IComponentMatcher) {
+fun LegacyFlickerTest.appWindowIsInvisibleAtEnd(component: IComponentMatcher) {
assertWmEnd { this.isAppWindowInvisible(component) }
}
-fun FlickerTest.appWindowIsNotContainAtStart(component: IComponentMatcher) {
+fun LegacyFlickerTest.appWindowIsNotContainAtStart(component: IComponentMatcher) {
assertWmStart { this.notContains(component) }
}
-fun FlickerTest.appWindowKeepVisible(component: IComponentMatcher) {
+fun LegacyFlickerTest.appWindowKeepVisible(component: IComponentMatcher) {
assertWm { this.isAppWindowVisible(component) }
}
-fun FlickerTest.dockedStackDividerIsVisibleAtEnd() {
+fun LegacyFlickerTest.dockedStackDividerIsVisibleAtEnd() {
assertLayersEnd { this.isVisible(DOCKED_STACK_DIVIDER_COMPONENT) }
}
-fun FlickerTest.dockedStackDividerBecomesVisible() {
+fun LegacyFlickerTest.dockedStackDividerBecomesVisible() {
assertLayers {
this.isInvisible(DOCKED_STACK_DIVIDER_COMPONENT)
.then()
@@ -356,7 +356,7 @@
}
}
-fun FlickerTest.dockedStackDividerBecomesInvisible() {
+fun LegacyFlickerTest.dockedStackDividerBecomesInvisible() {
assertLayers {
this.isVisible(DOCKED_STACK_DIVIDER_COMPONENT)
.then()
@@ -364,11 +364,11 @@
}
}
-fun FlickerTest.dockedStackDividerNotExistsAtEnd() {
+fun LegacyFlickerTest.dockedStackDividerNotExistsAtEnd() {
assertLayersEnd { this.notContains(DOCKED_STACK_DIVIDER_COMPONENT) }
}
-fun FlickerTest.appPairsPrimaryBoundsIsVisibleAtEnd(
+fun LegacyFlickerTest.appPairsPrimaryBoundsIsVisibleAtEnd(
rotation: Rotation,
primaryComponent: IComponentMatcher
) {
@@ -380,7 +380,7 @@
}
}
-fun FlickerTest.dockedStackPrimaryBoundsIsVisibleAtEnd(
+fun LegacyFlickerTest.dockedStackPrimaryBoundsIsVisibleAtEnd(
rotation: Rotation,
primaryComponent: IComponentMatcher
) {
@@ -392,7 +392,7 @@
}
}
-fun FlickerTest.appPairsSecondaryBoundsIsVisibleAtEnd(
+fun LegacyFlickerTest.appPairsSecondaryBoundsIsVisibleAtEnd(
rotation: Rotation,
secondaryComponent: IComponentMatcher
) {
@@ -404,7 +404,7 @@
}
}
-fun FlickerTest.dockedStackSecondaryBoundsIsVisibleAtEnd(
+fun LegacyFlickerTest.dockedStackSecondaryBoundsIsVisibleAtEnd(
rotation: Rotation,
secondaryComponent: IComponentMatcher
) {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/ICommonAssertions.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/ICommonAssertions.kt
index 02d9a056..7b32901 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/ICommonAssertions.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/ICommonAssertions.kt
@@ -18,7 +18,7 @@
import android.platform.test.annotations.Presubmit
import android.tools.common.traces.component.ComponentNameMatcher
-import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTest
import com.android.server.wm.flicker.entireScreenCovered
import com.android.server.wm.flicker.navBarLayerIsVisibleAtStartAndEnd
import com.android.server.wm.flicker.navBarLayerPositionAtStartAndEnd
@@ -32,7 +32,7 @@
import org.junit.Test
interface ICommonAssertions {
- val flicker: FlickerTest
+ val flicker: LegacyFlickerTest
/** Checks that all parts of the screen are covered during the transition */
@Presubmit @Test fun entireScreenCovered() = flicker.entireScreenCovered()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/BaseAppCompat.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/BaseAppCompat.kt
index 6178156..0f9579d 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/BaseAppCompat.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/BaseAppCompat.kt
@@ -20,21 +20,20 @@
import android.system.helpers.CommandsHelper
import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import com.android.server.wm.flicker.helpers.setRotation
+import android.tools.device.flicker.legacy.FlickerTestData
+import android.tools.device.flicker.legacy.LegacyFlickerTest
import com.android.server.wm.flicker.helpers.LetterboxAppHelper
-import android.tools.device.flicker.legacy.FlickerTestFactory
-import android.tools.device.flicker.legacy.IFlickerTestData
+import com.android.server.wm.flicker.helpers.setRotation
import com.android.wm.shell.flicker.BaseTest
-import com.android.wm.shell.flicker.appWindowIsVisibleAtStart
import com.android.wm.shell.flicker.appWindowIsVisibleAtEnd
+import com.android.wm.shell.flicker.appWindowIsVisibleAtStart
+import com.android.wm.shell.flicker.appWindowKeepVisible
import com.android.wm.shell.flicker.layerKeepVisible
import org.junit.After
import org.junit.Assume
import org.junit.Before
-import org.junit.runners.Parameterized
-abstract class BaseAppCompat(flicker: FlickerTest) : BaseTest(flicker) {
+abstract class BaseAppCompat(flicker: LegacyFlickerTest) : BaseTest(flicker) {
protected val context: Context = instrumentation.context
protected val letterboxApp = LetterboxAppHelper(instrumentation)
lateinit var cmdHelper: CommandsHelper
@@ -48,9 +47,7 @@
letterboxApp.launchViaIntent(wmHelper)
setEndRotation()
}
- teardown {
- letterboxApp.exit(wmHelper)
- }
+ teardown { letterboxApp.exit(wmHelper) }
}
@Before
@@ -58,12 +55,13 @@
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 +85,8 @@
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) {
@@ -101,9 +98,9 @@
return res != null && res.contains("true")
}
- fun IFlickerTestData.setStartRotation() = setRotation(flicker.scenario.startRotation)
+ fun FlickerTestData.setStartRotation() = setRotation(flicker.scenario.startRotation)
- fun IFlickerTestData.setEndRotation() = setRotation(flicker.scenario.endRotation)
+ fun FlickerTestData.setEndRotation() = setRotation(flicker.scenario.endRotation)
/** Checks that app entering letterboxed state have rounded corners */
fun assertLetterboxAppAtStartHasRoundedCorners() {
@@ -126,25 +123,21 @@
flicker.appWindowIsVisibleAtEnd(letterboxApp)
}
+ fun assertLetterboxAppKeepVisible() {
+ assertLetterboxAppWindowKeepVisible()
+ assertLetterboxAppLayerKeepVisible()
+ }
+
fun assertAppLetterboxedAtEnd() =
- flicker.assertLayersEnd { isVisible(ComponentNameMatcher.LETTERBOX) }
+ flicker.assertLayersEnd { isVisible(ComponentNameMatcher.LETTERBOX) }
fun assertAppLetterboxedAtStart() =
- flicker.assertLayersStart { isVisible(ComponentNameMatcher.LETTERBOX) }
+ flicker.assertLayersStart { isVisible(ComponentNameMatcher.LETTERBOX) }
+
+ fun assertAppStaysLetterboxed() =
+ flicker.assertLayers { isVisible(ComponentNameMatcher.LETTERBOX) }
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 c2141a3..a7bd258 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/OpenAppInSizeCompatModeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/OpenAppInSizeCompatModeTest.kt
@@ -17,10 +17,12 @@
package com.android.wm.shell.flicker.appcompat
import android.platform.test.annotations.Postsubmit
+import android.tools.common.flicker.assertions.FlickerTest
import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
import org.junit.Test
import org.junit.runner.RunWith
@@ -45,7 +47,7 @@
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-class OpenAppInSizeCompatModeTest(flicker: FlickerTest) : BaseAppCompat(flicker) {
+class OpenAppInSizeCompatModeTest(flicker: LegacyFlickerTest) : BaseAppCompat(flicker) {
/** {@inheritDoc} */
override val transition: FlickerBuilder.() -> Unit
@@ -70,9 +72,7 @@
@Test
fun letterboxedAppHasRoundedCorners() = assertLetterboxAppAtEndHasRoundedCorners()
- @Postsubmit
- @Test
- fun appIsLetterboxedAtEnd() = assertAppLetterboxedAtEnd()
+ @Postsubmit @Test fun appIsLetterboxedAtEnd() = assertAppLetterboxedAtEnd()
/**
* Checks that the [ComponentNameMatcher.ROTATION] layer appears during the transition, doesn't
@@ -90,4 +90,18 @@
.isInvisible(ComponentNameMatcher.ROTATION)
}
}
+
+ companion object {
+ /**
+ * Creates the test configurations.
+ *
+ * See [LegacyFlickerTestFactory.rotationTests] for configuring screen orientation and
+ * navigation modes.
+ */
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<FlickerTest> {
+ return LegacyFlickerTestFactory.rotationTests()
+ }
+ }
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/RepositionFixedPortraitAppTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/RepositionFixedPortraitAppTest.kt
new file mode 100644
index 0000000..e875aae
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/RepositionFixedPortraitAppTest.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.wm.shell.flicker.appcompat
+
+import android.platform.test.annotations.Postsubmit
+import android.tools.common.Rotation
+import android.tools.common.flicker.assertions.FlickerTest
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
+import android.tools.device.helpers.WindowUtils
+import androidx.test.filters.RequiresDevice
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+
+/**
+ * Test launching a fixed portrait letterboxed app in landscape and repositioning to the right.
+ *
+ * To run this test: `atest WMShellFlickerTests:RepositionFixedPortraitAppTest` Actions:
+ *
+ * ```
+ * 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: LegacyFlickerTest) : 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 [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and
+ * navigation modes.
+ */
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<FlickerTest> {
+ return LegacyFlickerTestFactory.nonRotationTests(
+ supportedRotations = listOf(Rotation.ROTATION_90)
+ )
+ }
+ }
+}
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 b0e1a42..a18a144 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/RestartAppInSizeCompatModeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/RestartAppInSizeCompatModeTest.kt
@@ -17,9 +17,11 @@
package com.android.wm.shell.flicker.appcompat
import android.platform.test.annotations.Postsubmit
+import android.tools.common.flicker.assertions.FlickerTest
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import android.tools.device.helpers.WindowUtils
import androidx.test.filters.RequiresDevice
import org.junit.Test
@@ -46,7 +48,7 @@
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-class RestartAppInSizeCompatModeTest(flicker: FlickerTest) : BaseAppCompat(flicker) {
+class RestartAppInSizeCompatModeTest(flicker: LegacyFlickerTest) : BaseAppCompat(flicker) {
/** {@inheritDoc} */
override val transition: FlickerBuilder.() -> Unit
@@ -69,13 +71,9 @@
}
}
- @Postsubmit
- @Test
- fun appLayerKeepVisible() = assertLetterboxAppLayerKeepVisible()
+ @Postsubmit @Test fun appLayerKeepVisible() = assertLetterboxAppLayerKeepVisible()
- @Postsubmit
- @Test
- fun appIsLetterboxedAtStart() = assertAppLetterboxedAtStart()
+ @Postsubmit @Test fun appIsLetterboxedAtStart() = assertAppLetterboxedAtStart()
@Postsubmit
@Test
@@ -88,4 +86,18 @@
val displayBounds = WindowUtils.getDisplayBounds(flicker.scenario.endRotation)
flicker.assertLayersEnd { visibleRegion(letterboxApp).coversAtMost(displayBounds) }
}
+
+ companion object {
+ /**
+ * Creates the test configurations.
+ *
+ * See [LegacyFlickerTestFactory.rotationTests] for configuring screen orientation and
+ * navigation modes.
+ */
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<FlickerTest> {
+ return LegacyFlickerTestFactory.rotationTests()
+ }
+ }
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/BaseBubbleScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/BaseBubbleScreen.kt
index bab81d7..5c7d1d8 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/BaseBubbleScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/BaseBubbleScreen.kt
@@ -23,9 +23,9 @@
import android.os.ServiceManager
import android.tools.common.Rotation
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
-import android.tools.device.flicker.legacy.IFlickerTestData
+import android.tools.device.flicker.legacy.FlickerTestData
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import android.tools.device.helpers.SYSTEMUI_PACKAGE
import androidx.test.uiautomator.By
import androidx.test.uiautomator.UiObject2
@@ -35,7 +35,7 @@
import org.junit.runners.Parameterized
/** Base configurations for Bubble flicker tests */
-abstract class BaseBubbleScreen(flicker: FlickerTest) : BaseTest(flicker) {
+abstract class BaseBubbleScreen(flicker: LegacyFlickerTest) : BaseTest(flicker) {
protected val context: Context = instrumentation.context
protected val testApp = LaunchBubbleHelper(instrumentation)
@@ -72,28 +72,31 @@
uid,
NotificationManager.BUBBLE_PREFERENCE_NONE
)
- testApp.exit()
+ device.wait(
+ Until.gone(By.res(SYSTEM_UI_PACKAGE, BUBBLE_RES_NAME)),
+ FIND_OBJECT_TIMEOUT
+ )
+ testApp.exit(wmHelper)
}
extraSpec(this)
}
}
- protected fun IFlickerTestData.waitAndGetAddBubbleBtn(): UiObject2? =
+ protected fun FlickerTestData.waitAndGetAddBubbleBtn(): UiObject2? =
device.wait(Until.findObject(By.text("Add Bubble")), FIND_OBJECT_TIMEOUT)
- protected fun IFlickerTestData.waitAndGetCancelAllBtn(): UiObject2? =
+ protected fun FlickerTestData.waitAndGetCancelAllBtn(): UiObject2? =
device.wait(Until.findObject(By.text("Cancel All Bubble")), FIND_OBJECT_TIMEOUT)
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<FlickerTest> {
- return FlickerTestFactory.nonRotationTests(
+ fun getParams() =
+ LegacyFlickerTestFactory.nonRotationTests(
supportedRotations = listOf(Rotation.ROTATION_0)
)
- }
- const val FIND_OBJECT_TIMEOUT = 2000L
+ const val FIND_OBJECT_TIMEOUT = 4000L
const val SYSTEM_UI_PACKAGE = SYSTEMUI_PACKAGE
const val BUBBLE_RES_NAME = "bubble_view"
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ChangeActiveActivityFromBubbleTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ChangeActiveActivityFromBubbleTest.kt
index 2474ecf..bc565bc 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ChangeActiveActivityFromBubbleTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ChangeActiveActivityFromBubbleTest.kt
@@ -21,7 +21,7 @@
import android.platform.test.annotations.Presubmit
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTest
import androidx.test.filters.RequiresDevice
import androidx.test.uiautomator.By
import androidx.test.uiautomator.UiObject2
@@ -44,7 +44,8 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FlakyTest(bugId = 217777115)
-open class ChangeActiveActivityFromBubbleTest(flicker: FlickerTest) : BaseBubbleScreen(flicker) {
+open class ChangeActiveActivityFromBubbleTest(flicker: LegacyFlickerTest) :
+ BaseBubbleScreen(flicker) {
/** {@inheritDoc} */
override val transition: FlickerBuilder.() -> Unit
get() = buildTransition {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ChangeActiveActivityFromBubbleTestCfArm.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ChangeActiveActivityFromBubbleTestCfArm.kt
index bdfdad5..abc6b9f 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ChangeActiveActivityFromBubbleTestCfArm.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ChangeActiveActivityFromBubbleTestCfArm.kt
@@ -17,11 +17,11 @@
package com.android.wm.shell.flicker.bubble
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
-import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTest
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-open class ChangeActiveActivityFromBubbleTestCfArm(flicker: FlickerTest) :
+open class ChangeActiveActivityFromBubbleTestCfArm(flicker: LegacyFlickerTest) :
ChangeActiveActivityFromBubbleTest(flicker)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/DragToDismissBubbleScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/DragToDismissBubbleScreenTest.kt
index 8474ce0..3f28ae8 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/DragToDismissBubbleScreenTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/DragToDismissBubbleScreenTest.kt
@@ -19,9 +19,10 @@
import android.content.Context
import android.graphics.Point
import android.platform.test.annotations.Presubmit
+import android.tools.common.flicker.subject.layers.LayersTraceSubject
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.util.DisplayMetrics
import android.view.WindowManager
import androidx.test.filters.RequiresDevice
@@ -44,7 +45,7 @@
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-open class DragToDismissBubbleScreenTest(flicker: FlickerTest) : BaseBubbleScreen(flicker) {
+open class DragToDismissBubbleScreenTest(flicker: LegacyFlickerTest) : BaseBubbleScreen(flicker) {
private val wm = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
private val displaySize = DisplayMetrics()
@@ -73,4 +74,14 @@
open fun testAppIsAlwaysVisible() {
flicker.assertLayers { this.isVisible(testApp) }
}
+
+ @Presubmit
+ @Test
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() {
+ flicker.assertLayers {
+ this.visibleLayersShownMoreThanOneConsecutiveEntry(
+ LayersTraceSubject.VISIBLE_FOR_MORE_THAN_ONE_ENTRY_IGNORE_LAYERS + listOf(testApp)
+ )
+ }
+ }
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/DragToDismissBubbleScreenTestCfArm.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/DragToDismissBubbleScreenTestCfArm.kt
index 62fa7b4..ee55eca 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/DragToDismissBubbleScreenTestCfArm.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/DragToDismissBubbleScreenTestCfArm.kt
@@ -17,11 +17,11 @@
package com.android.wm.shell.flicker.bubble
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
-import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTest
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-class DragToDismissBubbleScreenTestCfArm(flicker: FlickerTest) :
+class DragToDismissBubbleScreenTestCfArm(flicker: LegacyFlickerTest) :
DragToDismissBubbleScreenTest(flicker)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleOnLocksreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleOnLocksreenTest.kt
index 889e177..26aca18 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleOnLocksreenTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleOnLocksreenTest.kt
@@ -21,7 +21,7 @@
import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.view.WindowInsets
import android.view.WindowManager
import androidx.test.filters.RequiresDevice
@@ -48,7 +48,8 @@
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-class OpenActivityFromBubbleOnLocksreenTest(flicker: FlickerTest) : BaseBubbleScreen(flicker) {
+class OpenActivityFromBubbleOnLocksreenTest(flicker: LegacyFlickerTest) :
+ BaseBubbleScreen(flicker) {
/** {@inheritDoc} */
override val transition: FlickerBuilder.() -> Unit
@@ -80,7 +81,7 @@
instrumentation.uiAutomation.syncInputTransactions()
val showBubble =
device.wait(
- Until.findObject(By.res("com.android.systemui", "bubble_view")),
+ Until.findObject(By.res(SYSTEM_UI_PACKAGE, BUBBLE_RES_NAME)),
FIND_OBJECT_TIMEOUT
)
showBubble?.click() ?: error("Bubble notify not found")
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleTest.kt
index 07ba4133..5085394 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleTest.kt
@@ -19,7 +19,7 @@
import android.platform.test.annotations.Presubmit
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTest
import androidx.test.filters.RequiresDevice
import androidx.test.uiautomator.By
import androidx.test.uiautomator.Until
@@ -42,7 +42,7 @@
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-open class OpenActivityFromBubbleTest(flicker: FlickerTest) : BaseBubbleScreen(flicker) {
+open class OpenActivityFromBubbleTest(flicker: LegacyFlickerTest) : BaseBubbleScreen(flicker) {
/** {@inheritDoc} */
override val transition: FlickerBuilder.() -> Unit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleTestCfArm.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleTestCfArm.kt
index 6c61710..6a46d23 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleTestCfArm.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleTestCfArm.kt
@@ -17,10 +17,11 @@
package com.android.wm.shell.flicker.bubble
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
-import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTest
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-class OpenActivityFromBubbleTestCfArm(flicker: FlickerTest) : OpenActivityFromBubbleTest(flicker)
+class OpenActivityFromBubbleTestCfArm(flicker: LegacyFlickerTest) :
+ OpenActivityFromBubbleTest(flicker)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/SendBubbleNotificationTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/SendBubbleNotificationTest.kt
index 29f76d0..a926bb7 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/SendBubbleNotificationTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/SendBubbleNotificationTest.kt
@@ -19,7 +19,7 @@
import android.platform.test.annotations.Presubmit
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTest
import androidx.test.filters.RequiresDevice
import androidx.test.uiautomator.By
import androidx.test.uiautomator.Until
@@ -41,7 +41,7 @@
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-open class SendBubbleNotificationTest(flicker: FlickerTest) : BaseBubbleScreen(flicker) {
+open class SendBubbleNotificationTest(flicker: LegacyFlickerTest) : BaseBubbleScreen(flicker) {
/** {@inheritDoc} */
override val transition: FlickerBuilder.() -> Unit
@@ -55,6 +55,7 @@
FIND_OBJECT_TIMEOUT
)
?: error("No bubbles found")
+ device.waitForIdle()
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/SendBubbleNotificationTestCfArm.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/SendBubbleNotificationTestCfArm.kt
index e323ebf..a401cb4 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/SendBubbleNotificationTestCfArm.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/SendBubbleNotificationTestCfArm.kt
@@ -17,11 +17,11 @@
package com.android.wm.shell.flicker.bubble
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
-import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTest
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-open class SendBubbleNotificationTestCfArm(flicker: FlickerTest) :
+open class SendBubbleNotificationTestCfArm(flicker: LegacyFlickerTest) :
SendBubbleNotificationTest(flicker)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipFromSplitScreenOnGoToHomeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipFromSplitScreenOnGoToHomeTest.kt
index f7ce870..36bbafb 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipFromSplitScreenOnGoToHomeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipFromSplitScreenOnGoToHomeTest.kt
@@ -24,8 +24,8 @@
import android.tools.device.apphelpers.StandardAppHelper
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import android.tools.device.helpers.WindowUtils
import android.tools.device.traces.parsers.toFlickerComponent
import androidx.test.filters.RequiresDevice
@@ -69,17 +69,17 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class AutoEnterPipFromSplitScreenOnGoToHomeTest(flicker: FlickerTest) :
- AutoEnterPipOnGoToHomeTest(flicker) {
+class AutoEnterPipFromSplitScreenOnGoToHomeTest(flicker: LegacyFlickerTest) :
+ AutoEnterPipOnGoToHomeTest(flicker) {
private val portraitDisplayBounds = WindowUtils.getDisplayBounds(Rotation.ROTATION_0)
/** Second app used to enter split screen mode */
protected val secondAppForSplitScreen = getSplitScreenApp(instrumentation)
fun getSplitScreenApp(instrumentation: Instrumentation): StandardAppHelper =
- SimpleAppHelper(
- instrumentation,
- ActivityOptions.SplitScreen.Primary.LABEL,
- ActivityOptions.SplitScreen.Primary.COMPONENT.toFlickerComponent()
- )
+ SimpleAppHelper(
+ instrumentation,
+ ActivityOptions.SplitScreen.Primary.LABEL,
+ ActivityOptions.SplitScreen.Primary.COMPONENT.toFlickerComponent()
+ )
/** Defines the transition used to run the test */
override val transition: FlickerBuilder.() -> Unit
@@ -91,11 +91,11 @@
enterSplitScreen()
// wait until split screen is established
wmHelper
- .StateSyncBuilder()
- .withWindowSurfaceAppeared(pipApp)
- .withWindowSurfaceAppeared(secondAppForSplitScreen)
- .withSplitDividerVisible()
- .waitForAndVerify()
+ .StateSyncBuilder()
+ .withWindowSurfaceAppeared(pipApp)
+ .withWindowSurfaceAppeared(secondAppForSplitScreen)
+ .withSplitDividerVisible()
+ .waitForAndVerify()
pipApp.enableAutoEnterForPipActivity()
}
teardown {
@@ -120,8 +120,8 @@
// contains more than 3 task views. We need to use uiautomator directly to find the
// second task to split.
tapl.workspace.switchToOverview().overviewActions.clickSplit()
- val snapshots = tapl.device.wait(Until.findObjects(overviewSnapshotSelector),
- TIMEOUT_MS)
+ val snapshots =
+ tapl.device.wait(Until.findObjects(overviewSnapshotSelector), TIMEOUT_MS)
if (snapshots == null || snapshots.size < 1) {
error("Fail to find a overview snapshot to split.")
}
@@ -137,12 +137,12 @@
snapshots[0].click()
} else {
tapl.workspace
- .switchToOverview()
- .currentTask
- .tapMenu()
- .tapSplitMenuItem()
- .currentTask
- .open()
+ .switchToOverview()
+ .currentTask
+ .tapMenu()
+ .tapSplitMenuItem()
+ .currentTask
+ .open()
}
SystemClock.sleep(TIMEOUT_MS)
}
@@ -190,11 +190,10 @@
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<FlickerTest> {
- return FlickerTestFactory.nonRotationTests(
- // TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy.
- supportedNavigationModes = listOf(NavBar.MODE_GESTURAL)
+ fun getParams() =
+ LegacyFlickerTestFactory.nonRotationTests(
+ // TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy.
+ supportedNavigationModes = listOf(NavBar.MODE_GESTURAL)
)
- }
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt
index b95732e..2f7a25e 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt
@@ -19,7 +19,7 @@
import android.platform.test.annotations.Presubmit
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTest
import androidx.test.filters.RequiresDevice
import org.junit.Assume
import org.junit.FixMethodOrder
@@ -53,10 +53,9 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class AutoEnterPipOnGoToHomeTest(flicker: FlickerTest) : EnterPipViaAppUiButtonTest(flicker) {
- override val thisTransition: FlickerBuilder.() -> Unit = {
- transitions { tapl.goHome() }
- }
+open class AutoEnterPipOnGoToHomeTest(flicker: LegacyFlickerTest) :
+ EnterPipViaAppUiButtonTest(flicker) {
+ override val thisTransition: FlickerBuilder.() -> Unit = { transitions { tapl.goHome() } }
override val defaultEnterPip: FlickerBuilder.() -> Unit = {
setup {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipBySwipingDownTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipBySwipingDownTest.kt
index afcc172..68bc9a2 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipBySwipingDownTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipBySwipingDownTest.kt
@@ -20,7 +20,7 @@
import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTest
import androidx.test.filters.RequiresDevice
import org.junit.FixMethodOrder
import org.junit.Test
@@ -53,7 +53,7 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class ClosePipBySwipingDownTest(flicker: FlickerTest) : ClosePipTransition(flicker) {
+open class ClosePipBySwipingDownTest(flicker: LegacyFlickerTest) : ClosePipTransition(flicker) {
override val thisTransition: FlickerBuilder.() -> Unit = {
transitions {
val pipRegion = wmHelper.getWindowRegion(pipApp).bounds
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipBySwipingDownTestCfArm.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipBySwipingDownTestCfArm.kt
index 02f6010..7a66889 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipBySwipingDownTestCfArm.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipBySwipingDownTestCfArm.kt
@@ -18,8 +18,8 @@
import android.tools.common.Rotation
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@@ -28,20 +28,20 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class ClosePipBySwipingDownTestCfArm(flicker: FlickerTest) : ClosePipBySwipingDownTest(flicker) {
+class ClosePipBySwipingDownTestCfArm(flicker: LegacyFlickerTest) :
+ ClosePipBySwipingDownTest(flicker) {
companion object {
/**
* Creates the test configurations.
*
- * See [FlickerTestFactory.nonRotationTests] for configuring repetitions, screen orientation
- * and navigation modes.
+ * See [LegacyFlickerTestFactory.nonRotationTests] for configuring repetitions, screen
+ * orientation and navigation modes.
*/
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<FlickerTest> {
- return FlickerTestFactory.nonRotationTests(
+ fun getParams() =
+ LegacyFlickerTestFactory.nonRotationTests(
supportedRotations = listOf(Rotation.ROTATION_0)
)
- }
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipTransition.kt
index e52b71e..a17144b 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipTransition.kt
@@ -20,14 +20,14 @@
import android.tools.common.Rotation
import android.tools.common.traces.component.ComponentNameMatcher.Companion.LAUNCHER
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import com.android.server.wm.flicker.helpers.setRotation
import org.junit.Test
import org.junit.runners.Parameterized
/** Base class for exiting pip (closing pip window) without returning to the app */
-abstract class ClosePipTransition(flicker: FlickerTest) : PipTransition(flicker) {
+abstract class ClosePipTransition(flicker: LegacyFlickerTest) : PipTransition(flicker) {
override val thisTransition: FlickerBuilder.() -> Unit = {
setup { this.setRotation(flicker.scenario.startRotation) }
teardown { this.setRotation(Rotation.ROTATION_0) }
@@ -74,15 +74,14 @@
/**
* Creates the test configurations.
*
- * See [FlickerTestFactory.nonRotationTests] for configuring repetitions, screen orientation
- * and navigation modes.
+ * See [LegacyFlickerTestFactory.nonRotationTests] for configuring repetitions, screen
+ * orientation and navigation modes.
*/
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<FlickerTest> {
- return FlickerTestFactory.nonRotationTests(
+ fun getParams() =
+ LegacyFlickerTestFactory.nonRotationTests(
supportedRotations = listOf(Rotation.ROTATION_0)
)
- }
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipWithDismissButtonTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipWithDismissButtonTest.kt
index 86fe583..dc48696 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipWithDismissButtonTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipWithDismissButtonTest.kt
@@ -19,7 +19,7 @@
import android.platform.test.annotations.Presubmit
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTest
import androidx.test.filters.RequiresDevice
import org.junit.FixMethodOrder
import org.junit.Test
@@ -53,7 +53,7 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class ClosePipWithDismissButtonTest(flicker: FlickerTest) : ClosePipTransition(flicker) {
+open class ClosePipWithDismissButtonTest(flicker: LegacyFlickerTest) : ClosePipTransition(flicker) {
override val thisTransition: FlickerBuilder.() -> Unit = {
transitions { pipApp.closePipWindow(wmHelper) }
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipWithDismissButtonTestCfArm.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipWithDismissButtonTestCfArm.kt
index 05262fe..718b14b 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipWithDismissButtonTestCfArm.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipWithDismissButtonTestCfArm.kt
@@ -18,8 +18,8 @@
import android.tools.common.Rotation
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@@ -28,21 +28,20 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class ClosePipWithDismissButtonTestCfArm(flicker: FlickerTest) :
+open class ClosePipWithDismissButtonTestCfArm(flicker: LegacyFlickerTest) :
ClosePipWithDismissButtonTest(flicker) {
companion object {
/**
* Creates the test configurations.
*
- * See [FlickerTestFactory.nonRotationTests] for configuring repetitions, screen orientation
- * and navigation modes.
+ * See [LegacyFlickerTestFactory.nonRotationTests] for configuring repetitions, screen
+ * orientation and navigation modes.
*/
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<FlickerTest> {
- return FlickerTestFactory.nonRotationTests(
+ fun getParams() =
+ LegacyFlickerTestFactory.nonRotationTests(
supportedRotations = listOf(Rotation.ROTATION_0)
)
- }
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTest.kt
index 01d67cc..5e39262 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTest.kt
@@ -19,7 +19,7 @@
import android.platform.test.annotations.Presubmit
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTest
import androidx.test.filters.RequiresDevice
import org.junit.Assume
import org.junit.FixMethodOrder
@@ -44,10 +44,8 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class EnterPipOnUserLeaveHintTest(flicker: FlickerTest) : EnterPipTransition(flicker) {
- override val thisTransition: FlickerBuilder.() -> Unit = {
- transitions { tapl.goHome() }
- }
+open class EnterPipOnUserLeaveHintTest(flicker: LegacyFlickerTest) : EnterPipTransition(flicker) {
+ override val thisTransition: FlickerBuilder.() -> Unit = { transitions { tapl.goHome() } }
override val defaultEnterPip: FlickerBuilder.() -> Unit = {
setup {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTestCfArm.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTestCfArm.kt
index 90f99c0..2b3e76a 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTestCfArm.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTestCfArm.kt
@@ -17,7 +17,7 @@
package com.android.wm.shell.flicker.pip
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
-import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTest
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@@ -27,4 +27,5 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class EnterPipOnUserLeaveHintTestCfArm(flicker: FlickerTest) : EnterPipOnUserLeaveHintTest(flicker)
+class EnterPipOnUserLeaveHintTestCfArm(flicker: LegacyFlickerTest) :
+ EnterPipOnUserLeaveHintTest(flicker)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientation.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientation.kt
index 5480144..ec35837 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientation.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientation.kt
@@ -21,11 +21,12 @@
import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
+import android.tools.common.flicker.assertions.FlickerTest
import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import android.tools.device.helpers.WindowUtils
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.entireScreenCovered
@@ -68,15 +69,13 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class EnterPipToOtherOrientation(flicker: FlickerTest) : PipTransition(flicker) {
+open class EnterPipToOtherOrientation(flicker: LegacyFlickerTest) : PipTransition(flicker) {
private val testApp = FixedOrientationAppHelper(instrumentation)
private val startingBounds = WindowUtils.getDisplayBounds(Rotation.ROTATION_90)
private val endingBounds = WindowUtils.getDisplayBounds(Rotation.ROTATION_0)
override val thisTransition: FlickerBuilder.() -> Unit = {
- teardown {
- testApp.exit(wmHelper)
- }
+ teardown { testApp.exit(wmHelper) }
transitions {
// Enter PiP, and assert that the PiP is within bounds now that the device is back
// in portrait
@@ -95,14 +94,13 @@
setup {
// Launch a portrait only app on the fullscreen stack
testApp.launchViaIntent(
- wmHelper,
- stringExtras = mapOf(EXTRA_FIXED_ORIENTATION to ORIENTATION_PORTRAIT.toString())
+ wmHelper,
+ stringExtras = mapOf(EXTRA_FIXED_ORIENTATION to ORIENTATION_PORTRAIT.toString())
)
// Launch the PiP activity fixed as landscape, but don't enter PiP
pipApp.launchViaIntent(
- wmHelper,
- stringExtras =
- mapOf(EXTRA_FIXED_ORIENTATION to ORIENTATION_LANDSCAPE.toString())
+ wmHelper,
+ stringExtras = mapOf(EXTRA_FIXED_ORIENTATION to ORIENTATION_LANDSCAPE.toString())
)
}
}
@@ -207,13 +205,13 @@
/**
* Creates the test configurations.
*
- * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and
+ * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and
* navigation modes.
*/
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): Collection<FlickerTest> {
- return FlickerTestFactory.nonRotationTests(
+ return LegacyFlickerTestFactory.nonRotationTests(
supportedRotations = listOf(Rotation.ROTATION_0)
)
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationCfArm.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationCfArm.kt
index 5841666..9264219 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationCfArm.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationCfArm.kt
@@ -17,9 +17,10 @@
package com.android.wm.shell.flicker.pip
import android.tools.common.Rotation
+import android.tools.common.flicker.assertions.FlickerTest
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@@ -29,19 +30,19 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class EnterPipToOtherOrientationCfArm(flicker: FlickerTest) :
+open class EnterPipToOtherOrientationCfArm(flicker: LegacyFlickerTest) :
EnterPipToOtherOrientation(flicker) {
companion object {
/**
* Creates the test configurations.
*
- * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and
+ * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and
* navigation modes.
*/
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): Collection<FlickerTest> {
- return FlickerTestFactory.nonRotationTests(
+ return LegacyFlickerTestFactory.nonRotationTests(
supportedRotations = listOf(Rotation.ROTATION_0)
)
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTransition.kt
index cdbdb85..6d20740 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTransition.kt
@@ -20,16 +20,14 @@
import android.tools.common.Rotation
import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import org.junit.Test
import org.junit.runners.Parameterized
-abstract class EnterPipTransition(flicker: FlickerTest) : PipTransition(flicker) {
+abstract class EnterPipTransition(flicker: LegacyFlickerTest) : PipTransition(flicker) {
override val defaultEnterPip: FlickerBuilder.() -> Unit = {
- setup {
- pipApp.launchViaIntent(wmHelper)
- }
+ setup { pipApp.launchViaIntent(wmHelper) }
}
/** Checks [pipApp] window remains visible throughout the animation */
@@ -126,15 +124,14 @@
/**
* Creates the test configurations.
*
- * See [FlickerTestFactory.nonRotationTests] for configuring repetitions, screen orientation
- * and navigation modes.
+ * See [LegacyFlickerTestFactory.nonRotationTests] for configuring repetitions, screen
+ * orientation and navigation modes.
*/
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<FlickerTest> {
- return FlickerTestFactory.nonRotationTests(
+ fun getParams() =
+ LegacyFlickerTestFactory.nonRotationTests(
supportedRotations = listOf(Rotation.ROTATION_0)
)
- }
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipViaAppUiButtonTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipViaAppUiButtonTest.kt
index 95725b6..76c811c 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipViaAppUiButtonTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipViaAppUiButtonTest.kt
@@ -18,7 +18,7 @@
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTest
import androidx.test.filters.RequiresDevice
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
@@ -50,7 +50,7 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class EnterPipViaAppUiButtonTest(flicker: FlickerTest) : EnterPipTransition(flicker) {
+open class EnterPipViaAppUiButtonTest(flicker: LegacyFlickerTest) : EnterPipTransition(flicker) {
override val thisTransition: FlickerBuilder.() -> Unit = {
transitions { pipApp.clickEnterPipButton(wmHelper) }
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipViaAppUiButtonTestCfArm.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipViaAppUiButtonTestCfArm.kt
index 4390f0b..78e8049 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipViaAppUiButtonTestCfArm.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipViaAppUiButtonTestCfArm.kt
@@ -18,8 +18,8 @@
import android.tools.common.Rotation
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@@ -28,20 +28,20 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class EnterPipViaAppUiButtonTestCfArm(flicker: FlickerTest) : EnterPipViaAppUiButtonTest(flicker) {
+class EnterPipViaAppUiButtonTestCfArm(flicker: LegacyFlickerTest) :
+ EnterPipViaAppUiButtonTest(flicker) {
companion object {
/**
* Creates the test configurations.
*
- * See [FlickerTestFactory.nonRotationTests] for configuring repetitions, screen orientation
- * and navigation modes.
+ * See [LegacyFlickerTestFactory.nonRotationTests] for configuring repetitions, screen
+ * orientation and navigation modes.
*/
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<FlickerTest> {
- return FlickerTestFactory.nonRotationTests(
+ fun getParams() =
+ LegacyFlickerTestFactory.nonRotationTests(
supportedRotations = listOf(Rotation.ROTATION_0)
)
- }
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppTransition.kt
index 5ac9829..dfffba8 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppTransition.kt
@@ -19,14 +19,14 @@
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
import android.tools.common.traces.component.ComponentNameMatcher
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import com.android.server.wm.flicker.helpers.SimpleAppHelper
import org.junit.Test
import org.junit.runners.Parameterized
/** Base class for pip expand tests */
-abstract class ExitPipToAppTransition(flicker: FlickerTest) : PipTransition(flicker) {
+abstract class ExitPipToAppTransition(flicker: LegacyFlickerTest) : PipTransition(flicker) {
protected val testApp = SimpleAppHelper(instrumentation)
/**
@@ -130,15 +130,14 @@
/**
* Creates the test configurations.
*
- * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and
+ * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and
* navigation modes.
*/
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<FlickerTest> {
- return FlickerTestFactory.nonRotationTests(
+ fun getParams() =
+ LegacyFlickerTestFactory.nonRotationTests(
supportedRotations = listOf(Rotation.ROTATION_0)
)
- }
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaExpandButtonTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaExpandButtonTest.kt
index 0b3d16a..b80b748 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaExpandButtonTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaExpandButtonTest.kt
@@ -18,7 +18,7 @@
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTest
import androidx.test.filters.RequiresDevice
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
@@ -52,7 +52,8 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class ExitPipToAppViaExpandButtonTest(flicker: FlickerTest) : ExitPipToAppTransition(flicker) {
+open class ExitPipToAppViaExpandButtonTest(flicker: LegacyFlickerTest) :
+ ExitPipToAppTransition(flicker) {
override val thisTransition: FlickerBuilder.() -> Unit = {
setup {
// launch an app behind the pip one
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaExpandButtonTestCfArm.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaExpandButtonTestCfArm.kt
index eccb85d..e25c0d6 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaExpandButtonTestCfArm.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaExpandButtonTestCfArm.kt
@@ -18,8 +18,8 @@
import android.tools.common.Rotation
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@@ -28,21 +28,20 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class ExitPipToAppViaExpandButtonTestCfArm(flicker: FlickerTest) :
+class ExitPipToAppViaExpandButtonTestCfArm(flicker: LegacyFlickerTest) :
ExitPipToAppViaExpandButtonTest(flicker) {
companion object {
/**
* Creates the test configurations.
*
- * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and
+ * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and
* navigation modes.
*/
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<FlickerTest> {
- return FlickerTestFactory.nonRotationTests(
+ fun getParams() =
+ LegacyFlickerTestFactory.nonRotationTests(
supportedRotations = listOf(Rotation.ROTATION_0)
)
- }
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaIntentTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaIntentTest.kt
index bb2d40b..f003ed8 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaIntentTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaIntentTest.kt
@@ -18,7 +18,7 @@
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTest
import androidx.test.filters.RequiresDevice
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
@@ -51,7 +51,7 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class ExitPipToAppViaIntentTest(flicker: FlickerTest) : ExitPipToAppTransition(flicker) {
+open class ExitPipToAppViaIntentTest(flicker: LegacyFlickerTest) : ExitPipToAppTransition(flicker) {
override val thisTransition: FlickerBuilder.() -> Unit = {
setup {
// launch an app behind the pip one
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaIntentTestCfArm.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaIntentTestCfArm.kt
index 6ab6a1f..be19f3c 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaIntentTestCfArm.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaIntentTestCfArm.kt
@@ -18,8 +18,8 @@
import android.tools.common.Rotation
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@@ -28,20 +28,20 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class ExitPipToAppViaIntentTestCfArm(flicker: FlickerTest) : ExitPipToAppViaIntentTest(flicker) {
+class ExitPipToAppViaIntentTestCfArm(flicker: LegacyFlickerTest) :
+ ExitPipToAppViaIntentTest(flicker) {
companion object {
/**
* Creates the test configurations.
*
- * See [FlickerTestFactory.nonRotationTests] for configuring repetitions, screen orientation
- * and navigation modes.
+ * See [LegacyFlickerTestFactory.nonRotationTests] for configuring repetitions, screen
+ * orientation and navigation modes.
*/
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<FlickerTest> {
- return FlickerTestFactory.nonRotationTests(
+ fun getParams() =
+ LegacyFlickerTestFactory.nonRotationTests(
supportedRotations = listOf(Rotation.ROTATION_0)
)
- }
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt
index fd16b6e..a1d3a11 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt
@@ -21,8 +21,8 @@
import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
import org.junit.FixMethodOrder
import org.junit.Test
@@ -55,7 +55,7 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class ExpandPipOnDoubleClickTest(flicker: FlickerTest) : PipTransition(flicker) {
+open class ExpandPipOnDoubleClickTest(flicker: LegacyFlickerTest) : PipTransition(flicker) {
override val thisTransition: FlickerBuilder.() -> Unit = {
transitions { pipApp.doubleClickPipWindow(wmHelper) }
}
@@ -142,15 +142,14 @@
/**
* Creates the test configurations.
*
- * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and
+ * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and
* navigation modes.
*/
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<FlickerTest> {
- return FlickerTestFactory.nonRotationTests(
+ fun getParams() =
+ LegacyFlickerTestFactory.nonRotationTests(
supportedRotations = listOf(Rotation.ROTATION_0)
)
- }
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTestTestCfArm.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTestTestCfArm.kt
index c096234..3095cac 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTestTestCfArm.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTestTestCfArm.kt
@@ -18,8 +18,8 @@
import android.tools.common.Rotation
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@@ -28,21 +28,20 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class ExpandPipOnDoubleClickTestTestCfArm(flicker: FlickerTest) :
+class ExpandPipOnDoubleClickTestTestCfArm(flicker: LegacyFlickerTest) :
ExpandPipOnDoubleClickTest(flicker) {
companion object {
/**
* Creates the test configurations.
*
- * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and
+ * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and
* navigation modes.
*/
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<FlickerTest> {
- return FlickerTestFactory.nonRotationTests(
+ fun getParams() =
+ LegacyFlickerTestFactory.nonRotationTests(
supportedRotations = listOf(Rotation.ROTATION_0)
)
- }
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnPinchOpenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnPinchOpenTest.kt
index 253aa4c..8c8d280 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnPinchOpenTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnPinchOpenTest.kt
@@ -20,8 +20,8 @@
import android.tools.common.Rotation
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
import org.junit.FixMethodOrder
import org.junit.Test
@@ -34,7 +34,7 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class ExpandPipOnPinchOpenTest(flicker: FlickerTest) : PipTransition(flicker) {
+open class ExpandPipOnPinchOpenTest(flicker: LegacyFlickerTest) : PipTransition(flicker) {
override val thisTransition: FlickerBuilder.() -> Unit = {
transitions { pipApp.pinchOpenPipWindow(wmHelper, 0.25f, 30) }
}
@@ -55,15 +55,14 @@
/**
* Creates the test configurations.
*
- * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and
+ * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and
* navigation modes.
*/
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<FlickerTest> {
- return FlickerTestFactory.nonRotationTests(
+ fun getParams() =
+ LegacyFlickerTestFactory.nonRotationTests(
supportedRotations = listOf(Rotation.ROTATION_0)
)
- }
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnPinchOpenTestCfArm.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnPinchOpenTestCfArm.kt
index e064bf2..1a1ce68 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnPinchOpenTestCfArm.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnPinchOpenTestCfArm.kt
@@ -18,8 +18,8 @@
import android.tools.common.Rotation
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@@ -28,20 +28,20 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class ExpandPipOnPinchOpenTestCfArm(flicker: FlickerTest) : ExpandPipOnPinchOpenTest(flicker) {
+class ExpandPipOnPinchOpenTestCfArm(flicker: LegacyFlickerTest) :
+ ExpandPipOnPinchOpenTest(flicker) {
companion object {
/**
* Creates the test configurations.
*
- * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and
+ * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and
* navigation modes.
*/
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<FlickerTest> {
- return FlickerTestFactory.nonRotationTests(
+ fun getParams() =
+ LegacyFlickerTestFactory.nonRotationTests(
supportedRotations = listOf(Rotation.ROTATION_0)
)
- }
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownOnShelfHeightChange.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownOnShelfHeightChange.kt
index 094060f..4f88184 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownOnShelfHeightChange.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownOnShelfHeightChange.kt
@@ -19,7 +19,7 @@
import android.platform.test.annotations.Presubmit
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTest
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.Direction
import org.junit.FixMethodOrder
@@ -55,7 +55,8 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class MovePipDownOnShelfHeightChange(flicker: FlickerTest) : MovePipShelfHeightTransition(flicker) {
+class MovePipDownOnShelfHeightChange(flicker: LegacyFlickerTest) :
+ MovePipShelfHeightTransition(flicker) {
override val thisTransition: FlickerBuilder.() -> Unit = {
teardown { testApp.exit(wmHelper) }
transitions { testApp.launchViaIntent(wmHelper) }
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipOnImeVisibilityChangeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipOnImeVisibilityChangeTest.kt
index ff51c27..dffc822 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipOnImeVisibilityChangeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipOnImeVisibilityChangeTest.kt
@@ -18,11 +18,12 @@
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
+import android.tools.common.flicker.assertions.FlickerTest
import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import android.tools.device.helpers.WindowUtils
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.helpers.ImeAppHelper
@@ -38,7 +39,7 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class MovePipOnImeVisibilityChangeTest(flicker: FlickerTest) : PipTransition(flicker) {
+open class MovePipOnImeVisibilityChangeTest(flicker: LegacyFlickerTest) : PipTransition(flicker) {
private val imeApp = ImeAppHelper(instrumentation)
override val thisTransition: FlickerBuilder.() -> Unit = {
@@ -80,7 +81,7 @@
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): Collection<FlickerTest> {
- return FlickerTestFactory.nonRotationTests(
+ return LegacyFlickerTestFactory.nonRotationTests(
supportedRotations = listOf(Rotation.ROTATION_0)
)
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipOnImeVisibilityChangeTestCfArm.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipOnImeVisibilityChangeTestCfArm.kt
index d3d77d2..63292a4 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipOnImeVisibilityChangeTestCfArm.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipOnImeVisibilityChangeTestCfArm.kt
@@ -17,9 +17,10 @@
package com.android.wm.shell.flicker.pip
import android.tools.common.Rotation
+import android.tools.common.flicker.assertions.FlickerTest
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@@ -28,7 +29,7 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class MovePipOnImeVisibilityChangeTestCfArm(flicker: FlickerTest) :
+class MovePipOnImeVisibilityChangeTestCfArm(flicker: LegacyFlickerTest) :
MovePipOnImeVisibilityChangeTest(flicker) {
companion object {
private const val TAG_IME_VISIBLE = "imeIsVisible"
@@ -36,7 +37,7 @@
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): Collection<FlickerTest> {
- return FlickerTestFactory.nonRotationTests(
+ return LegacyFlickerTestFactory.nonRotationTests(
supportedRotations = listOf(Rotation.ROTATION_0)
)
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipShelfHeightTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipShelfHeightTransition.kt
index 109354a..9a2fa09 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipShelfHeightTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipShelfHeightTransition.kt
@@ -19,15 +19,15 @@
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
import android.tools.common.flicker.subject.region.RegionSubject
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import com.android.server.wm.flicker.helpers.FixedOrientationAppHelper
import com.android.wm.shell.flicker.Direction
import org.junit.Test
import org.junit.runners.Parameterized
/** Base class for pip tests with Launcher shelf height change */
-abstract class MovePipShelfHeightTransition(flicker: FlickerTest) : PipTransition(flicker) {
+abstract class MovePipShelfHeightTransition(flicker: LegacyFlickerTest) : PipTransition(flicker) {
protected val testApp = FixedOrientationAppHelper(instrumentation)
/** Checks [pipApp] window remains visible throughout the animation */
@@ -111,15 +111,14 @@
/**
* Creates the test configurations.
*
- * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and
+ * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and
* navigation modes.
*/
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<FlickerTest> {
- return FlickerTestFactory.nonRotationTests(
+ fun getParams() =
+ LegacyFlickerTestFactory.nonRotationTests(
supportedRotations = listOf(Rotation.ROTATION_0)
)
- }
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipUpOnShelfHeightChangeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipUpOnShelfHeightChangeTest.kt
index 27b061b..afb4af6 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipUpOnShelfHeightChangeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipUpOnShelfHeightChangeTest.kt
@@ -19,7 +19,7 @@
import android.platform.test.annotations.Presubmit
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTest
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.Direction
import org.junit.FixMethodOrder
@@ -55,14 +55,13 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class MovePipUpOnShelfHeightChangeTest(flicker: FlickerTest) :
+open class MovePipUpOnShelfHeightChangeTest(flicker: LegacyFlickerTest) :
MovePipShelfHeightTransition(flicker) {
- override val thisTransition: FlickerBuilder.() -> Unit =
- {
- setup { testApp.launchViaIntent(wmHelper) }
- transitions { tapl.pressHome() }
- teardown { testApp.exit(wmHelper) }
- }
+ override val thisTransition: FlickerBuilder.() -> Unit = {
+ setup { testApp.launchViaIntent(wmHelper) }
+ transitions { tapl.pressHome() }
+ teardown { testApp.exit(wmHelper) }
+ }
/** Checks that the visible region of [pipApp] window always moves up during the animation. */
@Presubmit @Test fun pipWindowMovesUp() = pipWindowMoves(Direction.UP)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipDragTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipDragTest.kt
index 9f81ba8..7085d55 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipDragTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipDragTest.kt
@@ -20,8 +20,8 @@
import android.platform.test.annotations.RequiresDevice
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import com.android.server.wm.flicker.testapp.ActivityOptions
import org.junit.FixMethodOrder
import org.junit.Test
@@ -34,7 +34,7 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class PipDragTest(flicker: FlickerTest) : PipTransition(flicker) {
+class PipDragTest(flicker: LegacyFlickerTest) : PipTransition(flicker) {
private var isDraggedLeft: Boolean = true
override val thisTransition: FlickerBuilder.() -> Unit = {
@@ -81,13 +81,11 @@
/**
* Creates the test configurations.
*
- * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and
+ * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and
* navigation modes.
*/
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<FlickerTest> {
- return FlickerTestFactory.nonRotationTests()
- }
+ fun getParams() = LegacyFlickerTestFactory.nonRotationTests()
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipDragThenSnapTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipDragThenSnapTest.kt
index 9fe9f52..2b87766 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipDragThenSnapTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipDragThenSnapTest.kt
@@ -21,8 +21,8 @@
import android.tools.common.Rotation
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import android.tools.device.flicker.rules.RemoveAllTasksButHomeRule
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.helpers.setRotation
@@ -38,7 +38,7 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class PipDragThenSnapTest(flicker: FlickerTest) : PipTransition(flicker) {
+class PipDragThenSnapTest(flicker: LegacyFlickerTest) : PipTransition(flicker) {
// represents the direction in which the pip window should be snapping
private var willSnapRight: Boolean = true
@@ -99,15 +99,14 @@
/**
* Creates the test configurations.
*
- * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and
+ * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and
* navigation modes.
*/
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<FlickerTest> {
- return FlickerTestFactory.nonRotationTests(
+ fun getParams() =
+ LegacyFlickerTestFactory.nonRotationTests(
supportedRotations = listOf(Rotation.ROTATION_0)
)
- }
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipPinchInTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipPinchInTest.kt
index 60bf5ff..adc5ee3 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipPinchInTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipPinchInTest.kt
@@ -21,8 +21,8 @@
import android.tools.common.Rotation
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
import org.junit.FixMethodOrder
import org.junit.Test
@@ -36,7 +36,7 @@
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@FlakyTest(bugId = 270677470)
-class PipPinchInTest(flicker: FlickerTest) : PipTransition(flicker) {
+class PipPinchInTest(flicker: LegacyFlickerTest) : PipTransition(flicker) {
override val thisTransition: FlickerBuilder.() -> Unit = {
transitions { pipApp.pinchInPipWindow(wmHelper, 0.4f, 30) }
}
@@ -57,15 +57,14 @@
/**
* Creates the test configurations.
*
- * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and
+ * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and
* navigation modes.
*/
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<FlickerTest> {
- return FlickerTestFactory.nonRotationTests(
+ fun getParams() =
+ LegacyFlickerTestFactory.nonRotationTests(
supportedRotations = listOf(Rotation.ROTATION_0)
)
- }
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt
index 17a178f..096af39 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt
@@ -22,7 +22,7 @@
import android.tools.common.Rotation
import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.rules.RemoveAllTasksButHomeRule.Companion.removeAllTasksButHome
import android.tools.device.helpers.WindowUtils
import com.android.server.wm.flicker.helpers.PipAppHelper
@@ -32,7 +32,7 @@
import com.google.common.truth.Truth
import org.junit.Test
-abstract class PipTransition(flicker: FlickerTest) : BaseTest(flicker) {
+abstract class PipTransition(flicker: LegacyFlickerTest) : BaseTest(flicker) {
protected val pipApp = PipAppHelper(instrumentation)
protected val displayBounds = WindowUtils.getDisplayBounds(flicker.scenario.startRotation)
protected val broadcastActionTrigger = BroadcastActionTrigger(instrumentation)
@@ -78,16 +78,16 @@
/** Defines the default method of entering PiP */
protected open val defaultEnterPip: FlickerBuilder.() -> Unit = {
setup {
- pipApp.launchViaIntentAndWaitForPip(wmHelper,
- stringExtras = mapOf(ActivityOptions.Pip.EXTRA_ENTER_PIP to "true"))
+ pipApp.launchViaIntentAndWaitForPip(
+ wmHelper,
+ stringExtras = mapOf(ActivityOptions.Pip.EXTRA_ENTER_PIP to "true")
+ )
}
}
/** Defines the default teardown required to clean up after the test */
protected open val defaultTeardown: FlickerBuilder.() -> Unit = {
- teardown {
- pipApp.exit(wmHelper)
- }
+ teardown { pipApp.exit(wmHelper) }
}
@Presubmit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinned.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinned.kt
index c618e5a..c315e74 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinned.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinned.kt
@@ -21,10 +21,11 @@
import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
+import android.tools.common.flicker.assertions.FlickerTest
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import android.tools.device.helpers.WindowUtils
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.testapp.ActivityOptions
@@ -46,7 +47,7 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class SetRequestedOrientationWhilePinned(flicker: FlickerTest) : PipTransition(flicker) {
+open class SetRequestedOrientationWhilePinned(flicker: LegacyFlickerTest) : PipTransition(flicker) {
private val startingBounds = WindowUtils.getDisplayBounds(Rotation.ROTATION_0)
private val endingBounds = WindowUtils.getDisplayBounds(Rotation.ROTATION_90)
@@ -69,20 +70,19 @@
setup {
// Launch the PiP activity fixed as landscape.
pipApp.launchViaIntent(
- wmHelper,
- stringExtras =
- mapOf(EXTRA_FIXED_ORIENTATION to ORIENTATION_LANDSCAPE.toString())
+ wmHelper,
+ stringExtras = mapOf(EXTRA_FIXED_ORIENTATION to ORIENTATION_LANDSCAPE.toString())
)
// Enter PiP.
broadcastActionTrigger.doAction(ActivityOptions.Pip.ACTION_ENTER_PIP)
// System bar may fade out during fixed rotation.
wmHelper
- .StateSyncBuilder()
- .withPipShown()
- .withRotation(Rotation.ROTATION_0)
- .withNavOrTaskBarVisible()
- .withStatusBarVisible()
- .waitForAndVerify()
+ .StateSyncBuilder()
+ .withPipShown()
+ .withRotation(Rotation.ROTATION_0)
+ .withNavOrTaskBarVisible()
+ .withStatusBarVisible()
+ .waitForAndVerify()
}
}
@@ -150,7 +150,7 @@
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): Collection<FlickerTest> {
- return FlickerTestFactory.nonRotationTests(
+ return LegacyFlickerTestFactory.nonRotationTests(
supportedRotations = listOf(Rotation.ROTATION_0)
)
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ShowPipAndRotateDisplay.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ShowPipAndRotateDisplay.kt
index 43d6c8f..0ff9cff 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ShowPipAndRotateDisplay.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ShowPipAndRotateDisplay.kt
@@ -17,10 +17,11 @@
package com.android.wm.shell.flicker.pip
import android.platform.test.annotations.Presubmit
+import android.tools.common.flicker.assertions.FlickerTest
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import android.tools.device.helpers.WindowUtils
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.helpers.SimpleAppHelper
@@ -58,7 +59,7 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class ShowPipAndRotateDisplay(flicker: FlickerTest) : PipTransition(flicker) {
+open class ShowPipAndRotateDisplay(flicker: LegacyFlickerTest) : PipTransition(flicker) {
private val testApp = SimpleAppHelper(instrumentation)
private val screenBoundsStart = WindowUtils.getDisplayBounds(flicker.scenario.startRotation)
private val screenBoundsEnd = WindowUtils.getDisplayBounds(flicker.scenario.endRotation)
@@ -154,13 +155,13 @@
/**
* Creates the test configurations.
*
- * See [FlickerTestFactory.nonRotationTests] for configuring repetitions, screen orientation
- * and navigation modes.
+ * See [LegacyFlickerTestFactory.nonRotationTests] for configuring repetitions, screen
+ * orientation and navigation modes.
*/
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): Collection<FlickerTest> {
- return FlickerTestFactory.rotationTests()
+ return LegacyFlickerTestFactory.rotationTests()
}
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ShowPipAndRotateDisplayCfArm.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ShowPipAndRotateDisplayCfArm.kt
index b7a2c47..2516471 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ShowPipAndRotateDisplayCfArm.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ShowPipAndRotateDisplayCfArm.kt
@@ -16,9 +16,10 @@
package com.android.wm.shell.flicker.pip
+import android.tools.common.flicker.assertions.FlickerTest
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@@ -27,18 +28,18 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class ShowPipAndRotateDisplayCfArm(flicker: FlickerTest) : ShowPipAndRotateDisplay(flicker) {
+class ShowPipAndRotateDisplayCfArm(flicker: LegacyFlickerTest) : ShowPipAndRotateDisplay(flicker) {
companion object {
/**
* Creates the test configurations.
*
- * See [FlickerTestFactory.nonRotationTests] for configuring repetitions, screen orientation
- * and navigation modes.
+ * See [LegacyFlickerTestFactory.nonRotationTests] for configuring repetitions, screen
+ * orientation and navigation modes.
*/
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): Collection<FlickerTest> {
- return FlickerTestFactory.rotationTests()
+ return LegacyFlickerTestFactory.rotationTests()
}
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt
index d1f0980..a43ad9b 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
@@ -23,8 +23,8 @@
import android.tools.common.traces.component.EdgeExtensionComponentMatcher
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.ICommonAssertions
import com.android.wm.shell.flicker.SPLIT_SCREEN_DIVIDER_COMPONENT
@@ -45,13 +45,13 @@
/**
* Test copy content from the left to the right side of the split-screen.
*
- * To run this test: `atest WMShellFlickerTests:CopyContentInSplit`
+ * To run this test: `atest WMShellFlickerTestsSplitScreen:CopyContentInSplit`
*/
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class CopyContentInSplit(override val flicker: FlickerTest) :
+class CopyContentInSplit(override val flicker: LegacyFlickerTest) :
CopyContentInSplitBenchmark(flicker), ICommonAssertions {
override val transition: FlickerBuilder.() -> Unit
get() = {
@@ -136,8 +136,6 @@
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<FlickerTest> {
- return FlickerTestFactory.nonRotationTests()
- }
+ fun getParams() = LegacyFlickerTestFactory.nonRotationTests()
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByDivider.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByDivider.kt
index 4505b99..0b8f109 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByDivider.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByDivider.kt
@@ -21,7 +21,7 @@
import android.platform.test.annotations.Presubmit
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.helpers.WindowUtils
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.ICommonAssertions
@@ -41,13 +41,13 @@
/**
* Test dismiss split screen by dragging the divider bar.
*
- * To run this test: `atest WMShellFlickerTests:DismissSplitScreenByDivider`
+ * To run this test: `atest WMShellFlickerTestsSplitScreen:DismissSplitScreenByDivider`
*/
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class DismissSplitScreenByDivider(override val flicker: FlickerTest) :
+class DismissSplitScreenByDivider(override val flicker: LegacyFlickerTest) :
DismissSplitScreenByDividerBenchmark(flicker), ICommonAssertions {
override val transition: FlickerBuilder.() -> Unit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt
index e05b221..38d4b40 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt
@@ -20,8 +20,8 @@
import android.platform.test.annotations.Presubmit
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.ICommonAssertions
import com.android.wm.shell.flicker.appWindowBecomesInvisible
@@ -38,13 +38,13 @@
/**
* Test dismiss split screen by go home.
*
- * To run this test: `atest WMShellFlickerTests:DismissSplitScreenByGoHome`
+ * To run this test: `atest WMShellFlickerTestsSplitScreen:DismissSplitScreenByGoHome`
*/
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class DismissSplitScreenByGoHome(override val flicker: FlickerTest) :
+class DismissSplitScreenByGoHome(override val flicker: LegacyFlickerTest) :
DismissSplitScreenByGoHomeBenchmark(flicker), ICommonAssertions {
override val transition: FlickerBuilder.() -> Unit
get() = {
@@ -154,8 +154,6 @@
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<FlickerTest> {
- return FlickerTestFactory.nonRotationTests()
- }
+ fun getParams() = LegacyFlickerTestFactory.nonRotationTests()
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt
index 63c5d14..a118c08 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
@@ -21,8 +21,8 @@
import android.platform.test.annotations.Presubmit
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.ICommonAssertions
import com.android.wm.shell.flicker.SPLIT_SCREEN_DIVIDER_COMPONENT
@@ -43,13 +43,13 @@
/**
* Test resize split by dragging the divider bar.
*
- * To run this test: `atest WMShellFlickerTests:DragDividerToResize`
+ * To run this test: `atest WMShellFlickerTestsSplitScreen:DragDividerToResize`
*/
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class DragDividerToResize(override val flicker: FlickerTest) :
+class DragDividerToResize(override val flicker: LegacyFlickerTest) :
DragDividerToResizeBenchmark(flicker), ICommonAssertions {
override val transition: FlickerBuilder.() -> Unit
get() = {
@@ -127,8 +127,6 @@
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<FlickerTest> {
- return FlickerTestFactory.nonRotationTests()
- }
+ fun getParams() = LegacyFlickerTestFactory.nonRotationTests()
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt
index e558686..05c0480 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt
@@ -22,8 +22,8 @@
import android.tools.common.NavBar
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.ICommonAssertions
import com.android.wm.shell.flicker.SPLIT_SCREEN_DIVIDER_COMPONENT
@@ -45,13 +45,13 @@
* Test enter split screen by dragging app icon from all apps. This test is only for large screen
* devices.
*
- * To run this test: `atest WMShellFlickerTests:EnterSplitScreenByDragFromAllApps`
+ * To run this test: `atest WMShellFlickerTestsSplitScreen:EnterSplitScreenByDragFromAllApps`
*/
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class EnterSplitScreenByDragFromAllApps(override val flicker: FlickerTest) :
+class EnterSplitScreenByDragFromAllApps(override val flicker: LegacyFlickerTest) :
EnterSplitScreenByDragFromAllAppsBenchmark(flicker), ICommonAssertions {
override val transition: FlickerBuilder.() -> Unit
get() = {
@@ -160,11 +160,10 @@
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<FlickerTest> {
- return FlickerTestFactory.nonRotationTests(
+ fun getParams() =
+ LegacyFlickerTestFactory.nonRotationTests(
// TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy.
supportedNavigationModes = listOf(NavBar.MODE_GESTURAL)
)
- }
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromNotification.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromNotification.kt
index ab8ecc5..3a75fa6 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromNotification.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromNotification.kt
@@ -22,8 +22,8 @@
import android.tools.common.NavBar
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.ICommonAssertions
import com.android.wm.shell.flicker.SPLIT_SCREEN_DIVIDER_COMPONENT
@@ -44,13 +44,13 @@
* Test enter split screen by dragging app icon from notification. This test is only for large
* screen devices.
*
- * To run this test: `atest WMShellFlickerTests:EnterSplitScreenByDragFromNotification`
+ * To run this test: `atest WMShellFlickerTestsSplitScreen:EnterSplitScreenByDragFromNotification`
*/
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class EnterSplitScreenByDragFromNotification(override val flicker: FlickerTest) :
+class EnterSplitScreenByDragFromNotification(override val flicker: LegacyFlickerTest) :
EnterSplitScreenByDragFromNotificationBenchmark(flicker), ICommonAssertions {
/** {@inheritDoc} */
override val transition: FlickerBuilder.() -> Unit
@@ -162,11 +162,10 @@
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<FlickerTest> {
- return FlickerTestFactory.nonRotationTests(
+ fun getParams() =
+ LegacyFlickerTestFactory.nonRotationTests(
// TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy.
supportedNavigationModes = listOf(NavBar.MODE_GESTURAL)
)
- }
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromShortcut.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromShortcut.kt
index 516ca97..6d73f92 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromShortcut.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromShortcut.kt
@@ -21,8 +21,8 @@
import android.tools.common.NavBar
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.ICommonAssertions
import com.android.wm.shell.flicker.appWindowIsVisibleAtEnd
@@ -41,13 +41,13 @@
/**
* Test enter split screen by dragging a shortcut. This test is only for large screen devices.
*
- * To run this test: `atest WMShellFlickerTests:EnterSplitScreenByDragFromShortcut`
+ * To run this test: `atest WMShellFlickerTestsSplitScreen:EnterSplitScreenByDragFromShortcut`
*/
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class EnterSplitScreenByDragFromShortcut(override val flicker: FlickerTest) :
+class EnterSplitScreenByDragFromShortcut(override val flicker: LegacyFlickerTest) :
EnterSplitScreenByDragFromShortcutBenchmark(flicker), ICommonAssertions {
override val transition: FlickerBuilder.() -> Unit
@@ -105,11 +105,10 @@
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<FlickerTest> {
- return FlickerTestFactory.nonRotationTests(
+ fun getParams() =
+ LegacyFlickerTestFactory.nonRotationTests(
// TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy.
supportedNavigationModes = listOf(NavBar.MODE_GESTURAL)
)
- }
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromTaskbar.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromTaskbar.kt
index 4af7e24..15cae69 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromTaskbar.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromTaskbar.kt
@@ -22,8 +22,8 @@
import android.tools.common.NavBar
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.ICommonAssertions
import com.android.wm.shell.flicker.SPLIT_SCREEN_DIVIDER_COMPONENT
@@ -45,13 +45,13 @@
* Test enter split screen by dragging app icon from taskbar. This test is only for large screen
* devices.
*
- * To run this test: `atest WMShellFlickerTests:EnterSplitScreenByDragFromTaskbar`
+ * To run this test: `atest WMShellFlickerTestsSplitScreen:EnterSplitScreenByDragFromTaskbar`
*/
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class EnterSplitScreenByDragFromTaskbar(override val flicker: FlickerTest) :
+class EnterSplitScreenByDragFromTaskbar(override val flicker: LegacyFlickerTest) :
EnterSplitScreenByDragFromTaskbarBenchmark(flicker), ICommonAssertions {
/** {@inheritDoc} */
override val transition: FlickerBuilder.() -> Unit
@@ -163,10 +163,9 @@
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<FlickerTest> {
- return FlickerTestFactory.nonRotationTests(
+ fun getParams() =
+ LegacyFlickerTestFactory.nonRotationTests(
supportedNavigationModes = listOf(NavBar.MODE_GESTURAL)
)
- }
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenFromOverview.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenFromOverview.kt
index faad9e8..90399fc 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenFromOverview.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenFromOverview.kt
@@ -20,8 +20,8 @@
import android.platform.test.annotations.Presubmit
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.ICommonAssertions
import com.android.wm.shell.flicker.appWindowBecomesVisible
@@ -40,13 +40,13 @@
/**
* Test enter split screen from Overview.
*
- * To run this test: `atest WMShellFlickerTests:EnterSplitScreenFromOverview`
+ * To run this test: `atest WMShellFlickerTestsSplitScreen:EnterSplitScreenFromOverview`
*/
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class EnterSplitScreenFromOverview(override val flicker: FlickerTest) :
+class EnterSplitScreenFromOverview(override val flicker: LegacyFlickerTest) :
EnterSplitScreenFromOverviewBenchmark(flicker), ICommonAssertions {
override val transition: FlickerBuilder.() -> Unit
get() = {
@@ -106,8 +106,6 @@
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<FlickerTest> {
- return FlickerTestFactory.nonRotationTests()
- }
+ fun getParams() = LegacyFlickerTestFactory.nonRotationTests()
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenBase.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenBase.kt
index 195b73a..580b153 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenBase.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenBase.kt
@@ -18,11 +18,11 @@
import android.content.Context
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTest
import com.android.server.wm.flicker.helpers.setRotation
import com.android.wm.shell.flicker.BaseBenchmarkTest
-abstract class SplitScreenBase(flicker: FlickerTest) : BaseBenchmarkTest(flicker) {
+abstract class SplitScreenBase(flicker: LegacyFlickerTest) : BaseBenchmarkTest(flicker) {
protected val context: Context = instrumentation.context
protected val primaryApp = SplitScreenUtils.getPrimary(instrumentation)
protected val secondaryApp = SplitScreenUtils.getSecondary(instrumentation)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenUtils.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenUtils.kt
index 1063dfd..27eaa40 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenUtils.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenUtils.kt
@@ -39,6 +39,7 @@
import com.android.server.wm.flicker.helpers.NotificationAppHelper
import com.android.server.wm.flicker.helpers.SimpleAppHelper
import com.android.server.wm.flicker.testapp.ActivityOptions
+import com.android.server.wm.flicker.testapp.ActivityOptions.SplitScreen.Primary
import com.android.wm.shell.flicker.LAUNCHER_UI_PACKAGE_NAME
import com.android.wm.shell.flicker.SYSTEM_UI_PACKAGE_NAME
import org.junit.Assert.assertNotNull
@@ -112,6 +113,17 @@
waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
}
+ fun enterSplitViaIntent(
+ wmHelper: WindowManagerStateHelper,
+ primaryApp: StandardAppHelper,
+ secondaryApp: StandardAppHelper
+ ) {
+ val stringExtras = mapOf(Primary.EXTRA_LAUNCH_ADJACENT to "true")
+ primaryApp.launchViaIntent(wmHelper, null, null,
+ stringExtras)
+ waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
+ }
+
fun splitFromOverview(tapl: LauncherInstrumentation, device: UiDevice) {
// Note: The initial split position in landscape is different between tablet and phone.
// In landscape, tablet will let the first app split to right side, and phone will
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt
index 03b8a75..e0a47b3 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
@@ -22,8 +22,8 @@
import android.tools.common.NavBar
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.ICommonAssertions
import com.android.wm.shell.flicker.SPLIT_SCREEN_DIVIDER_COMPONENT
@@ -44,13 +44,13 @@
/**
* Test double tap the divider bar to switch the two apps.
*
- * To run this test: `atest WMShellFlickerTests:SwitchAppByDoubleTapDivider`
+ * To run this test: `atest WMShellFlickerTestsSplitScreen:SwitchAppByDoubleTapDivider`
*/
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class SwitchAppByDoubleTapDivider(override val flicker: FlickerTest) :
+class SwitchAppByDoubleTapDivider(override val flicker: LegacyFlickerTest) :
SwitchAppByDoubleTapDividerBenchmark(flicker), ICommonAssertions {
override val transition: FlickerBuilder.() -> Unit
get() = {
@@ -120,11 +120,10 @@
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<FlickerTest> {
- return FlickerTestFactory.nonRotationTests(
+ fun getParams() =
+ LegacyFlickerTestFactory.nonRotationTests(
// TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy.
supportedNavigationModes = listOf(NavBar.MODE_GESTURAL)
)
- }
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromAnotherApp.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromAnotherApp.kt
index 078d95d..a406009 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromAnotherApp.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromAnotherApp.kt
@@ -21,8 +21,8 @@
import android.tools.common.NavBar
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.ICommonAssertions
import com.android.wm.shell.flicker.appWindowBecomesVisible
@@ -39,13 +39,13 @@
/**
* Test quick switch to split pair from another app.
*
- * To run this test: `atest WMShellFlickerTests:SwitchBackToSplitFromAnotherApp`
+ * To run this test: `atest WMShellFlickerTestsSplitScreen:SwitchBackToSplitFromAnotherApp`
*/
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class SwitchBackToSplitFromAnotherApp(override val flicker: FlickerTest) :
+class SwitchBackToSplitFromAnotherApp(override val flicker: LegacyFlickerTest) :
SwitchBackToSplitFromAnotherAppBenchmark(flicker), ICommonAssertions {
override val transition: FlickerBuilder.() -> Unit
get() = {
@@ -149,11 +149,10 @@
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<FlickerTest> {
- return FlickerTestFactory.nonRotationTests(
+ fun getParams() =
+ LegacyFlickerTestFactory.nonRotationTests(
// TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy.
supportedNavigationModes = listOf(NavBar.MODE_GESTURAL)
)
- }
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt
index 7c84243..251bd10 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt
@@ -21,8 +21,8 @@
import android.tools.common.NavBar
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.ICommonAssertions
import com.android.wm.shell.flicker.appWindowBecomesVisible
@@ -39,13 +39,13 @@
/**
* Test quick switch to split pair from home.
*
- * To run this test: `atest WMShellFlickerTests:SwitchBackToSplitFromHome`
+ * To run this test: `atest WMShellFlickerTestsSplitScreen:SwitchBackToSplitFromHome`
*/
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class SwitchBackToSplitFromHome(override val flicker: FlickerTest) :
+class SwitchBackToSplitFromHome(override val flicker: LegacyFlickerTest) :
SwitchBackToSplitFromHomeBenchmark(flicker), ICommonAssertions {
override val transition: FlickerBuilder.() -> Unit
get() = {
@@ -149,11 +149,10 @@
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<FlickerTest> {
- return FlickerTestFactory.nonRotationTests(
+ fun getParams() =
+ LegacyFlickerTestFactory.nonRotationTests(
// TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy.
supportedNavigationModes = listOf(NavBar.MODE_GESTURAL)
)
- }
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt
index 7c46d3e..1dd45fe 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt
@@ -21,8 +21,8 @@
import android.tools.common.NavBar
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.ICommonAssertions
import com.android.wm.shell.flicker.appWindowBecomesVisible
@@ -39,13 +39,13 @@
/**
* Test switch back to split pair from recent.
*
- * To run this test: `atest WMShellFlickerTests:SwitchBackToSplitFromRecent`
+ * To run this test: `atest WMShellFlickerTestsSplitScreen:SwitchBackToSplitFromRecent`
*/
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class SwitchBackToSplitFromRecent(override val flicker: FlickerTest) :
+class SwitchBackToSplitFromRecent(override val flicker: LegacyFlickerTest) :
SwitchBackToSplitFromRecentBenchmark(flicker), ICommonAssertions {
override val transition: FlickerBuilder.() -> Unit
get() = {
@@ -149,11 +149,10 @@
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<FlickerTest> {
- return FlickerTestFactory.nonRotationTests(
+ fun getParams() =
+ LegacyFlickerTestFactory.nonRotationTests(
// TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy.
supportedNavigationModes = listOf(NavBar.MODE_GESTURAL)
)
- }
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairs.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairs.kt
index 674ba40..8f867df 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
@@ -21,8 +21,8 @@
import android.platform.test.annotations.Presubmit
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.ICommonAssertions
import com.android.wm.shell.flicker.SPLIT_SCREEN_DIVIDER_COMPONENT
@@ -47,13 +47,13 @@
/**
* Test quick switch between two split pairs.
*
- * To run this test: `atest WMShellFlickerTests:SwitchBetweenSplitPairs`
+ * To run this test: `atest WMShellFlickerTestsSplitScreen:SwitchBetweenSplitPairs`
*/
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class SwitchBetweenSplitPairs(override val flicker: FlickerTest) :
+class SwitchBetweenSplitPairs(override val flicker: LegacyFlickerTest) :
SwitchBetweenSplitPairsBenchmark(flicker), ICommonAssertions {
override val transition: FlickerBuilder.() -> Unit
get() = {
@@ -223,8 +223,6 @@
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<FlickerTest> {
- return FlickerTestFactory.nonRotationTests()
- }
+ fun getParams() = LegacyFlickerTestFactory.nonRotationTests()
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/UnlockKeyguardToSplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/UnlockKeyguardToSplitScreen.kt
index 676c150..994d6cb 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/UnlockKeyguardToSplitScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/UnlockKeyguardToSplitScreen.kt
@@ -21,8 +21,8 @@
import android.tools.common.flicker.subject.region.RegionSubject
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.ICommonAssertions
import com.android.wm.shell.flicker.SPLIT_SCREEN_DIVIDER_COMPONENT
@@ -37,21 +37,21 @@
import org.junit.runners.Parameterized
/**
- * Test unlocking insecure keyguard to back to split screen tasks and verify the transition behavior.
+ * Test unlocking insecure keyguard to back to split screen tasks and verify the transition
+ * behavior.
*
- * To run this test: `atest WMShellFlickerTests:UnlockKeyguardToSplitScreen`
+ * To run this test: `atest WMShellFlickerTestsSplitScreen:UnlockKeyguardToSplitScreen`
*/
@RequiresDevice
@Postsubmit
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class UnlockKeyguardToSplitScreen(override val flicker: FlickerTest) :
- UnlockKeyguardToSplitScreenBenchmark(flicker), ICommonAssertions {
+class UnlockKeyguardToSplitScreen(override val flicker: LegacyFlickerTest) :
+ UnlockKeyguardToSplitScreenBenchmark(flicker), ICommonAssertions {
/** {@inheritDoc} */
override val transition: FlickerBuilder.() -> Unit
get() = {
- defaultSetup(this)
defaultTeardown(this)
thisTransition(this)
}
@@ -65,33 +65,35 @@
@Test
fun primaryAppBoundsIsVisibleAtEnd() =
- flicker.splitAppLayerBoundsIsVisibleAtEnd(
- primaryApp,
- landscapePosLeft = false,
- portraitPosTop = false
- )
+ flicker.splitAppLayerBoundsIsVisibleAtEnd(
+ primaryApp,
+ landscapePosLeft = false,
+ portraitPosTop = false
+ )
@Test
fun secondaryAppBoundsIsVisibleAtEnd() =
- flicker.splitAppLayerBoundsIsVisibleAtEnd(
- secondaryApp,
- landscapePosLeft = true,
- portraitPosTop = true
- )
+ flicker.splitAppLayerBoundsIsVisibleAtEnd(
+ secondaryApp,
+ landscapePosLeft = true,
+ portraitPosTop = true
+ )
- @Test
- fun primaryAppWindowIsVisibleAtEnd() = flicker.appWindowIsVisibleAtEnd(primaryApp)
+ @Test fun primaryAppWindowIsVisibleAtEnd() = flicker.appWindowIsVisibleAtEnd(primaryApp)
- @Test
- fun secondaryAppWindowIsVisibleAtEnd() = flicker.appWindowIsVisibleAtEnd(secondaryApp)
+ @Test fun secondaryAppWindowIsVisibleAtEnd() = flicker.appWindowIsVisibleAtEnd(secondaryApp)
@Test
fun notOverlapsForPrimaryAndSecondaryAppLayers() {
flicker.assertLayers {
this.invoke("notOverlapsForPrimaryAndSecondaryLayers") {
- val primaryAppRegions = it.subjects.filter { subject ->
- subject.name.contains(primaryApp.toLayerName()) && subject.isVisible
- }.mapNotNull { primaryApp -> primaryApp.layer.visibleRegion }.toTypedArray()
+ val primaryAppRegions =
+ it.subjects
+ .filter { subject ->
+ subject.name.contains(primaryApp.toLayerName()) && subject.isVisible
+ }
+ .mapNotNull { primaryApp -> primaryApp.layer.visibleRegion }
+ .toTypedArray()
val primaryAppRegionArea = RegionSubject(primaryAppRegions, it.timestamp)
it.visibleRegion(secondaryApp).notOverlaps(primaryAppRegionArea.region)
@@ -102,10 +104,9 @@
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<FlickerTest> {
- return FlickerTestFactory.nonRotationTests(
- supportedNavigationModes = listOf(NavBar.MODE_GESTURAL)
+ fun getParams() =
+ LegacyFlickerTestFactory.nonRotationTests(
+ supportedNavigationModes = listOf(NavBar.MODE_GESTURAL)
)
- }
}
-}
\ No newline at end of file
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/CopyContentInSplitBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/CopyContentInSplitBenchmark.kt
index c3c5f88..d1ca9ea 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/CopyContentInSplitBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/CopyContentInSplitBenchmark.kt
@@ -21,8 +21,8 @@
import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.splitscreen.SplitScreenBase
import com.android.wm.shell.flicker.splitscreen.SplitScreenUtils
@@ -36,7 +36,7 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class CopyContentInSplitBenchmark(override val flicker: FlickerTest) :
+open class CopyContentInSplitBenchmark(override val flicker: LegacyFlickerTest) :
SplitScreenBase(flicker) {
protected val textEditApp = SplitScreenUtils.getIme(instrumentation)
protected val magnifierLayer = ComponentNameMatcher("", "magnifier surface bbq wrapper#")
@@ -72,8 +72,6 @@
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<FlickerTest> {
- return FlickerTestFactory.nonRotationTests()
- }
+ fun getParams() = LegacyFlickerTestFactory.nonRotationTests()
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByDividerBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByDividerBenchmark.kt
index 37cd18f..73acb1f 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByDividerBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByDividerBenchmark.kt
@@ -20,8 +20,8 @@
import android.platform.test.annotations.Presubmit
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.splitScreenDismissed
import com.android.wm.shell.flicker.splitscreen.SplitScreenBase
@@ -36,7 +36,8 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class DismissSplitScreenByDividerBenchmark(flicker: FlickerTest) : SplitScreenBase(flicker) {
+open class DismissSplitScreenByDividerBenchmark(override val flicker: LegacyFlickerTest) :
+ SplitScreenBase(flicker) {
protected val thisTransition: FlickerBuilder.() -> Unit
get() = {
setup { SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp) }
@@ -76,8 +77,6 @@
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<FlickerTest> {
- return FlickerTestFactory.nonRotationTests()
- }
+ fun getParams() = LegacyFlickerTestFactory.nonRotationTests()
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByGoHomeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByGoHomeBenchmark.kt
index 0ec6dc9..86ffd2a 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByGoHomeBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByGoHomeBenchmark.kt
@@ -20,8 +20,8 @@
import android.platform.test.annotations.Presubmit
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.splitScreenDismissed
import com.android.wm.shell.flicker.splitscreen.SplitScreenBase
@@ -36,7 +36,7 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class DismissSplitScreenByGoHomeBenchmark(override val flicker: FlickerTest) :
+open class DismissSplitScreenByGoHomeBenchmark(override val flicker: LegacyFlickerTest) :
SplitScreenBase(flicker) {
protected val thisTransition: FlickerBuilder.() -> Unit
get() = {
@@ -63,8 +63,6 @@
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<FlickerTest> {
- return FlickerTestFactory.nonRotationTests()
- }
+ fun getParams() = LegacyFlickerTestFactory.nonRotationTests()
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DragDividerToResizeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DragDividerToResizeBenchmark.kt
index 190e2e7..dfde3b6 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DragDividerToResizeBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DragDividerToResizeBenchmark.kt
@@ -20,8 +20,8 @@
import android.platform.test.annotations.Presubmit
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.splitscreen.SplitScreenBase
import com.android.wm.shell.flicker.splitscreen.SplitScreenUtils
@@ -37,7 +37,7 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class DragDividerToResizeBenchmark(override val flicker: FlickerTest) :
+open class DragDividerToResizeBenchmark(override val flicker: LegacyFlickerTest) :
SplitScreenBase(flicker) {
protected val thisTransition: FlickerBuilder.() -> Unit
get() = {
@@ -69,8 +69,6 @@
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<FlickerTest> {
- return FlickerTestFactory.nonRotationTests()
- }
+ fun getParams() = LegacyFlickerTestFactory.nonRotationTests()
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromAllAppsBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromAllAppsBenchmark.kt
index 3a1d1a4..d13e413 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromAllAppsBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromAllAppsBenchmark.kt
@@ -21,8 +21,8 @@
import android.tools.common.NavBar
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.splitScreenEntered
import com.android.wm.shell.flicker.splitscreen.SplitScreenBase
@@ -39,7 +39,7 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class EnterSplitScreenByDragFromAllAppsBenchmark(override val flicker: FlickerTest) :
+open class EnterSplitScreenByDragFromAllAppsBenchmark(override val flicker: LegacyFlickerTest) :
SplitScreenBase(flicker) {
protected val thisTransition: FlickerBuilder.() -> Unit
@@ -84,11 +84,10 @@
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<FlickerTest> {
- return FlickerTestFactory.nonRotationTests(
+ fun getParams() =
+ LegacyFlickerTestFactory.nonRotationTests(
// TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy.
supportedNavigationModes = listOf(NavBar.MODE_GESTURAL)
)
- }
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromNotificationBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromNotificationBenchmark.kt
index 2033b7d..1d41669 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromNotificationBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromNotificationBenchmark.kt
@@ -21,8 +21,8 @@
import android.tools.common.NavBar
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.splitScreenEntered
import com.android.wm.shell.flicker.splitscreen.SplitScreenBase
@@ -39,8 +39,9 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class EnterSplitScreenByDragFromNotificationBenchmark(override val flicker: FlickerTest) :
- SplitScreenBase(flicker) {
+open class EnterSplitScreenByDragFromNotificationBenchmark(
+ override val flicker: LegacyFlickerTest
+) : SplitScreenBase(flicker) {
protected val sendNotificationApp = SplitScreenUtils.getSendNotification(instrumentation)
protected val thisTransition: FlickerBuilder.() -> Unit
get() = {
@@ -81,11 +82,10 @@
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<FlickerTest> {
- return FlickerTestFactory.nonRotationTests(
+ fun getParams() =
+ LegacyFlickerTestFactory.nonRotationTests(
// TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy.
supportedNavigationModes = listOf(NavBar.MODE_GESTURAL)
)
- }
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromShortcutBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromShortcutBenchmark.kt
index b7a7110..b4bafa7 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromShortcutBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromShortcutBenchmark.kt
@@ -21,8 +21,8 @@
import android.tools.common.NavBar
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.splitScreenEntered
import com.android.wm.shell.flicker.splitscreen.SplitScreenBase
@@ -39,7 +39,7 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class EnterSplitScreenByDragFromShortcutBenchmark(flicker: FlickerTest) :
+open class EnterSplitScreenByDragFromShortcutBenchmark(override val flicker: LegacyFlickerTest) :
SplitScreenBase(flicker) {
@Before
fun before() {
@@ -84,11 +84,10 @@
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<FlickerTest> {
- return FlickerTestFactory.nonRotationTests(
+ fun getParams() =
+ LegacyFlickerTestFactory.nonRotationTests(
// TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy.
supportedNavigationModes = listOf(NavBar.MODE_GESTURAL)
)
- }
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromTaskbarBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromTaskbarBenchmark.kt
index b1ce62f..da44ecd 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromTaskbarBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromTaskbarBenchmark.kt
@@ -21,8 +21,8 @@
import android.tools.common.NavBar
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.splitScreenEntered
import com.android.wm.shell.flicker.splitscreen.SplitScreenBase
@@ -39,7 +39,7 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class EnterSplitScreenByDragFromTaskbarBenchmark(override val flicker: FlickerTest) :
+open class EnterSplitScreenByDragFromTaskbarBenchmark(override val flicker: LegacyFlickerTest) :
SplitScreenBase(flicker) {
protected val thisTransition: FlickerBuilder.() -> Unit
get() = {
@@ -84,10 +84,9 @@
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<FlickerTest> {
- return FlickerTestFactory.nonRotationTests(
+ fun getParams() =
+ LegacyFlickerTestFactory.nonRotationTests(
supportedNavigationModes = listOf(NavBar.MODE_GESTURAL)
)
- }
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenFromOverviewBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenFromOverviewBenchmark.kt
index 14f0745..af06d6d 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenFromOverviewBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenFromOverviewBenchmark.kt
@@ -20,8 +20,8 @@
import android.platform.test.annotations.Presubmit
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.splitScreenEntered
import com.android.wm.shell.flicker.splitscreen.SplitScreenBase
@@ -36,7 +36,7 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class EnterSplitScreenFromOverviewBenchmark(override val flicker: FlickerTest) :
+open class EnterSplitScreenFromOverviewBenchmark(override val flicker: LegacyFlickerTest) :
SplitScreenBase(flicker) {
protected val thisTransition: FlickerBuilder.() -> Unit
get() = {
@@ -72,8 +72,6 @@
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<FlickerTest> {
- return FlickerTestFactory.nonRotationTests()
- }
+ fun getParams() = LegacyFlickerTestFactory.nonRotationTests()
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchAppByDoubleTapDividerBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchAppByDoubleTapDividerBenchmark.kt
index 65fb135..23156b5 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchAppByDoubleTapDividerBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchAppByDoubleTapDividerBenchmark.kt
@@ -22,8 +22,8 @@
import android.tools.common.Rotation
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import android.tools.device.helpers.WindowUtils
import android.tools.device.traces.parsers.WindowManagerStateHelper
import androidx.test.filters.RequiresDevice
@@ -39,7 +39,7 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class SwitchAppByDoubleTapDividerBenchmark(override val flicker: FlickerTest) :
+open class SwitchAppByDoubleTapDividerBenchmark(override val flicker: LegacyFlickerTest) :
SplitScreenBase(flicker) {
protected val thisTransition: FlickerBuilder.() -> Unit
get() = {
@@ -145,11 +145,10 @@
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<FlickerTest> {
- return FlickerTestFactory.nonRotationTests(
+ fun getParams() =
+ LegacyFlickerTestFactory.nonRotationTests(
// TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy.
supportedNavigationModes = listOf(NavBar.MODE_GESTURAL)
)
- }
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromAnotherAppBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromAnotherAppBenchmark.kt
index b333aba..2d810d3 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromAnotherAppBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromAnotherAppBenchmark.kt
@@ -21,8 +21,8 @@
import android.tools.common.NavBar
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.splitScreenEntered
import com.android.wm.shell.flicker.splitscreen.SplitScreenBase
@@ -37,7 +37,7 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class SwitchBackToSplitFromAnotherAppBenchmark(override val flicker: FlickerTest) :
+open class SwitchBackToSplitFromAnotherAppBenchmark(override val flicker: LegacyFlickerTest) :
SplitScreenBase(flicker) {
private val thirdApp = SplitScreenUtils.getNonResizeable(instrumentation)
@@ -71,11 +71,10 @@
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<FlickerTest> {
- return FlickerTestFactory.nonRotationTests(
+ fun getParams() =
+ LegacyFlickerTestFactory.nonRotationTests(
// TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy.
supportedNavigationModes = listOf(NavBar.MODE_GESTURAL)
)
- }
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromHomeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromHomeBenchmark.kt
index a27540e..f6df1e4 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromHomeBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromHomeBenchmark.kt
@@ -21,8 +21,8 @@
import android.tools.common.NavBar
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.splitScreenEntered
import com.android.wm.shell.flicker.splitscreen.SplitScreenBase
@@ -37,7 +37,7 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class SwitchBackToSplitFromHomeBenchmark(override val flicker: FlickerTest) :
+open class SwitchBackToSplitFromHomeBenchmark(override val flicker: LegacyFlickerTest) :
SplitScreenBase(flicker) {
protected val thisTransition: FlickerBuilder.() -> Unit
get() = {
@@ -69,11 +69,10 @@
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<FlickerTest> {
- return FlickerTestFactory.nonRotationTests(
+ fun getParams() =
+ LegacyFlickerTestFactory.nonRotationTests(
// TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy.
supportedNavigationModes = listOf(NavBar.MODE_GESTURAL)
)
- }
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromRecentBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromRecentBenchmark.kt
index 18bf4ff..ba46bdc 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromRecentBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromRecentBenchmark.kt
@@ -21,8 +21,8 @@
import android.tools.common.NavBar
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.splitScreenEntered
import com.android.wm.shell.flicker.splitscreen.SplitScreenBase
@@ -37,7 +37,7 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class SwitchBackToSplitFromRecentBenchmark(override val flicker: FlickerTest) :
+open class SwitchBackToSplitFromRecentBenchmark(override val flicker: LegacyFlickerTest) :
SplitScreenBase(flicker) {
protected val thisTransition: FlickerBuilder.() -> Unit
get() = {
@@ -69,11 +69,10 @@
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<FlickerTest> {
- return FlickerTestFactory.nonRotationTests(
+ fun getParams() =
+ LegacyFlickerTestFactory.nonRotationTests(
// TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy.
supportedNavigationModes = listOf(NavBar.MODE_GESTURAL)
)
- }
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBetweenSplitPairsBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBetweenSplitPairsBenchmark.kt
index c5fe61e..0d871e5 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBetweenSplitPairsBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBetweenSplitPairsBenchmark.kt
@@ -20,8 +20,8 @@
import android.platform.test.annotations.Presubmit
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.splitscreen.SplitScreenBase
import com.android.wm.shell.flicker.splitscreen.SplitScreenUtils
@@ -35,7 +35,7 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class SwitchBetweenSplitPairsBenchmark(override val flicker: FlickerTest) :
+open class SwitchBetweenSplitPairsBenchmark(override val flicker: LegacyFlickerTest) :
SplitScreenBase(flicker) {
protected val thirdApp = SplitScreenUtils.getIme(instrumentation)
protected val fourthApp = SplitScreenUtils.getSendNotification(instrumentation)
@@ -70,8 +70,6 @@
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<FlickerTest> {
- return FlickerTestFactory.nonRotationTests()
- }
+ fun getParams() = LegacyFlickerTestFactory.nonRotationTests()
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/UnlockKeyguardToSplitScreenBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/UnlockKeyguardToSplitScreenBenchmark.kt
index 5f16e5b..7952b71 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/UnlockKeyguardToSplitScreenBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/UnlockKeyguardToSplitScreenBenchmark.kt
@@ -19,8 +19,8 @@
import android.tools.common.NavBar
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.splitscreen.SplitScreenBase
import com.android.wm.shell.flicker.splitscreen.SplitScreenUtils
@@ -33,11 +33,11 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class UnlockKeyguardToSplitScreenBenchmark(override val flicker: FlickerTest) :
- SplitScreenBase(flicker) {
+open class UnlockKeyguardToSplitScreenBenchmark(override val flicker: LegacyFlickerTest) :
+ SplitScreenBase(flicker) {
protected val thisTransition: FlickerBuilder.() -> Unit
get() = {
- setup { SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp) }
+ setup { SplitScreenUtils.enterSplitViaIntent(wmHelper, primaryApp, secondaryApp) }
transitions {
device.sleep()
wmHelper.StateSyncBuilder().withAppTransitionIdle().waitForAndVerify()
@@ -58,10 +58,9 @@
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<FlickerTest> {
- return FlickerTestFactory.nonRotationTests(
- supportedNavigationModes = listOf(NavBar.MODE_GESTURAL)
+ fun getParams() =
+ LegacyFlickerTestFactory.nonRotationTests(
+ supportedNavigationModes = listOf(NavBar.MODE_GESTURAL)
)
- }
}
-}
\ No newline at end of file
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/trace_config/trace_config.textproto b/libs/WindowManager/Shell/tests/flicker/trace_config/trace_config.textproto
new file mode 100644
index 0000000..406ada9
--- /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/BubbleOverflowTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/BubbleOverflowTest.java
index 8278c67..0dc16f4 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/BubbleOverflowTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/BubbleOverflowTest.java
@@ -64,18 +64,27 @@
}
@Test
- public void test_initialize() {
+ public void test_initialize_forStack() {
assertThat(mOverflow.getExpandedView()).isNull();
- mOverflow.initialize(mBubbleController);
+ mOverflow.initialize(mBubbleController, /* forBubbleBar= */ false);
assertThat(mOverflow.getExpandedView()).isNotNull();
assertThat(mOverflow.getExpandedView().getBubbleKey()).isEqualTo(BubbleOverflow.KEY);
+ assertThat(mOverflow.getBubbleBarExpandedView()).isNull();
+ }
+
+ @Test
+ public void test_initialize_forBubbleBar() {
+ mOverflow.initialize(mBubbleController, /* forBubbleBar= */ true);
+
+ assertThat(mOverflow.getBubbleBarExpandedView()).isNotNull();
+ assertThat(mOverflow.getExpandedView()).isNull();
}
@Test
public void test_cleanUpExpandedState() {
- mOverflow.createExpandedView();
+ mOverflow.initialize(mBubbleController, /* forBubbleBar= */ false);
assertThat(mOverflow.getExpandedView()).isNotNull();
mOverflow.cleanUpExpandedState();
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/MockToken.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/MockToken.java
similarity index 89%
rename from libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/MockToken.java
rename to libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/MockToken.java
index 09d474d..a97c19f 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 @@
/**
* {@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/BubbleDataRepositoryTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataRepositoryTest.kt
new file mode 100644
index 0000000..6d9d62d
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataRepositoryTest.kt
@@ -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.wm.shell.bubbles
+
+import android.app.ActivityTaskManager
+import android.content.pm.LauncherApps
+import android.content.pm.ShortcutInfo
+import android.util.SparseArray
+import com.android.wm.shell.ShellTestCase
+import com.android.wm.shell.bubbles.storage.BubbleEntity
+import com.android.wm.shell.bubbles.storage.BubblePersistentRepository
+import com.android.wm.shell.common.ShellExecutor
+import com.google.common.truth.Truth.assertThat
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.spy
+
+class BubbleDataRepositoryTest : ShellTestCase() {
+
+ private val user0BubbleEntities = listOf(
+ BubbleEntity(
+ userId = 0,
+ packageName = "com.example.messenger",
+ shortcutId = "shortcut-1",
+ key = "0k1",
+ desiredHeight = 120,
+ desiredHeightResId = 0,
+ title = null,
+ taskId = 1,
+ locus = null,
+ isDismissable = true
+ ),
+ BubbleEntity(
+ userId = 10,
+ packageName = "com.example.chat",
+ shortcutId = "alice and bob",
+ key = "0k2",
+ desiredHeight = 0,
+ desiredHeightResId = 16537428,
+ title = "title",
+ taskId = 2,
+ locus = null
+ ),
+ BubbleEntity(
+ userId = 0,
+ packageName = "com.example.messenger",
+ shortcutId = "shortcut-2",
+ key = "0k3",
+ desiredHeight = 120,
+ desiredHeightResId = 0,
+ title = null,
+ taskId = ActivityTaskManager.INVALID_TASK_ID,
+ locus = null
+ )
+ )
+
+ private val user1BubbleEntities = listOf(
+ BubbleEntity(
+ userId = 1,
+ packageName = "com.example.messenger",
+ shortcutId = "shortcut-1",
+ key = "1k1",
+ desiredHeight = 120,
+ desiredHeightResId = 0,
+ title = null,
+ taskId = 3,
+ locus = null,
+ isDismissable = true
+ ),
+ BubbleEntity(
+ userId = 12,
+ packageName = "com.example.chat",
+ shortcutId = "alice and bob",
+ key = "1k2",
+ desiredHeight = 0,
+ desiredHeightResId = 16537428,
+ title = "title",
+ taskId = 4,
+ locus = null
+ ),
+ BubbleEntity(
+ userId = 1,
+ packageName = "com.example.messenger",
+ shortcutId = "shortcut-2",
+ key = "1k3",
+ desiredHeight = 120,
+ desiredHeightResId = 0,
+ title = null,
+ taskId = ActivityTaskManager.INVALID_TASK_ID,
+ locus = null
+ ),
+ BubbleEntity(
+ userId = 12,
+ packageName = "com.example.chat",
+ shortcutId = "alice",
+ key = "1k4",
+ desiredHeight = 0,
+ desiredHeightResId = 16537428,
+ title = "title",
+ taskId = 5,
+ locus = null
+ )
+ )
+
+ private val mainExecutor = mock(ShellExecutor::class.java)
+ private val launcherApps = mock(LauncherApps::class.java)
+
+ private val persistedBubbles = SparseArray<List<BubbleEntity>>()
+
+ private lateinit var dataRepository: BubbleDataRepository
+ private lateinit var persistentRepository: BubblePersistentRepository
+
+ @Before
+ fun setup() {
+ persistentRepository = spy(BubblePersistentRepository(mContext))
+ dataRepository = BubbleDataRepository(launcherApps, mainExecutor, persistentRepository)
+
+ // Add the bubbles to the persistent repository
+ persistedBubbles.put(0, user0BubbleEntities)
+ persistedBubbles.put(1, user1BubbleEntities)
+ persistentRepository.persistsToDisk(persistedBubbles)
+ }
+
+ @After
+ fun teardown() {
+ // Clean up any persisted bubbles for the next run
+ persistentRepository.persistsToDisk(SparseArray())
+ }
+
+ @Test
+ fun testLoadBubbles_invalidParent() {
+ val activeUserIds = listOf(10, 1, 12) // Missing user 0 in persistedBubbles
+ dataRepository.loadBubbles(1, activeUserIds) {
+ // Verify that user 0 has been removed from the persisted list
+ val entitiesByUser = persistentRepository.readFromDisk()
+ assertThat(entitiesByUser.get(0)).isNull()
+ }
+ }
+
+ @Test
+ fun testLoadBubbles_invalidChild() {
+ val activeUserIds = listOf(0, 10, 1) // Missing user 1's child user 12
+ dataRepository.loadBubbles(1, activeUserIds) {
+ // Build a list to compare against
+ val user1BubblesWithoutUser12 = mutableListOf<Bubble>()
+ val user1EntitiesWithoutUser12 = mutableListOf<BubbleEntity>()
+ for (entity in user1BubbleEntities) {
+ if (entity.userId != 12) {
+ user1BubblesWithoutUser12.add(entity.toBubble())
+ user1EntitiesWithoutUser12.add(entity)
+ }
+ }
+
+ // Verify that user 12 has been removed from the persisted list
+ val entitiesByUser = persistentRepository.readFromDisk()
+ assertThat(entitiesByUser.get(1)).isEqualTo(user1EntitiesWithoutUser12)
+ }
+ }
+
+ private fun BubbleEntity.toBubble(): Bubble {
+ return Bubble(
+ key,
+ mock(ShortcutInfo::class.java),
+ desiredHeight,
+ desiredHeightResId,
+ title,
+ taskId,
+ locus,
+ isDismissable,
+ mainExecutor,
+ mock(Bubbles.BubbleMetadataFlagListener::class.java)
+ )
+ }
+}
\ No newline at end of file
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 0000000..d38b848
--- /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 0000000..9dc816b
--- /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 0000000..145c8f0
--- /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 d6387ee..605a762 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 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 3bc2f0e..3fe78ef 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 @@
}
@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 @@
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 @@
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 @@
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 1335ebf..1477cf7 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 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 @@
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 @@
@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 @@
return DesktopTasksController(
context,
shellInit,
+ shellCommandHandler,
shellController,
displayController,
shellTaskOrganizer,
@@ -122,8 +131,10 @@
transitions,
enterDesktopTransitionHandler,
exitDesktopTransitionHandler,
+ mToggleResizeDesktopTaskTransitionHandler,
desktopModeTaskRepository,
- TestShellExecutor()
+ launchAdjacentController,
+ shellExecutor
)
}
@@ -262,8 +273,9 @@
}
@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 @@
}
@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 @@
}
@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 @@
}
@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 @@
}
@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 @@
}
@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 @@
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 cf1ff32..29a757c 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.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 68cb57c..b1befc4 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.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.Optional;
+
/** Tests for {@link MainStage} */
@SmallTest
@RunWith(AndroidJUnit4.class)
@@ -61,7 +63,7 @@
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 3b42a48..549bd3f 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.MockitoAnnotations;
import org.mockito.Spy;
+import java.util.Optional;
+
/** Tests for {@link SideStage} */
@SmallTest
@RunWith(AndroidJUnit4.class)
@@ -66,7 +68,7 @@
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 fb17d87..e8a1e91 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.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.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 @@
@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 @@
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 4e446c6..a3009a5 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.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 @@
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 3b05651..5efd9ad 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.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 @@
@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 @@
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 2dcdc74..e59d09c 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.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 @@
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 @@
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 1a1bebd..df1e2e1 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.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.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.Optional;
+
/**
* Tests for {@link StageTaskListener}
* Build/Install/Run:
@@ -71,6 +74,8 @@
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 @@
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 41bab95..23158ea 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.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 @@
@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 @@
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 8f84008..3fbab0f 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 @@
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 @@
@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 @@
@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 @@
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 @@
@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 @@
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 @@
@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 @@
@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 @@
@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 @@
@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 5a2326b..7fc1c99 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.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 @@
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 @@
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 @@
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 16b35ff..a5518eb 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -405,8 +405,17 @@
// 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 @@
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 5219b57..32ac5af 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -366,6 +366,12 @@
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 67d2184..917276b 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 02c4fea..be397a1 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
similarity index 71%
rename from packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/SlicePurchaseWebInterface.java
rename to packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/DataBoostWebServiceFlow.java
index 8547898..4500a22 100644
--- a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/SlicePurchaseWebInterface.java
+++ b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/DataBoostWebServiceFlow.java
@@ -24,13 +24,13 @@
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 @@
* This can be called using the JavaScript below:
* <script type="text/javascript">
* function getRequestedCapability(duration) {
- * SlicePurchaseWebInterface.getRequestedCapability();
+ * DataBoostWebServiceFlow.getRequestedCapability();
* }
* </script>
*/
@@ -57,16 +57,14 @@
*
* 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 @@
* 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 @@
@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 946185a..2530257d6 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.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 @@
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 @@
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 @@
// 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 daf0b42..cc103fa 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 @@
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 @@
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 @@
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 aae30df..a0b3469 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 2c0af5d..6c54f70 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 b8964b5..d9ddf71 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 3a4a211..8a6f227 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 1ac0ed3..28a06e6 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 79a045d..18ac2ae 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 bb16290..b37ab4b9 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 9c12f5a..41889fd 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 4df4177..bb53c5c 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 735b620..bc0af0bd 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 2de0838..538ee35 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 38a6dbc..b6f182d 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 7151823..b62496d 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 14f2454..df63e6c 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 d8c6ac3b..f487f94 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 37fc39f..1c8ecb7 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 2727d86..ca12145 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 cc221b4..bd0342d 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 2727d86..ca12145 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 2727d86..ca12145 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 431a69c..70af915 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 26868f6..cba0b46 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 fedb8ae..50d8806 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 29a04bc..2eff7eb 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 a4f29e3..e130074 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 d6c1a13..5e78aa5 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 72122cb..552c473 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 6b3ed28..753875b 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 b649f27..d3704a0 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 e7ff1e3..a7aa72d 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 436d6cb..443fb4b 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 dd8e831..494af37 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 d32a319..5e175bc 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 5943178..ba480f6 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 17f46ed..9385fd1 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 39ffcd7..c03a5ab 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 3f55d5d..fc9f5a1 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 117d2aa..be431b6 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 573b8a7..8982660 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 0439d5f1..186944a 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 e62afad..a7c6042 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 92951cb..5c6d24a 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 18bd88c..c734cb6 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 1080b63..5f04cd3 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 b131a2f..e4c56b2 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 568a1739..d33cced 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 08ea7a9..0072e4b 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 0a800ee..65a266d 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 43dedbd..1d29edc 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 7d053a7..e72e960 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 21dcfa1..1703742 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 0c31abf..613c9aa 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 b94cfac..50ab307 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 93745c8..ef110b6 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 3f298a0..57f2e23 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 2c794d9..083ec99 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 1f4506a..7a1df5f 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 7c79cb4..71d14e7 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 dbff4e2..a058374 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 91b5264..984ec48 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 1432a45..7ca172e 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 c45cda6..26647c8 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 8619ff4..ba60f56 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 c45cda6..26647c8 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 f74e08ee..84182e8 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 cdf1668..911bddc 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 41224c6..daa2058 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 220f00e..929b56c 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 562e474..b1b1e91 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 8e32ec7..037a707 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 34d9ca2..3817d58 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 54e2d18..f3be3be 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 6e8c607..7b94239 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 d049953..c1b8352 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 5e91614..52316cd 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 46e631c..cb01f2d 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 b358870..43d5996 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 c5229ab..1e8e842 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 221a869..39a2b1b 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 8acfc29..ce44941 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 95d5b1b..82b6937 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 24fdd4a..951f429 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 401a263..5edf9c0 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 52230cb..0878c23 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 c680157..4f50ef5 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 22495e2..7fb5ece 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 2502bbf..7a6fad4 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 e85190b..222877b 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 4154029..97016f5 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 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 @@
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 @@
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 @@
// 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 @@
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 @@
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 @@
throw new RuntimeException("Unsupported profile " + deviceProfile);
}
- mPermissionTypes = new ArrayList<>();
-
try {
vendorIcon = getVendorHeaderIcon(this, packageName, userId);
vendorName = getVendorHeaderName(this, packageName, userId);
@@ -486,17 +483,13 @@
}
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 @@
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 @@
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;
+ final Drawable profileIcon = getIcon(this, PROFILE_ICON.get(deviceProfile));
- if (!SUPPORTED_PROFILES.contains(deviceProfile)) {
- throw new RuntimeException("Unsupported profile " + deviceProfile);
- }
+ updatePermissionUi();
- 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));
-
- 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 @@
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 @@
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 @@
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 @@
// 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 7aed139..060c032 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceResources.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceResources.java
@@ -86,21 +86,11 @@
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 b265a42..e1eb36a 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 @@
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 @@
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 @@
}
}
+ 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 b522729..7401691 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 @@
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 7c2eff1..2a477f5 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-af/strings.xml b/packages/PackageInstaller/res/values-af/strings.xml
index 3545179..14ed179 100644
--- a/packages/PackageInstaller/res/values-af/strings.xml
+++ b/packages/PackageInstaller/res/values-af/strings.xml
@@ -26,7 +26,9 @@
<string name="install_done" msgid="5987363587661783896">"Program geïnstalleer."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"Wil jy hierdie program installeer?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"Wil jy hierdie program opdateer?"</string>
- <string name="install_confirm_question_update_owner_reminder" msgid="3750986542284587290">"Dateer hierdie app vanaf <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g> op?\n\nHierdie app kry gewoonlik opdaterings vanaf <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. As jy vanaf ’n ander bron opdateer, kan jy in die toekoms dalk opdaterings vanaf enige bron op jou foon kry. Appfunksie kan verander."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tablet" msgid="738046584021528374">"Dateer hierdie app vanaf <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g> op?\n\nHierdie app kry gewoonlik opdaterings vanaf <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. As jy vanaf ’n ander bron opdateer, kan jy in die toekoms dalk opdaterings vanaf enige bron op jou tablet kry. Appfunksies kan verander."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tv" msgid="3056133099508550163">"Dateer hierdie app vanaf <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g> op?\n\nHierdie app kry gewoonlik opdaterings vanaf <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. As jy vanaf ’n ander bron opdateer, kan jy in die toekoms dalk opdaterings vanaf enige bron op jou TV kry. Appfunksies kan verander."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="default" msgid="3750986542284587290">"Dateer hierdie app vanaf <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g> op?\n\nHierdie app kry gewoonlik opdaterings vanaf <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. As jy vanaf ’n ander bron opdateer, kan jy in die toekoms dalk opdaterings vanaf enige bron op jou foon kry. Appfunksie kan verander."</string>
<string name="install_failed" msgid="5777824004474125469">"Program nie geïnstalleer nie."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"Die installering van die pakket is geblokkeer."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"Program is nie geïnstalleer nie omdat pakket met \'n bestaande pakket bots."</string>
diff --git a/packages/PackageInstaller/res/values-am/strings.xml b/packages/PackageInstaller/res/values-am/strings.xml
index 3dab467c..1fb5c12 100644
--- a/packages/PackageInstaller/res/values-am/strings.xml
+++ b/packages/PackageInstaller/res/values-am/strings.xml
@@ -26,7 +26,9 @@
<string name="install_done" msgid="5987363587661783896">"መተግበሪያ ተጭኗል።"</string>
<string name="install_confirm_question" msgid="7663733664476363311">"ይህን መተግበሪያ መጫን ይፈልጋሉ?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"ይህን መተግበሪያ ማዘመን ይፈልጋሉ?"</string>
- <string name="install_confirm_question_update_owner_reminder" msgid="3750986542284587290">"ይህ መተግበሪያ ከ<xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g> ይዘምን?\n\nይህ መተግበሪያ በመደበኛነት ዝማኔዎችን ከ<xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g> ይቀበላል። ከተለየ ምንጭ በማዘመን በስልክዎ ላይ ካለ ማንኛውም ምንጭ የወደፊት ዝማኔዎችን ሊቀበሉ ይችላሉ። የመተግበሪያ ተግባራዊነት ሊለወጥ ይችላል።"</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tablet" msgid="738046584021528374">"ይህ መተግበሪያ ከ<xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g> ይዘምን?\n\nይህ መተግበሪያ በመደበኛነት ዝማኔዎችን ከ<xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g> ይቀበላል። ከተለየ ምንጭ በማዘመን በጡባዊዎ ላይ ካለ ማንኛውም ምንጭ የወደፊት ዝማኔዎችን ሊቀበሉ ይችላሉ። የመተግበሪያ ተግባራዊነት ሊለወጥ ይችላል።"</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tv" msgid="3056133099508550163">"ይህ መተግበሪያ ከ<xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g> ይዘምን?\n\nይህ መተግበሪያ በመደበኛነት ዝማኔዎችን ከ<xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g> ይቀበላል። ከተለየ ምንጭ በማዘመን በቲቪዎ ላይ ካለ ማንኛውም ምንጭ የወደፊት ዝማኔዎችን ሊቀበሉ ይችላሉ። የመተግበሪያ ተግባራዊነት ሊለወጥ ይችላል።"</string>
+ <string name="install_confirm_question_update_owner_reminder" product="default" msgid="3750986542284587290">"ይህ መተግበሪያ ከ<xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g> ይዘምን?\n\nይህ መተግበሪያ በመደበኛነት ዝማኔዎችን ከ<xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g> ይቀበላል። ከተለየ ምንጭ በማዘመን በስልክዎ ላይ ካለ ማንኛውም ምንጭ የወደፊት ዝማኔዎችን ሊቀበሉ ይችላሉ። የመተግበሪያ ተግባራዊነት ሊለወጥ ይችላል።"</string>
<string name="install_failed" msgid="5777824004474125469">"መተግበሪያ አልተጫነም።"</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"ጥቅሉ እንዳይጫን ታግዷል።"</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"እንደ ጥቅል ያልተጫነ መተግበሪያ ከነባር ጥቅል ጋር ይጋጫል።"</string>
diff --git a/packages/PackageInstaller/res/values-ar/strings.xml b/packages/PackageInstaller/res/values-ar/strings.xml
index 170c1fc..a92b016 100644
--- a/packages/PackageInstaller/res/values-ar/strings.xml
+++ b/packages/PackageInstaller/res/values-ar/strings.xml
@@ -26,7 +26,9 @@
<string name="install_done" msgid="5987363587661783896">"تم تثبيت التطبيق."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"هل تريد تثبيت هذا التطبيق؟"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"هل تريد تحديث هذا التطبيق؟"</string>
- <string name="install_confirm_question_update_owner_reminder" msgid="3750986542284587290">"هل تريد تحديث هذا التطبيق من خلال \"<xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>\"؟\n\nيتلقّى هذا التطبيق التحديثات عادةً من \"<xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>\". من خلال إجراء التحديث من مصدر مختلف، قد تتلقّى تحديثات في المستقبل من أي مصدر على هاتفك. قد تتغير وظائف التطبيق."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tablet" msgid="738046584021528374">"هل تريد تحديث هذا التطبيق من خلال \"<xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>\"؟\n\nيتلقّى هذا التطبيق التحديثات عادةً من \"<xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>\". من خلال تحديثه من مصدر مختلف، قد يتلقّى تحديثات في المستقبل من أي مصدر على جهازك اللوحي. قد تتغير وظائف التطبيق."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tv" msgid="3056133099508550163">"هل تريد تحديث هذا التطبيق من خلال \"<xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>\"؟\n\nيتلقّى هذا التطبيق التحديثات عادةً من \"<xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>\". من خلال تحديثه من مصدر مختلف، قد يتلقّى تحديثات في المستقبل من أي مصدر على التلفزيون. قد تتغير وظائف التطبيق."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="default" msgid="3750986542284587290">"هل تريد تحديث هذا التطبيق من خلال \"<xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>\"؟\n\nيتلقّى هذا التطبيق التحديثات عادةً من \"<xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>\". من خلال إجراء التحديث من مصدر مختلف، قد تتلقّى تحديثات في المستقبل من أي مصدر على هاتفك. قد تتغير وظائف التطبيق."</string>
<string name="install_failed" msgid="5777824004474125469">"التطبيق ليس مثبتًا."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"تم حظر تثبيت الحزمة."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"لم يتم تثبيت التطبيق لأن حزمة التثبيت تتعارض مع حزمة حالية."</string>
diff --git a/packages/PackageInstaller/res/values-as/strings.xml b/packages/PackageInstaller/res/values-as/strings.xml
index 37f6c13..e6b46ae 100644
--- a/packages/PackageInstaller/res/values-as/strings.xml
+++ b/packages/PackageInstaller/res/values-as/strings.xml
@@ -26,7 +26,9 @@
<string name="install_done" msgid="5987363587661783896">"এপ্ ইনষ্টল কৰা হ’ল।"</string>
<string name="install_confirm_question" msgid="7663733664476363311">"আপুনি এই এপ্টো ইনষ্টল কৰিবলৈ বিচাৰেনে?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"আপুনি এই এপ্টো আপডে’ট কৰিবলৈ বিচাৰেনে?"</string>
- <string name="install_confirm_question_update_owner_reminder" msgid="3750986542284587290">"এই এপ্টো <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>ৰ পৰা আপডে’ট কৰিবনে?\n\nএই এপ্টোৱে সাধাৰণতে <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>ৰ পৰা আপডে’ট লাভ কৰে। অন্য এটা উৎসৰ পৰা আপডে’ট কৰি আপুনি যিকোনো উৎসৰ পৰা আপোনাৰ ফ’নত অনাগত আপডে’টসমূহ পাব পাৰে। এপৰ কাৰ্যক্ষমতা সলনি হ’ব পাৰে।"</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tablet" msgid="738046584021528374">"এই এপ্টো <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>ৰ পৰা আপডে’ট কৰিবনে?\n\nএই এপ্টোৱে সাধাৰণতে <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>ৰ পৰা আপডে’ট লাভ কৰে। অন্য এটা উৎসৰ পৰা আপডে’ট কৰি আপুনি যিকোনো উৎসৰ পৰা আপোনাৰ টেবলেটত অনাগত আপডে’টসমূহ পাব পাৰে। এপৰ কাৰ্যক্ষমতা সলনি হ’ব পাৰে।"</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tv" msgid="3056133099508550163">"এই এপ্টো <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>ৰ পৰা আপডে’ট কৰিবনে?\n\nএই এপ্টোৱে সাধাৰণতে <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>ৰ পৰা আপডে’ট লাভ কৰে। অন্য এটা উৎসৰ পৰা আপডে’ট কৰি আপুনি যিকোনো উৎসৰ পৰা আপোনাৰ টিভিত অনাগত আপডে’টসমূহ পাব পাৰে। এপৰ কাৰ্যক্ষমতা সলনি হ’ব পাৰে।"</string>
+ <string name="install_confirm_question_update_owner_reminder" product="default" msgid="3750986542284587290">"এই এপ্টো <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>ৰ পৰা আপডে’ট কৰিবনে?\n\nএই এপ্টোৱে সাধাৰণতে <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>ৰ পৰা আপডে’ট লাভ কৰে। অন্য এটা উৎসৰ পৰা আপডে’ট কৰি আপুনি যিকোনো উৎসৰ পৰা আপোনাৰ ফ’নত অনাগত আপডে’টসমূহ পাব পাৰে। এপৰ কাৰ্যক্ষমতা সলনি হ’ব পাৰে।"</string>
<string name="install_failed" msgid="5777824004474125469">"এপ্ ইনষ্টল কৰা হোৱা নাই।"</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"পেকেজটোৰ ইনষ্টল অৱৰোধ কৰা হৈছে।"</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"এপ্টো ইনষ্টল কৰিব পৰা নগ\'ল কাৰণ ইয়াৰ সৈতে আগৰে পৰা থকা এটা পেকেজৰ সংঘাত হৈছে।"</string>
diff --git a/packages/PackageInstaller/res/values-az/strings.xml b/packages/PackageInstaller/res/values-az/strings.xml
index ae7b2fc..a1b4fca 100644
--- a/packages/PackageInstaller/res/values-az/strings.xml
+++ b/packages/PackageInstaller/res/values-az/strings.xml
@@ -26,7 +26,9 @@
<string name="install_done" msgid="5987363587661783896">"Tətbiq quraşdırılıb."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"Bu tətbiqi quraşdırmaq istəyirsiniz?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"Bu tətbiqi güncəlləmək istəyirsiniz?"</string>
- <string name="install_confirm_question_update_owner_reminder" msgid="3750986542284587290">"Bu tətbiq <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g> mənbəyindən güncəllənsin?\n\nBu tətbiq adətən <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g> mənbəyindən güncəllənmələr qəbul edir. Fərqli mənbədən güncəllədikdə telefonda istənilən mənbədən gələcəkdə güncəllənmələr qəbul edə bilərsiniz. Tətbiq funksionallığı dəyişə bilər."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tablet" msgid="738046584021528374">"Bu tətbiq <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g> mənbəyindən güncəllənsin?\n\nBu tətbiq adətən <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g> mənbəyindən güncəllənir. Fərqli mənbədən güncəllədikdə gələcəkdə planşetdə istənilən mənbədən güncəlləyə bilərsiniz. Tətbiq funksionallığı dəyişə bilər."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tv" msgid="3056133099508550163">"Bu tətbiq <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g> mənbəyindən güncəllənsin?\n\nBu tətbiq adətən <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g> mənbəyindən güncəllənir. Fərqli mənbədən güncəllədikdə gələcəkdə TV-də istənilən mənbədən güncəlləyə bilərsiniz. Tətbiq funksionallığı dəyişə bilər."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="default" msgid="3750986542284587290">"Bu tətbiq <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g> mənbəyindən güncəllənsin?\n\nBu tətbiq adətən <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g> mənbəyindən güncəllənmələr qəbul edir. Fərqli mənbədən güncəllədikdə telefonda istənilən mənbədən gələcəkdə güncəllənmələr qəbul edə bilərsiniz. Tətbiq funksionallığı dəyişə bilər."</string>
<string name="install_failed" msgid="5777824004474125469">"Tətbiq quraşdırılmayıb."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"Paketin quraşdırılması blok edildi."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"Bu paketin mövcud paket ilə ziddiyətinə görə tətbiq quraşdırılmadı."</string>
diff --git a/packages/PackageInstaller/res/values-b+sr+Latn/strings.xml b/packages/PackageInstaller/res/values-b+sr+Latn/strings.xml
index 2b0fa82..953b227 100644
--- a/packages/PackageInstaller/res/values-b+sr+Latn/strings.xml
+++ b/packages/PackageInstaller/res/values-b+sr+Latn/strings.xml
@@ -26,7 +26,9 @@
<string name="install_done" msgid="5987363587661783896">"Aplikacija je instalirana."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"Želite da instalirate ovu aplikaciju?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"Želite da ažurirate ovu aplikaciju?"</string>
- <string name="install_confirm_question_update_owner_reminder" msgid="3750986542284587290">"Želite da ažurirate ovu aplikaciju iz izvora <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nOva aplikacija se obično ažurira iz izvora <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Ako ažurirate iz drugog izvora, možete da primate buduća ažuriranja iz bilo kog izvora na telefonu. Funkcije aplikacije mogu da se promene."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tablet" msgid="738046584021528374">"Želite da ažurirate ovu aplikaciju iz izvora <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nOva aplikacija se obično ažurira iz izvora <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Ako ažurirate iz drugog izvora, možete da primate buduća ažuriranja iz bilo kog izvora na tabletu. Funkcije aplikacije mogu da se promene."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tv" msgid="3056133099508550163">"Želite da ažurirate ovu aplikaciju iz izvora <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nOva aplikacija se obično ažurira iz izvora <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Ako ažurirate iz drugog izvora, možete da primate buduća ažuriranja iz bilo kog izvora na TV-u. Funkcije aplikacije mogu da se promene."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="default" msgid="3750986542284587290">"Želite da ažurirate ovu aplikaciju iz izvora <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nOva aplikacija se obično ažurira iz izvora <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Ako ažurirate iz drugog izvora, možete da primate buduća ažuriranja iz bilo kog izvora na telefonu. Funkcije aplikacije mogu da se promene."</string>
<string name="install_failed" msgid="5777824004474125469">"Aplikacija nije instalirana."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"Instaliranje paketa je blokirano."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"Aplikacija nije instalirana jer je paket neusaglašen sa postojećim paketom."</string>
diff --git a/packages/PackageInstaller/res/values-be/strings.xml b/packages/PackageInstaller/res/values-be/strings.xml
index d18e009..0e47d72 100644
--- a/packages/PackageInstaller/res/values-be/strings.xml
+++ b/packages/PackageInstaller/res/values-be/strings.xml
@@ -26,7 +26,9 @@
<string name="install_done" msgid="5987363587661783896">"Праграма ўсталявана."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"Усталяваць гэту праграму?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"Абнавіць гэту праграму?"</string>
- <string name="install_confirm_question_update_owner_reminder" msgid="3750986542284587290">"Абнавіць праграму ад <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nЗвычайна гэтая праграма атрымлівае абнаўленні ад <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Пры абнаўленні з іншай крыніцы вы, магчыма, будзеце атрымліваць будучыя абнаўленні з любой крыніцы на тэлефоне. Функцыі праграмы могуць змяніцца."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tablet" msgid="738046584021528374">"Абнавіць праграму з <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nЗвычайна гэтая праграма атрымлівае абнаўленні з <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Калі абнавіць праграму з іншай крыніцы, то ў будучыні вы, магчыма, будзеце атрымліваць абнаўленні з любых крыніц на планшэце. Функцыі праграмы могуць змяніцца."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tv" msgid="3056133099508550163">"Абнавіць праграму з <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nЗвычайна гэтая праграма атрымлівае абнаўленні з <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Калі абнавіць праграму з іншай крыніцы, то ў будучыні вы, магчыма, будзеце атрымліваць абнаўленні з любых крыніц на тэлевізары. Функцыі праграмы могуць змяніцца."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="default" msgid="3750986542284587290">"Абнавіць праграму ад <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nЗвычайна гэтая праграма атрымлівае абнаўленні ад <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Пры абнаўленні з іншай крыніцы вы, магчыма, будзеце атрымліваць будучыя абнаўленні з любой крыніцы на тэлефоне. Функцыі праграмы могуць змяніцца."</string>
<string name="install_failed" msgid="5777824004474125469">"Праграма не ўсталявана."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"Усталяванне пакета заблакіравана."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"Праграма не ўсталявана, таму што пакет канфліктуе з існуючым пакетам."</string>
diff --git a/packages/PackageInstaller/res/values-bg/strings.xml b/packages/PackageInstaller/res/values-bg/strings.xml
index 6dc927f..6a38129 100644
--- a/packages/PackageInstaller/res/values-bg/strings.xml
+++ b/packages/PackageInstaller/res/values-bg/strings.xml
@@ -26,7 +26,9 @@
<string name="install_done" msgid="5987363587661783896">"Приложението бе инсталирано."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"Искате ли да инсталирате това приложение?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"Искате ли да актуализирате това приложение?"</string>
- <string name="install_confirm_question_update_owner_reminder" msgid="3750986542284587290">"Да се актуализира ли това приложение от <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nТо обикновено получава актуализации от <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Ако инсталирате актуализация от друг източник, може да получавате бъдещи актуализации от който и да е източник на телефона си. Функционалността на приложението може да се промени."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tablet" msgid="738046584021528374">"Да се актуализира ли това приложение от <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nТо обикновено получава актуализации от <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Ако инсталирате актуализация от друг източник, може да получавате бъдещи актуализации от който и да е източник на таблета си. Функционалността на приложението може да се промени."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tv" msgid="3056133099508550163">"Да се актуализира ли това приложение от <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nТо обикновено получава актуализации от <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Ако инсталирате актуализация от друг източник, може да получавате бъдещи актуализации от който и да е източник на телевизора си. Функционалността на приложението може да се промени."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="default" msgid="3750986542284587290">"Да се актуализира ли това приложение от <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nТо обикновено получава актуализации от <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Ако инсталирате актуализация от друг източник, може да получавате бъдещи актуализации от който и да е източник на телефона си. Функционалността на приложението може да се промени."</string>
<string name="install_failed" msgid="5777824004474125469">"Приложението не бе инсталирано."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"Инсталирането на пакета бе блокирано."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"Приложението не бе инсталирано, тъй като пакетът е в конфликт със съществуващ пакет."</string>
diff --git a/packages/PackageInstaller/res/values-bn/strings.xml b/packages/PackageInstaller/res/values-bn/strings.xml
index 5b5c6dc..95cec04 100644
--- a/packages/PackageInstaller/res/values-bn/strings.xml
+++ b/packages/PackageInstaller/res/values-bn/strings.xml
@@ -26,7 +26,9 @@
<string name="install_done" msgid="5987363587661783896">"অ্যাপটি ইনস্টল করা হয়ে গেছে।"</string>
<string name="install_confirm_question" msgid="7663733664476363311">"আপনি কি এই অ্যাপটি ইনস্টল করতে চান?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"আপনি কি এই অ্যাপটি আপডেট করতে চান?"</string>
- <string name="install_confirm_question_update_owner_reminder" msgid="3750986542284587290">"<xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g> থেকে এই অ্যাপ আপডেট করবেন?\n\nএই অ্যাপ সাধারণত <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g> থেকে আপডেট পায়। অন্য কোনও সোর্স থেকে আপডেট করলে, আপনার ফোনে ভবিষ্যতে যেকোনও সোর্স থেকে আপডেট পেতে পারেন। অ্যাপের কার্যকারিতা পরিবর্তন হতে পারে।"</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tablet" msgid="738046584021528374">"<xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>-এর থেকে এই অ্যাপ আপডেট করতে চান?\n\nসাধারণত <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>-এর থেকেই এই অ্যাপ আপডেট পায়। অন্য কোনও সোর্স থেকে আপডেট করলে, আপনার ট্যাবলেটে ভবিষ্যতে যেকোনও সোর্স থেকে আপডেট পাঠানো হতে পারে। তার ফলে অ্যাপের কার্যকারিতার ক্ষেত্রে পরিবর্তন হতে পারে।"</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tv" msgid="3056133099508550163">"<xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>-এর থেকে এই অ্যাপ আপডেট করতে চান?\n\nসাধারণত <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>-এর থেকেই এই অ্যাপ আপডেট পায়। অন্য কোনও সোর্স থেকে আপডেট করলে, আপনার টিভিতে ভবিষ্যতে যেকোনও সোর্স থেকে আপডেট পাঠানো হতে পারে। তার ফলে অ্যাপের কার্যকারিতার ক্ষেত্রে পরিবর্তন হতে পারে।"</string>
+ <string name="install_confirm_question_update_owner_reminder" product="default" msgid="3750986542284587290">"<xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g> থেকে এই অ্যাপ আপডেট করবেন?\n\nএই অ্যাপ সাধারণত <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g> থেকে আপডেট পায়। অন্য কোনও সোর্স থেকে আপডেট করলে, আপনার ফোনে ভবিষ্যতে যেকোনও সোর্স থেকে আপডেট পেতে পারেন। অ্যাপের কার্যকারিতা পরিবর্তন হতে পারে।"</string>
<string name="install_failed" msgid="5777824004474125469">"অ্যাপটি ইনস্টল করা হয়নি।"</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"ইনস্টল হওয়া থেকে প্যাকেজটিকে ব্লক করা হয়েছে।"</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"আগে থেকেই থাকা একটি প্যাকেজের সাথে প্যাকেজটির সমস্যা সৃষ্টি হওয়ায় অ্যাপটি ইনস্টল করা যায়নি।"</string>
diff --git a/packages/PackageInstaller/res/values-bs/strings.xml b/packages/PackageInstaller/res/values-bs/strings.xml
index e728937..56de9f2 100644
--- a/packages/PackageInstaller/res/values-bs/strings.xml
+++ b/packages/PackageInstaller/res/values-bs/strings.xml
@@ -26,7 +26,9 @@
<string name="install_done" msgid="5987363587661783896">"Aplikacija je instalirana."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"Želite li instalirati ovu aplikaciju?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"Želite li ažurirati ovu aplikaciju?"</string>
- <string name="install_confirm_question_update_owner_reminder" msgid="3750986542284587290">"Ažurirati aplikaciju iz izvora <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nOva aplikacija obično prima ažuriranja iz izvora <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Ako je ažurirate iz drugog izvora, možda ćete primati buduća ažuriranja iz bilo kojeg izvora na telefonu. Funkcionalnost aplikacije se može promijeniti."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tablet" msgid="738046584021528374">"Ažurirati aplikaciju iz izvora <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nOva aplikacija obično prima ažuriranja iz izvora <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Ako je ažurirate iz drugog izvora, možda ćete primati buduća ažuriranja iz bilo kojeg izvora na tabletu. Funkcionalnost aplikacije se može promijeniti."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tv" msgid="3056133099508550163">"Ažurirati aplikaciju iz izvora <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nOva aplikacija obično prima ažuriranja iz izvora <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Ako je ažurirate iz drugog izvora, možda ćete primati buduća ažuriranja iz bilo kojeg izvora na TV-u. Funkcionalnost aplikacije se može promijeniti."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="default" msgid="3750986542284587290">"Ažurirati aplikaciju iz izvora <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nOva aplikacija obično prima ažuriranja iz izvora <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Ako je ažurirate iz drugog izvora, možda ćete primati buduća ažuriranja iz bilo kojeg izvora na telefonu. Funkcionalnost aplikacije se može promijeniti."</string>
<string name="install_failed" msgid="5777824004474125469">"Aplikacija nije instalirana."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"Instaliranje ovog paketa je blokirano."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"Aplikacija nije instalirana jer paket nije usaglašen s postojećim paketom."</string>
diff --git a/packages/PackageInstaller/res/values-ca/strings.xml b/packages/PackageInstaller/res/values-ca/strings.xml
index 8b0d1a8..00d6f24 100644
--- a/packages/PackageInstaller/res/values-ca/strings.xml
+++ b/packages/PackageInstaller/res/values-ca/strings.xml
@@ -26,7 +26,9 @@
<string name="install_done" msgid="5987363587661783896">"S\'ha instal·lat l\'aplicació."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"Vols instal·lar aquesta aplicació?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"Vols actualitzar aquesta aplicació?"</string>
- <string name="install_confirm_question_update_owner_reminder" msgid="3750986542284587290">"Vols actualitzar l\'aplicació des de <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nAquesta aplicació sol rebre actualitzacions a través de <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Si l\'actualitzes des d\'una font diferent, pot ser que en el futur rebis actualitzacions des de qualsevol font del teu telèfon. És possible que la funcionalitat de l\'aplicació canviï."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tablet" msgid="738046584021528374">"Vols actualitzar l\'aplicació de <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nAquesta aplicació sol rebre actualitzacions de <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Si l\'actualitzes des d\'una font diferent, pot ser que en el futur rebis actualitzacions des de qualsevol font de la teva tauleta. És possible que la funcionalitat de l\'aplicació canviï."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tv" msgid="3056133099508550163">"Vols actualitzar l\'aplicació de <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nAquesta aplicació sol rebre actualitzacions de <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Si l\'actualitzes des d\'una font diferent, pot ser que en el futur rebis actualitzacions des de qualsevol font del teu televisor. És possible que la funcionalitat de l\'aplicació canviï."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="default" msgid="3750986542284587290">"Vols actualitzar l\'aplicació des de <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nAquesta aplicació sol rebre actualitzacions a través de <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Si l\'actualitzes des d\'una font diferent, pot ser que en el futur rebis actualitzacions des de qualsevol font del teu telèfon. És possible que la funcionalitat de l\'aplicació canviï."</string>
<string name="install_failed" msgid="5777824004474125469">"No s\'ha instal·lat l\'aplicació."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"El paquet s\'ha bloquejat perquè no es pugui instal·lar."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"L\'aplicació no s\'ha instal·lat perquè el paquet entra en conflicte amb un d\'existent."</string>
diff --git a/packages/PackageInstaller/res/values-cs/strings.xml b/packages/PackageInstaller/res/values-cs/strings.xml
index c96d27e..117c6f6 100644
--- a/packages/PackageInstaller/res/values-cs/strings.xml
+++ b/packages/PackageInstaller/res/values-cs/strings.xml
@@ -26,7 +26,9 @@
<string name="install_done" msgid="5987363587661783896">"Aplikace je nainstalována."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"Chcete tuto aplikaci nainstalovat?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"Chcete tuto aplikaci aktualizovat?"</string>
- <string name="install_confirm_question_update_owner_reminder" msgid="3750986542284587290">"Aktualizovat tuto aplikaci ze zdroje <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nTato aplikace obvykle dostává aktualizace ze zdroje <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Pokud ji aktualizujete z jiného zdroje, budete v budoucnu do telefonu moci dostávat aktualizace z libovolného zdroje. Funkčnost aplikace se může změnit."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tablet" msgid="738046584021528374">"Aktualizovat tuto aplikaci ze zdroje <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nTato aplikace obvykle dostává aktualizace ze zdroje <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Pokud ji aktualizujete z jiného zdroje, budete v budoucnu do tabletu moci dostávat aktualizace z libovolného zdroje. Funkčnost aplikace se může změnit."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tv" msgid="3056133099508550163">"Aktualizovat tuto aplikaci ze zdroje <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nTato aplikace obvykle dostává aktualizace ze zdroje <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Pokud ji aktualizujete z jiného zdroje, budete v budoucnu do televize moci dostávat aktualizace z libovolného zdroje. Funkčnost aplikace se může změnit."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="default" msgid="3750986542284587290">"Aktualizovat tuto aplikaci ze zdroje <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nTato aplikace obvykle dostává aktualizace ze zdroje <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Pokud ji aktualizujete z jiného zdroje, budete v budoucnu do telefonu moci dostávat aktualizace z libovolného zdroje. Funkčnost aplikace se může změnit."</string>
<string name="install_failed" msgid="5777824004474125469">"Aplikaci nelze nainstalovat."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"Instalace balíčku byla zablokována."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"Aplikaci nelze nainstalovat, protože balíček je v konfliktu se stávajícím balíčkem."</string>
diff --git a/packages/PackageInstaller/res/values-da/strings.xml b/packages/PackageInstaller/res/values-da/strings.xml
index d8759d4..e34d8cf 100644
--- a/packages/PackageInstaller/res/values-da/strings.xml
+++ b/packages/PackageInstaller/res/values-da/strings.xml
@@ -26,7 +26,9 @@
<string name="install_done" msgid="5987363587661783896">"Appen er installeret."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"Vil du installere denne app?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"Vil du opdatere denne app?"</string>
- <string name="install_confirm_question_update_owner_reminder" msgid="3750986542284587290">"Vil du opdatere denne app fra <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nDenne app plejer at modtage opdateringer fra <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Hvis du opdaterer fra en anden kilde, vil du kunne modtage opdateringer fra en hvilken som helst kilde på din telefon fremover. Appfunktionaliteten kan ændre sig."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tablet" msgid="738046584021528374">"Vil du opdatere denne app fra <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nDenne app plejer at modtage opdateringer fra <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Hvis du opdaterer fra en anden kilde, vil du kunne modtage opdateringer fra en hvilken som helst kilde på din tablet fremover. Appfunktionaliteten kan ændre sig."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tv" msgid="3056133099508550163">"Vil du opdatere denne app fra <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nDenne app plejer at modtage opdateringer fra <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Hvis du opdaterer fra en anden kilde, vil du kunne modtage opdateringer fra en hvilken som helst kilde på dit fjernsyn fremover. Appfunktionaliteten kan ændre sig."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="default" msgid="3750986542284587290">"Vil du opdatere denne app fra <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nDenne app plejer at modtage opdateringer fra <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Hvis du opdaterer fra en anden kilde, vil du kunne modtage opdateringer fra en hvilken som helst kilde på din telefon fremover. Appfunktionaliteten kan ændre sig."</string>
<string name="install_failed" msgid="5777824004474125469">"Appen blev ikke installeret."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"Pakken blev forhindret i at blive installeret."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"Appen blev ikke installeret, da pakken er i strid med en eksisterende pakke."</string>
diff --git a/packages/PackageInstaller/res/values-de/strings.xml b/packages/PackageInstaller/res/values-de/strings.xml
index bb55708..dc9c6e6 100644
--- a/packages/PackageInstaller/res/values-de/strings.xml
+++ b/packages/PackageInstaller/res/values-de/strings.xml
@@ -26,7 +26,9 @@
<string name="install_done" msgid="5987363587661783896">"App wurde installiert."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"Möchtest du diese App installieren?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"Möchtest du diese App aktualisieren?"</string>
- <string name="install_confirm_question_update_owner_reminder" msgid="3750986542284587290">"Diese App mit einem Update von <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g> aktualisieren?\n\nSie erhält normalerweise Updates von <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Wenn du ein Update von einer anderen Quelle verwendest, erhältst du möglicherweise zukünftige Updates von beliebigen Quellen auf deinem Smartphone. Die Funktionalität der App kann sich ändern."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tablet" msgid="738046584021528374">"Diese App mit einem Update von <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g> aktualisieren?\n\nSie erhält normalerweise Updates von <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Wenn du ein Update von einer anderen Quelle verwendest, erhältst du möglicherweise zukünftige Updates von beliebigen Quellen auf deinem Tablet. Die Funktionalität der App kann sich ändern."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tv" msgid="3056133099508550163">"Diese App mit einem Update von <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g> aktualisieren?\n\nSie erhält normalerweise Updates von <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Wenn du ein Update von einer anderen Quelle verwendest, erhältst du möglicherweise zukünftige Updates von beliebigen Quellen auf deinem Fernseher. Die Funktionalität der App kann sich ändern."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="default" msgid="3750986542284587290">"Diese App mit einem Update von <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g> aktualisieren?\n\nSie erhält normalerweise Updates von <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Wenn du ein Update von einer anderen Quelle verwendest, erhältst du möglicherweise zukünftige Updates von beliebigen Quellen auf deinem Smartphone. Die Funktionalität der App kann sich ändern."</string>
<string name="install_failed" msgid="5777824004474125469">"App wurde nicht installiert."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"Die Installation des Pakets wurde blockiert."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"Die App wurde nicht installiert, da das Paket in Konflikt mit einem bestehenden Paket steht."</string>
diff --git a/packages/PackageInstaller/res/values-el/strings.xml b/packages/PackageInstaller/res/values-el/strings.xml
index 9721a19..092f34c 100644
--- a/packages/PackageInstaller/res/values-el/strings.xml
+++ b/packages/PackageInstaller/res/values-el/strings.xml
@@ -26,7 +26,9 @@
<string name="install_done" msgid="5987363587661783896">"Η εφαρμογή εγκαταστάθηκε."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"Θέλετε να εγκαταστήσετε αυτήν την εφαρμογή;"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"Θέλετε να ενημερώσετε αυτήν την εφαρμογή;"</string>
- <string name="install_confirm_question_update_owner_reminder" msgid="3750986542284587290">"Να ενημερωθεί αυτή η εφαρμογή από <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>;\n\nΗ συγκεκριμένη εφαρμογή λαμβάνει συνήθως ενημερώσεις από <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Αν κάνετε την ενημέρωση από διαφορετική πηγή, μπορεί να λαμβάνετε μελλοντικές ενημερώσεις από οποιαδήποτε πηγή στο τηλέφωνό σας. Η λειτουργικότητα της εφαρμογής μπορεί να αλλάξει."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tablet" msgid="738046584021528374">"Να ενημερωθεί αυτή η εφαρμογή από <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>;\n\nΗ συγκεκριμένη εφαρμογή λαμβάνει συνήθως ενημερώσεις από <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Αν κάνετε την ενημέρωση από διαφορετική πηγή, μπορεί να λαμβάνετε μελλοντικές ενημερώσεις από οποιαδήποτε πηγή στο tablet σας. Η λειτουργικότητα της εφαρμογής μπορεί να αλλάξει."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tv" msgid="3056133099508550163">"Να ενημερωθεί αυτή η εφαρμογή από <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>;\n\nΗ συγκεκριμένη εφαρμογή λαμβάνει συνήθως ενημερώσεις από <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Αν κάνετε την ενημέρωση από διαφορετική πηγή, μπορεί να λαμβάνετε μελλοντικές ενημερώσεις από οποιαδήποτε πηγή στην τηλεόρασή σας. Η λειτουργικότητα της εφαρμογής μπορεί να αλλάξει."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="default" msgid="3750986542284587290">"Να ενημερωθεί αυτή η εφαρμογή από <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>;\n\nΗ συγκεκριμένη εφαρμογή λαμβάνει συνήθως ενημερώσεις από <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Αν κάνετε την ενημέρωση από διαφορετική πηγή, μπορεί να λαμβάνετε μελλοντικές ενημερώσεις από οποιαδήποτε πηγή στο τηλέφωνό σας. Η λειτουργικότητα της εφαρμογής μπορεί να αλλάξει."</string>
<string name="install_failed" msgid="5777824004474125469">"Η εφαρμογή δεν εγκαταστάθηκε."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"Η εγκατάσταση του πακέτου αποκλείστηκε."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"Η εφαρμογή δεν εγκαταστάθηκε, επειδή το πακέτο είναι σε διένεξη με κάποιο υπάρχον πακέτο."</string>
diff --git a/packages/PackageInstaller/res/values-en-rAU/strings.xml b/packages/PackageInstaller/res/values-en-rAU/strings.xml
index 543dbf6..16e87a2 100644
--- a/packages/PackageInstaller/res/values-en-rAU/strings.xml
+++ b/packages/PackageInstaller/res/values-en-rAU/strings.xml
@@ -26,7 +26,9 @@
<string name="install_done" msgid="5987363587661783896">"App installed."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"Do you want to install this app?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"Do you want to update this app?"</string>
- <string name="install_confirm_question_update_owner_reminder" msgid="3750986542284587290">"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" msgid="738046584021528374">"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>
+ <string name="install_confirm_question_update_owner_reminder" product="tv" msgid="3056133099508550163">"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>
+ <string name="install_confirm_question_update_owner_reminder" product="default" msgid="3750986542284587290">"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_failed" msgid="5777824004474125469">"App not installed."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"The package was blocked from being installed."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"App not installed as package conflicts with an existing package."</string>
diff --git a/packages/PackageInstaller/res/values-en-rCA/strings.xml b/packages/PackageInstaller/res/values-en-rCA/strings.xml
index f2457f2..03c0c27 100644
--- a/packages/PackageInstaller/res/values-en-rCA/strings.xml
+++ b/packages/PackageInstaller/res/values-en-rCA/strings.xml
@@ -26,7 +26,9 @@
<string name="install_done" msgid="5987363587661783896">"App installed."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"Do you want to install this app?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"Do you want to update this app?"</string>
- <string name="install_confirm_question_update_owner_reminder" msgid="3750986542284587290">"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" msgid="738046584021528374">"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>
+ <string name="install_confirm_question_update_owner_reminder" product="tv" msgid="3056133099508550163">"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>
+ <string name="install_confirm_question_update_owner_reminder" product="default" msgid="3750986542284587290">"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_failed" msgid="5777824004474125469">"App not installed."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"The package was blocked from being installed."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"App not installed as package conflicts with an existing package."</string>
diff --git a/packages/PackageInstaller/res/values-en-rGB/strings.xml b/packages/PackageInstaller/res/values-en-rGB/strings.xml
index 543dbf6..16e87a2 100644
--- a/packages/PackageInstaller/res/values-en-rGB/strings.xml
+++ b/packages/PackageInstaller/res/values-en-rGB/strings.xml
@@ -26,7 +26,9 @@
<string name="install_done" msgid="5987363587661783896">"App installed."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"Do you want to install this app?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"Do you want to update this app?"</string>
- <string name="install_confirm_question_update_owner_reminder" msgid="3750986542284587290">"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" msgid="738046584021528374">"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>
+ <string name="install_confirm_question_update_owner_reminder" product="tv" msgid="3056133099508550163">"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>
+ <string name="install_confirm_question_update_owner_reminder" product="default" msgid="3750986542284587290">"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_failed" msgid="5777824004474125469">"App not installed."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"The package was blocked from being installed."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"App not installed as package conflicts with an existing package."</string>
diff --git a/packages/PackageInstaller/res/values-en-rIN/strings.xml b/packages/PackageInstaller/res/values-en-rIN/strings.xml
index 543dbf6..16e87a2 100644
--- a/packages/PackageInstaller/res/values-en-rIN/strings.xml
+++ b/packages/PackageInstaller/res/values-en-rIN/strings.xml
@@ -26,7 +26,9 @@
<string name="install_done" msgid="5987363587661783896">"App installed."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"Do you want to install this app?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"Do you want to update this app?"</string>
- <string name="install_confirm_question_update_owner_reminder" msgid="3750986542284587290">"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" msgid="738046584021528374">"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>
+ <string name="install_confirm_question_update_owner_reminder" product="tv" msgid="3056133099508550163">"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>
+ <string name="install_confirm_question_update_owner_reminder" product="default" msgid="3750986542284587290">"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_failed" msgid="5777824004474125469">"App not installed."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"The package was blocked from being installed."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"App not installed as package conflicts with an existing package."</string>
diff --git a/packages/PackageInstaller/res/values-en-rXC/strings.xml b/packages/PackageInstaller/res/values-en-rXC/strings.xml
index a674c20..c36b3e9 100644
--- a/packages/PackageInstaller/res/values-en-rXC/strings.xml
+++ b/packages/PackageInstaller/res/values-en-rXC/strings.xml
@@ -26,7 +26,9 @@
<string name="install_done" msgid="5987363587661783896">"App installed."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"Do you want to install this app?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"Do you want to update this app?"</string>
- <string name="install_confirm_question_update_owner_reminder" msgid="3750986542284587290">"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" msgid="738046584021528374">"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>
+ <string name="install_confirm_question_update_owner_reminder" product="tv" msgid="3056133099508550163">"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>
+ <string name="install_confirm_question_update_owner_reminder" product="default" msgid="3750986542284587290">"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_failed" msgid="5777824004474125469">"App not installed."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"The package was blocked from being installed."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"App not installed as package conflicts with an existing package."</string>
diff --git a/packages/PackageInstaller/res/values-es-rUS/strings.xml b/packages/PackageInstaller/res/values-es-rUS/strings.xml
index eb3abe9..23d1291 100644
--- a/packages/PackageInstaller/res/values-es-rUS/strings.xml
+++ b/packages/PackageInstaller/res/values-es-rUS/strings.xml
@@ -26,7 +26,9 @@
<string name="install_done" msgid="5987363587661783896">"Se instaló la app."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"¿Deseas instalar esta app?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"¿Deseas actualizar esta app?"</string>
- <string name="install_confirm_question_update_owner_reminder" msgid="3750986542284587290">"¿Quieres actualizar esta app a través de <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nEn general, esta suele recibir actualizaciones de <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Si actualizas a través de otra fuente, es posible que recibas las próximas actualizaciones de cualquier fuente en el teléfono. Por ende, podría verse afectada la funcionalidad de la app."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tablet" msgid="738046584021528374">"¿Quieres actualizar esta app a través <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nPor lo general, suele recibir actualizaciones de <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Si actualizas a través de otra fuente, es posible que recibas las próximas actualizaciones de cualquier fuente en la tablet. Por ende, podría verse afectada la funcionalidad de la app."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tv" msgid="3056133099508550163">"¿Quieres actualizar esta app a través <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nPor lo general, suele recibir actualizaciones de <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Si actualizas a través de otra fuente, es posible que recibas las próximas actualizaciones de cualquier fuente en la TV. Por ende, podría verse afectada la funcionalidad de la app."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="default" msgid="3750986542284587290">"¿Quieres actualizar esta app a través de <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nEn general, esta suele recibir actualizaciones de <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Si actualizas a través de otra fuente, es posible que recibas las próximas actualizaciones de cualquier fuente en el teléfono. Por ende, podría verse afectada la funcionalidad de la app."</string>
<string name="install_failed" msgid="5777824004474125469">"No se instaló la app."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"Se bloqueó el paquete para impedir la instalación."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"No se instaló la app debido a un conflicto con un paquete."</string>
diff --git a/packages/PackageInstaller/res/values-es/strings.xml b/packages/PackageInstaller/res/values-es/strings.xml
index 9005718..62e4776 100644
--- a/packages/PackageInstaller/res/values-es/strings.xml
+++ b/packages/PackageInstaller/res/values-es/strings.xml
@@ -26,7 +26,9 @@
<string name="install_done" msgid="5987363587661783896">"Aplicación instalada."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"¿Quieres instalar esta aplicación?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"¿Quieres actualizar esta aplicación?"</string>
- <string name="install_confirm_question_update_owner_reminder" msgid="3750986542284587290">"¿Actualizar esta aplicación a través de <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nEsta aplicación normalmente recibe actualizaciones a través de <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Si la actualizas usando otra fuente, puede que recibas futuras actualizaciones a través de cualquier fuente en tu teléfono. La funcionalidad de la aplicación puede cambiar."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tablet" msgid="738046584021528374">"¿Actualizar esta aplicación con <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nEsta aplicación suele recibir actualizaciones de <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Si actualizas a través de otra fuente, puede que recibas futuras actualizaciones de cualquier fuente de tu tablet. La funcionalidad de la aplicación puede cambiar."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tv" msgid="3056133099508550163">"¿Actualizar esta aplicación con <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nEsta aplicación suele recibir actualizaciones de <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Si actualizas a través de otra fuente, puede que recibas futuras actualizaciones de cualquier fuente de tu TV. La funcionalidad de la aplicación puede cambiar."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="default" msgid="3750986542284587290">"¿Actualizar esta aplicación a través de <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nEsta aplicación normalmente recibe actualizaciones a través de <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Si la actualizas usando otra fuente, puede que recibas futuras actualizaciones a través de cualquier fuente en tu teléfono. La funcionalidad de la aplicación puede cambiar."</string>
<string name="install_failed" msgid="5777824004474125469">"No se ha instalado la aplicación."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"Se ha bloqueado la instalación del paquete."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"La aplicación no se ha instalado debido a un conflicto con un paquete."</string>
diff --git a/packages/PackageInstaller/res/values-et/strings.xml b/packages/PackageInstaller/res/values-et/strings.xml
index cf488a9..f518b50 100644
--- a/packages/PackageInstaller/res/values-et/strings.xml
+++ b/packages/PackageInstaller/res/values-et/strings.xml
@@ -26,7 +26,9 @@
<string name="install_done" msgid="5987363587661783896">"Rakendus on installitud."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"Kas soovite selle rakenduse installida?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"Kas soovite seda rakendust värskendada?"</string>
- <string name="install_confirm_question_update_owner_reminder" msgid="3750986542284587290">"Kas värskendada seda rakendust allikast <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nSee rakendus saab tavaliselt värskendusi allikast <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Muust allikast värskendamise korral võite edaspidi saada telefonis värskendusi mis tahes allikast. Rakenduse funktsioonid võivad muutuda."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tablet" msgid="738046584021528374">"Kas värskendada seda rakendust allikast <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nSee rakendus saab tavaliselt värskendusi allikast <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Muust allikast värskendamise korral võite edaspidi saada tahvelarvutis värskendusi mis tahes allikast. Rakenduse funktsioonid võivad muutuda."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tv" msgid="3056133099508550163">"Kas värskendada seda rakendust allikast <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nSee rakendus saab tavaliselt värskendusi allikast <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Muust allikast värskendamise korral võite edaspidi saada teleris värskendusi mis tahes allikast. Rakenduse funktsioonid võivad muutuda."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="default" msgid="3750986542284587290">"Kas värskendada seda rakendust allikast <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nSee rakendus saab tavaliselt värskendusi allikast <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Muust allikast värskendamise korral võite edaspidi saada telefonis värskendusi mis tahes allikast. Rakenduse funktsioonid võivad muutuda."</string>
<string name="install_failed" msgid="5777824004474125469">"Rakendus pole installitud."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"Paketi installimine blokeeriti."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"Rakendust ei installitud, kuna pakett on olemasoleva paketiga vastuolus."</string>
diff --git a/packages/PackageInstaller/res/values-eu/strings.xml b/packages/PackageInstaller/res/values-eu/strings.xml
index 9dadbed..580791c8 100644
--- a/packages/PackageInstaller/res/values-eu/strings.xml
+++ b/packages/PackageInstaller/res/values-eu/strings.xml
@@ -26,7 +26,9 @@
<string name="install_done" msgid="5987363587661783896">"Instalatu da aplikazioa."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"Aplikazioa instalatu nahi duzu?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"Aplikazioa eguneratu nahi duzu?"</string>
- <string name="install_confirm_question_update_owner_reminder" msgid="3750986542284587290">"Aplikazioa <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g> zerbitzutik eguneratu nahi duzu?\n\nAplikazioak <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g> zerbitzutik jaso ohi ditu eguneratzeak. Beste iturburu batetik eguneratuz gero, baliteke aurrerantzeko eguneratzeak telefonoko edozein iturburutatik jasotzea. Baliteke aplikazioaren funtzioak aldatzea."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tablet" msgid="738046584021528374">"Aplikazioa <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g> zerbitzutik eguneratu nahi duzu?\n\nAplikazioak <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g> zerbitzutik jaso ohi ditu eguneratzeak. Beste iturburu batetik eguneratuz gero, baliteke aurrerantzeko eguneratzeak tabletako edozein iturburutatik jasotzea. Baliteke aplikazioaren funtzioak aldatzea."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tv" msgid="3056133099508550163">"Aplikazioa <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g> zerbitzutik eguneratu nahi duzu?\n\nAplikazioak <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g> zerbitzutik jaso ohi ditu eguneratzeak. Beste iturburu batetik eguneratuz gero, baliteke aurrerantzeko eguneratzeak telebistako edozein iturburutatik jasotzea. Baliteke aplikazioaren funtzioak aldatzea."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="default" msgid="3750986542284587290">"Aplikazioa <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g> zerbitzutik eguneratu nahi duzu?\n\nAplikazioak <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g> zerbitzutik jaso ohi ditu eguneratzeak. Beste iturburu batetik eguneratuz gero, baliteke aurrerantzeko eguneratzeak telefonoko edozein iturburutatik jasotzea. Baliteke aplikazioaren funtzioak aldatzea."</string>
<string name="install_failed" msgid="5777824004474125469">"Ez da instalatu aplikazioa."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"Paketea instalatzeko aukera blokeatu egin da."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"Ez da instalatu aplikazioa, gatazka bat sortu delako lehendik dagoen pakete batekin."</string>
diff --git a/packages/PackageInstaller/res/values-fa/strings.xml b/packages/PackageInstaller/res/values-fa/strings.xml
index 73b070d..4bde113 100644
--- a/packages/PackageInstaller/res/values-fa/strings.xml
+++ b/packages/PackageInstaller/res/values-fa/strings.xml
@@ -26,7 +26,9 @@
<string name="install_done" msgid="5987363587661783896">"برنامه نصب شد."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"میخواهید این برنامه را نصب کنید؟"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"میخواهید این برنامه را بهروزرسانی کنید؟"</string>
- <string name="install_confirm_question_update_owner_reminder" msgid="3750986542284587290">"این برنامه ازطریق <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g> بهروز شود؟\n\nاین برنامه معمولاً بهروزرسانیها را از <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g> دریافت میکند. با بهروزرسانی از منبعی متفاوت، ممکن است بهروزرسانیهای بعدی را از هر منبعی در تلفنتان دریافت کنید. قابلیتهای برنامه ممکن است تغییر کند."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tablet" msgid="738046584021528374">"این برنامه ازطریق <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g> بهروز شود؟\n\nاین برنامه معمولاً بهروزرسانیها را از <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g> دریافت میکند. با بهروزرسانی از منبعی متفاوت، ممکن است بهروزرسانیهای آتی را از هر منبعی در رایانه لوحیتان دریافت کنید. قابلیتهای برنامه ممکن است تغییر کند."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tv" msgid="3056133099508550163">"این برنامه ازطریق <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g> بهروز شود؟\n\nاین برنامه معمولاً بهروزرسانیها را از <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g> دریافت میکند. با بهروزرسانی از منبعی متفاوت، ممکن است بهروزرسانیهای آتی را از هر منبعی در تلویزیون دریافت کنید. قابلیتهای برنامه ممکن است تغییر کند."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="default" msgid="3750986542284587290">"این برنامه ازطریق <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g> بهروز شود؟\n\nاین برنامه معمولاً بهروزرسانیها را از <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g> دریافت میکند. با بهروزرسانی از منبعی متفاوت، ممکن است بهروزرسانیهای بعدی را از هر منبعی در تلفنتان دریافت کنید. قابلیتهای برنامه ممکن است تغییر کند."</string>
<string name="install_failed" msgid="5777824004474125469">"برنامه نصب نشد."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"از نصب شدن بسته جلوگیری شد."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"برنامه نصب نشد چون بسته با بسته موجود تداخل دارد."</string>
diff --git a/packages/PackageInstaller/res/values-fi/strings.xml b/packages/PackageInstaller/res/values-fi/strings.xml
index ee8910b..e71ebaf 100644
--- a/packages/PackageInstaller/res/values-fi/strings.xml
+++ b/packages/PackageInstaller/res/values-fi/strings.xml
@@ -26,7 +26,9 @@
<string name="install_done" msgid="5987363587661783896">"Sovellus on asennettu."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"Haluatko asentaa tämän sovelluksen?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"Haluatko päivittää tämän sovelluksen?"</string>
- <string name="install_confirm_question_update_owner_reminder" msgid="3750986542284587290">"Voiko <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g> päivittää sovelluksen?\n\nTämän sovelluksen päivitykset tarjoaa yleensä <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Kun päivität uudesta lähteestä, tulevat päivitykset voivat tulla mistä tahansa puhelimella olevasta lähteestä. Sovelluksen toiminnot voivat muuttua."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tablet" msgid="738046584021528374">"Voiko <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g> päivittää sovelluksen?\n\nTämän sovelluksen päivitykset tarjoaa yleensä <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Kun päivität uudesta lähteestä, tulevat päivitykset voivat tulla mistä tahansa tabletilla olevasta lähteestä. Sovelluksen toiminnot voivat muuttua."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tv" msgid="3056133099508550163">"Voiko <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g> päivittää sovelluksen?\n\nTämän sovelluksen päivitykset tarjoaa yleensä <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Kun päivität uudesta lähteestä, tulevat päivitykset voivat tulla mistä tahansa televisiolla olevasta lähteestä. Sovelluksen toiminnot voivat muuttua."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="default" msgid="3750986542284587290">"Voiko <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g> päivittää sovelluksen?\n\nTämän sovelluksen päivitykset tarjoaa yleensä <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Kun päivität uudesta lähteestä, tulevat päivitykset voivat tulla mistä tahansa puhelimella olevasta lähteestä. Sovelluksen toiminnot voivat muuttua."</string>
<string name="install_failed" msgid="5777824004474125469">"Sovellusta ei asennettu."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"Paketin asennus estettiin."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"Sovellusta ei asennettu, koska paketti on ristiriidassa nykyisen paketin kanssa."</string>
diff --git a/packages/PackageInstaller/res/values-fr-rCA/strings.xml b/packages/PackageInstaller/res/values-fr-rCA/strings.xml
index b971c35..6e48030 100644
--- a/packages/PackageInstaller/res/values-fr-rCA/strings.xml
+++ b/packages/PackageInstaller/res/values-fr-rCA/strings.xml
@@ -26,7 +26,9 @@
<string name="install_done" msgid="5987363587661783896">"Application installée."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"Voulez-vous installer cette application?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"Voulez-vous mettre à jour cette application?"</string>
- <string name="install_confirm_question_update_owner_reminder" msgid="3750986542284587290">"Mettre à jour cette application à partir de <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nCette application reçoit normalement des mises à jour de <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. En effectuant une mise à jour à partir d\'une source différente, vous pourriez recevoir des mises à jour futures à partir de n\'importe quelle source sur votre téléphone. Le fonctionnement de l\'application peut en être modifié."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tablet" msgid="738046584021528374">"Mettre à jour cette application à partir de <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nCette application reçoit normalement des mises à jour de <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. En effectuant une mise à jour à partir d\'une source différente, vous pourriez recevoir des mises à jour futures à partir de n\'importe quelle source sur votre tablette. Le fonctionnement de l\'application peut en être modifié."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tv" msgid="3056133099508550163">"Mettre à jour cette application à partir de <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nCette application reçoit normalement des mises à jour de <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. En effectuant une mise à jour à partir d\'une source différente, vous pourriez recevoir des mises à jour futures à partir de n\'importe quelle source sur votre téléviseur. Le fonctionnement de l\'application peut en être modifié."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="default" msgid="3750986542284587290">"Mettre à jour cette application à partir de <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nCette application reçoit normalement des mises à jour de <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. En effectuant une mise à jour à partir d\'une source différente, vous pourriez recevoir des mises à jour futures à partir de n\'importe quelle source sur votre téléphone. Le fonctionnement de l\'application peut en être modifié."</string>
<string name="install_failed" msgid="5777824004474125469">"Application non installée."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"L\'installation du paquet a été bloquée."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"L\'application n\'a pas été installée, car le paquet entre en conflit avec un paquet existant."</string>
diff --git a/packages/PackageInstaller/res/values-fr/strings.xml b/packages/PackageInstaller/res/values-fr/strings.xml
index 08d37d1..4b52724 100644
--- a/packages/PackageInstaller/res/values-fr/strings.xml
+++ b/packages/PackageInstaller/res/values-fr/strings.xml
@@ -26,7 +26,9 @@
<string name="install_done" msgid="5987363587661783896">"Application installée."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"Voulez-vous installer cette appli ?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"Voulez-vous mettre à jour cette appli ?"</string>
- <string name="install_confirm_question_update_owner_reminder" msgid="3750986542284587290">"Mettre à jour cette appli à partir de <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g> ?\n\nCette appli reçoit normalement des mises à jour de <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Si vous effectuez la mise à jour à partir d\'une autre source, vous recevrez peut-être les prochaines mises à jour depuis n\'importe quelle source sur votre téléphone. Le fonctionnement de l\'application est susceptible de changer."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tablet" msgid="738046584021528374">"Mettre à jour cette appli à partir de <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g> ?\n\nCette appli reçoit normalement des mises à jour de <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Si vous effectuez la mise à jour à partir d\'une autre source, vous recevrez peut-être les prochaines mises à jour depuis n\'importe quelle source sur votre tablette. Le fonctionnement de l\'application est susceptible d\'évoluer."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tv" msgid="3056133099508550163">"Mettre à jour cette appli à partir de <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g> ?\n\nCette appli reçoit normalement des mises à jour de <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Si vous effectuez la mise à jour à partir d\'une autre source, vous recevrez peut-être les prochaines mises à jour depuis n\'importe quelle source sur votre téléviseur. Le fonctionnement de l\'application est susceptible d\'évoluer."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="default" msgid="3750986542284587290">"Mettre à jour cette appli à partir de <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g> ?\n\nCette appli reçoit normalement des mises à jour de <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Si vous effectuez la mise à jour à partir d\'une autre source, vous recevrez peut-être les prochaines mises à jour depuis n\'importe quelle source sur votre téléphone. Le fonctionnement de l\'application est susceptible de changer."</string>
<string name="install_failed" msgid="5777824004474125469">"Application non installée."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"L\'installation du package a été bloquée."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"L\'application n\'a pas été installée, car le package est en conflit avec un package déjà présent."</string>
diff --git a/packages/PackageInstaller/res/values-gl/strings.xml b/packages/PackageInstaller/res/values-gl/strings.xml
index d6cbf60..5427cc6 100644
--- a/packages/PackageInstaller/res/values-gl/strings.xml
+++ b/packages/PackageInstaller/res/values-gl/strings.xml
@@ -26,7 +26,9 @@
<string name="install_done" msgid="5987363587661783896">"Instalouse a aplicación."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"Queres instalar esta aplicación?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"Queres actualizar esta aplicación?"</string>
- <string name="install_confirm_question_update_owner_reminder" msgid="3750986542284587290">"Queres actualizar esta aplicación desde <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nAs actualizacións desta aplicación adoitan provir de <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Se actualizas a aplicación desde unha fonte diferente, pode que, a partir de agora, recibas actualizacións de calquera fonte no teléfono. Poderían cambiar as funcións da aplicación."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tablet" msgid="738046584021528374">"Queres actualizar esta aplicación desde <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nAs actualizacións desta aplicación adoitan provir de <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Se actualizas a aplicación desde unha fonte diferente, pode que, a partir de agora, recibas actualizacións de calquera fonte na tableta. Poderían cambiar as funcións da aplicación."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tv" msgid="3056133099508550163">"Queres actualizar esta aplicación desde <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nAs actualizacións desta aplicación adoitan provir de <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Se actualizas a aplicación desde unha fonte diferente, pode que, a partir de agora, recibas actualizacións de calquera fonte na televisión. Poderían cambiar as funcións da aplicación."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="default" msgid="3750986542284587290">"Queres actualizar esta aplicación desde <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nAs actualizacións desta aplicación adoitan provir de <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Se actualizas a aplicación desde unha fonte diferente, pode que, a partir de agora, recibas actualizacións de calquera fonte no teléfono. Poderían cambiar as funcións da aplicación."</string>
<string name="install_failed" msgid="5777824004474125469">"Non se instalou a aplicación"</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"Bloqueouse a instalación do paquete."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"A aplicación non se instalou porque o paquete presenta un conflito cun paquete que xa hai."</string>
diff --git a/packages/PackageInstaller/res/values-gu/strings.xml b/packages/PackageInstaller/res/values-gu/strings.xml
index dcaa48f..155a31b 100644
--- a/packages/PackageInstaller/res/values-gu/strings.xml
+++ b/packages/PackageInstaller/res/values-gu/strings.xml
@@ -26,7 +26,9 @@
<string name="install_done" msgid="5987363587661783896">"ઍપ્લિકેશન ઇન્સ્ટૉલ કરી."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"શું તમે આ ઍપ ઇન્સ્ટૉલ કરવા માગો છો?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"શું તમે આ ઍપ અપડેટ કરવા માગો છો?"</string>
- <string name="install_confirm_question_update_owner_reminder" msgid="3750986542284587290">"આ ઍપને <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>થી અપડેટ કરવી છે?\n\nઆ ઍપ સામાન્ય રીતે <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>થી અપડેટ મેળવે છે. અલગ સૉર્સથી અપડેટ કરીને, તમે તમારા ફોન પર કોઈપણ સૉર્સથી ભાવિ અપડેટ મેળવી શકો છો. ઍપની કાર્યક્ષમતા બદલાઈ શકે છે."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tablet" msgid="738046584021528374">"આ ઍપને <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>થી અપડેટ કરવી છે?\n\nઆ ઍપ સામાન્ય રીતે <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>થી અપડેટ મેળવે છે. અલગ સૉર્સથી અપડેટ કરીને, તમે તમારા ટૅબ્લેટ પર કોઈપણ સૉર્સથી ભાવિ અપડેટ મેળવી શકો છો. ઍપની કાર્યક્ષમતા બદલાઈ શકે છે."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tv" msgid="3056133099508550163">"આ ઍપને <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>થી અપડેટ કરવી છે?\n\nઆ ઍપ સામાન્ય રીતે <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>થી અપડેટ મેળવે છે. અલગ સૉર્સથી અપડેટ કરીને, તમે તમારા ટીવી પર કોઈપણ સૉર્સથી ભાવિ અપડેટ મેળવી શકો છો. ઍપની કાર્યક્ષમતા બદલાઈ શકે છે."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="default" msgid="3750986542284587290">"આ ઍપને <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>થી અપડેટ કરવી છે?\n\nઆ ઍપ સામાન્ય રીતે <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>થી અપડેટ મેળવે છે. અલગ સૉર્સથી અપડેટ કરીને, તમે તમારા ફોન પર કોઈપણ સૉર્સથી ભાવિ અપડેટ મેળવી શકો છો. ઍપની કાર્યક્ષમતા બદલાઈ શકે છે."</string>
<string name="install_failed" msgid="5777824004474125469">"ઍપ્લિકેશન ઇન્સ્ટૉલ કરી નથી."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"પૅકેજને ઇન્સ્ટૉલ થવાથી બ્લૉક કરવામાં આવ્યું હતું."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"પૅકેજનો અસ્તિત્વમાંના પૅકેજ સાથે વિરોધાભાસ હોવાને કારણે ઍપ્લિકેશન ઇન્સ્ટૉલ થઈ નથી."</string>
diff --git a/packages/PackageInstaller/res/values-hi/strings.xml b/packages/PackageInstaller/res/values-hi/strings.xml
index df3353b..131d9d5 100644
--- a/packages/PackageInstaller/res/values-hi/strings.xml
+++ b/packages/PackageInstaller/res/values-hi/strings.xml
@@ -26,7 +26,9 @@
<string name="install_done" msgid="5987363587661783896">"ऐप्लिकेशन इंस्टॉल हो गया."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"क्या आपको यह ऐप्लिकेशन इंस्टॉल करना है?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"क्या आप इस ऐप्लिकेशन को अपडेट करना चाहते हैं?"</string>
- <string name="install_confirm_question_update_owner_reminder" msgid="3750986542284587290">"क्या इस ऐप्लिकेशन को <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g> से अपडेट करना है?\n\nआम तौर पर, इस ऐप्लिकेशन को <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g> से अपडेट मिलते हैं. किसी दूसरे सोर्स से अपडेट करने पर, आपको नए अपडेट फ़ोन पर मौजूद किसी भी सोर्स से मिल सकते हैं. इसके साथ ही, ऐप्लिकेशन की मुख्य सुविधाओं और उनके काम करने के तरीके में बदलाव आ सकता है."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tablet" msgid="738046584021528374">"क्या इस ऐप्लिकेशन को <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g> से अपडेट करना है?\n\nआम तौर पर, इस ऐप्लिकेशन को <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g> से अपडेट मिलते हैं. किसी दूसरे सोर्स से अपडेट करने पर, आपको आगे से अपने टैबलेट पर किसी भी सोर्स से अपडेट मिल सकते हैं. ऐप्लिकेशन की मुख्य सुविधाएं और उसके काम करने का तरीका बदल सकता है."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tv" msgid="3056133099508550163">"क्या इस ऐप्लिकेशन को <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g> से अपडेट करना है?\n\nआम तौर पर, इस ऐप्लिकेशन को <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g> से अपडेट मिलते हैं. किसी दूसरे सोर्स से अपडेट करने पर, आपको आगे से अपने टीवी पर किसी भी सोर्स से अपडेट मिल सकते हैं. ऐप्लिकेशन की मुख्य सुविधाएं और उसके काम करने का तरीका बदल सकता है."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="default" msgid="3750986542284587290">"क्या इस ऐप्लिकेशन को <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g> से अपडेट करना है?\n\nआम तौर पर, इस ऐप्लिकेशन को <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g> से अपडेट मिलते हैं. किसी दूसरे सोर्स से अपडेट करने पर, आपको नए अपडेट फ़ोन पर मौजूद किसी भी सोर्स से मिल सकते हैं. इसके साथ ही, ऐप्लिकेशन की मुख्य सुविधाओं और उनके काम करने के तरीके में बदलाव आ सकता है."</string>
<string name="install_failed" msgid="5777824004474125469">"ऐप्लिकेशन इंस्टॉल नहीं हुआ."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"पैकेज को इंस्टॉल होने से ब्लॉक किया हुआ है."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"ऐप्लिकेशन इंस्टॉल नहीं हुआ क्योंकि पैकेज का किसी मौजूदा पैकेज से विरोध है."</string>
diff --git a/packages/PackageInstaller/res/values-hr/strings.xml b/packages/PackageInstaller/res/values-hr/strings.xml
index 74c1fa1..7eb2e80 100644
--- a/packages/PackageInstaller/res/values-hr/strings.xml
+++ b/packages/PackageInstaller/res/values-hr/strings.xml
@@ -26,7 +26,9 @@
<string name="install_done" msgid="5987363587661783896">"Aplikacija je instalirana."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"Želite li instalirati ovu aplikaciju?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"Želite li ažurirati ovu aplikaciju?"</string>
- <string name="install_confirm_question_update_owner_reminder" msgid="3750986542284587290">"Želite li ovu aplikaciju ažurirati s izvora <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nOva aplikacija obično prima ažuriranja s izvora <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Ako je ažurirate s nekog drugog izvora, buduća ažuriranja možete primati s bilo kojeg izvora na svojem telefonu. Funkcije aplikacije mogu se promijeniti."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tablet" msgid="738046584021528374">"Želite li ažurirati ovu aplikaciju s izvora <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nOva aplikacija obično prima ažuriranja s izvora <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Ako je ažurirate s nekog drugog izvora, buduća ažuriranja možete primati s bilo kojeg izvora na svojem tabletu. Funkcije aplikacije mogu se promijeniti."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tv" msgid="3056133099508550163">"Želite li ažurirati ovu aplikaciju s izvora <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nOva aplikacija obično prima ažuriranja s izvora <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Ako je ažurirate s nekog drugog izvora, buduća ažuriranja možete primati s bilo kojeg izvora na svojem TV-u. Funkcije aplikacije mogu se promijeniti."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="default" msgid="3750986542284587290">"Želite li ovu aplikaciju ažurirati s izvora <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nOva aplikacija obično prima ažuriranja s izvora <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Ako je ažurirate s nekog drugog izvora, buduća ažuriranja možete primati s bilo kojeg izvora na svojem telefonu. Funkcije aplikacije mogu se promijeniti."</string>
<string name="install_failed" msgid="5777824004474125469">"Aplikacija nije instalirana."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"Instaliranje paketa blokirano je."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"Aplikacija koja nije instalirana kao paket u sukobu je s postojećim paketom."</string>
diff --git a/packages/PackageInstaller/res/values-hu/strings.xml b/packages/PackageInstaller/res/values-hu/strings.xml
index 823c20c..ac19a48 100644
--- a/packages/PackageInstaller/res/values-hu/strings.xml
+++ b/packages/PackageInstaller/res/values-hu/strings.xml
@@ -26,7 +26,9 @@
<string name="install_done" msgid="5987363587661783896">"Alkalmazás telepítve."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"Telepíti ezt az alkalmazást?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"Frissíti ezt az alkalmazást?"</string>
- <string name="install_confirm_question_update_owner_reminder" msgid="3750986542284587290">"Frissíti az appot ebből a forrásból: <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nEz az app általában a következő forrásból kap frissítéseket: <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Ha másik forrásból frissíti, a későbbiekben bármelyik forrásból kaphat frissítéseket a telefonján. Emiatt megváltozhat az app működése."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tablet" msgid="738046584021528374">"A(z) <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g> forrásból frissíti ezt az alkalmazást?\n\nEz az alkalmazás általában a következő forrásból kap frissítéseket: <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Ha másik forrásból frissíti, a későbbiekben bármelyik forrásból kaphat frissítéseket a táblagépén. Emiatt megváltozhat az alkalmazás működése."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tv" msgid="3056133099508550163">"A(z) <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g> forrásból frissíti ezt az alkalmazást?\n\nEz az alkalmazás általában a következő forrásból kap frissítéseket: <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Ha másik forrásból frissíti, a későbbiekben bármelyik forrásból kaphat frissítéseket a tévéjén. Emiatt megváltozhat az alkalmazás működése."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="default" msgid="3750986542284587290">"Frissíti az appot ebből a forrásból: <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nEz az app általában a következő forrásból kap frissítéseket: <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Ha másik forrásból frissíti, a későbbiekben bármelyik forrásból kaphat frissítéseket a telefonján. Emiatt megváltozhat az app működése."</string>
<string name="install_failed" msgid="5777824004474125469">"Az alkalmazás nincs telepítve."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"A csomag telepítését letiltotta a rendszer."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"A nem csomagként telepített alkalmazás ütközik egy már létező csomaggal."</string>
diff --git a/packages/PackageInstaller/res/values-hy/strings.xml b/packages/PackageInstaller/res/values-hy/strings.xml
index 4f56568..fe2f06b 100644
--- a/packages/PackageInstaller/res/values-hy/strings.xml
+++ b/packages/PackageInstaller/res/values-hy/strings.xml
@@ -26,7 +26,9 @@
<string name="install_done" msgid="5987363587661783896">"Հավելվածը տեղադրված է:"</string>
<string name="install_confirm_question" msgid="7663733664476363311">"Տեղադրե՞լ այս հավելվածը:"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"Թարմացնե՞լ այս հավելվածը։"</string>
- <string name="install_confirm_question_update_owner_reminder" msgid="3750986542284587290">"Թարմացնե՞լ այս հավելվածը <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>-ից։\n\nՍովորաբար այս հավելվածի թարմացումները ստացվում են <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>-ից։ Եթե թարմացնեք այլ աղբյուրից, հետագայում կարող եք ձեր հեռախոսում թարմացումներ ստանալ ցանկացած աղբյուրից։ Հավելվածի գործառույթները կարող են փոփոխվել։"</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tablet" msgid="738046584021528374">"Թարմացնե՞լ այս հավելվածը <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>-ից։\n\nՍովորաբար այս հավելվածի թարմացումները ստացվում են <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>-ից։ Եթե թարմացնեք այլ աղբյուրից, հետագայում կարող եք ձեր պլանշետում թարմացումներ ստանալ ցանկացած աղբյուրից։ Հավելվածի գործառույթները կարող են փոփոխվել։"</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tv" msgid="3056133099508550163">"Թարմացնե՞լ այս հավելվածը <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>-ից։\n\nՍովորաբար այս հավելվածի թարմացումները ստացվում են <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>-ից։ Եթե թարմացնեք այլ աղբյուրից, հետագայում կարող եք ձեր հեռուստացույցում թարմացումներ ստանալ ցանկացած աղբյուրից։ Հավելվածի գործառույթները կարող են փոփոխվել։"</string>
+ <string name="install_confirm_question_update_owner_reminder" product="default" msgid="3750986542284587290">"Թարմացնե՞լ այս հավելվածը <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>-ից։\n\nՍովորաբար այս հավելվածի թարմացումները ստացվում են <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>-ից։ Եթե թարմացնեք այլ աղբյուրից, հետագայում կարող եք ձեր հեռախոսում թարմացումներ ստանալ ցանկացած աղբյուրից։ Հավելվածի գործառույթները կարող են փոփոխվել։"</string>
<string name="install_failed" msgid="5777824004474125469">"Հավելվածը տեղադրված չէ:"</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"Փաթեթի տեղադրումն արգելափակվել է:"</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"Հավելվածը չի տեղադրվել, քանի որ տեղադրման փաթեթն ունի հակասություն առկա փաթեթի հետ:"</string>
diff --git a/packages/PackageInstaller/res/values-in/strings.xml b/packages/PackageInstaller/res/values-in/strings.xml
index 0758a1d..a0c76c6 100644
--- a/packages/PackageInstaller/res/values-in/strings.xml
+++ b/packages/PackageInstaller/res/values-in/strings.xml
@@ -26,7 +26,9 @@
<string name="install_done" msgid="5987363587661783896">"Aplikasi terinstal."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"Ingin menginstal aplikasi ini?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"Ingin mengupdate aplikasi ini?"</string>
- <string name="install_confirm_question_update_owner_reminder" msgid="3750986542284587290">"Update aplikasi ini dari <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nAplikasi ini biasanya menerima update dari <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Dengan mengupdate dari sumber yang berbeda, Anda mungkin menerima update berikutnya dari sumber mana pun di ponsel. Fungsi aplikasi mungkin berubah."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tablet" msgid="738046584021528374">"Update aplikasi ini dari <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nAplikasi ini biasanya menerima update dari <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Dengan mengupdate dari sumber yang berbeda, Anda mungkin menerima update berikutnya dari sumber mana pun di tablet. Fungsi aplikasi mungkin berubah."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tv" msgid="3056133099508550163">"Update aplikasi ini dari <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nAplikasi ini biasanya menerima update dari <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Dengan mengupdate dari sumber yang berbeda, Anda mungkin menerima update berikutnya dari sumber mana pun di TV. Fungsi aplikasi mungkin berubah."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="default" msgid="3750986542284587290">"Update aplikasi ini dari <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nAplikasi ini biasanya menerima update dari <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Dengan mengupdate dari sumber yang berbeda, Anda mungkin menerima update berikutnya dari sumber mana pun di ponsel. Fungsi aplikasi mungkin berubah."</string>
<string name="install_failed" msgid="5777824004474125469">"Aplikasi tidak terinstal."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"Paket diblokir sehingga tidak dapat diinstal."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"Aplikasi tidak diinstal karena paket ini bentrok dengan paket yang sudah ada."</string>
diff --git a/packages/PackageInstaller/res/values-is/strings.xml b/packages/PackageInstaller/res/values-is/strings.xml
index 6cbb2ee..f0dce1e 100644
--- a/packages/PackageInstaller/res/values-is/strings.xml
+++ b/packages/PackageInstaller/res/values-is/strings.xml
@@ -26,7 +26,9 @@
<string name="install_done" msgid="5987363587661783896">"Forritið er uppsett."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"Viltu setja upp þetta forrit?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"Viltu uppfæra þetta forrit?"</string>
- <string name="install_confirm_question_update_owner_reminder" msgid="3750986542284587290">"Uppfæra þetta forrit frá <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nÞetta forrit fær venjulega uppfærslur frá <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Með því að uppfæra frá öðrum uppruna gætirðu fengið framtíðaruppfærslur frá hvaða uppruna sem er í símanum. Forritseiginleikar kunna að breytast."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tablet" msgid="738046584021528374">"Uppfæra þetta forrit frá <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nÞetta forrit fær venjulega uppfærslur frá <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Með því að uppfæra frá öðrum uppruna gætirðu fengið framtíðaruppfærslur frá hvaða uppruna sem er í spjaldtölvunni. Forritseiginleikar kunna að breytast."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tv" msgid="3056133099508550163">"Uppfæra þetta forrit frá <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nÞetta forrit fær venjulega uppfærslur frá <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Með því að uppfæra frá öðrum uppruna gætirðu fengið framtíðaruppfærslur frá hvaða uppruna sem er í sjónvarpinu. Forritseiginleikar kunna að breytast."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="default" msgid="3750986542284587290">"Uppfæra þetta forrit frá <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nÞetta forrit fær venjulega uppfærslur frá <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Með því að uppfæra frá öðrum uppruna gætirðu fengið framtíðaruppfærslur frá hvaða uppruna sem er í símanum. Forritseiginleikar kunna að breytast."</string>
<string name="install_failed" msgid="5777824004474125469">"Forritið er ekki uppsett."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"Lokað var á uppsetningu pakkans."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"Forritið var ekki sett upp vegna árekstra á milli pakkans og annars pakka."</string>
diff --git a/packages/PackageInstaller/res/values-it/strings.xml b/packages/PackageInstaller/res/values-it/strings.xml
index 323db1d..b43570f 100644
--- a/packages/PackageInstaller/res/values-it/strings.xml
+++ b/packages/PackageInstaller/res/values-it/strings.xml
@@ -26,7 +26,9 @@
<string name="install_done" msgid="5987363587661783896">"App installata."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"Vuoi installare questa app?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"Vuoi aggiornare questa app?"</string>
- <string name="install_confirm_question_update_owner_reminder" msgid="3750986542284587290">"Vuoi aggiornare questa app tramite <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nGeneralmente l\'app viene aggiornata tramite <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Se la aggiorni da un\'origine diversa, in futuro potresti ricevere aggiornamenti da qualsiasi origine sul telefono. La funzionalità dell\'app potrebbe cambiare."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tablet" msgid="738046584021528374">"Vuoi aggiornare questa app tramite <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nGeneralmente l\'app viene aggiornata tramite <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Se la aggiorni da un\'origine diversa, in futuro potresti ricevere aggiornamenti da qualsiasi origine sul tablet. La funzionalità dell\'app potrebbe cambiare."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tv" msgid="3056133099508550163">"Vuoi aggiornare questa app tramite <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nGeneralmente l\'app viene aggiornata tramite <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Se la aggiorni da un\'origine diversa, in futuro potresti ricevere aggiornamenti da qualsiasi origine sulla TV. La funzionalità dell\'app potrebbe cambiare."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="default" msgid="3750986542284587290">"Vuoi aggiornare questa app tramite <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nGeneralmente l\'app viene aggiornata tramite <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Se la aggiorni da un\'origine diversa, in futuro potresti ricevere aggiornamenti da qualsiasi origine sul telefono. La funzionalità dell\'app potrebbe cambiare."</string>
<string name="install_failed" msgid="5777824004474125469">"App non installata."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"È stata bloccata l\'installazione del pacchetto."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"App non installata poiché il pacchetto è in conflitto con un pacchetto esistente."</string>
diff --git a/packages/PackageInstaller/res/values-iw/strings.xml b/packages/PackageInstaller/res/values-iw/strings.xml
index 24beba6..692ce2d 100644
--- a/packages/PackageInstaller/res/values-iw/strings.xml
+++ b/packages/PackageInstaller/res/values-iw/strings.xml
@@ -26,7 +26,9 @@
<string name="install_done" msgid="5987363587661783896">"האפליקציה הותקנה."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"האם ברצונך להתקין אפליקציה זו?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"האם ברצונך לעדכן אפליקציה זו?"</string>
- <string name="install_confirm_question_update_owner_reminder" msgid="3750986542284587290">"לקבל את העדכון לאפליקציה הזו מ-<xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nהאפליקציה הזו בדרך כלל מקבלת עדכונים מ: <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. אם בחרת לעדכן ממקור אחר, יכול להיות שבעתיד יתקבלו עדכונים ממקורות אחרים בטלפון. תכונות האפליקציה יכולות להשתנות."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tablet" msgid="738046584021528374">"לקבל את העדכון לאפליקציה הזו מ-<xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nהאפליקציה הזו בדרך כלל מקבלת עדכונים מ-<xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. אם בחרת לעדכן ממקור אחר, יכול להיות שבעתיד יתקבלו עדכונים ממקורות אחרים בטאבלט. תכונות האפליקציה עשויות להשתנות."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tv" msgid="3056133099508550163">"לקבל את העדכון לאפליקציה הזו מ-<xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nהאפליקציה הזו בדרך כלל מקבלת עדכונים מ-<xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. אם בחרת לעדכן ממקור אחר, יכול להיות שבעתיד יתקבלו עדכונים ממקורות אחרים בטלוויזיה. תכונות האפליקציה עשויות להשתנות."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="default" msgid="3750986542284587290">"לקבל את העדכון לאפליקציה הזו מ-<xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nהאפליקציה הזו בדרך כלל מקבלת עדכונים מ: <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. אם בחרת לעדכן ממקור אחר, יכול להיות שבעתיד יתקבלו עדכונים ממקורות אחרים בטלפון. תכונות האפליקציה יכולות להשתנות."</string>
<string name="install_failed" msgid="5777824004474125469">"האפליקציה לא הותקנה."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"החבילה נחסמה להתקנה."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"האפליקציה לא הותקנה כי החבילה מתנגשת עם חבילה קיימת."</string>
diff --git a/packages/PackageInstaller/res/values-ja/strings.xml b/packages/PackageInstaller/res/values-ja/strings.xml
index 95b789e..b91312b 100644
--- a/packages/PackageInstaller/res/values-ja/strings.xml
+++ b/packages/PackageInstaller/res/values-ja/strings.xml
@@ -26,7 +26,9 @@
<string name="install_done" msgid="5987363587661783896">"アプリをインストールしました。"</string>
<string name="install_confirm_question" msgid="7663733664476363311">"このアプリをインストールしますか?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"このアプリを更新しますか?"</string>
- <string name="install_confirm_question_update_owner_reminder" msgid="3750986542284587290">"このアプリを <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g> から更新しますか?\n\nこのアプリは通常、<xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g> からアップデートを受信しています。別の提供元から更新することにより、お使いのスマートフォンで今後のアップデートを任意の提供元から受け取ることになります。アプリの機能は変更される場合があります。"</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tablet" msgid="738046584021528374">"このアプリを <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g> から更新しますか?\n\nこのアプリは通常、<xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g> からアップデートを受信しています。別の提供元から更新することにより、お使いのタブレットで今後のアップデートを任意の提供元から受け取ることになります。アプリの機能は変更される場合があります。"</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tv" msgid="3056133099508550163">"このアプリを <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g> から更新しますか?\n\nこのアプリは通常、<xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g> からアップデートを受信しています。別の提供元から更新することにより、お使いのテレビで今後のアップデートを任意の提供元から受け取ることになります。アプリの機能は変更される場合があります。"</string>
+ <string name="install_confirm_question_update_owner_reminder" product="default" msgid="3750986542284587290">"このアプリを <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g> から更新しますか?\n\nこのアプリは通常、<xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g> からアップデートを受信しています。別の提供元から更新することにより、お使いのスマートフォンで今後のアップデートを任意の提供元から受け取ることになります。アプリの機能は変更される場合があります。"</string>
<string name="install_failed" msgid="5777824004474125469">"アプリはインストールされていません。"</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"パッケージのインストールはブロックされています。"</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"パッケージが既存のパッケージと競合するため、アプリをインストールできませんでした。"</string>
diff --git a/packages/PackageInstaller/res/values-ka/strings.xml b/packages/PackageInstaller/res/values-ka/strings.xml
index 0699f0b..497f3b0 100644
--- a/packages/PackageInstaller/res/values-ka/strings.xml
+++ b/packages/PackageInstaller/res/values-ka/strings.xml
@@ -26,7 +26,9 @@
<string name="install_done" msgid="5987363587661783896">"აპი დაინსტალირებულია."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"გნებავთ ამ აპის დაყენება?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"გსურთ ამ აპის განახლება?"</string>
- <string name="install_confirm_question_update_owner_reminder" msgid="3750986542284587290">"გსურთ განაახლოთ ეს აპი <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>-ისგან?\n\nეს აპი, როგორც წესი, განახლებებს იღებს <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>-ისგან. აპის სხვა წყაროდან განახლებით შემდგომში განახლებების მიღებას შეძლებთ ნებისმიერი წყაროდან თქვენს ტელეფონზე. აპის ფუნქციები, შესაძლოა, შეიცვალოს."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tablet" msgid="738046584021528374">"გსურთ განაახლოთ ეს აპი <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>-ისგან?\n\nეს აპი, როგორც წესი, განახლებებს იღებს <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>-ისგან. სხვა წყაროდან განახლებით, შეგიძლიათ მიიღოთ მომავალი განახლებები ტაბლეტზე არსებული ნებისმიერი წყაროდან. აპის ფუნქციები, შესაძლოა, შეიცვალოს."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tv" msgid="3056133099508550163">"გსურთ განაახლოთ ეს აპი <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>-ისგან?\n\nეს აპი, როგორც წესი, განახლებებს იღებს <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>-ისგან. სხვა წყაროდან განახლებით, შეგიძლიათ მიიღოთ მომავალი განახლებები ტელევიზორზე არსებული ნებისმიერი წყაროდან. აპის ფუნქციები, შესაძლოა, შეიცვალოს."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="default" msgid="3750986542284587290">"გსურთ განაახლოთ ეს აპი <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>-ისგან?\n\nეს აპი, როგორც წესი, განახლებებს იღებს <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>-ისგან. აპის სხვა წყაროდან განახლებით შემდგომში განახლებების მიღებას შეძლებთ ნებისმიერი წყაროდან თქვენს ტელეფონზე. აპის ფუნქციები, შესაძლოა, შეიცვალოს."</string>
<string name="install_failed" msgid="5777824004474125469">"აპი დაუინსტალირებელია."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"ამ პაკეტის ინსტალაცია დაბლოკილია."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"აპი ვერ დაინსტალირდა, რადგან პაკეტი კონფლიქტშია არსებულ პაკეტთან."</string>
diff --git a/packages/PackageInstaller/res/values-kk/strings.xml b/packages/PackageInstaller/res/values-kk/strings.xml
index 129267f..a7ae943 100644
--- a/packages/PackageInstaller/res/values-kk/strings.xml
+++ b/packages/PackageInstaller/res/values-kk/strings.xml
@@ -26,7 +26,9 @@
<string name="install_done" msgid="5987363587661783896">"Қолданба орнатылды."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"Бұл қолданбаны орнатқыңыз келе ме?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"Бұл қолданбаны жаңартқыңыз келе ме?"</string>
- <string name="install_confirm_question_update_owner_reminder" msgid="3750986542284587290">"Бұл қолданба <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g> арқылы жаңартылсын ба?\n\nБұл қолданба әдетте <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g> көмегімен жаңартылады. Басқа дереккөзден жаңартсаңыз, алдағы жаңартулар телефоныңыздағы кез келген дереккөзден келуі мүмкін. Қолданба функциялары өзгеруі мүмкін."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tablet" msgid="738046584021528374">"Бұл қолданба <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g> арқылы жаңартылсын ба?\n\nБұл қолданба әдетте <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g> көмегімен жаңартылады. Басқа дереккөзден жаңартсаңыз, планшетіңіздегі кез келген дереккөзден алдағы жаңартулар берілуі мүмкін. Қолданба функциялары өзгеруі мүмкін."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tv" msgid="3056133099508550163">"Бұл қолданба <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g> арқылы жаңартылсын ба?\n\nБұл қолданба әдетте <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g> көмегімен жаңартылады. Басқа дереккөзден жаңартсаңыз, теледидарыңыздағы кез келген дереккөзден алдағы жаңартулар берілуі мүмкін. Қолданба функциялары өзгеруі мүмкін."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="default" msgid="3750986542284587290">"Бұл қолданба <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g> арқылы жаңартылсын ба?\n\nБұл қолданба әдетте <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g> көмегімен жаңартылады. Басқа дереккөзден жаңартсаңыз, алдағы жаңартулар телефоныңыздағы кез келген дереккөзден келуі мүмкін. Қолданба функциялары өзгеруі мүмкін."</string>
<string name="install_failed" msgid="5777824004474125469">"Қолданба орнатылмады."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"Пакетті орнатуға тыйым салынды."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"Жаңа пакет пен бұрыннан бар пакеттің арасында қайшылық туындағандықтан, қолданба орнатылмады."</string>
diff --git a/packages/PackageInstaller/res/values-km/strings.xml b/packages/PackageInstaller/res/values-km/strings.xml
index 04dc574..52bfce3 100644
--- a/packages/PackageInstaller/res/values-km/strings.xml
+++ b/packages/PackageInstaller/res/values-km/strings.xml
@@ -26,7 +26,9 @@
<string name="install_done" msgid="5987363587661783896">"បានដំឡើងកម្មវិធី។"</string>
<string name="install_confirm_question" msgid="7663733664476363311">"តើអ្នកចង់ដំឡើងកម្មវិធីនេះដែរទេ?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"តើអ្នកចង់ដំឡើងកំណែកម្មវិធីនេះដែរទេ?"</string>
- <string name="install_confirm_question_update_owner_reminder" msgid="3750986542284587290">"ដំឡើងកំណែកម្មវិធីនេះពី <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g> ឬ?\n\nកម្មវិធីនេះជាធម្មតាទទួលបានកំណែថ្មីពី <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>។ តាមរយៈការដំឡើងកំណែពីប្រភពផ្សេង អ្នកអាចនឹងទទួលបានកំណែថ្មីនាពេលអនាគតពីប្រភពណាក៏បាននៅលើទូរសព្ទរបស់អ្នក។ មុខងារកម្មវិធីអាចមានការផ្លាស់ប្ដូរ។"</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tablet" msgid="738046584021528374">"ដំឡើងកំណែកម្មវិធីនេះពី <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g> ឬ?\n\nកម្មវិធីនេះជាធម្មតាទទួលបានកំណែថ្មីៗពី <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>។ តាមរយៈការដំឡើងកំណែពីប្រភពផ្សេង អ្នកអាចទទួលបានកំណែថ្មីៗនាពេលអនាគតពីប្រភពណាក៏បាននៅលើថេប្លេតរបស់អ្នក។ មុខងារកម្មវិធីអាចមានការផ្លាស់ប្ដូរ។"</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tv" msgid="3056133099508550163">"ដំឡើងកំណែកម្មវិធីនេះពី <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g> ឬ?\n\nកម្មវិធីនេះជាធម្មតាទទួលបានកំណែថ្មីៗពី <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>។ តាមរយៈការដំឡើងកំណែពីប្រភពផ្សេង អ្នកអាចទទួលបានកំណែថ្មីៗនាពេលអនាគតពីប្រភពណាក៏បាននៅលើទូរទស្សន៍របស់អ្នក។ មុខងារកម្មវិធីអាចមានការផ្លាស់ប្ដូរ។"</string>
+ <string name="install_confirm_question_update_owner_reminder" product="default" msgid="3750986542284587290">"ដំឡើងកំណែកម្មវិធីនេះពី <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g> ឬ?\n\nកម្មវិធីនេះជាធម្មតាទទួលបានកំណែថ្មីពី <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>។ តាមរយៈការដំឡើងកំណែពីប្រភពផ្សេង អ្នកអាចនឹងទទួលបានកំណែថ្មីនាពេលអនាគតពីប្រភពណាក៏បាននៅលើទូរសព្ទរបស់អ្នក។ មុខងារកម្មវិធីអាចមានការផ្លាស់ប្ដូរ។"</string>
<string name="install_failed" msgid="5777824004474125469">"មិនបានដំឡើងកម្មវិធីទេ។"</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"កញ្ចប់ត្រូវបានទប់ស្កាត់មិនឱ្យដំឡើង។"</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"កម្មវិធីមិនបានដំឡើងទេ ដោយសារកញ្ចប់កម្មវិធីមិនត្រូវគ្នាជាមួយកញ្ចប់ដែលមានស្រាប់។"</string>
diff --git a/packages/PackageInstaller/res/values-kn/strings.xml b/packages/PackageInstaller/res/values-kn/strings.xml
index c9455ae..1d3cfd3 100644
--- a/packages/PackageInstaller/res/values-kn/strings.xml
+++ b/packages/PackageInstaller/res/values-kn/strings.xml
@@ -26,7 +26,9 @@
<string name="install_done" msgid="5987363587661783896">"ಆ್ಯಪ್ ಅನ್ನು ಇನ್ಸ್ಟಾಲ್ ಮಾಡಲಾಗಿದೆ."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"ನೀವು ಈ ಆ್ಯಪ್ ಅನ್ನು ಇನ್ಸ್ಟಾಲ್ ಮಾಡಲು ಬಯಸುವಿರಾ?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"ನೀವು ಈ ಆ್ಯಪ್ ಅನ್ನು ಅಪ್ಡೇಟ್ ಮಾಡಲು ಬಯಸುವಿರಾ?"</string>
- <string name="install_confirm_question_update_owner_reminder" msgid="3750986542284587290">"<xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g> ನಿಂದ ಈ ಆ್ಯಪ್ ಅನ್ನು ಅಪ್ಡೇಟ್ ಮಾಡಬೇಕೇ?\n\nಈ ಆ್ಯಪ್ ಸಾಮಾನ್ಯವಾಗಿ <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g> ನಿಂದ ಅಪ್ಡೇಟ್ಗಳನ್ನು ಸ್ವೀಕರಿಸುತ್ತದೆ. ಬೇರೆ ಮೂಲವೊಂದರಿಂದ ಅಪ್ಡೇಟ್ ಮಾಡುವ ಮೂಲಕ, ನಿಮ್ಮ ಫೋನ್ನಲ್ಲಿರುವ ಯಾವುದೇ ಮೂಲದಿಂದ ಭವಿಷ್ಯದ ಅಪ್ಡೇಟ್ಗಳನ್ನು ನೀವು ಸ್ವೀಕರಿಸಬಹುದು. ಆ್ಯಪ್ನ ಕಾರ್ಯಚಟುವಟಿಕೆಯು ಬದಲಾಗಬಹುದು."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tablet" msgid="738046584021528374">"<xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g> ನಿಂದ ಈ ಆ್ಯಪ್ ಅನ್ನು ಅಪ್ಡೇಟ್ ಮಾಡಬೇಕೇ?\n\nಈ ಆ್ಯಪ್ ಸಾಮಾನ್ಯವಾಗಿ <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g> ನಿಂದ ಅಪ್ಡೇಟ್ಗಳನ್ನು ಸ್ವೀಕರಿಸುತ್ತದೆ. ಬೇರೆ ಮೂಲವೊಂದರಿಂದ ಅಪ್ಡೇಟ್ ಮಾಡುವ ಮೂಲಕ, ನಿಮ್ಮ ಟ್ಯಾಬ್ಲೆಟ್ನಲ್ಲಿರುವ ಯಾವುದೇ ಮೂಲದಿಂದ ಭವಿಷ್ಯದ ಅಪ್ಡೇಟ್ಗಳನ್ನು ನೀವು ಸ್ವೀಕರಿಸಬಹುದು. ಆ್ಯಪ್ನ ಕಾರ್ಯಚಟುವಟಿಕೆಯು ಬದಲಾಗಬಹುದು."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tv" msgid="3056133099508550163">"<xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g> ನಿಂದ ಈ ಆ್ಯಪ್ ಅನ್ನು ಅಪ್ಡೇಟ್ ಮಾಡಬೇಕೇ?\n\nಈ ಆ್ಯಪ್ ಸಾಮಾನ್ಯವಾಗಿ <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g> ನಿಂದ ಅಪ್ಡೇಟ್ಗಳನ್ನು ಸ್ವೀಕರಿಸುತ್ತದೆ. ಬೇರೆ ಮೂಲವೊಂದರಿಂದ ಅಪ್ಡೇಟ್ ಮಾಡುವ ಮೂಲಕ, ನಿಮ್ಮ ಟಿವಿಯಲ್ಲಿರುವ ಯಾವುದೇ ಮೂಲದಿಂದ ಭವಿಷ್ಯದ ಅಪ್ಡೇಟ್ಗಳನ್ನು ನೀವು ಸ್ವೀಕರಿಸಬಹುದು. ಆ್ಯಪ್ನ ಕಾರ್ಯಚಟುವಟಿಕೆಯು ಬದಲಾಗಬಹುದು."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="default" msgid="3750986542284587290">"<xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g> ನಿಂದ ಈ ಆ್ಯಪ್ ಅನ್ನು ಅಪ್ಡೇಟ್ ಮಾಡಬೇಕೇ?\n\nಈ ಆ್ಯಪ್ ಸಾಮಾನ್ಯವಾಗಿ <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g> ನಿಂದ ಅಪ್ಡೇಟ್ಗಳನ್ನು ಸ್ವೀಕರಿಸುತ್ತದೆ. ಬೇರೆ ಮೂಲವೊಂದರಿಂದ ಅಪ್ಡೇಟ್ ಮಾಡುವ ಮೂಲಕ, ನಿಮ್ಮ ಫೋನ್ನಲ್ಲಿರುವ ಯಾವುದೇ ಮೂಲದಿಂದ ಭವಿಷ್ಯದ ಅಪ್ಡೇಟ್ಗಳನ್ನು ನೀವು ಸ್ವೀಕರಿಸಬಹುದು. ಆ್ಯಪ್ನ ಕಾರ್ಯಚಟುವಟಿಕೆಯು ಬದಲಾಗಬಹುದು."</string>
<string name="install_failed" msgid="5777824004474125469">"ಆ್ಯಪ್ ಇನ್ಸ್ಟಾಲ್ ಮಾಡಲಾಗಿಲ್ಲ."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"ಇನ್ಸ್ಟಾಲ್ ಮಾಡುವ ಪ್ಯಾಕೇಜ್ ಅನ್ನು ನಿರ್ಬಂಧಿಸಲಾಗಿದೆ."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"ಪ್ಯಾಕೇಜ್ನಂತೆ ಇನ್ಸ್ಟಾಲ್ ಮಾಡಲಾಗಿರುವ ಆ್ಯಪ್ ಅಸ್ತಿತ್ವದಲ್ಲಿರುವ ಪ್ಯಾಕೇಜ್ ಜೊತೆಗೆ ಸಂಘರ್ಷವಾಗುತ್ತದೆ."</string>
diff --git a/packages/PackageInstaller/res/values-ko/strings.xml b/packages/PackageInstaller/res/values-ko/strings.xml
index ef39eef..5c22453 100644
--- a/packages/PackageInstaller/res/values-ko/strings.xml
+++ b/packages/PackageInstaller/res/values-ko/strings.xml
@@ -26,7 +26,9 @@
<string name="install_done" msgid="5987363587661783896">"앱이 설치되었습니다."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"이 앱을 설치하시겠습니까?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"이 앱을 업데이트하시겠습니까?"</string>
- <string name="install_confirm_question_update_owner_reminder" msgid="3750986542284587290">"<xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>에서 이 앱에 대한 업데이트를 받으시겠습니까?\n\n평소에는 <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>에서 앱을 업데이트했습니다. 다른 출처에서 앱을 업데이트하면 향후 휴대전화에 있는 어떤 출처에서든지 업데이트를 받을 수 있습니다. 앱 기능도 변경될 수 있습니다."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tablet" msgid="738046584021528374">"<xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>에서 이 앱에 대한 업데이트를 받으시겠습니까?\n\n평소에는 <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>에서 앱을 업데이트했습니다. 다른 출처에서 업데이트를 받으면 향후 태블릿에 있는 어떤 출처에서든지 업데이트를 받을 수 있습니다. 앱 기능도 변경될 수 있습니다."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tv" msgid="3056133099508550163">"<xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>에서 이 앱에 대한 업데이트를 받으시겠습니까?\n\n평소에는 <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>에서 앱을 업데이트했습니다. 다른 출처에서 앱을 업데이트하면 향후 TV에 있는 어떤 출처에서든지 업데이트를 받을 수 있습니다. 앱 기능도 변경될 수 있습니다."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="default" msgid="3750986542284587290">"<xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>에서 이 앱에 대한 업데이트를 받으시겠습니까?\n\n평소에는 <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>에서 앱을 업데이트했습니다. 다른 출처에서 앱을 업데이트하면 향후 휴대전화에 있는 어떤 출처에서든지 업데이트를 받을 수 있습니다. 앱 기능도 변경될 수 있습니다."</string>
<string name="install_failed" msgid="5777824004474125469">"앱이 설치되지 않았습니다."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"패키지 설치가 차단되었습니다."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"패키지가 기존 패키지와 충돌하여 앱이 설치되지 않았습니다."</string>
diff --git a/packages/PackageInstaller/res/values-ky/strings.xml b/packages/PackageInstaller/res/values-ky/strings.xml
index 8b166c8..febc30c 100644
--- a/packages/PackageInstaller/res/values-ky/strings.xml
+++ b/packages/PackageInstaller/res/values-ky/strings.xml
@@ -26,7 +26,9 @@
<string name="install_done" msgid="5987363587661783896">"Колдонмо орнотулду."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"Бул колдонмону орнотоюн деп жатасызбы?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"Бул колдонмону жаңыртайын деп жатасызбы?"</string>
- <string name="install_confirm_question_update_owner_reminder" msgid="3750986542284587290">"Колдонмону <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g> аркылуу жаңыртасызбы?\n\nАдатта бул колдонмонун жаңыртууларын <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g> жөнөтөт. Эгер колдонмону башка булактан жаңыртсаңыз, эртеңки күнү телефонуңуз ар кайсы булактан жаңырып, колдонмонун функциялары өзгөрүшү мүмкүн."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tablet" msgid="738046584021528374">"Колдонмо <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g> аркылуу жаңыртылсынбы?\n\nАдатта бул колдонмо <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g> жөнөткөн жаңыртууларды алат. Башка булактан жаңыртcаңыз, кийинки жаңыртуулар планшетиңиздеги ар кандай булактардан алынышы мүмкүн. Колдонмонун функциялары өзгөрүшү мүмкүн."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tv" msgid="3056133099508550163">"Колдонмо <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g> аркылуу жаңыртылсынбы?\n\nАдатта бул колдонмо <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g> жөнөткөн жаңыртууларды алат. Башка булактан жаңыртcаңыз, кийинки жаңыртуулар сыналгыңыздагы ар кандай булактардан алынышы мүмкүн. Колдонмонун функциялары өзгөрүшү мүмкүн."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="default" msgid="3750986542284587290">"Колдонмону <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g> аркылуу жаңыртасызбы?\n\nАдатта бул колдонмонун жаңыртууларын <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g> жөнөтөт. Эгер колдонмону башка булактан жаңыртсаңыз, эртеңки күнү телефонуңуз ар кайсы булактан жаңырып, колдонмонун функциялары өзгөрүшү мүмкүн."</string>
<string name="install_failed" msgid="5777824004474125469">"Колдонмо орнотулган жок."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"Топтомду орнотууга болбойт."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"Башка топтом менен дал келбегендиктен колдонмо орнотулган жок."</string>
diff --git a/packages/PackageInstaller/res/values-lo/strings.xml b/packages/PackageInstaller/res/values-lo/strings.xml
index f3912cc..667e074 100644
--- a/packages/PackageInstaller/res/values-lo/strings.xml
+++ b/packages/PackageInstaller/res/values-lo/strings.xml
@@ -26,7 +26,9 @@
<string name="install_done" msgid="5987363587661783896">"ຕິດຕັ້ງແອັບແລ້ວ."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"ທ່ານຕ້ອງການຕິດຕັ້ງແອັບນີ້ບໍ່?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"ທ່ານຕ້ອງການອັບເດດແອັບນີ້ບໍ່?"</string>
- <string name="install_confirm_question_update_owner_reminder" msgid="3750986542284587290">"ອັບເດດແອັບນີ້ຈາກ <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g> ບໍ?\n\nໂດຍທົ່ວໄປແລ້ວແອັບນີ້ຈະໄດ້ຮັບການອັບເດດຈາກ <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. ການອັບເດດຈາກແຫຼ່ງທີ່ມາອື່ນອາດເຮັດໃຫ້ໂທລະສັບຂອງທ່ານໄດ້ຮັບການອັບເດດຈາກແຫຼ່ງທີ່ມານັ້ນໃນອະນາຄົດ. ຟັງຊັນການເຮັດວຽກຂອງແອັບອາດມີການປ່ຽນແປງ."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tablet" msgid="738046584021528374">"ອັບເດດແອັບນີ້ຈາກ <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g> ບໍ?\n\nໂດຍທົ່ວໄປແລ້ວແອັບນີ້ຈະໄດ້ຮັບການອັບເດດຈາກ <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. ໂດຍການອັບເດດຈາກແຫຼ່ງອື່ນ, ທ່ານອາດຈະໄດ້ຮັບການອັບເດດໃນອະນາຄົດຈາກແຫຼ່ງທີ່ມາໃດກໍໄດ້ຢູ່ແທັບເລັດຂອງທ່ານ. ຟັງຊັນການນຳໃຊ້ແອັບອາດມີການປ່ຽນແປງ."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tv" msgid="3056133099508550163">"ອັບເດດແອັບນີ້ຈາກ <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g> ບໍ?\n\nໂດຍທົ່ວໄປແລ້ວແອັບນີ້ຈະໄດ້ຮັບການອັບເດດຈາກ <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. ໂດຍການອັບເດດຈາກແຫຼ່ງອື່ນ, ທ່ານອາດຈະໄດ້ຮັບການອັບເດດໃນອະນາຄົດຈາກແຫຼ່ງທີ່ມາໃດກໍໄດ້ຢູ່ໂທລະທັດຂອງທ່ານ. ຟັງຊັນການນຳໃຊ້ແອັບອາດມີການປ່ຽນແປງ."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="default" msgid="3750986542284587290">"ອັບເດດແອັບນີ້ຈາກ <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g> ບໍ?\n\nໂດຍທົ່ວໄປແລ້ວແອັບນີ້ຈະໄດ້ຮັບການອັບເດດຈາກ <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. ການອັບເດດຈາກແຫຼ່ງທີ່ມາອື່ນອາດເຮັດໃຫ້ໂທລະສັບຂອງທ່ານໄດ້ຮັບການອັບເດດຈາກແຫຼ່ງທີ່ມານັ້ນໃນອະນາຄົດ. ຟັງຊັນການເຮັດວຽກຂອງແອັບອາດມີການປ່ຽນແປງ."</string>
<string name="install_failed" msgid="5777824004474125469">"ບໍ່ໄດ້ຕິດຕັ້ງແອັບເທື່ອ."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"ແພັກເກດຖືກບລັອກບໍ່ໃຫ້ໄດ້ຮັບການຕິດຕັ້ງ."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"ບໍ່ໄດ້ຕິດຕັ້ງແອັບເນື່ອງຈາກແພັກເກດຂັດແຍ່ງກັບແພັກເກດທີ່ມີຢູ່ກ່ອນແລ້ວ."</string>
diff --git a/packages/PackageInstaller/res/values-lt/strings.xml b/packages/PackageInstaller/res/values-lt/strings.xml
index a7ec560e..1fe963d 100644
--- a/packages/PackageInstaller/res/values-lt/strings.xml
+++ b/packages/PackageInstaller/res/values-lt/strings.xml
@@ -26,7 +26,9 @@
<string name="install_done" msgid="5987363587661783896">"Programa įdiegta."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"Ar norite įdiegti šią programą?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"Ar norite atnaujinti šią programą?"</string>
- <string name="install_confirm_question_update_owner_reminder" msgid="3750986542284587290">"Atnaujinti šią programą iš <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nŠi programa įprastai gauna naujinius iš <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Atnaujinę iš kito šaltinio, būsimus naujinius galite gauti iš bet kurio šaltinio telefone. Gali būti pakeistos programos funkcijos."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tablet" msgid="738046584021528374">"Atnaujinti šią programą iš <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nŠi programa įprastai gauna naujinius iš <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Atnaujinę iš kito šaltinio, būsimus naujinius galite gauti iš bet kurio šaltinio planšetiniame kompiuteryje. Gali būti pakeistos programos funkcijos."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tv" msgid="3056133099508550163">"Atnaujinti šią programą iš <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nŠi programa įprastai gauna naujinius iš <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Atnaujinę iš kito šaltinio, būsimus naujinius galite gauti iš bet kurio šaltinio televizoriuje. Gali būti pakeistos programos funkcijos."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="default" msgid="3750986542284587290">"Atnaujinti šią programą iš <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nŠi programa įprastai gauna naujinius iš <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Atnaujinę iš kito šaltinio, būsimus naujinius galite gauti iš bet kurio šaltinio telefone. Gali būti pakeistos programos funkcijos."</string>
<string name="install_failed" msgid="5777824004474125469">"Programa neįdiegta."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"Paketas užblokuotas ir negali būti įdiegtas."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"Programa neįdiegta, nes paketas nesuderinamas su esamu paketu."</string>
diff --git a/packages/PackageInstaller/res/values-lv/strings.xml b/packages/PackageInstaller/res/values-lv/strings.xml
index 17dd542..8676579 100644
--- a/packages/PackageInstaller/res/values-lv/strings.xml
+++ b/packages/PackageInstaller/res/values-lv/strings.xml
@@ -26,7 +26,9 @@
<string name="install_done" msgid="5987363587661783896">"Lietotne ir instalēta."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"Vai vēlaties instalēt šo lietotni?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"Vai vēlaties atjaunināt šo lietotni?"</string>
- <string name="install_confirm_question_update_owner_reminder" msgid="3750986542284587290">"Vai atjaunināt šo lietotni, izmantojot “<xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>”?\n\nŠī lietotne parasti saņem atjauninājumus no “<xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>”. Veicot atjaunināšanu no cita avota, iespējams, turpmāk tālrunī saņemsiet atjauninājumus no jebkāda avota. Lietotnes funkcionalitāte var mainīties."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tablet" msgid="738046584021528374">"Vai atjaunināt šo lietotni, izmantojot “<xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>”?\n\nŠai lietotnei parasti saņemat atjauninājumus no “<xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>”. Veicot atjaunināšanu no cita avota, iespējams, turpmāk planšetdatorā saņemsiet atjauninājumus no jebkāda avota. Lietotnes funkcionalitāte var mainīties."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tv" msgid="3056133099508550163">"Vai atjaunināt šo lietotni, izmantojot “<xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>”?\n\nŠai lietotnei parasti saņemat atjauninājumus no “<xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>”. Veicot atjaunināšanu no cita avota, iespējams, turpmāk televizorā saņemsiet atjauninājumus no jebkāda avota. Lietotnes funkcionalitāte var mainīties."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="default" msgid="3750986542284587290">"Vai atjaunināt šo lietotni, izmantojot “<xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>”?\n\nŠī lietotne parasti saņem atjauninājumus no “<xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>”. Veicot atjaunināšanu no cita avota, iespējams, turpmāk tālrunī saņemsiet atjauninājumus no jebkāda avota. Lietotnes funkcionalitāte var mainīties."</string>
<string name="install_failed" msgid="5777824004474125469">"Lietotne nav instalēta."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"Pakotnes instalēšana tika bloķēta."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"Lietotne netika instalēta, jo pastāv pakotnes konflikts ar esošu pakotni."</string>
diff --git a/packages/PackageInstaller/res/values-mk/strings.xml b/packages/PackageInstaller/res/values-mk/strings.xml
index 5aaea17..c8f439e 100644
--- a/packages/PackageInstaller/res/values-mk/strings.xml
+++ b/packages/PackageInstaller/res/values-mk/strings.xml
@@ -26,7 +26,9 @@
<string name="install_done" msgid="5987363587661783896">"Апликацијата е инсталирана."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"Дали сакате да ја инсталирате апликацијава?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"Дали сакате да ја ажурирате апликацијава?"</string>
- <string name="install_confirm_question_update_owner_reminder" msgid="3750986542284587290">"Да се ажурира апликацијава од <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nАпликацијава вообичаено добива ажурирања од<xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Со ажурирање од различен извор, може да добивате идни ажурирања од кој било извор на вашиот телефон. Функционалноста на апликацијата може да се промени."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tablet" msgid="738046584021528374">"Да се ажурира апликацијава од <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nАпликацијава вообичаено добива ажурирања од<xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Со ажурирање од различен извор, може да добивате идни ажурирања од кој било извор на вашиот таблет. Функционалноста на апликацијата може да се промени."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tv" msgid="3056133099508550163">"Да се ажурира апликацијава од <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nАпликацијава вообичаено добива ажурирања од<xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Со ажурирање од различен извор, може да добивате идни ажурирања од кој било извор на вашиот телевизор. Функционалноста на апликацијата може да се промени."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="default" msgid="3750986542284587290">"Да се ажурира апликацијава од <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nАпликацијава вообичаено добива ажурирања од<xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Со ажурирање од различен извор, може да добивате идни ажурирања од кој било извор на вашиот телефон. Функционалноста на апликацијата може да се промени."</string>
<string name="install_failed" msgid="5777824004474125469">"Апликацијата не е инсталирана."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"Инсталирањето на пакетот е блокирано."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"Апликација што не е инсталирана како пакет е во конфликт со постоечки пакет."</string>
diff --git a/packages/PackageInstaller/res/values-ml/strings.xml b/packages/PackageInstaller/res/values-ml/strings.xml
index 0535843..f051cc1 100644
--- a/packages/PackageInstaller/res/values-ml/strings.xml
+++ b/packages/PackageInstaller/res/values-ml/strings.xml
@@ -26,7 +26,9 @@
<string name="install_done" msgid="5987363587661783896">"ആപ്പ് ഇൻസ്റ്റാൾ ചെയ്തു."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"ഈ ആപ്പ് ഇൻസ്റ്റാൾ ചെയ്യണോ?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"ഈ ആപ്പ് അപ്ഡേറ്റ് ചെയ്യണോ?"</string>
- <string name="install_confirm_question_update_owner_reminder" msgid="3750986542284587290">"<xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g> എന്നതിൽ നിന്ന് ഈ ആപ്പ് അപ്ഡേറ്റ് ചെയ്യണോ?\n\nഈ ആപ്പിന് സാധാരണയായി <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g> എന്നതിൽ നിന്ന് അപ്ഡേറ്റുകൾ ലഭിക്കാറുണ്ട്. മറ്റൊരു ഉറവിടത്തിൽ നിന്ന് അപ്ഡേറ്റ് ചെയ്യുന്നത് വഴി, നിങ്ങളുടെ ഫോണിലെ ഏത് ഉറവിടത്തിൽ നിന്നും ഭാവിയിൽ അപ്ഡേറ്റുകൾ ലഭിക്കാൻ ഇടയുണ്ട്. ആപ്പ് ഫംഗ്ഷണാലിറ്റിയിൽ വ്യത്യാസം വന്നേക്കാം."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tablet" msgid="738046584021528374">"<xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g> എന്നതിൽ നിന്ന് ഈ ആപ്പ് അപ്ഡേറ്റ് ചെയ്യണോ?\n\nഈ ആപ്പിന് സാധാരണയായി <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g> എന്നതിൽ നിന്ന് അപ്ഡേറ്റുകൾ ലഭിക്കാറുണ്ട്. മറ്റൊരു ഉറവിടത്തിൽ നിന്ന് അപ്ഡേറ്റ് ചെയ്യുന്നതിലൂടെ, നിങ്ങളുടെ ടാബ്ലെറ്റിലെ ഏത് ഉറവിടത്തിൽ നിന്നും ഭാവിയിൽ അപ്ഡേറ്റുകൾ ലഭിച്ചേക്കാം. ആപ്പ് ഫംഗ്ഷണാലിറ്റിയിൽ വ്യത്യാസം വന്നേക്കാം."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tv" msgid="3056133099508550163">"<xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g> എന്നതിൽ നിന്ന് ഈ ആപ്പ് അപ്ഡേറ്റ് ചെയ്യണോ?\n\nഈ ആപ്പിന് സാധാരണയായി <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g> എന്നതിൽ നിന്ന് അപ്ഡേറ്റുകൾ ലഭിക്കാറുണ്ട്. മറ്റൊരു ഉറവിടത്തിൽ നിന്ന് അപ്ഡേറ്റ് ചെയ്യുന്നതിലൂടെ, നിങ്ങളുടെ ടിവിയിലെ ഏത് ഉറവിടത്തിൽ നിന്നും ഭാവിയിൽ അപ്ഡേറ്റുകൾ ലഭിച്ചേക്കാം. ആപ്പ് ഫംഗ്ഷണാലിറ്റിയിൽ വ്യത്യാസം വന്നേക്കാം."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="default" msgid="3750986542284587290">"<xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g> എന്നതിൽ നിന്ന് ഈ ആപ്പ് അപ്ഡേറ്റ് ചെയ്യണോ?\n\nഈ ആപ്പിന് സാധാരണയായി <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g> എന്നതിൽ നിന്ന് അപ്ഡേറ്റുകൾ ലഭിക്കാറുണ്ട്. മറ്റൊരു ഉറവിടത്തിൽ നിന്ന് അപ്ഡേറ്റ് ചെയ്യുന്നത് വഴി, നിങ്ങളുടെ ഫോണിലെ ഏത് ഉറവിടത്തിൽ നിന്നും ഭാവിയിൽ അപ്ഡേറ്റുകൾ ലഭിക്കാൻ ഇടയുണ്ട്. ആപ്പ് ഫംഗ്ഷണാലിറ്റിയിൽ വ്യത്യാസം വന്നേക്കാം."</string>
<string name="install_failed" msgid="5777824004474125469">"ആപ്പ് ഇൻസ്റ്റാൾ ചെയ്തിട്ടില്ല."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"പാക്കേജ് ഇൻസ്റ്റാൾ ചെയ്യുന്നത് ബ്ലോക്ക് ചെയ്തു."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"പാക്കേജിന് നിലവിലുള്ള പാക്കേജുമായി പൊരുത്തക്കേടുള്ളതിനാൽ, ആപ്പ് ഇൻസ്റ്റാൾ ചെയ്തില്ല."</string>
diff --git a/packages/PackageInstaller/res/values-mn/strings.xml b/packages/PackageInstaller/res/values-mn/strings.xml
index 84a3909..6098eb8 100644
--- a/packages/PackageInstaller/res/values-mn/strings.xml
+++ b/packages/PackageInstaller/res/values-mn/strings.xml
@@ -26,7 +26,9 @@
<string name="install_done" msgid="5987363587661783896">"Аппыг суулгасан."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"Та энэ аппыг суулгахыг хүсэж байна уу?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"Та энэ аппыг шинэчлэхийг хүсэж байна уу?"</string>
- <string name="install_confirm_question_update_owner_reminder" msgid="3750986542284587290">"Аппыг <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>-с шинэчлэх үү?\n\nЭнэ апп ихэвчлэн <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>-с шинэчлэлт хүлээн авдаг. Өөр эх сурвалжаас шинэчилснээр та ирээдүйн шинэчлэлтийг утсан дээрх аливаа эх сурвалжаас хүлээн авч магадгүй. Аппын ажиллагаа өөрчлөгдөж магадгүй."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tablet" msgid="738046584021528374">"Энэ аппыг <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>-с шинэчлэх үү?\n\nЭнэ апп ихэвчлэн <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>-с шинэчлэлт хүлээн авдаг. Өөр эх сурвалжаас шинэчилснээр та ирээдүйн шинэчлэлтийг таблет дээрх аливаа эх сурвалжаас хүлээн авч магадгүй. Аппын ажиллагаа өөрчлөгдөж магадгүй."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tv" msgid="3056133099508550163">"Энэ аппыг <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>-с шинэчлэх үү?\n\nЭнэ апп ихэвчлэн <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>-с шинэчлэлт хүлээн авдаг. Өөр эх сурвалжаас шинэчилснээр та ирээдүйн шинэчлэлтийг ТВ дээрх аливаа эх сурвалжаас хүлээн авч магадгүй. Аппын ажиллагаа өөрчлөгдөж магадгүй."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="default" msgid="3750986542284587290">"Аппыг <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>-с шинэчлэх үү?\n\nЭнэ апп ихэвчлэн <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>-с шинэчлэлт хүлээн авдаг. Өөр эх сурвалжаас шинэчилснээр та ирээдүйн шинэчлэлтийг утсан дээрх аливаа эх сурвалжаас хүлээн авч магадгүй. Аппын ажиллагаа өөрчлөгдөж магадгүй."</string>
<string name="install_failed" msgid="5777824004474125469">"Аппыг суулгаагүй."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"Багц суулгахыг блоклосон байна."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"Багц одоо байгаа багцтай тохирохгүй байгаа тул аппыг суулгаж чадсангүй."</string>
diff --git a/packages/PackageInstaller/res/values-mr/strings.xml b/packages/PackageInstaller/res/values-mr/strings.xml
index 367dede..5364fa4 100644
--- a/packages/PackageInstaller/res/values-mr/strings.xml
+++ b/packages/PackageInstaller/res/values-mr/strings.xml
@@ -26,7 +26,9 @@
<string name="install_done" msgid="5987363587661783896">"अॅप इंस्टॉल झाले."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"तुम्हाला हे ॲप इंस्टॉल करायचे आहे का?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"तुम्हाला हे ॲप अपडेट करायचे आहे का?"</string>
- <string name="install_confirm_question_update_owner_reminder" msgid="3750986542284587290">"<xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g> वरून हे अॅप अपडेट करायचे आहे का?\n\nया अॅपला सामान्यतः <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g> कडून अपडेट मिळतात. वेगवेगळ्या स्रोताकडून अपडेट करून, तुम्हाला तुमच्या फोनवरील कोणत्याही स्रोताकडून भविष्यातील अपडेट मिळू शकतात. अॅपची कार्यक्षमता बदलू शकते."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tablet" msgid="738046584021528374">"<xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g> वरून हे अॅप अपडेट करायचे आहे का?\n\nया अॅपला सामान्यतः <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g> कडून अपडेट मिळतात. वेगवेगळ्या स्रोताकडून अपडेट करून, तुम्हाला तुमच्या टॅबलेटवरील कोणत्याही स्रोताकडून भविष्यातील अपडेट मिळू शकतात. अॅपची कार्यक्षमता बदलू शकते."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tv" msgid="3056133099508550163">"<xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g> वरून हे अॅप अपडेट करायचे आहे का?\n\nया अॅपला सामान्यतः <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g> कडून अपडेट मिळतात. वेगवेगळ्या स्रोताकडून अपडेट करून, तुम्हाला तुमच्या टीव्हीवरील कोणत्याही स्रोताकडून भविष्यातील अपडेट मिळू शकतात. अॅपची कार्यक्षमता बदलू शकते."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="default" msgid="3750986542284587290">"<xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g> वरून हे अॅप अपडेट करायचे आहे का?\n\nया अॅपला सामान्यतः <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g> कडून अपडेट मिळतात. वेगवेगळ्या स्रोताकडून अपडेट करून, तुम्हाला तुमच्या फोनवरील कोणत्याही स्रोताकडून भविष्यातील अपडेट मिळू शकतात. अॅपची कार्यक्षमता बदलू शकते."</string>
<string name="install_failed" msgid="5777824004474125469">"अॅप इंस्टॉल झाले नाही."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"पॅकेज इंस्टॉल होण्यापासून ब्लॉक केले होते."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"पॅकेजचा विद्यमान पॅकेजशी विरोध असल्याने अॅप इंस्टॉल झाले नाही."</string>
diff --git a/packages/PackageInstaller/res/values-ms/strings.xml b/packages/PackageInstaller/res/values-ms/strings.xml
index 6ab6622..7f62f5b 100644
--- a/packages/PackageInstaller/res/values-ms/strings.xml
+++ b/packages/PackageInstaller/res/values-ms/strings.xml
@@ -26,7 +26,9 @@
<string name="install_done" msgid="5987363587661783896">"Aplikasi dipasang."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"Adakah anda ingin memasang aplikasi ini?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"Adakah anda mahu mengemas kini apl ini?"</string>
- <string name="install_confirm_question_update_owner_reminder" msgid="3750986542284587290">"Kemas kinikan apl ini daripada <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nApl ini biasanya menerima kemaskinian daripada <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Dengan membuat kemaskinian daripada sumber yang berbeza, anda mungkin menerima kemaskinian masa hadapan daripada sebarang sumber pada telefon anda. Fungsi apl mungkin berubah."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tablet" msgid="738046584021528374">"Kemas kinikan apl ini daripada <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nApl ini biasanya menerima kemaskinian daripada <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Dengan membuat kemaskinian daripada sumber yang berbeza, anda mungkin menerima kemaskinian masa hadapan daripada sebarang sumber pada tablet anda. Fungsi apl mungkin berubah."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tv" msgid="3056133099508550163">"Kemas kinikan apl ini daripada <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nApl ini biasanya menerima kemaskinian daripada <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Dengan membuat kemaskinian daripada sumber yang berbeza, anda mungkin menerima kemaskinian masa hadapan daripada sebarang sumber pada TV anda. Fungsi apl mungkin berubah."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="default" msgid="3750986542284587290">"Kemas kinikan apl ini daripada <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nApl ini biasanya menerima kemaskinian daripada <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Dengan membuat kemaskinian daripada sumber yang berbeza, anda mungkin menerima kemaskinian masa hadapan daripada sebarang sumber pada telefon anda. Fungsi apl mungkin berubah."</string>
<string name="install_failed" msgid="5777824004474125469">"Aplikasi tidak dipasang."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"Pakej ini telah disekat daripada dipasang."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"Apl tidak dipasang kerana pakej bercanggah dengan pakej yang sedia ada."</string>
diff --git a/packages/PackageInstaller/res/values-my/strings.xml b/packages/PackageInstaller/res/values-my/strings.xml
index cb05b06..4eafd7a 100644
--- a/packages/PackageInstaller/res/values-my/strings.xml
+++ b/packages/PackageInstaller/res/values-my/strings.xml
@@ -26,7 +26,9 @@
<string name="install_done" msgid="5987363587661783896">"အက်ပ်ထည့်သွင်းပြီးပါပြီ။"</string>
<string name="install_confirm_question" msgid="7663733664476363311">"ဤအက်ပ်ကို ထည့်သွင်းလိုသလား။"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"ဤအက်ပ်ကို အပ်ဒိတ်လုပ်လိုသလား။"</string>
- <string name="install_confirm_question_update_owner_reminder" msgid="3750986542284587290">"ဤအက်ပ်ကို <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g> မှ အပ်ဒိတ်လုပ်မလား။\n\nဤအက်ပ်သည် ပုံမှန်အားဖြင့် <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g> မှ အပ်ဒိတ်များ ရရှိသည်။ မတူညီသောရင်းမြစ်မှ အပ်ဒိတ်လုပ်ခြင်းဖြင့် ဖုန်းပေါ်တွင် နောင်လာမည့်အပ်ဒိတ်များကို မည်သည့်ရင်းမြစ်မှမဆို လက်ခံရယူနိုင်သည်။ အက်ပ်လုပ်ဆောင်ချက် ပြောင်းလဲနိုင်သည်။"</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tablet" msgid="738046584021528374">"ဤအက်ပ်ကို <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g> မှ အပ်ဒိတ်လုပ်မလား။\n\nဤအက်ပ်သည် ပုံမှန်အားဖြင့် <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g> မှ အပ်ဒိတ်များ ရရှိသည်။ မတူညီသောရင်းမြစ်မှ အပ်ဒိတ်လုပ်ခြင်းဖြင့် တက်ဘလက်ပေါ်တွင် နောင်လာမည့်အပ်ဒိတ်များကို မည်သည့်ရင်းမြစ်မဆိုမှ လက်ခံရယူနိုင်သည်။ အက်ပ်လုပ်ဆောင်ချက် ပြောင်းလဲနိုင်သည်။"</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tv" msgid="3056133099508550163">"ဤအက်ပ်ကို <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g> မှ အပ်ဒိတ်လုပ်မလား။\n\nဤအက်ပ်သည် ပုံမှန်အားဖြင့် <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g> မှ အပ်ဒိတ်များ ရရှိသည်။ မတူညီသောရင်းမြစ်မှ အပ်ဒိတ်လုပ်ခြင်းဖြင့် TV တွင် နောင်လာမည့်အပ်ဒိတ်များကို မည်သည့်ရင်းမြစ်မဆိုမှ လက်ခံရယူနိုင်သည်။ အက်ပ်လုပ်ဆောင်ချက် ပြောင်းလဲနိုင်သည်။"</string>
+ <string name="install_confirm_question_update_owner_reminder" product="default" msgid="3750986542284587290">"ဤအက်ပ်ကို <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g> မှ အပ်ဒိတ်လုပ်မလား။\n\nဤအက်ပ်သည် ပုံမှန်အားဖြင့် <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g> မှ အပ်ဒိတ်များ ရရှိသည်။ မတူညီသောရင်းမြစ်မှ အပ်ဒိတ်လုပ်ခြင်းဖြင့် ဖုန်းပေါ်တွင် နောင်လာမည့်အပ်ဒိတ်များကို မည်သည့်ရင်းမြစ်မှမဆို လက်ခံရယူနိုင်သည်။ အက်ပ်လုပ်ဆောင်ချက် ပြောင်းလဲနိုင်သည်။"</string>
<string name="install_failed" msgid="5777824004474125469">"အက်ပ်မထည့်သွင်းရသေးပါ"</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"ပက်ကေ့ဂျ်ထည့်သွင်းခြင်းကို ပိတ်ထားသည်။"</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"ပက်ကေ့ဂျ်အဖြစ် ထည့်သွင်းမထားသော အက်ပ်သည် လက်ရှိပက်ကေ့ဂျ်နှင့် တိုက်နေသည်။"</string>
diff --git a/packages/PackageInstaller/res/values-nb/strings.xml b/packages/PackageInstaller/res/values-nb/strings.xml
index 7532d7b..7d87473 100644
--- a/packages/PackageInstaller/res/values-nb/strings.xml
+++ b/packages/PackageInstaller/res/values-nb/strings.xml
@@ -26,7 +26,9 @@
<string name="install_done" msgid="5987363587661783896">"Appen er installert."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"Vil du installere denne appen?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"Vil du oppdatere denne appen?"</string>
- <string name="install_confirm_question_update_owner_reminder" msgid="3750986542284587290">"Vil du oppdatere denne appen fra <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nDenne appen mottar vanligvis oppdateringer fra <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Hvis du oppdaterer fra en annen kilde, kan du få fremtidige oppdateringer fra en hvilken som helst kilde på telefonen. Appfunksjonaliteten kan endres."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tablet" msgid="738046584021528374">"Vil du oppdatere denne appen fra <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nDenne appen får vanligvis oppdateringer fra <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Hvis du oppdaterer fra en annen kilde, kan du få fremtidige oppdateringer fra en hvilken som helst kilde på nettbrettet. Appfunksjonaliteten kan endres."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tv" msgid="3056133099508550163">"Vil du oppdatere denne appen fra <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nDenne appen får vanligvis oppdateringer fra <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Hvis du oppdaterer fra en annen kilde, kan du få fremtidige oppdateringer fra en hvilken som helst kilde på TV-en. Appfunksjonaliteten kan endres."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="default" msgid="3750986542284587290">"Vil du oppdatere denne appen fra <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nDenne appen mottar vanligvis oppdateringer fra <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Hvis du oppdaterer fra en annen kilde, kan du få fremtidige oppdateringer fra en hvilken som helst kilde på telefonen. Appfunksjonaliteten kan endres."</string>
<string name="install_failed" msgid="5777824004474125469">"Appen ble ikke installert."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"Pakken er blokkert fra å bli installert."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"Appen ble ikke installert fordi pakken er i konflikt med en eksisterende pakke."</string>
diff --git a/packages/PackageInstaller/res/values-ne/strings.xml b/packages/PackageInstaller/res/values-ne/strings.xml
index 0d7068e..ee0782a 100644
--- a/packages/PackageInstaller/res/values-ne/strings.xml
+++ b/packages/PackageInstaller/res/values-ne/strings.xml
@@ -26,7 +26,9 @@
<string name="install_done" msgid="5987363587661783896">"एप इन्स्टल गरियो।"</string>
<string name="install_confirm_question" msgid="7663733664476363311">"तपाईं यो एप इन्स्टल गर्न चाहनुहुन्छ?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"तपाईं यो एप अपडेट गर्न चाहनुहुन्छ?"</string>
- <string name="install_confirm_question_update_owner_reminder" msgid="3750986542284587290">"यो एप <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g> बाट अपडेट गर्ने हो?\n\nयो एपले सामान्यतया <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g> बाट अपडेट प्राप्त गर्छ। तपाईंले कुनै फरक स्रोतबाट अपडेट गर्नुभयो भने तपाईं भविष्यमा आफ्नो फोनमा भएको जुनसुकै स्रोतबाट अपडेटहरू प्राप्त गर्न सक्नुहुन्छ। यसो गर्दा एपका मुख्य सुविधाहरूले काम गर्ने तरिका परिवर्तन हुन सक्छ।"</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tablet" msgid="738046584021528374">"यो एप <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g> बाट अपडेट गर्ने हो?\n\nयो एपले सामान्यतया <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g> बाट अपडेट प्राप्त गर्छ। तपाईंले कुनै फरक स्रोतबाट अपडेट गर्नुभयो भने तपाईं भविष्यमा आफ्नो ट्याब्लेटमा भएको जुनसुकै स्रोतबाट अपडेटहरू प्राप्त गर्न सक्नुहुन्छ। यसो गर्दा एपको मुख्य सुविधा परिवर्तन हुन सक्छ।"</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tv" msgid="3056133099508550163">"यो एप <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g> बाट अपडेट गर्ने हो?\n\nयो एपले सामान्यतया <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g> बाट अपडेट प्राप्त गर्छ। तपाईंले कुनै फरक स्रोतबाट अपडेट गर्नुभयो भने तपाईं भविष्यमा आफ्नो टिभीमा भएको जुनसुकै स्रोतबाट अपडेटहरू प्राप्त गर्न सक्नुहुन्छ। यसो गर्दा एपको मुख्य सुविधा परिवर्तन हुन सक्छ।"</string>
+ <string name="install_confirm_question_update_owner_reminder" product="default" msgid="3750986542284587290">"यो एप <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g> बाट अपडेट गर्ने हो?\n\nयो एपले सामान्यतया <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g> बाट अपडेट प्राप्त गर्छ। तपाईंले कुनै फरक स्रोतबाट अपडेट गर्नुभयो भने तपाईं भविष्यमा आफ्नो फोनमा भएको जुनसुकै स्रोतबाट अपडेटहरू प्राप्त गर्न सक्नुहुन्छ। यसो गर्दा एपका मुख्य सुविधाहरूले काम गर्ने तरिका परिवर्तन हुन सक्छ।"</string>
<string name="install_failed" msgid="5777824004474125469">"एप स्थापना गरिएन।"</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"यो प्याकेज स्थापना गर्ने क्रममा अवरोध गरियो।"</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"प्याकेजका रूपमा स्थापना नगरिएको एप विद्यमान प्याकेजसँग मेल खाँदैन।"</string>
diff --git a/packages/PackageInstaller/res/values-nl/strings.xml b/packages/PackageInstaller/res/values-nl/strings.xml
index fab6d51..47a5ec0 100644
--- a/packages/PackageInstaller/res/values-nl/strings.xml
+++ b/packages/PackageInstaller/res/values-nl/strings.xml
@@ -26,7 +26,9 @@
<string name="install_done" msgid="5987363587661783896">"App geïnstalleerd."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"Wil je deze app installeren?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"Wil je deze app updaten?"</string>
- <string name="install_confirm_question_update_owner_reminder" msgid="3750986542284587290">"Deze app updaten via <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nDeze app krijgt gewoonlijk updates via <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Als je updatet via een andere bron, kun je toekomstige updates via elke bron op je telefoon krijgen. De app-functionaliteit kan veranderen."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tablet" msgid="738046584021528374">"Deze app updaten via <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nDeze app krijgt gewoonlijk updates via <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Als je updatet via een andere bron, kun je toekomstige updates via elke bron op je tablet krijgen. De app-functionaliteit kan veranderen."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tv" msgid="3056133099508550163">"Deze app updaten via <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nDeze app krijgt gewoonlijk updates via <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Als je updatet via een andere bron, kun je toekomstige updates via elke bron op je tv krijgen. De app-functionaliteit kan veranderen."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="default" msgid="3750986542284587290">"Deze app updaten via <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nDeze app krijgt gewoonlijk updates via <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Als je updatet via een andere bron, kun je toekomstige updates via elke bron op je telefoon krijgen. De app-functionaliteit kan veranderen."</string>
<string name="install_failed" msgid="5777824004474125469">"App niet geïnstalleerd."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"De installatie van het pakket is geblokkeerd."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"App die niet is geïnstalleerd als pakket conflicteert met een bestaand pakket."</string>
diff --git a/packages/PackageInstaller/res/values-or/strings.xml b/packages/PackageInstaller/res/values-or/strings.xml
index b6caebd..7c91ac4 100644
--- a/packages/PackageInstaller/res/values-or/strings.xml
+++ b/packages/PackageInstaller/res/values-or/strings.xml
@@ -26,7 +26,9 @@
<string name="install_done" msgid="5987363587661783896">"ଆପ ଇନଷ୍ଟଲ ହୋଇଗଲା।"</string>
<string name="install_confirm_question" msgid="7663733664476363311">"ଆପଣ ଏହି ଆପକୁ ଇନଷ୍ଟଲ୍ କରିବା ପାଇଁ ଚାହୁଁଛନ୍ତି କି?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"ଆପଣ ଏହି ଆପକୁ ଅପଡେଟ୍ କରିବା ପାଇଁ ଚାହୁଁଛନ୍ତି କି?"</string>
- <string name="install_confirm_question_update_owner_reminder" msgid="3750986542284587290">"<xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>ରୁ ଏହି ଆପକୁ ଅପଡେଟ କରିବେ?\n\nଏହି ଆପ ସାଧାରଣତଃ <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>ରୁ ଅପଡେଟଗୁଡ଼ିକ ପାଏ। ଏକ ଭିନ୍ନ ସୋର୍ସରୁ ଅପଡେଟ କରି ଆପଣଙ୍କ ଫୋନରେ ଯେ କୌଣସି ସୋର୍ସରୁ ଭବିଷ୍ୟତର ଅପଡେଟଗୁଡ଼ିକ ଆପଣ ପାଇପାରନ୍ତି। ଆପ କାର୍ଯ୍ୟକ୍ଷମତା ପରିବର୍ତ୍ତନ ହୋଇପାରେ।"</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tablet" msgid="738046584021528374">"<xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>ରୁ ଏହି ଆପକୁ ଅପଡେଟ କରିବେ?\n\nଏହି ଆପ ସାଧାରଣତଃ <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>ରୁ ଅପଡେଟ ପାଇଥାଏ। ଏକ ଭିନ୍ନ ସୋର୍ସରୁ ଅପଡେଟ କରି ଆପଣ ଆପଣଙ୍କ ଫୋନରେ ଯେ କୌଣସି ସୋର୍ସରୁ ଭବିଷ୍ୟତର ଅପଡେଟଗୁଡ଼ିକ ପାଇପାରନ୍ତି। ଆପ କାର୍ଯ୍ୟକ୍ଷମତା ପରିବର୍ତ୍ତନ ହୋଇପାରେ।"</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tv" msgid="3056133099508550163">"<xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>ରୁ ଏହି ଆପକୁ ଅପଡେଟ କରିବେ?\n\nଏହି ଆପ ସାଧାରଣତଃ <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>ରୁ ଅପଡେଟ ପାଇଥାଏ। ଏକ ଭିନ୍ନ ସୋର୍ସରୁ ଅପଡେଟ କରି ଆପଣ ଆପଣଙ୍କ ଫୋନରେ ଯେ କୌଣସି ସୋର୍ସରୁ ଭବିଷ୍ୟତର ଅପଡେଟଗୁଡ଼ିକ ପାଇପାରନ୍ତି। ଆପ କାର୍ଯ୍ୟକ୍ଷମତା ପରିବର୍ତ୍ତନ ହୋଇପାରେ।"</string>
+ <string name="install_confirm_question_update_owner_reminder" product="default" msgid="3750986542284587290">"<xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>ରୁ ଏହି ଆପକୁ ଅପଡେଟ କରିବେ?\n\nଏହି ଆପ ସାଧାରଣତଃ <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>ରୁ ଅପଡେଟଗୁଡ଼ିକ ପାଏ। ଏକ ଭିନ୍ନ ସୋର୍ସରୁ ଅପଡେଟ କରି ଆପଣଙ୍କ ଫୋନରେ ଯେ କୌଣସି ସୋର୍ସରୁ ଭବିଷ୍ୟତର ଅପଡେଟଗୁଡ଼ିକ ଆପଣ ପାଇପାରନ୍ତି। ଆପ କାର୍ଯ୍ୟକ୍ଷମତା ପରିବର୍ତ୍ତନ ହୋଇପାରେ।"</string>
<string name="install_failed" msgid="5777824004474125469">"ଆପ୍ ଇନଷ୍ଟଲ୍ ହୋଇନାହିଁ।"</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"ଏହି ପ୍ୟାକେଜ୍କୁ ଇନଷ୍ଟଲ୍ କରାଯିବାରୁ ଅବରୋଧ କରାଯାଇଥିଲା।"</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"ପୂର୍ବରୁ ଥିବା ପ୍ୟାକେଜ୍ ସହ ଏହି ପ୍ୟାକେଜ୍ର ସମସ୍ୟା ଉପୁଯିବାରୁ ଆପ୍ ଇନଷ୍ଟଲ୍ ହୋଇପାରିଲା ନାହିଁ।"</string>
diff --git a/packages/PackageInstaller/res/values-pa/strings.xml b/packages/PackageInstaller/res/values-pa/strings.xml
index 1ef4921..40ed479 100644
--- a/packages/PackageInstaller/res/values-pa/strings.xml
+++ b/packages/PackageInstaller/res/values-pa/strings.xml
@@ -26,7 +26,9 @@
<string name="install_done" msgid="5987363587661783896">"ਐਪ ਸਥਾਪਤ ਕੀਤੀ ਗਈ।"</string>
<string name="install_confirm_question" msgid="7663733664476363311">"ਕੀ ਤੁਸੀਂ ਇਸ ਐਪ ਨੂੰ ਸਥਾਪਤ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"ਕੀ ਤੁਸੀਂ ਇਸ ਐਪ ਨੂੰ ਅੱਪਡੇਟ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ?"</string>
- <string name="install_confirm_question_update_owner_reminder" msgid="3750986542284587290">"ਕੀ ਇਸ ਐਪ ਨੂੰ <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g> ਤੋਂ ਅੱਪਡੇਟ ਕਰਨਾ ਹੈ?\n\nਇਸ ਐਪ ਨੂੰ ਆਮ ਤੌਰ \'ਤੇ <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g> ਤੋਂ ਅੱਪਡੇਟਾਂ ਪ੍ਰਾਪਤ ਹੁੰਦੀਆਂ ਹਨ। ਕਿਸੇ ਵੱਖਰੇ ਸਰੋਤ ਤੋਂ ਅੱਪਡੇਟ ਕਰ ਕੇ, ਤੁਸੀਂ ਆਪਣੇ ਫ਼ੋਨ \'ਤੇ ਕਿਸੇ ਵੀ ਸਰੋਤ ਤੋਂ ਭਵਿੱਖੀ ਅੱਪਡੇਟਾਂ ਪ੍ਰਾਪਤ ਕਰ ਸਕਦੇ ਹੋ। ਐਪ ਪ੍ਰਕਾਰਜਾਤਮਕਤਾ ਬਦਲ ਸਕਦੀ ਹੈ।"</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tablet" msgid="738046584021528374">"ਕੀ ਇਸ ਐਪ ਨੂੰ <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g> ਤੋਂ ਅੱਪਡੇਟ ਕਰਨਾ ਹੈ?\n\nਇਸ ਐਪ ਨੂੰ ਆਮ ਤੌਰ \'ਤੇ <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g> ਤੋਂ ਅੱਪਡੇਟਾਂ ਪ੍ਰਾਪਤ ਹੁੰਦੀਆਂ ਹਨ। ਕਿਸੇ ਵੱਖਰੇ ਸਰੋਤ ਤੋਂ ਅੱਪਡੇਟ ਕਰ ਕੇ, ਤੁਸੀਂ ਆਪਣੇ ਟੈਬਲੈੱਟ \'ਤੇ ਕਿਸੇ ਵੀ ਸਰੋਤ ਤੋਂ ਭਵਿੱਖੀ ਅੱਪਡੇਟਾਂ ਨੂੰ ਪ੍ਰਾਪਤ ਕਰ ਸਕਦੇ ਹੋ। ਐਪ ਪ੍ਰਕਾਰਜਾਤਮਕਤਾ ਬਦਲ ਸਕਦੀ ਹੈ।"</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tv" msgid="3056133099508550163">"ਕੀ ਇਸ ਐਪ ਨੂੰ <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g> ਤੋਂ ਅੱਪਡੇਟ ਕਰਨਾ ਹੈ?\n\nਇਸ ਐਪ ਨੂੰ ਆਮ ਤੌਰ \'ਤੇ <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g> ਤੋਂ ਅੱਪਡੇਟਾਂ ਪ੍ਰਾਪਤ ਹੁੰਦੀਆਂ ਹਨ। ਕਿਸੇ ਵੱਖਰੇ ਸਰੋਤ ਤੋਂ ਅੱਪਡੇਟ ਕਰ ਕੇ, ਤੁਸੀਂ ਆਪਣੇ ਟੀਵੀ \'ਤੇ ਕਿਸੇ ਵੀ ਸਰੋਤ ਤੋਂ ਭਵਿੱਖੀ ਅੱਪਡੇਟਾਂ ਨੂੰ ਪ੍ਰਾਪਤ ਕਰ ਸਕਦੇ ਹੋ। ਐਪ ਪ੍ਰਕਾਰਜਾਤਮਕਤਾ ਬਦਲ ਸਕਦੀ ਹੈ।"</string>
+ <string name="install_confirm_question_update_owner_reminder" product="default" msgid="3750986542284587290">"ਕੀ ਇਸ ਐਪ ਨੂੰ <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g> ਤੋਂ ਅੱਪਡੇਟ ਕਰਨਾ ਹੈ?\n\nਇਸ ਐਪ ਨੂੰ ਆਮ ਤੌਰ \'ਤੇ <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g> ਤੋਂ ਅੱਪਡੇਟਾਂ ਪ੍ਰਾਪਤ ਹੁੰਦੀਆਂ ਹਨ। ਕਿਸੇ ਵੱਖਰੇ ਸਰੋਤ ਤੋਂ ਅੱਪਡੇਟ ਕਰ ਕੇ, ਤੁਸੀਂ ਆਪਣੇ ਫ਼ੋਨ \'ਤੇ ਕਿਸੇ ਵੀ ਸਰੋਤ ਤੋਂ ਭਵਿੱਖੀ ਅੱਪਡੇਟਾਂ ਪ੍ਰਾਪਤ ਕਰ ਸਕਦੇ ਹੋ। ਐਪ ਪ੍ਰਕਾਰਜਾਤਮਕਤਾ ਬਦਲ ਸਕਦੀ ਹੈ।"</string>
<string name="install_failed" msgid="5777824004474125469">"ਐਪ ਸਥਾਪਤ ਨਹੀਂ ਕੀਤੀ ਗਈ।"</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"ਪੈਕੇਜ ਨੂੰ ਸਥਾਪਤ ਹੋਣ ਤੋਂ ਬਲਾਕ ਕੀਤਾ ਗਿਆ ਸੀ।"</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"ਪੈਕੇਜ ਦੇ ਇੱਕ ਮੌਜੂਦਾ ਪੈਕੇਜ ਨਾਲ ਵਿਵਾਦ ਹੋਣ ਕਰਕੇ ਐਪ ਸਥਾਪਤ ਨਹੀਂ ਕੀਤੀ ਗਈ।"</string>
diff --git a/packages/PackageInstaller/res/values-pl/strings.xml b/packages/PackageInstaller/res/values-pl/strings.xml
index e44a391..264d247 100644
--- a/packages/PackageInstaller/res/values-pl/strings.xml
+++ b/packages/PackageInstaller/res/values-pl/strings.xml
@@ -26,7 +26,9 @@
<string name="install_done" msgid="5987363587661783896">"Aplikacja została zainstalowana."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"Zainstalować tę aplikację?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"Zaktualizować tę aplikację?"</string>
- <string name="install_confirm_question_update_owner_reminder" msgid="3750986542284587290">"Zastosować do aplikacji aktualizację pochodzącą z tego źródła (<xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>)?\n\nAktualizacje dla tej aplikacji zwykle dostarcza <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Jeśli zastosujesz aplikację pochodzącą z innego źródła, możesz w przyszłości otrzymywać na telefonie aktualizacje z dowolnych źródeł. Funkcje aplikacji mogą się zmienić."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tablet" msgid="738046584021528374">"Zastosować do aplikacji aktualizację pochodzącą z tego źródła (<xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>)?\n\nAktualizacje dla tej aplikacji zwykle dostarcza <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Jeśli zastosujesz aplikację pochodzącą z innego źródła, możesz w przyszłości otrzymywać na tablecie aktualizacje z dowolnych źródeł. Funkcje aplikacji mogą się zmienić."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tv" msgid="3056133099508550163">"Zastosować do aplikacji aktualizację pochodzącą z tego źródła (<xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>)?\n\nAktualizacje dla tej aplikacji zwykle dostarcza <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Jeśli zastosujesz aplikację pochodzącą z innego źródła, możesz w przyszłości otrzymywać na telewizorze aktualizacje z dowolnych źródeł. Funkcje aplikacji mogą się zmienić."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="default" msgid="3750986542284587290">"Zastosować do aplikacji aktualizację pochodzącą z tego źródła (<xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>)?\n\nAktualizacje dla tej aplikacji zwykle dostarcza <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Jeśli zastosujesz aplikację pochodzącą z innego źródła, możesz w przyszłości otrzymywać na telefonie aktualizacje z dowolnych źródeł. Funkcje aplikacji mogą się zmienić."</string>
<string name="install_failed" msgid="5777824004474125469">"Aplikacja nie została zainstalowana."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"Instalacja pakietu została zablokowana."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"Aplikacja nie została zainstalowana, bo powoduje konflikt z istniejącym pakietem."</string>
diff --git a/packages/PackageInstaller/res/values-pt-rBR/strings.xml b/packages/PackageInstaller/res/values-pt-rBR/strings.xml
index 923c3bd..38ea89e 100644
--- a/packages/PackageInstaller/res/values-pt-rBR/strings.xml
+++ b/packages/PackageInstaller/res/values-pt-rBR/strings.xml
@@ -26,7 +26,9 @@
<string name="install_done" msgid="5987363587661783896">"App instalado."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"Quer instalar esse app?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"Quer atualizar esse app?"</string>
- <string name="install_confirm_question_update_owner_reminder" msgid="3750986542284587290">"Atualizar este app com <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nEste app normalmente recebe atualizações de <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Se você usa uma fonte diferente, o smartphone vai aceitar outras fontes para fazer as próximas atualizações. A funcionalidade do app pode mudar."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tablet" msgid="738046584021528374">"Atualizar este app com <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nAs atualizações dele normalmente são feitas com <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Ao usar uma fonte diferente, as próximas atualizações poderão ser feitas com qualquer fonte no tablet. A funcionalidade do app pode mudar."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tv" msgid="3056133099508550163">"Atualizar este app com <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nAs atualizações dele normalmente são feitas com <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Ao usar uma fonte diferente, as próximas atualizações poderão ser feitas com qualquer fonte na TV. A funcionalidade do app pode mudar."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="default" msgid="3750986542284587290">"Atualizar este app com <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nEste app normalmente recebe atualizações de <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Se você usa uma fonte diferente, o smartphone vai aceitar outras fontes para fazer as próximas atualizações. A funcionalidade do app pode mudar."</string>
<string name="install_failed" msgid="5777824004474125469">"O app não foi instalado."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"A instalação do pacote foi bloqueada."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"Como o pacote tem um conflito com um pacote já existente, o app não foi instalado."</string>
diff --git a/packages/PackageInstaller/res/values-pt-rPT/strings.xml b/packages/PackageInstaller/res/values-pt-rPT/strings.xml
index de85f66..4400615 100644
--- a/packages/PackageInstaller/res/values-pt-rPT/strings.xml
+++ b/packages/PackageInstaller/res/values-pt-rPT/strings.xml
@@ -26,7 +26,9 @@
<string name="install_done" msgid="5987363587661783896">"App instalada."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"Instalar esta app?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"Pretende atualizar esta app?"</string>
- <string name="install_confirm_question_update_owner_reminder" msgid="3750986542284587290">"Atualizar esta app a partir de <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nNormalmente, esta app recebe atualizações de <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Se atualizar a partir de uma fonte diferente, poderá receber futuras atualizações de qualquer fonte no seu telemóvel. A funcionalidade da app pode sofrer alterações."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tablet" msgid="738046584021528374">"Atualizar esta app a partir de <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nNormalmente, esta app recebe atualizações de <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Se atualizar a partir de uma origem diferente, poderá receber futuras atualizações de qualquer origem no seu tablet. A funcionalidade da app pode sofrer alterações."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tv" msgid="3056133099508550163">"Atualizar esta app a partir de <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nNormalmente, esta app recebe atualizações de <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Se atualizar a partir de uma origem diferente, poderá receber futuras atualizações de qualquer origem na sua TV. A funcionalidade da app pode sofrer alterações."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="default" msgid="3750986542284587290">"Atualizar esta app a partir de <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nNormalmente, esta app recebe atualizações de <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Se atualizar a partir de uma fonte diferente, poderá receber futuras atualizações de qualquer fonte no seu telemóvel. A funcionalidade da app pode sofrer alterações."</string>
<string name="install_failed" msgid="5777824004474125469">"Aplicação não instalada."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"Foi bloqueada a instalação do pacote."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"A app não foi instalada porque o pacote entra em conflito com um pacote existente."</string>
diff --git a/packages/PackageInstaller/res/values-pt/strings.xml b/packages/PackageInstaller/res/values-pt/strings.xml
index 923c3bd..38ea89e 100644
--- a/packages/PackageInstaller/res/values-pt/strings.xml
+++ b/packages/PackageInstaller/res/values-pt/strings.xml
@@ -26,7 +26,9 @@
<string name="install_done" msgid="5987363587661783896">"App instalado."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"Quer instalar esse app?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"Quer atualizar esse app?"</string>
- <string name="install_confirm_question_update_owner_reminder" msgid="3750986542284587290">"Atualizar este app com <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nEste app normalmente recebe atualizações de <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Se você usa uma fonte diferente, o smartphone vai aceitar outras fontes para fazer as próximas atualizações. A funcionalidade do app pode mudar."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tablet" msgid="738046584021528374">"Atualizar este app com <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nAs atualizações dele normalmente são feitas com <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Ao usar uma fonte diferente, as próximas atualizações poderão ser feitas com qualquer fonte no tablet. A funcionalidade do app pode mudar."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tv" msgid="3056133099508550163">"Atualizar este app com <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nAs atualizações dele normalmente são feitas com <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Ao usar uma fonte diferente, as próximas atualizações poderão ser feitas com qualquer fonte na TV. A funcionalidade do app pode mudar."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="default" msgid="3750986542284587290">"Atualizar este app com <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nEste app normalmente recebe atualizações de <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Se você usa uma fonte diferente, o smartphone vai aceitar outras fontes para fazer as próximas atualizações. A funcionalidade do app pode mudar."</string>
<string name="install_failed" msgid="5777824004474125469">"O app não foi instalado."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"A instalação do pacote foi bloqueada."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"Como o pacote tem um conflito com um pacote já existente, o app não foi instalado."</string>
diff --git a/packages/PackageInstaller/res/values-ro/strings.xml b/packages/PackageInstaller/res/values-ro/strings.xml
index de4dd55..471ff09 100644
--- a/packages/PackageInstaller/res/values-ro/strings.xml
+++ b/packages/PackageInstaller/res/values-ro/strings.xml
@@ -26,7 +26,9 @@
<string name="install_done" msgid="5987363587661783896">"Aplicație instalată."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"Vrei să instalezi această aplicație?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"Vrei să actualizezi această aplicație?"</string>
- <string name="install_confirm_question_update_owner_reminder" msgid="3750986542284587290">"Actualizezi aplicația de la <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nDe obicei, aplicația primește actualizări de la <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Dacă actualizezi din altă sursă, este posibil să primești actualizări viitoare din orice sursă pe telefon. Funcționalitatea aplicației se poate modifica."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tablet" msgid="738046584021528374">"Actualizează aplicația de la <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nDe obicei, aplicația primește actualizări de la <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Dacă actualizezi din altă sursă, este posibil să primești actualizări viitoare din orice sursă pe tabletă. Funcționalitatea aplicației se poate modifica."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tv" msgid="3056133099508550163">"Actualizează aplicația de la <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nDe obicei, aplicația primește actualizări de la <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Dacă actualizezi din altă sursă, este posibil să primești actualizări viitoare din orice sursă pe televizor. Funcționalitatea aplicației se poate modifica."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="default" msgid="3750986542284587290">"Actualizezi aplicația de la <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nDe obicei, aplicația primește actualizări de la <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Dacă actualizezi din altă sursă, este posibil să primești actualizări viitoare din orice sursă pe telefon. Funcționalitatea aplicației se poate modifica."</string>
<string name="install_failed" msgid="5777824004474125469">"Aplicația nu a fost instalată."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"Instalarea pachetului a fost blocată."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"Aplicația nu a fost instalată deoarece pachetul intră în conflict cu un pachet existent."</string>
diff --git a/packages/PackageInstaller/res/values-ru/strings.xml b/packages/PackageInstaller/res/values-ru/strings.xml
index d1f56fd..11e2ebf 100644
--- a/packages/PackageInstaller/res/values-ru/strings.xml
+++ b/packages/PackageInstaller/res/values-ru/strings.xml
@@ -26,7 +26,9 @@
<string name="install_done" msgid="5987363587661783896">"Приложение установлено."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"Установить приложение?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"Обновить приложение?"</string>
- <string name="install_confirm_question_update_owner_reminder" msgid="3750986542284587290">"Обновить приложение отсюда: <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nСтандартный источник обновлений этого приложения – <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Если обновить приложение из другого источника, для последующих обновлений будут использоваться любые источники на телефоне. Функции приложения могут измениться."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tablet" msgid="738046584021528374">"Обновить приложение из <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nОбычно это приложение получает обновления из <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Если обновить приложение из другого источника, в будущем для обновления могут использоваться любые источники на планшете. Функции приложения могут измениться."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tv" msgid="3056133099508550163">"Обновить приложение из <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nОбычно это приложение получает обновления из <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Если обновить приложение из другого источника, в будущем для обновления могут использоваться любые источники на телевизоре. Функции приложения могут измениться."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="default" msgid="3750986542284587290">"Обновить приложение отсюда: <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nСтандартный источник обновлений этого приложения – <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Если обновить приложение из другого источника, для последующих обновлений будут использоваться любые источники на телефоне. Функции приложения могут измениться."</string>
<string name="install_failed" msgid="5777824004474125469">"Приложение не установлено."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"Установка пакета заблокирована."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"Приложение не установлено, так как оно конфликтует с другим пакетом."</string>
diff --git a/packages/PackageInstaller/res/values-si/strings.xml b/packages/PackageInstaller/res/values-si/strings.xml
index c300b68..b0392ed 100644
--- a/packages/PackageInstaller/res/values-si/strings.xml
+++ b/packages/PackageInstaller/res/values-si/strings.xml
@@ -26,7 +26,9 @@
<string name="install_done" msgid="5987363587661783896">"යෙදුම ස්ථාපනය කර ඇත."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"මෙම යෙදුම ස්ථාපනය කිරීමට ඔබට අවශ්යද?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"ඔබට මෙම යෙදුම යාවත්කාලීන කිරීමට අවශ්යද?"</string>
- <string name="install_confirm_question_update_owner_reminder" msgid="3750986542284587290">"<xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g> වෙතින් මෙම යෙදුම යාවත්කාලීන කරන්න ද?\n\nමෙම යෙදුමට සාමාන්යයෙන් <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g> සිට යාවත්කාලීන ලැබේ. වෙනස් මූලාශ්රයකින් යාවත්කාලීන කිරීමෙන්, ඔබට ඔබේ දුරකථනයෙහි ඕනෑම මූලාශ්රයකින් අනාගත යාවත්කාලීන ලැබීමට ඉඩ ඇත. යෙදුම් ක්රියාකාරිත්වය වෙනස් වීමට ඉඩ ඇත."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tablet" msgid="738046584021528374">"<xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g> වෙතින් මෙම යෙදුම යාවත්කාලීන කරන්න ද?\n\nමෙම යෙදුමට සාමාන්යයෙන් <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g> සිට යාවත්කාලීන ලැබේ. වෙනස් මූලාශ්රයකින් යාවත්කාලීන කිරීමෙන්, ඔබට ඔබේ ටැබ්ලටයෙහි ඕනෑම මූලාශ්රයකින් අනාගත යාවත්කාලීන ලැබීමට ඉඩ ඇත. යෙදුම් ක්රියාකාරිත්වය වෙනස් වීමට ඉඩ ඇත."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tv" msgid="3056133099508550163">"<xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g> වෙතින් මෙම යෙදුම යාවත්කාලීන කරන්න ද?\n\nමෙම යෙදුමට සාමාන්යයෙන් <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g> සිට යාවත්කාලීන ලැබේ. වෙනස් මූලාශ්රයකින් යාවත්කාලීන කිරීමෙන්, ඔබට ඔබේ TV මත ඕනෑම මූලාශ්රයකින් අනාගත යාවත්කාලීන ලැබීමට ඉඩ ඇත. යෙදුම් ක්රියාකාරිත්වය වෙනස් වීමට ඉඩ ඇත."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="default" msgid="3750986542284587290">"<xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g> වෙතින් මෙම යෙදුම යාවත්කාලීන කරන්න ද?\n\nමෙම යෙදුමට සාමාන්යයෙන් <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g> සිට යාවත්කාලීන ලැබේ. වෙනස් මූලාශ්රයකින් යාවත්කාලීන කිරීමෙන්, ඔබට ඔබේ දුරකථනයෙහි ඕනෑම මූලාශ්රයකින් අනාගත යාවත්කාලීන ලැබීමට ඉඩ ඇත. යෙදුම් ක්රියාකාරිත්වය වෙනස් වීමට ඉඩ ඇත."</string>
<string name="install_failed" msgid="5777824004474125469">"යෙදුම ස්ථාපනය කර නැත."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"මෙම පැකේජය ස්ථාපනය කිරීම අවහිර කරන ලදි."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"පැකේජය දැනට පවතින පැකේජයක් සමග ගැටෙන නිසා යෙදුම ස්ථාපනය නොකරන ලදී."</string>
diff --git a/packages/PackageInstaller/res/values-sk/strings.xml b/packages/PackageInstaller/res/values-sk/strings.xml
index 58a3a35..8e84671 100644
--- a/packages/PackageInstaller/res/values-sk/strings.xml
+++ b/packages/PackageInstaller/res/values-sk/strings.xml
@@ -26,7 +26,9 @@
<string name="install_done" msgid="5987363587661783896">"Aplikácia bola nainštalovaná."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"Chcete túto aplikáciu nainštalovať?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"Chcete túto aplikáciu aktualizovať?"</string>
- <string name="install_confirm_question_update_owner_reminder" msgid="3750986542284587290">"Chcete aktualizovať túto aplikáciu zo zdroja <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nTúto aplikáciu obvykle aktualizuje <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Ak vykonáte aktualizáciu z iného zdroja, aplikácia sa v budúcnosti môže aktualizovať z ľubovoľného zdroja v telefóne. Funkcie aplikácie sa môžu zmeniť."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tablet" msgid="738046584021528374">"Chcete aktualizovať túto aplikáciu zo zdroja <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nTáto aplikácia obvykle dostáva aktualizácie zo zdroja <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Ak aktualizujete z iného zdroja, môžete v budúcnosti dostávať aktualizácie z ľubovoľného zdroja v tablete. Funkcie aplikácie sa môžu zmeniť."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tv" msgid="3056133099508550163">"Chcete aktualizovať túto aplikáciu zo zdroja <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nTáto aplikácia obvykle dostáva aktualizácie zo zdroja <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Ak aktualizujete z iného zdroja, môžete v budúcnosti dostávať aktualizácie z ľubovoľného zdroja v televízore. Funkcie aplikácie sa môžu zmeniť."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="default" msgid="3750986542284587290">"Chcete aktualizovať túto aplikáciu zo zdroja <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nTúto aplikáciu obvykle aktualizuje <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Ak vykonáte aktualizáciu z iného zdroja, aplikácia sa v budúcnosti môže aktualizovať z ľubovoľného zdroja v telefóne. Funkcie aplikácie sa môžu zmeniť."</string>
<string name="install_failed" msgid="5777824004474125469">"Aplikácia nebola nainštalovaná."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"Inštalácia balíka bola zablokovaná."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"Aplikácia sa nenainštalovala, pretože balík je v konflikte s existujúcim balíkom."</string>
diff --git a/packages/PackageInstaller/res/values-sl/strings.xml b/packages/PackageInstaller/res/values-sl/strings.xml
index 00c3d15..0c67373 100644
--- a/packages/PackageInstaller/res/values-sl/strings.xml
+++ b/packages/PackageInstaller/res/values-sl/strings.xml
@@ -26,7 +26,9 @@
<string name="install_done" msgid="5987363587661783896">"Aplikacija je nameščena."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"Ali želite namestiti to aplikacijo?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"Ali želite posodobiti to aplikacijo?"</string>
- <string name="install_confirm_question_update_owner_reminder" msgid="3750986542284587290">"Želite to aplikacijo posodobiti iz vira <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nTa aplikacija običajno prejema posodobitve iz vira <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Če jo posodobite iz drugega vira, boste prihodnje posodobitve morda prejemali iz katerega koli vira v telefonu. Funkcija aplikacije se lahko spremeni."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tablet" msgid="738046584021528374">"Želite to aplikacijo posodobiti iz vira <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nTa aplikacija običajno prejema posodobitve iz vira <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Če jo posodobite iz drugega vira, boste prihodnje posodobitve morda prejemali iz katerega koli vira v tabličnem računalniku. Funkcija aplikacije se lahko spremeni."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tv" msgid="3056133099508550163">"Želite to aplikacijo posodobiti iz vira <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nTa aplikacija običajno prejema posodobitve iz vira <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Če jo posodobite iz drugega vira, boste prihodnje posodobitve morda prejemali iz katerega koli vira v televizorju. Funkcija aplikacije se lahko spremeni."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="default" msgid="3750986542284587290">"Želite to aplikacijo posodobiti iz vira <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nTa aplikacija običajno prejema posodobitve iz vira <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Če jo posodobite iz drugega vira, boste prihodnje posodobitve morda prejemali iz katerega koli vira v telefonu. Funkcija aplikacije se lahko spremeni."</string>
<string name="install_failed" msgid="5777824004474125469">"Aplikacija ni nameščena."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"Namestitev paketa je bila blokirana."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"Aplikacija ni bila nameščena, ker je paket v navzkrižju z obstoječim paketom."</string>
diff --git a/packages/PackageInstaller/res/values-sq/strings.xml b/packages/PackageInstaller/res/values-sq/strings.xml
index 9904bc0..f2cbf31 100644
--- a/packages/PackageInstaller/res/values-sq/strings.xml
+++ b/packages/PackageInstaller/res/values-sq/strings.xml
@@ -26,7 +26,9 @@
<string name="install_done" msgid="5987363587661783896">"Aplikacioni u instalua."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"Dëshiron ta instalosh këtë aplikacion?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"Dëshiron ta përditësosh këtë aplikacion?"</string>
- <string name="install_confirm_question_update_owner_reminder" msgid="3750986542284587290">"Të përditësohet ky aplikacion nga <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nKy aplikacion zakonisht merr përditësime nga <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Duke përditësuar nga një burim tjetër, mund të marrësh përditësime të ardhshme nga çdo burim në telefonin tënd. Funksionaliteti i aplikacionit mund të ndryshojë."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tablet" msgid="738046584021528374">"Të përditësohet ky aplikacion nga <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nKy aplikacion zakonisht merr përditësime nga <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Duke përditësuar nga një burim tjetër, mund të marrësh përditësime të ardhshme nga çdo burim në tabletin tënd. Funksionaliteti i aplikacionit mund të ndryshojë."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tv" msgid="3056133099508550163">"Të përditësohet ky aplikacion nga <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nKy aplikacion zakonisht merr përditësime nga <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Duke përditësuar nga një burim tjetër, mund të marrësh përditësime të ardhshme nga çdo burim në televizorin tënd. Funksionaliteti i aplikacionit mund të ndryshojë."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="default" msgid="3750986542284587290">"Të përditësohet ky aplikacion nga <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nKy aplikacion zakonisht merr përditësime nga <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Duke përditësuar nga një burim tjetër, mund të marrësh përditësime të ardhshme nga çdo burim në telefonin tënd. Funksionaliteti i aplikacionit mund të ndryshojë."</string>
<string name="install_failed" msgid="5777824004474125469">"Aplikacioni nuk u instalua."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"Instalimi paketës u bllokua."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"Aplikacioni nuk u instalua pasi paketa është në konflikt me një paketë ekzistuese."</string>
diff --git a/packages/PackageInstaller/res/values-sr/strings.xml b/packages/PackageInstaller/res/values-sr/strings.xml
index 5a0f52d..867bc84 100644
--- a/packages/PackageInstaller/res/values-sr/strings.xml
+++ b/packages/PackageInstaller/res/values-sr/strings.xml
@@ -26,7 +26,9 @@
<string name="install_done" msgid="5987363587661783896">"Апликација је инсталирана."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"Желите да инсталирате ову апликацију?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"Желите да ажурирате ову апликацију?"</string>
- <string name="install_confirm_question_update_owner_reminder" msgid="3750986542284587290">"Желите да ажурирате ову апликацију из извора <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nОва апликација се обично ажурира из извора <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Ако ажурирате из другог извора, можете да примате будућа ажурирања из било ког извора на телефону. Функције апликације могу да се промене."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tablet" msgid="738046584021528374">"Желите да ажурирате ову апликацију из извора <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nОва апликација се обично ажурира из извора <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Ако ажурирате из другог извора, можете да примате будућа ажурирања из било ког извора на таблету. Функције апликације могу да се промене."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tv" msgid="3056133099508550163">"Желите да ажурирате ову апликацију из извора <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nОва апликација се обично ажурира из извора <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Ако ажурирате из другог извора, можете да примате будућа ажурирања из било ког извора на ТВ-у. Функције апликације могу да се промене."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="default" msgid="3750986542284587290">"Желите да ажурирате ову апликацију из извора <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nОва апликација се обично ажурира из извора <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Ако ажурирате из другог извора, можете да примате будућа ажурирања из било ког извора на телефону. Функције апликације могу да се промене."</string>
<string name="install_failed" msgid="5777824004474125469">"Апликација није инсталирана."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"Инсталирање пакета је блокирано."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"Апликација није инсталирана јер је пакет неусаглашен са постојећим пакетом."</string>
diff --git a/packages/PackageInstaller/res/values-sv/strings.xml b/packages/PackageInstaller/res/values-sv/strings.xml
index ec6af2e..8c2b1dd 100644
--- a/packages/PackageInstaller/res/values-sv/strings.xml
+++ b/packages/PackageInstaller/res/values-sv/strings.xml
@@ -26,7 +26,9 @@
<string name="install_done" msgid="5987363587661783896">"Appen har installerats."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"Vill du installera den här appen?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"Vill du uppdatera den här appen?"</string>
- <string name="install_confirm_question_update_owner_reminder" msgid="3750986542284587290">"Vill du uppdatera den här appen från <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nAppen tar vanligtvis emot uppdateringar från <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Genom att uppdatera från en annan källa kan du komma att ta emot framtida uppdateringar från olika källor på telefonen. Appfunktioner kan förändras."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tablet" msgid="738046584021528374">"Vill du uppdatera den här appen från <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nAppen tar vanligtvis emot uppdateringar från <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Om du uppdaterar från en annan källa kanske du får framtida uppdateringar från olika källor på surfplattan. Appfunktioner kan förändras."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tv" msgid="3056133099508550163">"Vill du uppdatera den här appen från <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nAppen tar vanligtvis emot uppdateringar från <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Om du uppdaterar från en annan källa kanske du får framtida uppdateringar från olika källor på tv:n. Appfunktioner kan förändras."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="default" msgid="3750986542284587290">"Vill du uppdatera den här appen från <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nAppen tar vanligtvis emot uppdateringar från <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Genom att uppdatera från en annan källa kan du komma att ta emot framtida uppdateringar från olika källor på telefonen. Appfunktioner kan förändras."</string>
<string name="install_failed" msgid="5777824004474125469">"Appen har inte installerats."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"Paketet har blockerats för installation."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"Appen har inte installerats på grund av en konflikt mellan detta paket och ett befintligt paket."</string>
diff --git a/packages/PackageInstaller/res/values-sw/strings.xml b/packages/PackageInstaller/res/values-sw/strings.xml
index 0eb224a..d8cb12a 100644
--- a/packages/PackageInstaller/res/values-sw/strings.xml
+++ b/packages/PackageInstaller/res/values-sw/strings.xml
@@ -26,7 +26,9 @@
<string name="install_done" msgid="5987363587661783896">"Imesakinisha programu."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"Ungependa kusakinisha programu hii?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"Ungependa kusasisha programu hii?"</string>
- <string name="install_confirm_question_update_owner_reminder" msgid="3750986542284587290">"Ungependa kusasisha programu hii kutoka kwenye <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nProgramu hii kwa kawaida hupokea masasisho kutoka <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Kwa kusasisha kutoka chanzo tofauti, huenda ukapokea masasisho ya siku zijazo kutoka chanzo chochote kwenye simu yako. Utendaji wa programu unaweza kubadilika."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tablet" msgid="738046584021528374">"Ungependa kusasisha programu hii kutoka kwa <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nProgramu hii kwa kawaida hupokea masasisho kutoka kwa <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Kwa kusasisha kutoka kwenye chanzo tofauti, unaweza kupokea masasisho ya baadaye kutoka kwenye chanzo chochote katika kishikwambi chako. Utendaji wa programu unaweza kubadilika."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tv" msgid="3056133099508550163">"Ungependa kusasisha programu hii kutoka kwa <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nProgramu hii kwa kawaida hupokea masasisho kutoka kwa <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Kwa kusasisha kutoka kwenye chanzo tofauti, unaweza kupokea masasisho ya baadaye kutoka kwenye chanzo chochote katika TV yako. Utendaji wa programu unaweza kubadilika."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="default" msgid="3750986542284587290">"Ungependa kusasisha programu hii kutoka kwenye <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nProgramu hii kwa kawaida hupokea masasisho kutoka <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Kwa kusasisha kutoka chanzo tofauti, huenda ukapokea masasisho ya siku zijazo kutoka chanzo chochote kwenye simu yako. Utendaji wa programu unaweza kubadilika."</string>
<string name="install_failed" msgid="5777824004474125469">"Imeshindwa kusakinisha programu."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"Kifurushi kimezuiwa kisisakinishwe."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"Programu haikusakinishwa kwa sababu kifurushi kinakinzana na kifurushi kingine kilichopo."</string>
diff --git a/packages/PackageInstaller/res/values-ta/strings.xml b/packages/PackageInstaller/res/values-ta/strings.xml
index c60910c..06870aa 100644
--- a/packages/PackageInstaller/res/values-ta/strings.xml
+++ b/packages/PackageInstaller/res/values-ta/strings.xml
@@ -26,7 +26,9 @@
<string name="install_done" msgid="5987363587661783896">"ஆப்ஸ் நிறுவப்பட்டது."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"இந்த ஆப்ஸை நிறுவ வேண்டுமா?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"இந்த ஆப்ஸைப் புதுப்பிக்க வேண்டுமா?"</string>
- <string name="install_confirm_question_update_owner_reminder" msgid="3750986542284587290">"<xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g> இலிருந்து இந்த ஆப்ஸைப் புதுப்பிக்க வேண்டுமா?\n\nபொதுவாக இந்த ஆப்ஸ்<xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g> இலிருந்து புதுப்பிப்புகளைப் பெறும். வேறொன்றின் மூலம் புதுப்பித்தால் எதிர்காலத்தில் மொபைலில் வேறு இடத்திலிருந்து புதுப்பிப்புகளை நீங்கள் பெறக்கூடும். ஆப்ஸ் செயல்பாடுகள் மாறுபடக்கூடும்."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tablet" msgid="738046584021528374">"<xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g> இல் இருந்து இந்த ஆப்ஸைப் புதுப்பிக்க வேண்டுமா?\n\nபொதுவாக இந்த ஆப்ஸ் <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g> இல் இருந்து புதுப்பிப்புகளைப் பெறும். வேறு உரிமையாளர் மூலம் புதுப்பித்தால் எதிர்காலத்தில் டேப்லெட்டில் இடம்பெற்றுள்ள எந்த உரிமையாளரிடம் இருந்தும் புதுப்பிப்புகளைப் பெறக்கூடும். ஆப்ஸ் செயல்பாடுகள் மாறுபடக்கூடும்."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tv" msgid="3056133099508550163">"<xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g> இல் இருந்து இந்த ஆப்ஸைப் புதுப்பிக்க வேண்டுமா?\n\nபொதுவாக இந்த ஆப்ஸ் <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g> இல் இருந்து புதுப்பிப்புகளைப் பெறும். வேறு உரிமையாளர் மூலம் புதுப்பித்தால் எதிர்காலத்தில் டிவியில் இடம்பெற்றுள்ள எந்த உரிமையாளரிடம் இருந்தும் புதுப்பிப்புகளைப் பெறக்கூடும். ஆப்ஸ் செயல்பாடுகள் மாறுபடக்கூடும்."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="default" msgid="3750986542284587290">"<xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g> இலிருந்து இந்த ஆப்ஸைப் புதுப்பிக்க வேண்டுமா?\n\nபொதுவாக இந்த ஆப்ஸ்<xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g> இலிருந்து புதுப்பிப்புகளைப் பெறும். வேறொன்றின் மூலம் புதுப்பித்தால் எதிர்காலத்தில் மொபைலில் வேறு இடத்திலிருந்து புதுப்பிப்புகளை நீங்கள் பெறக்கூடும். ஆப்ஸ் செயல்பாடுகள் மாறுபடக்கூடும்."</string>
<string name="install_failed" msgid="5777824004474125469">"ஆப்ஸ் நிறுவப்படவில்லை."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"இந்தத் தொகுப்பு நிறுவப்படுவதிலிருந்து தடுக்கப்பட்டது."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"இந்தத் தொகுப்பு ஏற்கனவே உள்ள தொகுப்புடன் முரண்படுவதால் ஆப்ஸ் நிறுவப்படவில்லை."</string>
diff --git a/packages/PackageInstaller/res/values-te/strings.xml b/packages/PackageInstaller/res/values-te/strings.xml
index 25673f3..df871a6 100644
--- a/packages/PackageInstaller/res/values-te/strings.xml
+++ b/packages/PackageInstaller/res/values-te/strings.xml
@@ -26,7 +26,9 @@
<string name="install_done" msgid="5987363587661783896">"యాప్ ఇన్స్టాల్ చేయబడింది."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"మీరు ఈ యాప్ను ఇన్స్టాల్ చేయాలనుకుంటున్నారా?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"మీరు ఈ యాప్ను అప్డేట్ చేయాలనుకుంటున్నారా?"</string>
- <string name="install_confirm_question_update_owner_reminder" msgid="3750986542284587290">"<xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g> ద్వారా ఈ యాప్ను అప్డేట్ చేయాలా?\n\nఈ యాప్ సాధారణంగా <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g> నుండి అప్డేట్లను అందుకుంటుంది. వేరే సోర్స్ ద్వారా అప్డేట్ చేయడం వల్ల, భవిష్యత్తులో మీ ఫోన్లోని ఏ సోర్స్ ద్వారా అయినా అప్డేట్లను పొందవచ్చు. యాప్ ఫంక్షనాలిటీ మారవచ్చు."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tablet" msgid="738046584021528374">"<xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g> నుండి ఈ యాప్ను అప్డేట్ చేయాలా?\n\nఈ యాప్ సాధారణంగా <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g> నుండి అప్డేట్లను అందుకుంటుంది. విభిన్నమైన సోర్స్ నుండి అప్డేట్ చేయడం ద్వారా, మీరు మీ టాబ్లెట్లోని ఏదైనా సోర్స్ నుండి భవిష్యత్తు అప్డేట్లను పొందవచ్చు. యాప్ ఫంక్షనాలిటీ మారవచ్చు."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tv" msgid="3056133099508550163">"<xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g> నుండి ఈ యాప్ను అప్డేట్ చేయాలా?\n\nఈ యాప్ సాధారణంగా <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g> నుండి అప్డేట్లను అందుకుంటుంది. విభిన్నమైన సోర్స్ నుండి అప్డేట్ చేయడం ద్వారా, మీరు మీ TVలోని ఏదైనా సోర్స్ నుండి భవిష్యత్తు అప్డేట్లను పొందవచ్చు. యాప్ ఫంక్షనాలిటీ మారవచ్చు."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="default" msgid="3750986542284587290">"<xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g> ద్వారా ఈ యాప్ను అప్డేట్ చేయాలా?\n\nఈ యాప్ సాధారణంగా <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g> నుండి అప్డేట్లను అందుకుంటుంది. వేరే సోర్స్ ద్వారా అప్డేట్ చేయడం వల్ల, భవిష్యత్తులో మీ ఫోన్లోని ఏ సోర్స్ ద్వారా అయినా అప్డేట్లను పొందవచ్చు. యాప్ ఫంక్షనాలిటీ మారవచ్చు."</string>
<string name="install_failed" msgid="5777824004474125469">"యాప్ ఇన్స్టాల్ చేయబడలేదు."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"ప్యాకేజీ ఇన్స్టాల్ కాకుండా బ్లాక్ చేయబడింది."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"ప్యాకేజీ, అలాగే ఇప్పటికే ఉన్న ప్యాకేజీ మధ్య వైరుధ్యం ఉన్నందున యాప్ ఇన్స్టాల్ చేయబడలేదు."</string>
diff --git a/packages/PackageInstaller/res/values-th/strings.xml b/packages/PackageInstaller/res/values-th/strings.xml
index c8f3275..5a837fc 100644
--- a/packages/PackageInstaller/res/values-th/strings.xml
+++ b/packages/PackageInstaller/res/values-th/strings.xml
@@ -26,7 +26,9 @@
<string name="install_done" msgid="5987363587661783896">"ติดตั้งแอปแล้ว"</string>
<string name="install_confirm_question" msgid="7663733664476363311">"คุณต้องการติดตั้งแอปนี้ไหม"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"คุณต้องการอัปเดตแอปนี้ไหม"</string>
- <string name="install_confirm_question_update_owner_reminder" msgid="3750986542284587290">"อัปเดตแอปนี้จาก <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g> ไหม\n\nโดยปกติแล้ว แอปนี้จะได้รับการอัปเดตจาก <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g> การอัปเดตจากแหล่งที่มาอื่นอาจทำให้โทรศัพท์ของคุณได้รับการอัปเดตจากแหล่งที่มาใดก็ได้ในอนาคต ฟังก์ชันการทำงานของแอปอาจมีการเปลี่ยนแปลง"</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tablet" msgid="738046584021528374">"อัปเดตแอปนี้จาก <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g> ไหม\n\nโดยปกติแล้ว แอปนี้จะได้รับการอัปเดตจาก <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g> การอัปเดตจากแหล่งที่มาอื่นอาจทำให้แท็บเล็ตของคุณได้รับการอัปเดตจากแหล่งที่มานั้นในอนาคต ฟังก์ชันการทำงานของแอปอาจมีการเปลี่ยนแปลง"</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tv" msgid="3056133099508550163">"อัปเดตแอปนี้จาก <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g> ไหม\n\nโดยปกติแล้ว แอปนี้จะได้รับการอัปเดตจาก <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g> การอัปเดตจากแหล่งที่มาอื่นอาจทำให้ทีวีของคุณได้รับการอัปเดตจากแหล่งที่มานั้นในอนาคต ฟังก์ชันการทำงานของแอปอาจมีการเปลี่ยนแปลง"</string>
+ <string name="install_confirm_question_update_owner_reminder" product="default" msgid="3750986542284587290">"อัปเดตแอปนี้จาก <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g> ไหม\n\nโดยปกติแล้ว แอปนี้จะได้รับการอัปเดตจาก <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g> การอัปเดตจากแหล่งที่มาอื่นอาจทำให้โทรศัพท์ของคุณได้รับการอัปเดตจากแหล่งที่มาใดก็ได้ในอนาคต ฟังก์ชันการทำงานของแอปอาจมีการเปลี่ยนแปลง"</string>
<string name="install_failed" msgid="5777824004474125469">"ไม่ได้ติดตั้งแอป"</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"มีการบล็อกแพ็กเกจไม่ให้ติดตั้ง"</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"ไม่ได้ติดตั้งแอปเพราะแพ็กเกจขัดแย้งกับแพ็กเกจที่มีอยู่"</string>
diff --git a/packages/PackageInstaller/res/values-tl/strings.xml b/packages/PackageInstaller/res/values-tl/strings.xml
index 4d516b5..af79ffe 100644
--- a/packages/PackageInstaller/res/values-tl/strings.xml
+++ b/packages/PackageInstaller/res/values-tl/strings.xml
@@ -26,7 +26,9 @@
<string name="install_done" msgid="5987363587661783896">"Na-install na ang app."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"Gusto mo bang i-install ang app na ito?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"Gusto mo bang i-update ang app na ito?"</string>
- <string name="install_confirm_question_update_owner_reminder" msgid="3750986542284587290">"I-update itong app na mula sa <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nKaraniwang nakakatanggap ang app na ito ng mga update mula sa <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Sa pag-update mula sa ibang pinagmulan, puwede kang makatanggap ng mga update mula sa anumang pinagmulan sa iyong telepono sa hinaharap. Posibleng magbago ang functionality ng app."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tablet" msgid="738046584021528374">"I-update itong app na mula sa <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nKaraniwang nakakatanggap ang app na ito ng mga update mula sa <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Sa pag-update mula sa ibang pinagmulan, puwede kang makatanggap ng mga update mula sa anumang pinagmulan sa iyong tablet sa hinaharap. Posibleng magbago ang functionality ng app."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tv" msgid="3056133099508550163">"I-update itong app na mula sa <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nKaraniwang nakakatanggap ang app na ito ng mga update mula sa <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Sa pag-update mula sa ibang pinagmulan, puwede kang makatanggap ng mga update mula sa anumang pinagmulan sa iyong TV sa hinaharap. Posibleng magbago ang functionality ng app."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="default" msgid="3750986542284587290">"I-update itong app na mula sa <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nKaraniwang nakakatanggap ang app na ito ng mga update mula sa <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Sa pag-update mula sa ibang pinagmulan, puwede kang makatanggap ng mga update mula sa anumang pinagmulan sa iyong telepono sa hinaharap. Posibleng magbago ang functionality ng app."</string>
<string name="install_failed" msgid="5777824004474125469">"Hindi na-install ang app."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"Na-block ang pag-install sa package."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"Hindi na-install ang app dahil nagkakaproblema ang package sa isang dati nang package."</string>
diff --git a/packages/PackageInstaller/res/values-tr/strings.xml b/packages/PackageInstaller/res/values-tr/strings.xml
index 050d398..4a0d869 100644
--- a/packages/PackageInstaller/res/values-tr/strings.xml
+++ b/packages/PackageInstaller/res/values-tr/strings.xml
@@ -26,7 +26,9 @@
<string name="install_done" msgid="5987363587661783896">"Uygulama yüklendi."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"Bu uygulamayı yüklemek istiyor musunuz?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"Bu uygulamayı güncellemek istiyor musunuz?"</string>
- <string name="install_confirm_question_update_owner_reminder" msgid="3750986542284587290">"Bu uygulama <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g> kaynağından güncellensin mi?\n\nBu uygulama genellikle <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g> kaynağından güncelleme alır. Farklı bir kaynaktan güncellerseniz ileride telefonunuzda herhangi bir kaynaktan güncelleme alabilirsiniz. Uygulama işlevselliği değişebilir."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tablet" msgid="738046584021528374">"Bu uygulama <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g> kaynağından güncellensin mi?\n\nBu uygulama genellikle <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g> kaynağından güncelleme alır. Farklı bir kaynaktan güncellerseniz ileride tabletinizde herhangi bir kaynaktan güncelleme alabilirsiniz. Uygulama işlevselliği değişebilir."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tv" msgid="3056133099508550163">"Bu uygulama <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g> kaynağından güncellensin mi?\n\nBu uygulama genellikle <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g> kaynağından güncelleme alır. Farklı bir kaynaktan güncellerseniz ileride televizyonunuzda herhangi bir kaynaktan güncelleme alabilirsiniz. Uygulama işlevselliği değişebilir."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="default" msgid="3750986542284587290">"Bu uygulama <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g> kaynağından güncellensin mi?\n\nBu uygulama genellikle <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g> kaynağından güncelleme alır. Farklı bir kaynaktan güncellerseniz ileride telefonunuzda herhangi bir kaynaktan güncelleme alabilirsiniz. Uygulama işlevselliği değişebilir."</string>
<string name="install_failed" msgid="5777824004474125469">"Uygulama yüklenmedi."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"Paketin yüklemesi engellendi."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"Paket, mevcut bir paketle çakıştığından uygulama yüklenemedi."</string>
diff --git a/packages/PackageInstaller/res/values-uk/strings.xml b/packages/PackageInstaller/res/values-uk/strings.xml
index ec10962..a3b8e81 100644
--- a/packages/PackageInstaller/res/values-uk/strings.xml
+++ b/packages/PackageInstaller/res/values-uk/strings.xml
@@ -26,7 +26,9 @@
<string name="install_done" msgid="5987363587661783896">"Програму встановлено."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"Установити цей додаток?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"Оновити цей додаток?"</string>
- <string name="install_confirm_question_update_owner_reminder" msgid="3750986542284587290">"Оновити цей додаток через <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nЗазвичай цей додаток отримує оновлення в інший спосіб (<xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>). Якщо встановити оновлення з іншого джерела, надалі на ваш телефон зможуть надходити оновлення з будь-яких джерел. Це може змінити функції додатка."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tablet" msgid="738046584021528374">"Оновити цей додаток від <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nЗазвичай цей додаток отримує оновлення від <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Якщо встановити оновлення з іншого джерела, надалі на ваш планшет зможуть надходити оновлення з будь-яких джерел. Це може змінити функції додатка."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tv" msgid="3056133099508550163">"Оновити цей додаток від <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nЗазвичай цей додаток отримує оновлення від <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Якщо встановити оновлення з іншого джерела, надалі на ваш телевізор зможуть надходити оновлення з будь-яких джерел. Це може змінити функції додатка."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="default" msgid="3750986542284587290">"Оновити цей додаток через <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nЗазвичай цей додаток отримує оновлення в інший спосіб (<xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>). Якщо встановити оновлення з іншого джерела, надалі на ваш телефон зможуть надходити оновлення з будь-яких джерел. Це може змінити функції додатка."</string>
<string name="install_failed" msgid="5777824004474125469">"Програму не встановлено."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"Встановлення пакета заблоковано."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"Додаток не встановлено, оскільки пакет конфліктує з наявним пакетом."</string>
diff --git a/packages/PackageInstaller/res/values-ur/strings.xml b/packages/PackageInstaller/res/values-ur/strings.xml
index b3b4c0d..c2a318b 100644
--- a/packages/PackageInstaller/res/values-ur/strings.xml
+++ b/packages/PackageInstaller/res/values-ur/strings.xml
@@ -26,7 +26,9 @@
<string name="install_done" msgid="5987363587661783896">"ایپ انسٹال ہو گئی۔"</string>
<string name="install_confirm_question" msgid="7663733664476363311">"کیا آپ یہ ایپ انسٹال کرنا چاہتے ہیں؟"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"کیا آپ یہ ایپ اپ ڈیٹ کرنا چاہتے ہیں؟"</string>
- <string name="install_confirm_question_update_owner_reminder" msgid="3750986542284587290">"اس ایپ کو <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g> سے اپ ڈیٹ کریں؟\n\n اس ایپ کو عام طور پر <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g> سے اپ ڈیٹس موصول ہوتی ہیں۔ کسی مختلف ذریعے سے اپ ڈیٹ کر کے، آپ اپنے فون پر کسی بھی ذریعے سے مستقبل کی اپ ڈیٹس حاصل کر سکتے ہیں۔ ایپ کی فعالیت تبدیل ہو سکتی ہے۔"</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tablet" msgid="738046584021528374">"اس ایپ کو <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g> سے اپ ڈیٹ کریں؟\n\nاس ایپ کو عام طور پر <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g> سے اپ ڈیٹس موصول ہوتی ہیں۔ کسی مختلف وسیلے سے اپ ڈیٹ کر کے، آپ اپنے ٹیبلیٹ پر کسی بھی وسیلے سے مستقبل کی اپ ڈیٹس حاصل کر سکتے ہیں۔ ایپ کی فعالیت تبدیل ہو سکتی ہے۔"</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tv" msgid="3056133099508550163">"اس ایپ کو <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g> سے اپ ڈیٹ کریں؟\n\nاس ایپ کو عام طور پر <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g> سے اپ ڈیٹس موصول ہوتی ہیں۔ کسی مختلف وسیلے سے اپ ڈیٹ کر کے، آپ اپنے TV پر کسی بھی وسیلے سے مستقبل کی اپ ڈیٹس حاصل کر سکتے ہیں۔ ایپ کی فعالیت تبدیل ہو سکتی ہے۔"</string>
+ <string name="install_confirm_question_update_owner_reminder" product="default" msgid="3750986542284587290">"اس ایپ کو <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g> سے اپ ڈیٹ کریں؟\n\n اس ایپ کو عام طور پر <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g> سے اپ ڈیٹس موصول ہوتی ہیں۔ کسی مختلف ذریعے سے اپ ڈیٹ کر کے، آپ اپنے فون پر کسی بھی ذریعے سے مستقبل کی اپ ڈیٹس حاصل کر سکتے ہیں۔ ایپ کی فعالیت تبدیل ہو سکتی ہے۔"</string>
<string name="install_failed" msgid="5777824004474125469">"ایپ انسٹال نہیں ہوئی۔"</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"پیکج کو انسٹال ہونے سے مسدود کر دیا گیا تھا۔"</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"ایپ انسٹال نہیں ہوئی کیونکہ پیکج ایک موجودہ پیکیج سے متصادم ہے۔"</string>
diff --git a/packages/PackageInstaller/res/values-uz/strings.xml b/packages/PackageInstaller/res/values-uz/strings.xml
index 2993663c..691633a 100644
--- a/packages/PackageInstaller/res/values-uz/strings.xml
+++ b/packages/PackageInstaller/res/values-uz/strings.xml
@@ -26,7 +26,9 @@
<string name="install_done" msgid="5987363587661783896">"Ilova o‘rnatildi."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"Bu ilovani oʻrnatmoqchimisiz?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"Bu ilova yangilansinmi?"</string>
- <string name="install_confirm_question_update_owner_reminder" msgid="3750986542284587290">"Bu ilova <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g> orqali yangilansinmi?\n\nBu ilova odatda <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g> orqali yangilanishlar oladi. Boshqa manbadan yangilash orqali siz kelajakdagi yangilanishlarni telefoningizda istalgan manbadan olishingiz mumkin. Ilova funksiyalari oʻzgarishi mumkin."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tablet" msgid="738046584021528374">"Bu ilova <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g> orqali yangilansinmi?\n\nBu ilova odatda <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g> orqali yangilanishlar oladi. Boshqa manbadan yangilash orqali siz kelajakdagi yangilanishlarni planshetingizda istalgan manbadan olishingiz mumkin. Ilova funksiyalari oʻzgarishi mumkin."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tv" msgid="3056133099508550163">"Bu ilova <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g> orqali yangilansinmi?\n\nBu ilova odatda <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g> orqali yangilanishlar oladi. Boshqa manbadan yangilash orqali siz kelajakdagi yangilanishlarni televizoringizda istalgan manbadan olishingiz mumkin. Ilova funksiyalari oʻzgarishi mumkin."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="default" msgid="3750986542284587290">"Bu ilova <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g> orqali yangilansinmi?\n\nBu ilova odatda <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g> orqali yangilanishlar oladi. Boshqa manbadan yangilash orqali siz kelajakdagi yangilanishlarni telefoningizda istalgan manbadan olishingiz mumkin. Ilova funksiyalari oʻzgarishi mumkin."</string>
<string name="install_failed" msgid="5777824004474125469">"Ilova o‘rnatilmadi."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"Paket o‘rnatilishga qarshi bloklangan."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"Paket mavjud paket bilan zid kelganligi uchun ilovani o‘rnatib bo‘lmadi."</string>
diff --git a/packages/PackageInstaller/res/values-vi/strings.xml b/packages/PackageInstaller/res/values-vi/strings.xml
index f6ffa3a..4976d66 100644
--- a/packages/PackageInstaller/res/values-vi/strings.xml
+++ b/packages/PackageInstaller/res/values-vi/strings.xml
@@ -26,7 +26,9 @@
<string name="install_done" msgid="5987363587661783896">"Ứng dụng đã được cài đặt."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"Bạn có muốn cài đặt ứng dụng này không?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"Bạn có muốn cập nhật ứng dụng này không?"</string>
- <string name="install_confirm_question_update_owner_reminder" msgid="3750986542284587290">"Cập nhật ứng dụng này của <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nỨng dụng này thường nhận thông tin cập nhật từ <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Khi cập nhật từ một nguồn khác, trong tương lai, bạn có thể nhận thông tin cập nhật từ nguồn bất kỳ trên điện thoại của bạn. Chức năng ứng dụng có thể thay đổi."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tablet" msgid="738046584021528374">"Cập nhật ứng dụng này từ <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nỨng dụng này thường nhận các bản cập nhật từ <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Khi cập nhật từ một nguồn khác, bạn có thể nhận các bản cập nhật trong tương lai từ nguồn bất kỳ trên máy tính bảng của mình. Chức năng ứng dụng có thể thay đổi."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tv" msgid="3056133099508550163">"Cập nhật ứng dụng này từ <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nỨng dụng này thường nhận các bản cập nhật từ <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Khi cập nhật từ một nguồn khác, bạn có thể nhận các bản cập nhật trong tương lai từ nguồn bất kỳ trên TV của mình. Chức năng ứng dụng có thể thay đổi."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="default" msgid="3750986542284587290">"Cập nhật ứng dụng này của <xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nỨng dụng này thường nhận thông tin cập nhật từ <xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Khi cập nhật từ một nguồn khác, trong tương lai, bạn có thể nhận thông tin cập nhật từ nguồn bất kỳ trên điện thoại của bạn. Chức năng ứng dụng có thể thay đổi."</string>
<string name="install_failed" msgid="5777824004474125469">"Ứng dụng chưa được cài đặt."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"Đã chặn cài đặt gói."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"Chưa cài đặt được ứng dụng do gói xung đột với một gói hiện có."</string>
diff --git a/packages/PackageInstaller/res/values-zh-rCN/strings.xml b/packages/PackageInstaller/res/values-zh-rCN/strings.xml
index 5c2d11f..075467d 100644
--- a/packages/PackageInstaller/res/values-zh-rCN/strings.xml
+++ b/packages/PackageInstaller/res/values-zh-rCN/strings.xml
@@ -26,7 +26,9 @@
<string name="install_done" msgid="5987363587661783896">"已安装应用。"</string>
<string name="install_confirm_question" msgid="7663733664476363311">"要安装此应用吗?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"要更新此应用吗?"</string>
- <string name="install_confirm_question_update_owner_reminder" msgid="3750986542284587290">"要通过<xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>更新此应用吗?\n\n此应用通常通过<xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>接收更新。如果通过其他来源更新,手机未来可能会收到任何来源的更新。应用功能可能会变化。"</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tablet" msgid="738046584021528374">"要通过<xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>更新此应用?\n\n此应用通常通过<xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>接收更新。如果通过其他来源更新,平板电脑未来可能会收到任何来源的更新。应用功能可能会变化。"</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tv" msgid="3056133099508550163">"要通过<xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>更新此应用?\n\n此应用通常通过<xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>接收更新。如果通过其他来源更新,电视未来可能会收到任何来源的更新。应用功能可能会变化。"</string>
+ <string name="install_confirm_question_update_owner_reminder" product="default" msgid="3750986542284587290">"要通过<xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>更新此应用吗?\n\n此应用通常通过<xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>接收更新。如果通过其他来源更新,手机未来可能会收到任何来源的更新。应用功能可能会变化。"</string>
<string name="install_failed" msgid="5777824004474125469">"未安装应用。"</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"系统已禁止安装该软件包。"</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"应用未安装:软件包与现有软件包存在冲突。"</string>
diff --git a/packages/PackageInstaller/res/values-zh-rHK/strings.xml b/packages/PackageInstaller/res/values-zh-rHK/strings.xml
index b36770d..c971682 100644
--- a/packages/PackageInstaller/res/values-zh-rHK/strings.xml
+++ b/packages/PackageInstaller/res/values-zh-rHK/strings.xml
@@ -26,7 +26,9 @@
<string name="install_done" msgid="5987363587661783896">"已安裝應用程式。"</string>
<string name="install_confirm_question" msgid="7663733664476363311">"要安裝此應用程式嗎?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"要更新此應用程式嗎?"</string>
- <string name="install_confirm_question_update_owner_reminder" msgid="3750986542284587290">"要從「<xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>」更新此應用程式嗎?\n\n在正常情況下,系統會透過「<xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>」更新此應用程式。如果透過其他來源更新,手機未來可能會收到任何來源的更新。應用程式功能可能會有變動。"</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tablet" msgid="738046584021528374">"要從「<xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>」更新此應用程式嗎?\n\n在正常情況下,系統會透過「<xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>」更新此應用程式。如果透過其他來源更新,平板電腦未來可能會收到任何來源的更新。應用程式功能可能會有變動。"</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tv" msgid="3056133099508550163">"要從「<xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>」更新此應用程式嗎?\n\n在正常情況下,系統會透過「<xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>」更新此應用程式。如果透過其他來源更新,電視未來可能會收到任何來源的更新。應用程式功能可能會有變動。"</string>
+ <string name="install_confirm_question_update_owner_reminder" product="default" msgid="3750986542284587290">"要從「<xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>」更新此應用程式嗎?\n\n在正常情況下,系統會透過「<xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>」更新此應用程式。如果透過其他來源更新,手機未來可能會收到任何來源的更新。應用程式功能可能會有變動。"</string>
<string name="install_failed" msgid="5777824004474125469">"未安裝應用程式。"</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"套件已遭封鎖,無法安裝。"</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"套件與現有的套件發生衝突,無法安裝應用程式。"</string>
diff --git a/packages/PackageInstaller/res/values-zh-rTW/strings.xml b/packages/PackageInstaller/res/values-zh-rTW/strings.xml
index 2a87eb8..d9027cc 100644
--- a/packages/PackageInstaller/res/values-zh-rTW/strings.xml
+++ b/packages/PackageInstaller/res/values-zh-rTW/strings.xml
@@ -26,7 +26,9 @@
<string name="install_done" msgid="5987363587661783896">"已安裝應用程式。"</string>
<string name="install_confirm_question" msgid="7663733664476363311">"要安裝這個應用程式嗎?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"要更新這個應用程式嗎?"</string>
- <string name="install_confirm_question_update_owner_reminder" msgid="3750986542284587290">"要透過「<xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>」更新這個應用程式嗎?\n\n在正常情況下,系統會透過「<xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>」更新這個應用程式。如果透過其他來源更新,手機未來可能會收到任何來源的更新。應用程式功能可能會有變動。"</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tablet" msgid="738046584021528374">"要透過「<xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>」更新這個應用程式嗎?\n\n在正常情況下,系統會透過「<xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>」更新這個應用程式。如果透過其他來源更新,平板電腦未來可能會收到任何來源的更新。應用程式功能可能會有變動。"</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tv" msgid="3056133099508550163">"要透過「<xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>」更新這個應用程式嗎?\n\n在正常情況下,系統會透過「<xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>」更新這個應用程式。如果透過其他來源更新,TV 裝置未來可能會收到任何來源的更新。應用程式功能可能會有變動。"</string>
+ <string name="install_confirm_question_update_owner_reminder" product="default" msgid="3750986542284587290">"要透過「<xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>」更新這個應用程式嗎?\n\n在正常情況下,系統會透過「<xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>」更新這個應用程式。如果透過其他來源更新,手機未來可能會收到任何來源的更新。應用程式功能可能會有變動。"</string>
<string name="install_failed" msgid="5777824004474125469">"未安裝應用程式。"</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"系統已封鎖這個套件,因此無法安裝。"</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"應用程式套件與現有套件衝突,因此未能完成安裝。"</string>
diff --git a/packages/PackageInstaller/res/values-zu/strings.xml b/packages/PackageInstaller/res/values-zu/strings.xml
index ca9c63b..13419d9 100644
--- a/packages/PackageInstaller/res/values-zu/strings.xml
+++ b/packages/PackageInstaller/res/values-zu/strings.xml
@@ -26,7 +26,9 @@
<string name="install_done" msgid="5987363587661783896">"Uhlelo lokusebenza olufakiwe."</string>
<string name="install_confirm_question" msgid="7663733664476363311">"Ingabe ufuna ukufaka le app?"</string>
<string name="install_confirm_question_update" msgid="3348888852318388584">"Ingabe ufuna ukubuyekeza le app?"</string>
- <string name="install_confirm_question_update_owner_reminder" msgid="3750986542284587290">"Buyekeza le app kusuka ku-<xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nNgokuvamile le app ithola izibuyekezo kusuka ku-<xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Ngokubuyekeza kusuka kumthombo ohlukile, ungase uthole izibuyekezo zesikhathi esizayo kusuka kunoma yimuphi umthombo efonini yakho. Okwenziwa yi-app kungase kushintshe."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tablet" msgid="738046584021528374">"Buyekeza le app kusuka ku-<xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nNgokuvamile le app ithola izibuyekezo kusuka ku-<xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Ngokubuyekeza kusuka kumthombo ohlukile, ungase uthole izibuyekezo zesikhathi esizayo kusuka kunoma yimuphi umthombo kuthebulethi yakho. Okwenziwa yi-app kungase kushintshe."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tv" msgid="3056133099508550163">"Buyekeza le app kusuka ku-<xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nNgokuvamile le app ithola izibuyekezo kusuka ku-<xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Ngokubuyekeza kusuka kumthombo ohlukile, ungase uthole izibuyekezo zesikhathi esizayo kusuka kunoma yimuphi umthombo ku-TV yakho. Okwenziwa yi-app kungase kushintshe."</string>
+ <string name="install_confirm_question_update_owner_reminder" product="default" msgid="3750986542284587290">"Buyekeza le app kusuka ku-<xliff:g id="NEW_UPDATE_OWNER">%1$s</xliff:g>?\n\nNgokuvamile le app ithola izibuyekezo kusuka ku-<xliff:g id="EXISTING_UPDATE_OWNER">%2$s</xliff:g>. Ngokubuyekeza kusuka kumthombo ohlukile, ungase uthole izibuyekezo zesikhathi esizayo kusuka kunoma yimuphi umthombo efonini yakho. Okwenziwa yi-app kungase kushintshe."</string>
<string name="install_failed" msgid="5777824004474125469">"Uhlelo lokusebenza alufakiwe."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"Iphakheji livinjiwe kusukela ekufakweni."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"Uhlelo lokusebenza alufakiwe njengoba ukuphakheja kushayisana nephakheji elikhona."</string>
diff --git a/packages/PackageInstaller/res/values/strings.xml b/packages/PackageInstaller/res/values/strings.xml
index a2118fa..c52fde6 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 7b17cbd..19d74b3 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 @@
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 73c03a5..ff991d2 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 @@
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 d154156..e071c11 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 @@
}
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 b/packages/SettingsLib/Spa/build.gradle
deleted file mode 100644
index e68ef85..0000000
--- a/packages/SettingsLib/Spa/build.gradle
+++ /dev/null
@@ -1,40 +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.
- */
-
-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
-}
-subprojects {
- tasks.withType(KotlinCompile).configureEach {
- kotlinOptions {
- jvmTarget = "17"
- freeCompilerArgs = ["-Xjvm-default=all"]
- }
- }
-}
diff --git a/packages/SettingsLib/Spa/build.gradle.kts b/packages/SettingsLib/Spa/build.gradle.kts
new file mode 100644
index 0000000..64b67d7
--- /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 212aa7b..0000000
--- 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/gallery/build.gradle.kts b/packages/SettingsLib/Spa/gallery/build.gradle.kts
new file mode 100644
index 0000000..7f689c1
--- /dev/null
+++ b/packages/SettingsLib/Spa/gallery/build.gradle.kts
@@ -0,0 +1,45 @@
+/*
+ * 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.application)
+ alias(libs.plugins.kotlin.android)
+}
+
+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 0000000..9a16df8
--- /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 ed85e33..33f49e3 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
similarity index 77%
rename from packages/SettingsLib/Spa/settings.gradle
rename to packages/SettingsLib/Spa/settings.gradle.kts
index 1c5a1ce..9909781 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 a591366..0000000
--- 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 0000000..329d80e
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/build.gradle.kts
@@ -0,0 +1,110 @@
+/*
+ * 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(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/spa/src/com/android/settingslib/spa/framework/BrowseActivity.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/BrowseActivity.kt
index 0f5862a..afce16c 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/BrowseActivity.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/BrowseActivity.kt
@@ -24,7 +24,7 @@
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.annotation.VisibleForTesting
-import androidx.compose.animation.AnimatedContentScope
+import androidx.compose.animation.AnimatedContentTransitionScope
import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.animation.core.tween
import androidx.compose.animation.fadeIn
@@ -146,22 +146,26 @@
arguments = spp.parameter,
enterTransition = {
slideIntoContainer(
- AnimatedContentScope.SlideDirection.Start, animationSpec = slideEffect
+ AnimatedContentTransitionScope.SlideDirection.Start,
+ animationSpec = slideEffect
) + fadeIn(animationSpec = fadeEffect)
},
exitTransition = {
slideOutOfContainer(
- AnimatedContentScope.SlideDirection.Start, animationSpec = slideEffect
+ AnimatedContentTransitionScope.SlideDirection.Start,
+ animationSpec = slideEffect
) + fadeOut(animationSpec = fadeEffect)
},
popEnterTransition = {
slideIntoContainer(
- AnimatedContentScope.SlideDirection.End, animationSpec = slideEffect
+ AnimatedContentTransitionScope.SlideDirection.End,
+ animationSpec = slideEffect
) + fadeIn(animationSpec = fadeEffect)
},
popExitTransition = {
slideOutOfContainer(
- AnimatedContentScope.SlideDirection.End, animationSpec = slideEffect
+ AnimatedContentTransitionScope.SlideDirection.End,
+ animationSpec = slideEffect
) + fadeOut(animationSpec = fadeEffect)
},
) { navBackStackEntry ->
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/AnimatedNavHost.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/AnimatedNavHost.kt
index 57bb838..81bbc24 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/AnimatedNavHost.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/AnimatedNavHost.kt
@@ -18,7 +18,7 @@
import androidx.activity.compose.LocalOnBackPressedDispatcherOwner
import androidx.compose.animation.AnimatedContent
-import androidx.compose.animation.AnimatedContentScope
+import androidx.compose.animation.AnimatedContentTransitionScope
import androidx.compose.animation.ContentTransform
import androidx.compose.animation.EnterTransition
import androidx.compose.animation.ExitTransition
@@ -78,14 +78,10 @@
modifier: Modifier = Modifier,
contentAlignment: Alignment = Alignment.Center,
route: String? = null,
- enterTransition: (AnimatedContentScope<NavBackStackEntry>.() -> EnterTransition) =
- { fadeIn(animationSpec = tween(700)) },
- exitTransition: (AnimatedContentScope<NavBackStackEntry>.() -> ExitTransition) =
- { fadeOut(animationSpec = tween(700)) },
- popEnterTransition: (AnimatedContentScope<NavBackStackEntry>.() -> EnterTransition) =
- enterTransition,
- popExitTransition: (AnimatedContentScope<NavBackStackEntry>.() -> ExitTransition) =
- exitTransition,
+ enterTransition: (AnimatedScope.() -> EnterTransition) = { fadeIn(animationSpec = tween(700)) },
+ exitTransition: (AnimatedScope.() -> ExitTransition) = { fadeOut(animationSpec = tween(700)) },
+ popEnterTransition: (AnimatedScope.() -> EnterTransition) = enterTransition,
+ popExitTransition: (AnimatedScope.() -> ExitTransition) = exitTransition,
builder: NavGraphBuilder.() -> Unit
) {
AnimatedNavHost(
@@ -123,14 +119,10 @@
graph: NavGraph,
modifier: Modifier = Modifier,
contentAlignment: Alignment = Alignment.Center,
- enterTransition: (AnimatedContentScope<NavBackStackEntry>.() -> EnterTransition) =
- { fadeIn(animationSpec = tween(700)) },
- exitTransition: (AnimatedContentScope<NavBackStackEntry>.() -> ExitTransition) =
- { fadeOut(animationSpec = tween(700)) },
- popEnterTransition: (AnimatedContentScope<NavBackStackEntry>.() -> EnterTransition) =
- enterTransition,
- popExitTransition: (AnimatedContentScope<NavBackStackEntry>.() -> ExitTransition) =
- exitTransition,
+ enterTransition: (AnimatedScope.() -> EnterTransition) = { fadeIn(animationSpec = tween(700)) },
+ exitTransition: (AnimatedScope.() -> ExitTransition) = { fadeOut(animationSpec = tween(700)) },
+ popEnterTransition: (AnimatedScope.() -> EnterTransition) = enterTransition,
+ popExitTransition: (AnimatedScope.() -> ExitTransition) = exitTransition,
) {
val lifecycleOwner = LocalLifecycleOwner.current
@@ -168,7 +160,7 @@
val backStackEntry = visibleEntries.lastOrNull()
if (backStackEntry != null) {
- val finalEnter: AnimatedContentScope<NavBackStackEntry>.() -> EnterTransition = {
+ val finalEnter: AnimatedScope.() -> EnterTransition = {
val targetDestination = targetState.destination as AnimatedComposeNavigator.Destination
if (composeNavigator.isPop.value) {
@@ -182,7 +174,7 @@
}
}
- val finalExit: AnimatedContentScope<NavBackStackEntry>.() -> ExitTransition = {
+ val finalExit: AnimatedScope.() -> ExitTransition = {
val initialDestination =
initialState.destination as AnimatedComposeNavigator.Destination
@@ -245,19 +237,16 @@
DialogHost(dialogNavigator)
}
-@ExperimentalAnimationApi
-internal val enterTransitions =
- mutableMapOf<String?,
- (AnimatedContentScope<NavBackStackEntry>.() -> EnterTransition?)?>()
+@OptIn(ExperimentalAnimationApi::class)
+internal typealias AnimatedScope = AnimatedContentTransitionScope<NavBackStackEntry>
@ExperimentalAnimationApi
-internal val exitTransitions =
- mutableMapOf<String?, (AnimatedContentScope<NavBackStackEntry>.() -> ExitTransition?)?>()
+internal val enterTransitions = mutableMapOf<String?, (AnimatedScope.() -> EnterTransition?)?>()
@ExperimentalAnimationApi
-internal val popEnterTransitions =
- mutableMapOf<String?, (AnimatedContentScope<NavBackStackEntry>.() -> EnterTransition?)?>()
+internal val exitTransitions = mutableMapOf<String?, (AnimatedScope.() -> ExitTransition?)?>()
+@ExperimentalAnimationApi
+internal val popEnterTransitions = mutableMapOf<String?, (AnimatedScope.() -> EnterTransition?)?>()
@ExperimentalAnimationApi
-internal val popExitTransitions =
- mutableMapOf<String?, (AnimatedContentScope<NavBackStackEntry>.() -> ExitTransition?)?>()
+internal val popExitTransitions = mutableMapOf<String?, (AnimatedScope.() -> ExitTransition?)?>()
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/NavGraphBuilder.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/NavGraphBuilder.kt
index 9e58603..bf92f5d 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/NavGraphBuilder.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/NavGraphBuilder.kt
@@ -16,7 +16,6 @@
package com.android.settingslib.spa.framework.compose
-import androidx.compose.animation.AnimatedContentScope
import androidx.compose.animation.AnimatedVisibilityScope
import androidx.compose.animation.EnterTransition
import androidx.compose.animation.ExitTransition
@@ -25,9 +24,7 @@
import androidx.navigation.NamedNavArgument
import androidx.navigation.NavBackStackEntry
import androidx.navigation.NavDeepLink
-import androidx.navigation.NavGraph
import androidx.navigation.NavGraphBuilder
-import androidx.navigation.compose.navigation
import androidx.navigation.get
/**
@@ -47,14 +44,10 @@
route: String,
arguments: List<NamedNavArgument> = emptyList(),
deepLinks: List<NavDeepLink> = emptyList(),
- enterTransition: (AnimatedContentScope<NavBackStackEntry>.() -> EnterTransition?)? = null,
- exitTransition: (AnimatedContentScope<NavBackStackEntry>.() -> ExitTransition?)? = null,
- popEnterTransition: (
- AnimatedContentScope<NavBackStackEntry>.() -> EnterTransition?
- )? = enterTransition,
- popExitTransition: (
- AnimatedContentScope<NavBackStackEntry>.() -> ExitTransition?
- )? = exitTransition,
+ enterTransition: (AnimatedScope.() -> EnterTransition?)? = null,
+ exitTransition: (AnimatedScope.() -> ExitTransition?)? = null,
+ popEnterTransition: (AnimatedScope.() -> EnterTransition?)? = enterTransition,
+ popExitTransition: (AnimatedScope.() -> ExitTransition?)? = exitTransition,
content: @Composable AnimatedVisibilityScope.(NavBackStackEntry) -> Unit
) {
addDestination(
@@ -76,43 +69,3 @@
}
)
}
-
-/**
- * Construct a nested [NavGraph]
- *
- * @param startDestination the starting destination's route for this NavGraph
- * @param route the destination's unique route
- * @param arguments list of arguments to associate with destination
- * @param deepLinks list of deep links to associate with the destinations
- * @param enterTransition callback to define enter transitions for destination in this NavGraph
- * @param exitTransition callback to define exit transitions for destination in this NavGraph
- * @param popEnterTransition callback to define pop enter transitions for destination in this
- * NavGraph
- * @param popExitTransition callback to define pop exit transitions for destination in this NavGraph
- * @param builder the builder used to construct the graph
- *
- * @return the newly constructed nested NavGraph
- */
-@ExperimentalAnimationApi
-public fun NavGraphBuilder.navigation(
- startDestination: String,
- route: String,
- arguments: List<NamedNavArgument> = emptyList(),
- deepLinks: List<NavDeepLink> = emptyList(),
- enterTransition: (AnimatedContentScope<NavBackStackEntry>.() -> EnterTransition?)? = null,
- exitTransition: (AnimatedContentScope<NavBackStackEntry>.() -> ExitTransition?)? = null,
- popEnterTransition: (
- AnimatedContentScope<NavBackStackEntry>.() -> EnterTransition?
- )? = enterTransition,
- popExitTransition: (
- AnimatedContentScope<NavBackStackEntry>.() -> ExitTransition?
- )? = exitTransition,
- builder: NavGraphBuilder.() -> Unit
-) {
- navigation(startDestination, route, arguments, deepLinks, builder).apply {
- enterTransition?.let { enterTransitions[route] = enterTransition }
- exitTransition?.let { exitTransitions[route] = exitTransition }
- popEnterTransition?.let { popEnterTransitions[route] = popEnterTransition }
- popExitTransition?.let { popExitTransitions[route] = popExitTransition }
- }
-}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SettingsPager.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SettingsPager.kt
index 79635a0..aa148b0 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SettingsPager.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SettingsPager.kt
@@ -20,20 +20,12 @@
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.pager.HorizontalPager
-import androidx.compose.foundation.pager.PagerState
import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.material3.TabRow
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.LaunchedEffect
-import androidx.compose.runtime.SideEffect
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.rememberCoroutineScope
-import androidx.compose.runtime.saveable.rememberSaveable
-import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.platform.LocalConfiguration
import com.android.settingslib.spa.framework.theme.SettingsDimension
import kotlin.math.absoluteValue
import kotlinx.coroutines.launch
@@ -49,7 +41,7 @@
Column {
val coroutineScope = rememberCoroutineScope()
- val pagerState = rememberPageStateFixed()
+ val pagerState = rememberPagerState { titles.size }
TabRow(
selectedTabIndex = pagerState.currentPage,
@@ -72,46 +64,8 @@
}
}
- HorizontalPager(pageCount = titles.size, state = pagerState) { page ->
+ HorizontalPager(state = pagerState) { page ->
content(page)
}
}
}
-
-/**
- * Gets the state of [PagerState].
- *
- * This is a work around.
- *
- * TODO: Remove this and replace with rememberPageState() after the Compose Foundation 1.5.0-alpha04
- * updated in the platform.
- */
-@Composable
-@OptIn(ExperimentalFoundationApi::class)
-private fun rememberPageStateFixed(): PagerState {
- var currentPage by rememberSaveable { mutableStateOf(0) }
- var targetPage by rememberSaveable { mutableStateOf(-1) }
- val pagerState = rememberPagerState()
- val configuration = LocalConfiguration.current
- var lastScreenWidthDp by rememberSaveable { mutableStateOf(-1) }
- val screenWidthDp = configuration.screenWidthDp
- LaunchedEffect(screenWidthDp) {
- // Reset pager state to fix an issue after configuration change.
- // When we declare android:configChanges in the manifest, the pager state is in a weird
- // state after configuration change.
- targetPage = currentPage
- lastScreenWidthDp = screenWidthDp
- }
- LaunchedEffect(targetPage) {
- if (targetPage != -1) {
- pagerState.scrollToPage(targetPage)
- targetPage = -1
- }
- }
- SideEffect {
- if (lastScreenWidthDp == screenWidthDp) {
- currentPage = pagerState.currentPage
- }
- }
- return pagerState
-}
diff --git a/packages/SettingsLib/Spa/tests/Android.bp b/packages/SettingsLib/Spa/tests/Android.bp
index b4c67cc..f9e64ae 100644
--- a/packages/SettingsLib/Spa/tests/Android.bp
+++ b/packages/SettingsLib/Spa/tests/Android.bp
@@ -31,7 +31,6 @@
"SpaLib",
"SpaLibTestUtils",
"androidx.compose.runtime_runtime",
- "androidx.lifecycle_lifecycle-runtime-testing",
"androidx.test.ext.junit",
"androidx.test.runner",
"mockito-target-minus-junit4",
diff --git a/packages/SettingsLib/Spa/testutils/Android.bp b/packages/SettingsLib/Spa/testutils/Android.bp
index 2c1e1c2..e4d56cc 100644
--- a/packages/SettingsLib/Spa/testutils/Android.bp
+++ b/packages/SettingsLib/Spa/testutils/Android.bp
@@ -29,6 +29,7 @@
"androidx.compose.runtime_runtime",
"androidx.compose.ui_ui-test-junit4",
"androidx.compose.ui_ui-test-manifest",
+ "androidx.lifecycle_lifecycle-runtime-testing",
"mockito",
"truth-prebuilt",
],
diff --git a/packages/SettingsLib/Spa/testutils/build.gradle b/packages/SettingsLib/Spa/testutils/build.gradle
deleted file mode 100644
index 23a9add..0000000
--- 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 0000000..f5a22c9
--- /dev/null
+++ b/packages/SettingsLib/Spa/testutils/build.gradle.kts
@@ -0,0 +1,47 @@
+/*
+ * 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") {
+ kotlin.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("androidx.lifecycle:lifecycle-runtime-testing")
+ api(libs.truth)
+ api("org.mockito:mockito-core:2.21.0")
+ debugApi("androidx.compose.ui:ui-test-manifest:$jetpackComposeVersion")
+}
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppListRepository.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppListRepository.kt
index 2b38b4c..8e0cf89 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 @@
/**
* 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 @@
/** 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 @@
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 @@
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 @@
): 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 030b70a7..07e4235 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 @@
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 @@
}
},
) { 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 b5a4929..6a76c93 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 @@
)
@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 @@
}
}
-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 302f780..375ed60 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 @@
val appList = repository.loadApps(
userId = ADMIN_USER_ID,
- showInstantApps = false,
+ loadInstantApps = false,
)
assertThat(appList).containsExactly(NORMAL_APP)
@@ -120,7 +120,7 @@
val appList = repository.loadApps(
userId = ADMIN_USER_ID,
- showInstantApps = true,
+ loadInstantApps = true,
)
assertThat(appList).containsExactly(NORMAL_APP, INSTANT_APP)
@@ -325,6 +325,21 @@
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 6889e5d..9b22497 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 @@
private object FakeAppListRepository : AppListRepository {
override suspend fun loadApps(
userId: Int,
- showInstantApps: Boolean,
+ loadInstantApps: Boolean,
matchAnyUserForAdmin: Boolean,
) = listOf(APP)
@@ -97,6 +97,9 @@
): 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 14cb1de9..0614f9d 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 1e77759..a920b50 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 54f8096..2f30508 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 4ffaf1b..2ded3c6 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>
+ 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"/>
+ <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>
+</LinearLayout>
-</ScrollView>
diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml
index 98ad6e3..a005693 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 b8b1b69..28ca2bb 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 18dc07f..aff5c81 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 725a057..c1128ae 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 ec1b8cf..57b4694 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 91ca8a6..e9e0e90 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 71aac42..0f6112c 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 2779689..abd90d0 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 68283ca..b037b13 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 8e589f6..1a37a3f 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 f22b489b8..233b3e1 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 363c22e..41d3d52 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 1df4dee..30022d3 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 ac5d152..e9d885a 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 8f75180..19413c1 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 3a50fa3..58ea681 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 9ee30e1..aab6224 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 3a50fa3..58ea681 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 3a50fa3..58ea681 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 9ad8a39..d69cda5 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 7ac4ab0..39fd341 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 6ffed25..df256e5 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 434e7d7..be847b3 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 591d3cf..93abed2 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 15e0968..9f33fa4 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 b3a7560..6ac22b2 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 8d3eae4..532374da 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 a4e43c7..683cfa5 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 dbc0ba61..0661d7a 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 ee44c0a..b92d2f2 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 b97837b..bd508d9 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,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-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml
index 6acfb95..7a8507d 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 14ecad0..a67748f 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 5083165..b72b094 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 c5e6912..07dd53a 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 5d18930..bb7bf70 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 85d4985..5926a6b 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 af56031..51bda70 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 0032351..d15b4ef 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 147512b..f63d4c2 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 7df9fb3..883dea6 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 81d2669..f7a8bfd 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 134f591..e29e22b 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 ffa05cc..653ac23 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 7e7d382..c2aa652 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 28fd87e..0a429fd 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 8246c6b..78246dc 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 807df3a..e3fbcdf 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 583b069..6ba8d5c 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 610f365..0f4d30d 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 040f2c8..29fb6d4 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 932ef6c..e9e0ee4 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 460eb56..0a41c0f 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 8458a62..7346d91 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 a082be6..dc68bf3 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 349b9d3..5d61132 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 1b456b9..51fc99a2 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 164f24d..bf7470c 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 3e20201..3c4c4bc 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 58a89d9..62f7e81 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 678c076..6880511 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 847bfb5..49e3f91 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 678c076..6880511 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 7fa228b..1dce0e1 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 a53b67a..79db17c 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 ab5d3ce..c43f5dd 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 daf9492..ec383f9 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 7164da6..4d0e7a8 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 7fd6ec9..2fdac37 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 86675cf..dc698ce 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 0d71afd..64412dd5 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 303131e..4893fe83 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 83aa2af..b72c533 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 a216c81..ae1b251 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 32629c3..14ef3a3 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 e60faf6..ab4acb6 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 3ea12ad..abbef53 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 95ece26..bf297e6 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 70aa9a3..db2bb26 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 5f42969..96e5d6a 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 d75f1b2..7035077 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 6787f32..fabc004 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 e6da4bc..9ec57a3 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 e33934c..59e66aa 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 5c7a7b5..d292bd5 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 91549d7..07854bd 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 b44c0e0..60bc226 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 4ac4aae..1249c6b 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 cfff519..918d696 100644
--- a/packages/SettingsLib/search/Android.bp
+++ b/packages/SettingsLib/search/Android.bp
@@ -7,8 +7,18 @@
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 @@
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 05226db..0000000
--- 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
similarity index 92%
rename from packages/SettingsLib/search/src/com/android/settingslib/search/SearchIndexable.java
rename to packages/SettingsLib/search/interface-src/com/android/settingslib/search/SearchIndexable.java
index 638fa3e9..174f337 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 @@
/**
* 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 @@
/**
* 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 e92157e..fa43915 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.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 @@
* 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 @@
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 @@
}
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 @@
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 @@
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 @@
@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 79fb566..a05a6e9 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 @@
"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 9567a3b..ea65ade 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.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 @@
}
/**
+ * 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 e61c8f5..997d1f4 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 @@
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 @@
cancelCallback.run();
clear();
});
+ mCustomDialogHelper.setMessagePadding(MESSAGE_PADDING);
mUserCreationDialog.setCanceledOnTouchOutside(true);
return mUserCreationDialog;
}
@@ -212,7 +214,6 @@
}
updateLayout();
});
- return;
}
private void updateLayout() {
@@ -234,7 +235,6 @@
}
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 @@
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 @@
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 e55d7ea..cd5f597 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.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.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 @@
* 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 @@
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 @@
}
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 @@
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 de48814..5201b3d 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 @@
}
/**
+ * 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 e989ed2..b538077 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 @@
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 f760032..f595cd3 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 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 @@
@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 @@
() -> 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 @@
AlertDialog dialog = (AlertDialog) mController.createDialog(
mActivity, mActivityStarter, mCurrentIcon, "test",
- "title", successCallback, cancelCallback);
+ successCallback, cancelCallback);
dialog.show();
dialog.cancel();
@@ -159,9 +156,9 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 873b434..dd8eb3b 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -590,6 +590,7 @@
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 @@
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 7df3dfb..a443b5c 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -250,17 +250,17 @@
"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 5c03698..2913c16 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 48dd08f..dbfa192 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 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
similarity index 91%
rename from packages/SystemUI/animation/src/com/android/systemui/animation/LaunchableFrameLayout.kt
rename to packages/SystemUI/animation/src/com/android/systemui/animation/view/LaunchableFrameLayout.kt
index 2eb503b..7538f18 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 8fe1f48..4fe9f89 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 @@
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 cc33745..82fe3f2 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 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 @@
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 0e79b18..7926f92 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.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 @@
}
}
}
+
+ 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 8844114..b3d2e35 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 @@
animationSpec =
tween(
durationMillis = SELECTED_DOT_REACTION_ANIMATION_DURATION_MS,
- easing = Easings.StandardAccelerateEasing,
+ easing = Easings.StandardAccelerate,
),
)
} else {
@@ -140,7 +140,7 @@
animationSpec =
tween(
durationMillis = SELECTED_DOT_RETRACT_ANIMATION_DURATION_MS,
- easing = Easings.StandardDecelerateEasing,
+ easing = Easings.StandardDecelerate,
),
)
}
@@ -333,7 +333,7 @@
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 @@
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 968e5ab..323fed0 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.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 @@
// 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()
-
- // Show the failure animation if the user entered the wrong input.
- LaunchedEffect(animateFailure) {
- if (animateFailure) {
- showFailureAnimation()
- viewModel.onFailureAnimationShown()
- }
- }
-
Column(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = modifier,
) {
- 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,
- )
- )
- }
- }
- }
-
+ PinInputDisplay(viewModel)
Spacer(Modifier.height(100.dp))
+ PinPad(viewModel)
+ }
+}
- 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 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)
+ }
+ }
+
+ Row(
+ modifier =
+ Modifier.heightIn(min = entryShapeSize)
+ // Pins overflowing horizontally should still be shown as scrolling.
+ .wrapContentSize(unbounded = true),
+ ) {
+ 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
+ }
- 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,
- )
- }
+ ObscuredInputEntry(updateTransition(visibility, label = "Pin Entry $entry"))
- PinButton(
- onClicked = { viewModel.onPinButtonClicked(0) },
- isEnabled = isInputEnabled,
- ) { contentColor ->
- PinDigit(0, contentColor)
- }
-
- PinButton(
- onClicked = { viewModel.onAuthenticateButtonClicked() },
- isEnabled = isInputEnabled,
- isIconButton = true,
- ) { contentColor ->
- PinIcon(
- Icon.Resource(
- res = R.drawable.ic_keyboard_tab_36dp,
- contentDescription =
- ContentDescription.Resource(R.string.keyboardview_keycode_enter),
- ),
- contentColor,
- )
+ 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)
+ }
+ }
+ }
}
}
}
}
+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()
+}
+
@Composable
-private fun PinDigit(
+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
+ }
+
+ 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
+ }
+ }
+
+ 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))
+
+ layout(animatedEntryWidth.roundToPx(), entryShapeSize.roundToPx()) {
+ placeable.place(
+ ((animatedEntryWidth - animatedShapeSize) / 2f).roundToPx(),
+ ((entryShapeSize - animatedShapeSize) / 2f).roundToPx()
+ )
+ }
+ }
+}
+
+@Composable
+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 @@
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 @@
}
},
) {
- 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 2f41ea9..8fe9656 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 702cc05..1443465 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 @@
"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")),
)
@@ -112,6 +113,7 @@
val logBuffer: LogBuffer? = null,
val keepAllLoaded: Boolean,
subTag: String,
+ var isTransitClockEnabled: Boolean = false,
) {
private val TAG = "${ClockRegistry::class.simpleName} ($subTag)"
interface ClockChangeListener {
@@ -203,6 +205,10 @@
var isClockListChanged = false
for (clock in plugin.getClocks()) {
val id = clock.clockId
+ if (!isTransitClockEnabled && id == "DIGITAL_CLOCK_METRO") {
+ continue
+ }
+
val info =
availableClocks.concurrentGetOrPut(id, ClockInfo(clock, plugin, manager)) {
isClockListChanged = true
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/customization/data/content/CustomizationProviderContract.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/customization/data/content/CustomizationProviderContract.kt
index b6d5ef3..95a9ce9 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/customization/data/content/CustomizationProviderContract.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/customization/data/content/CustomizationProviderContract.kt
@@ -184,6 +184,9 @@
/** Flag denoting AI Wallpapers are enabled in wallpaper picker. */
const val FLAG_NAME_WALLPAPER_PICKER_UI_FOR_AIWP = "wallpaper_picker_ui_for_aiwp"
+ /** Flag denoting transit clock are enabled in wallpaper picker. */
+ const val FLAG_NAME_TRANSIT_CLOCK = "lockscreen_custom_transit_clock"
+
object Columns {
/** String. Unique ID for the flag. */
const val NAME = "name"
diff --git a/packages/SystemUI/docs/clock-plugins.md b/packages/SystemUI/docs/clock-plugins.md
index 2226d79..9cb115a 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 2baeaf6..9cc87fd 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 @@
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 bef61b8..0000000
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockPlugin.java
+++ /dev/null
@@ -1,145 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software distributed under the
- * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the specific language governing
- * permissions and limitations under the License.
- */
-package com.android.systemui.plugins;
-
-import android.graphics.Bitmap;
-import android.graphics.Paint.Style;
-import android.view.View;
-
-import com.android.systemui.plugins.annotations.ProvidesInterface;
-
-import java.util.TimeZone;
-
-/**
- * Plugin used to replace main clock in keyguard.
- * @deprecated Migrating to ClockProviderPlugin
- */
-@Deprecated
-@ProvidesInterface(action = ClockPlugin.ACTION, version = ClockPlugin.VERSION)
-public interface ClockPlugin extends Plugin {
-
- String ACTION = "com.android.systemui.action.PLUGIN_CLOCK";
- int VERSION = 5;
-
- /**
- * Get the name of the clock face.
- *
- * This name should not be translated.
- */
- String getName();
-
- /**
- * Get the title of the clock face to be shown in the picker app.
- */
- String getTitle();
-
- /**
- * Get thumbnail of clock face to be shown in the picker app.
- */
- Bitmap getThumbnail();
-
- /**
- * Get preview images of clock face to be shown in the picker app.
- *
- * Preview image should be realistic and show what the clock face will look like on AOD and lock
- * screen.
- *
- * @param width width of the preview image, should be the same as device width in pixels.
- * @param height height of the preview image, should be the same as device height in pixels.
- */
- Bitmap getPreview(int width, int height);
-
- /**
- * Get clock view.
- * @return clock view from plugin.
- */
- View getView();
-
- /**
- * Get clock view for a large clock that appears behind NSSL.
- */
- default View getBigClockView() {
- return null;
- }
-
- /**
- * Returns the preferred Y position of the clock.
- *
- * @param totalHeight Height of the parent container.
- * @return preferred Y position.
- */
- int getPreferredY(int totalHeight);
-
- /**
- * Allows the plugin to clean up resources when no longer needed.
- *
- * Called when the view previously created by {@link ClockPlugin#getView()} has been detached
- * from the view hierarchy.
- */
- void onDestroyView();
-
- /**
- * Set clock paint style.
- * @param style The new style to set in the paint.
- */
- void setStyle(Style style);
-
- /**
- * Set clock text color.
- * @param color A color value.
- */
- void setTextColor(int color);
-
- /**
- * Sets the color palette for the clock face.
- * @param supportsDarkText Whether dark text can be displayed.
- * @param colors Colors that should be used on the clock face, ordered from darker to lighter.
- */
- default void setColorPalette(boolean supportsDarkText, int[] colors) {}
-
- /**
- * Set the amount (ratio) that the device has transitioned to doze.
- * @param darkAmount Amount of transition to doze: 1f for doze and 0f for awake.
- */
- default void setDarkAmount(float darkAmount) {}
-
- /**
- * Notifies that time tick alarm from doze service fired.
- *
- * Implement this method instead of registering a broadcast listener for TIME_TICK.
- */
- default void onTimeTick() {}
-
- /**
- * Notifies that the time zone has changed.
- *
- * Implement this method instead of registering a broadcast listener for TIME_ZONE_CHANGED.
- */
- default void onTimeZoneChanged(TimeZone timeZone) {}
-
- /**
- * Notifies that the time format has changed.
- *
- * @param timeFormat "12" for 12-hour format, "24" for 24-hour format
- */
- default void onTimeFormatChanged(String timeFormat) {}
-
- /**
- * Indicates whether the keyguard status area (date) should be shown below
- * the clock.
- */
- default boolean shouldShowStatusArea() {
- return true;
- }
-}
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/WeatherData.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/WeatherData.kt
index a4b1cee..f83fa33 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/WeatherData.kt
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/WeatherData.kt
@@ -2,14 +2,18 @@
import android.os.Bundle
import android.util.Log
+import android.view.View
import androidx.annotation.VisibleForTesting
+typealias WeatherTouchAction = (View) -> Unit
+
class WeatherData
constructor(
val description: String,
val state: WeatherStateIcon,
val useCelsius: Boolean,
val temperature: Int,
+ val touchAction: WeatherTouchAction? = null,
) {
companion object {
const val DEBUG = true
@@ -20,7 +24,7 @@
@VisibleForTesting const val TEMPERATURE_KEY = "temperature"
private const val INVALID_WEATHER_ICON_STATE = -1
- fun fromBundle(extras: Bundle): WeatherData? {
+ fun fromBundle(extras: Bundle, touchAction: WeatherTouchAction? = null): WeatherData? {
val description = extras.getString(DESCRIPTION_KEY)
val state =
WeatherStateIcon.fromInt(extras.getInt(STATE_KEY, INVALID_WEATHER_ICON_STATE))
@@ -41,7 +45,8 @@
description = description,
state = state,
useCelsius = extras.getBoolean(USE_CELSIUS_KEY),
- temperature = temperature
+ temperature = temperature,
+ touchAction = touchAction
)
if (DEBUG) {
Log.i(TAG, "Weather data parsed $result from $extras")
diff --git a/packages/SystemUI/proguard.flags b/packages/SystemUI/proguard.flags
index a8ed843..b534fcec 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 3412a30..2fc1d2e 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 78a7c17..d9011c2 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 01c5443..c7d2d81 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/battery_percentage_view.xml b/packages/SystemUI/res/layout/battery_percentage_view.xml
index b9b1bb1..82facd0 100644
--- a/packages/SystemUI/res/layout/battery_percentage_view.xml
+++ b/packages/SystemUI/res/layout/battery_percentage_view.xml
@@ -20,7 +20,7 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/battery_percentage_view"
android:layout_width="wrap_content"
- android:layout_height="match_parent"
+ android:layout_height="wrap_content"
android:singleLine="true"
android:textAppearance="@style/TextAppearance.StatusBar.Clock"
android:textColor="?android:attr/textColorPrimary"
diff --git a/packages/SystemUI/res/layout/combined_qs_header.xml b/packages/SystemUI/res/layout/combined_qs_header.xml
index 386c9d6..665c612 100644
--- a/packages/SystemUI/res/layout/combined_qs_header.xml
+++ b/packages/SystemUI/res/layout/combined_qs_header.xml
@@ -74,7 +74,7 @@
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 @@
<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_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_constraintEnd_toStartOf="@id/statusIcons"
- app:layout_constraintTop_toTopOf="@id/clock"
- app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintHorizontal_bias="1"
- />
+ 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 a3a7135..66c57fc 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 07c428b..6194311 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/keyguard_status_bar.xml b/packages/SystemUI/res/layout/keyguard_status_bar.xml
index 64c4eff..fc0bf24 100644
--- a/packages/SystemUI/res/layout/keyguard_status_bar.xml
+++ b/packages/SystemUI/res/layout/keyguard_status_bar.xml
@@ -47,7 +47,10 @@
android:layout_height="match_parent"
android:layout_marginEnd="@dimen/status_bar_padding_end"
android:gravity="center_vertical|end">
- <include layout="@layout/system_icons" />
+ <include layout="@layout/system_icons"
+ android:layout_gravity="center_vertical"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
</FrameLayout>
<ImageView android:id="@+id/multi_user_avatar"
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 0000000..0dcd15b
--- /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/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index d710676..e214666 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -132,21 +132,6 @@
android:id="@+id/lock_icon_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
- <!-- Background protection -->
- <ImageView
- android:id="@+id/lock_icon_bg"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:src="@drawable/fingerprint_bg"
- android:visibility="invisible"/>
-
- <ImageView
- android:id="@+id/lock_icon"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_gravity="center"
- android:scaleType="centerCrop"/>
-
</com.android.keyguard.LockIconView>
<include
diff --git a/packages/SystemUI/res/layout/super_notification_shade.xml b/packages/SystemUI/res/layout/super_notification_shade.xml
index d9fe949..2fde947 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/system_icons.xml b/packages/SystemUI/res/layout/system_icons.xml
index 816dfd3..6a14c21 100644
--- a/packages/SystemUI/res/layout/system_icons.xml
+++ b/packages/SystemUI/res/layout/system_icons.xml
@@ -18,20 +18,23 @@
xmlns:systemui="http://schemas.android.com/apk/res-auto"
android:id="@+id/system_icons"
android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:layout_gravity="center_vertical|end"
+ android:layout_height="wrap_content"
+ android:paddingBottom="@dimen/status_bar_icons_padding_bottom"
+ android:paddingTop="@dimen/status_bar_icons_padding_top"
+ android:paddingStart="@dimen/status_bar_icons_padding_start"
+ android:paddingEnd="@dimen/status_bar_icons_padding_end"
android:gravity="center_vertical">
<com.android.systemui.statusbar.phone.StatusIconContainer android:id="@+id/statusIcons"
android:layout_width="0dp"
android:layout_weight="1"
- android:layout_height="match_parent"
+ android:layout_height="wrap_content"
android:paddingEnd="@dimen/signal_cluster_battery_padding"
android:gravity="center_vertical"
android:orientation="horizontal"/>
<com.android.systemui.battery.BatteryMeterView android:id="@+id/battery"
- android:layout_height="match_parent"
+ android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:clipToPadding="false"
android:clipChildren="false"
diff --git a/packages/SystemUI/res/layout/window_magnification_settings_view.xml b/packages/SystemUI/res/layout/window_magnification_settings_view.xml
index a8febe7..efdb0a3 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 a307aee..2fa9711 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,7 @@
<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>
+ <string name="accessibility_bouncer" msgid="5896923685673320070">"voer skermslot in"</string>
<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 +1109,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 869f582..6d6b435 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,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{# ማሳወቂያ}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 fa9146e..614b968 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,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{إشعار واحد}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 85214bf..06c9f53d 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,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{# টা জাননী}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 10097b8..a413e6b 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,7 @@
<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>
+ <string name="accessibility_bouncer" msgid="5896923685673320070">"ekran kilidi daxil edin"</string>
<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 +1109,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 74d16a4..3d52d51 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,7 @@
<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>
+ <string name="accessibility_bouncer" msgid="5896923685673320070">"unesite zaključavanje ekrana"</string>
<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 +1109,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 57e6d92..5f2d7b5 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,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{# апавяшчэнне}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 d373394..9201f40 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,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-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml
index aa0a7a7..d8cb70d 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,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{#টি বিজ্ঞপ্তি}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 2c13d16..89c964d 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,7 @@
<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>
+ <string name="accessibility_bouncer" msgid="5896923685673320070">"unos zaključavanja ekrana"</string>
<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 +1109,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 a4cb466..e0e78ae 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,7 @@
<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>
+ <string name="accessibility_bouncer" msgid="5896923685673320070">"utilitza el bloqueig de pantalla"</string>
<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 +1109,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 53e47e3..522300b 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,7 @@
<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>
+ <string name="accessibility_bouncer" msgid="5896923685673320070">"zadejte zámek obrazovky"</string>
<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 +1109,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 bc700e1..e9a522f 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,7 @@
<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>
+ <string name="accessibility_bouncer" msgid="5896923685673320070">"angiv skærmlås"</string>
<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 +1109,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 98a1d67d..dc06ee1 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,7 @@
<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>
+ <string name="accessibility_bouncer" msgid="5896923685673320070">"Displaysperre-Anmeldedaten eingeben"</string>
<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 +1109,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 9425b21..08d5b4b 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,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-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml
index ddd54c0..e4719e4 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,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-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml
index 4f46c00..43d58ca 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 ddd54c0..e4719e4 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,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-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index ddd54c0..e4719e4 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,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-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml
index c95d6aa..b4c5d10 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 e2ac0ff..0678e68 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,7 @@
<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>
+ <string name="accessibility_bouncer" msgid="5896923685673320070">"ingresa el bloqueo pantalla"</string>
<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 +1109,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 76428b7..0dfd63c 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,7 @@
<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>
+ <string name="accessibility_bouncer" msgid="5896923685673320070">"Poner bloqueo de pantalla"</string>
<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 +1109,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 16a6245..ae75d09 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,7 @@
<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>
+ <string name="accessibility_bouncer" msgid="5896923685673320070">"sisesta ekraanilukk"</string>
<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 +1109,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 3ef0b28d..ff78952 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,7 @@
<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>
+ <string name="accessibility_bouncer" msgid="5896923685673320070">"erabili pantailaren blokeoa"</string>
<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 +1109,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 456d91f..cc26355 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,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{# اعلان}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 673f9c2..2b93430 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,7 @@
<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>
+ <string name="accessibility_bouncer" msgid="5896923685673320070">"käytä näytön lukitustapaa"</string>
<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 +1109,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 2340325..2ac7309 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,7 @@
<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>
+ <string name="accessibility_bouncer" msgid="5896923685673320070">"entrer verrouillage de l\'écran"</string>
<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 +1109,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 f0c0fdc..7646a88 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,7 @@
<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>
+ <string name="accessibility_bouncer" msgid="5896923685673320070">"accéder au verrouillage de l\'écran"</string>
<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 +1109,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 b4a8663..cb2375f 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,7 @@
<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>
+ <string name="accessibility_bouncer" msgid="5896923685673320070">"introducir bloqueo de pantalla"</string>
<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 +1109,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 639c6c3..c3be28c 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,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{# નોટિફિકેશન}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 1d70a2a..60fd680 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/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{# सूचना}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 66c75c3..02c1be7 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,7 @@
<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>
+ <string name="accessibility_bouncer" msgid="5896923685673320070">"unesite zaključavanje zaslona"</string>
<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 +1109,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 7b5c943..b5081d0 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,7 @@
<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>
+ <string name="accessibility_bouncer" msgid="5896923685673320070">"képernyőzár megadása"</string>
<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 +1109,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 85ad884..cfc92a4 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,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{# ծանուցում}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 dd426c7..515f80f 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,7 @@
<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>
+ <string name="accessibility_bouncer" msgid="5896923685673320070">"masukkan kunci layar"</string>
<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 +1109,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 838fd5b..fb73c0b 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,7 @@
<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>
+ <string name="accessibility_bouncer" msgid="5896923685673320070">"sláðu inn skjálás"</string>
<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 +1109,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 9c784b6..62d5a46 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,7 @@
<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>
+ <string name="accessibility_bouncer" msgid="5896923685673320070">"inserisci blocco schermo"</string>
<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 +1109,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 c836388..7497bd4 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,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{התראה אחת}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 f100299..d74d2f1 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 8ed9c43..8662f98 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,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-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml
index b131ceb..4fd3242 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,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-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml
index 02a05e9..5132ac0 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,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 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 6d3a988..5fa9464 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,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{# ನೋಟಿಫಿಕೇಶನ್}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 373f84e..eb4afbb 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,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-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml
index 6d7632df..c8f6c37 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,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-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml
index 1ea2b5f..756444d 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,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-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index c0526f3..ff745b5 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,7 @@
<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>
+ <string name="accessibility_bouncer" msgid="5896923685673320070">"įvesti ekrano užraktą"</string>
<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 +1109,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 2022d7f..c8c18fb 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,7 @@
<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>
+ <string name="accessibility_bouncer" msgid="5896923685673320070">"ievadīt ekrāna bloķēšanas informāciju"</string>
<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 +1109,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 13fd190..e429979 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,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">"внесете PIN/шема/лозинка"</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{# известување}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 ec1d42c..83d045e 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 c81542a..98d0ddd 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,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-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml
index ace116c..6547cfe 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,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-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml
index 9153f1f..3d1a222 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,7 @@
<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>
+ <string name="accessibility_bouncer" msgid="5896923685673320070">"masukkan kunci skrin"</string>
<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 +1109,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 729ff60..34f580a 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 e5e9072..a6111d9 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,7 @@
<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>
+ <string name="accessibility_bouncer" msgid="5896923685673320070">"legg inn skjermlåsen"</string>
<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 +1109,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 f21d9c2..610e28b 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,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-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index 5e66b1c..3f7f829 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,7 @@
<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>
+ <string name="accessibility_bouncer" msgid="5896923685673320070">"schermvergrendeling invoeren"</string>
<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 +1109,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 95d16e0..3728d5a 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,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-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml
index 858ed86..bb511e3 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,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{# ਸੂਚਨਾ}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 101f05b..5dcdd7e 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,7 @@
<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>
+ <string name="accessibility_bouncer" msgid="5896923685673320070">"Wprowadź blokadę ekranu"</string>
<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 +1109,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 579a9bd..3ac3692 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,7 @@
<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>
+ <string name="accessibility_bouncer" msgid="5896923685673320070">"inserir bloqueio de tela"</string>
<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 +1109,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 aca644f..6d54a64 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,7 @@
<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>
+ <string name="accessibility_bouncer" msgid="5896923685673320070">"introduzir bloqueio de ecrã"</string>
<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 +1109,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 579a9bd..3ac3692 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,7 @@
<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>
+ <string name="accessibility_bouncer" msgid="5896923685673320070">"inserir bloqueio de tela"</string>
<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 +1109,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 39e6182..d0764dd 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,7 @@
<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>
+ <string name="accessibility_bouncer" msgid="5896923685673320070">"intră în blocarea ecranului"</string>
<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 +1109,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 44df742..91a8534 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,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{# уведомление}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 5669e0e..1efb408 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,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{දැනුම්දීම් #ක්}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 8950d22..b353293 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,7 @@
<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>
+ <string name="accessibility_bouncer" msgid="5896923685673320070">"zadať zámku obrazovky"</string>
<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 +1109,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 96ad5ba..4dd753b 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,7 @@
<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>
+ <string name="accessibility_bouncer" msgid="5896923685673320070">"odklenite zaslon"</string>
<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 +1109,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 e700944..475e799 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,7 @@
<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>
+ <string name="accessibility_bouncer" msgid="5896923685673320070">"hyr te kyçja e ekranit"</string>
<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 +1109,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 af8174e..eb69224 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,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{# обавештење}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 1368794..7e2e006 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,7 @@
<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>
+ <string name="accessibility_bouncer" msgid="5896923685673320070">"ange skärmlåset"</string>
<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 +1109,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 e4d3e56a..676edb7 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,7 @@
<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>
+ <string name="accessibility_bouncer" msgid="5896923685673320070">"weka kifunga skrini"</string>
<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 +1109,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-sw720dp/dimens.xml b/packages/SystemUI/res/values-sw720dp/dimens.xml
index 2086459..d277dae 100644
--- a/packages/SystemUI/res/values-sw720dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw720dp/dimens.xml
@@ -16,8 +16,15 @@
*/
-->
<resources>
+ <!-- padding for container with status icons and battery -->
+ <dimen name="status_bar_icons_padding_end">12dp</dimen>
+ <!-- it's a bit smaller on large screen to account for status_bar_icon_horizontal_margin -->
+ <dimen name="status_bar_icons_padding_start">10dp</dimen>
+
+ <dimen name="status_bar_padding_end">0dp</dimen>
+
<!-- gap on either side of status bar notification icons -->
- <dimen name="status_bar_icon_padding">1dp</dimen>
+ <dimen name="status_bar_icon_horizontal_margin">1dp</dimen>
<dimen name="controls_header_horizontal_padding">28dp</dimen>
<dimen name="controls_content_margin_horizontal">40dp</dimen>
diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml
index a7e3efb..390bd47 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,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-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml
index 25bf528..a6cbf3d 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 0ca154e..2ace86f 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 578c843..307d113 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,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-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index 81a78c9..3997539 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 83b1f16..6962aa4 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,7 @@
<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>
+ <string name="accessibility_bouncer" msgid="5896923685673320070">"ekran kilidini gir"</string>
<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 +1109,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 6df0774..93807d0 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,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{# сповіщення}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 715ec91..ff4965f 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,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-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml
index 968be51..538fe16d 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,7 @@
<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>
+ <string name="accessibility_bouncer" msgid="5896923685673320070">"ekran qulfini kiriting"</string>
<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 +1109,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 23fa188..4120e57e 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,7 @@
<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>
+ <string name="accessibility_bouncer" msgid="5896923685673320070">"nhập phương thức mở khoá màn hình"</string>
<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 +1109,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 18fc396..e167c61 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 d222b75..4066a89 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 e2f9c9e..7abe1de 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 74a6884..e5b4c14 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,7 @@
<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>
+ <string name="accessibility_bouncer" msgid="5896923685673320070">"faka ukukhiya isikrini"</string>
<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 +1109,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 bd86e51..3a1d1a8 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 166bd2a..9c864ab 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>
@@ -701,6 +698,10 @@
-->
<integer name="config_face_auth_supported_posture">0</integer>
+ <!-- Components to allow running fingerprint listening if their activity is occluding the lock screen. -->
+ <string-array name="config_fingerprint_listen_on_occluding_activity_packages" translatable="false">
+ </string-array>
+
<!-- Whether the communal service should be enabled -->
<bool name="config_communalServiceEnabled">false</bool>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 4f768cc..3bd7a06 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>
@@ -323,8 +326,16 @@
<!-- opacity at which Notification icons will be drawn in the status bar -->
<item type="dimen" name="status_bar_icon_drawing_alpha">90%</item>
+ <!-- paddings for container with status icons and battery -->
+ <!-- padding start is a bit smaller than end to account for status icon margin-->
+ <dimen name="status_bar_icons_padding_start">11dp</dimen>
+
+ <dimen name="status_bar_icons_padding_end">0dp</dimen>
+ <dimen name="status_bar_icons_padding_bottom">8dp</dimen>
+ <dimen name="status_bar_icons_padding_top">8dp</dimen>
+
<!-- gap on either side of status bar notification icons -->
- <dimen name="status_bar_icon_padding">0dp</dimen>
+ <dimen name="status_bar_icon_horizontal_margin">0dp</dimen>
<!-- the padding on the start of the statusbar -->
<dimen name="status_bar_padding_start">8dp</dimen>
@@ -1557,7 +1568,8 @@
<!-- Status bar user chip -->
<dimen name="status_bar_user_chip_avatar_size">16dp</dimen>
- <dimen name="status_bar_user_chip_end_margin">12dp</dimen>
+ <!-- below also works as break between user chip and hover state of status icons -->
+ <dimen name="status_bar_user_chip_end_margin">4dp</dimen>
<dimen name="status_bar_user_chip_text_size">12sp</dimen>
<!-- System UI Dialog -->
diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml
index e5c9461..134a7a9 100644
--- a/packages/SystemUI/res/values/ids.xml
+++ b/packages/SystemUI/res/values/ids.xml
@@ -206,5 +206,11 @@
<!-- 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" />
+ <item type="id" name="lock_icon" />
+ <item type="id" name="lock_icon_bg" />
+</resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 663efea..25f3d22 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 8512f6f..c167256 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" />
+ app:curveFit="linear" />
<KeyAttribute
- app:motionTarget="@id/statusIcons"
+ app:motionTarget="@id/shade_header_system_icons"
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" />
- <KeyAttribute
- app:motionTarget="@id/batteryRemainingIcon"
- 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 bf576dc..39f4c81 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 1950965..50a388d 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 8039c68..7b4282f 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 1f61c64..97d6099 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 @@
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 @@
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 @@
private Builder(Subscription nestedSubscription, Callback callback) {
mNestedSubscription = nestedSubscription;
mCallback = callback;
- mConditions = new ArraySet();
- mPreconditions = new ArraySet();
+ mConditions = new ArraySet<>();
}
/**
@@ -352,29 +350,6 @@
}
/**
- * 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 @@
* @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 117cf78a..4b31498 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 @@
*
* 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 @@
*/
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 751a3f8..2e6c485 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 @@
* @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 9a00447..0fbeb1a 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 @@
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 @@
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 @@
(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/shared/src/com/android/systemui/shared/system/InteractionJankMonitorWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/InteractionJankMonitorWrapper.java
index 4269530..c844db7 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/InteractionJankMonitorWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/InteractionJankMonitorWrapper.java
@@ -61,6 +61,8 @@
InteractionJankMonitor.CUJ_LAUNCHER_APP_SWIPE_TO_RECENTS;
public static final int CUJ_OPEN_SEARCH_RESULT =
InteractionJankMonitor.CUJ_LAUNCHER_OPEN_SEARCH_RESULT;
+ public static final int CUJ_SHADE_EXPAND_FROM_STATUS_BAR =
+ InteractionJankMonitor.CUJ_SHADE_EXPAND_FROM_STATUS_BAR;
@IntDef({
CUJ_APP_LAUNCH_FROM_RECENTS,
@@ -76,6 +78,7 @@
CUJ_CLOSE_ALL_APPS_SWIPE,
CUJ_CLOSE_ALL_APPS_TO_HOME,
CUJ_OPEN_SEARCH_RESULT,
+ CUJ_SHADE_EXPAND_FROM_STATUS_BAR,
})
@Retention(RetentionPolicy.SOURCE)
public @interface CujType {
diff --git a/packages/SystemUI/src/com/android/keyguard/BouncerKeyguardMessageArea.kt b/packages/SystemUI/src/com/android/keyguard/BouncerKeyguardMessageArea.kt
index 0842953..78a5c98 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.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 8ea4c31a..84a2c25 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 @@
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 @@
}
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 @@
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 @@
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 @@
) { 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 @@
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 d7e8616..bb11217 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
@@ -143,6 +143,7 @@
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 376e27c..360f755 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
@@ -303,9 +303,10 @@
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 @@
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 @@
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 @@
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 41c1eda..62a2f90 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -437,10 +437,6 @@
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 @@
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 @@
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/KeyguardFingerprintListenModel.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardFingerprintListenModel.kt
index f2685c5..f23ae67 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardFingerprintListenModel.kt
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardFingerprintListenModel.kt
@@ -27,6 +27,7 @@
override var userId: Int = 0,
override var listening: Boolean = false,
// keepSorted
+ var allowOnCurrentOccludingActivity: Boolean = false,
var alternateBouncerShowing: Boolean = false,
var biometricEnabledForUser: Boolean = false,
var bouncerIsOrWillShow: Boolean = false,
@@ -58,6 +59,7 @@
userId.toString(),
listening.toString(),
// keep sorted
+ allowOnCurrentOccludingActivity.toString(),
alternateBouncerShowing.toString(),
biometricEnabledForUser.toString(),
bouncerIsOrWillShow.toString(),
@@ -98,6 +100,7 @@
userId = model.userId
listening = model.listening
// keep sorted
+ allowOnCurrentOccludingActivity = model.allowOnCurrentOccludingActivity
alternateBouncerShowing = model.alternateBouncerShowing
biometricEnabledForUser = model.biometricEnabledForUser
bouncerIsOrWillShow = model.bouncerIsOrWillShow
@@ -144,6 +147,7 @@
"userId",
"listening",
// keep sorted
+ "allowOnCurrentOccludingActivity",
"alternateBouncerShowing",
"biometricAllowedForUser",
"bouncerIsOrWillShow",
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
index 3c05299..20e4656 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.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 @@
/** 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 3d255a5..49f788c 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
@@ -236,6 +236,7 @@
getKeyguardSecurityCallback().onCancelClicked();
});
}
+ mView.onDevicePostureChanged(mPostureController.getDevicePosture());
mPostureController.addCallback(mPostureCallback);
}
@@ -368,6 +369,7 @@
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 bf9c3bb..2878df2 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityCallback.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityCallback.java
@@ -87,6 +87,11 @@
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 74b29a7..7c511a3 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.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 @@
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 @@
@Override
public void onUserInput() {
+ mBouncerMessageInteractor.onPrimaryBouncerUserInput();
mKeyguardFaceAuthInteractor.onPrimaryBouncerUserInput();
mUpdateMonitor.cancelFaceAuth();
}
@@ -207,7 +210,15 @@
}
@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 @@
TelephonyManager telephonyManager,
ViewMediatorCallback viewMediatorCallback,
AudioManager audioManager,
- KeyguardFaceAuthInteractor keyguardFaceAuthInteractor
+ KeyguardFaceAuthInteractor keyguardFaceAuthInteractor,
+ BouncerMessageInteractor bouncerMessageInteractor
) {
super(view);
mLockPatternUtils = lockPatternUtils;
@@ -411,6 +423,7 @@
mViewMediatorCallback = viewMediatorCallback;
mAudioManager = audioManager;
mKeyguardFaceAuthInteractor = keyguardFaceAuthInteractor;
+ mBouncerMessageInteractor = bouncerMessageInteractor;
}
@Override
@@ -431,6 +444,7 @@
// 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 1461dbe..7c8d91f 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimInputView.kt
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimInputView.kt
@@ -22,7 +22,7 @@
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 ae061c0..89ef749 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -18,6 +18,8 @@
import static android.app.StatusBarManager.SESSION_KEYGUARD;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.content.Intent.ACTION_USER_REMOVED;
import static android.content.Intent.ACTION_USER_STOPPED;
@@ -76,9 +78,9 @@
import android.annotation.AnyThread;
import android.annotation.MainThread;
import android.annotation.SuppressLint;
-import android.app.ActivityTaskManager;
import android.app.ActivityTaskManager.RootTaskInfo;
import android.app.AlarmManager;
+import android.app.IActivityTaskManager;
import android.app.admin.DevicePolicyManager;
import android.app.trust.TrustManager;
import android.content.BroadcastReceiver;
@@ -155,6 +157,8 @@
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;
@@ -303,6 +307,7 @@
private final AuthController mAuthController;
private final UiEventLogger mUiEventLogger;
private final Set<Integer> mFaceAcquiredInfoIgnoreList;
+ private final Set<String> mAllowFingerprintOnOccludingActivitiesFromPackage;
private final PackageManager mPackageManager;
private int mStatusBarState;
private final StatusBarStateController.StateListener mStatusBarStateControllerListener =
@@ -344,6 +349,7 @@
private boolean mSecureCameraLaunched;
@VisibleForTesting
protected boolean mTelephonyCapable;
+ private boolean mAllowFingerprintOnCurrentOccludingActivity;
// Device provisioning state
private boolean mDeviceProvisioned;
@@ -380,12 +386,15 @@
private final ActiveUnlockConfig mActiveUnlockConfig;
private final IDreamManager mDreamManager;
private final TelephonyManager mTelephonyManager;
+ private final FeatureFlags mFeatureFlags;
@Nullable
private final FingerprintManager mFpm;
@Nullable
private final FaceManager mFaceManager;
@Nullable
private KeyguardFaceAuthInteractor mFaceAuthInteractor;
+ private final TaskStackChangeListeners mTaskStackChangeListeners;
+ private final IActivityTaskManager mActivityTaskManager;
private final LockPatternUtils mLockPatternUtils;
@VisibleForTesting
@DevicePostureInt
@@ -1462,14 +1471,6 @@
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 +1981,6 @@
@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 +2327,10 @@
@Nullable BiometricManager biometricManager,
FaceWakeUpTriggersConfig faceWakeUpTriggersConfig,
DevicePostureController devicePostureController,
- Optional<FingerprintInteractiveToAuthProvider> interactiveToAuthProvider) {
+ Optional<FingerprintInteractiveToAuthProvider> interactiveToAuthProvider,
+ FeatureFlags featureFlags,
+ TaskStackChangeListeners taskStackChangeListeners,
+ IActivityTaskManager activityTaskManagerService) {
mContext = context;
mSubscriptionManager = subscriptionManager;
mUserTracker = userTracker;
@@ -2365,6 +2362,7 @@
mPackageManager = packageManager;
mFpm = fingerprintManager;
mFaceManager = faceManager;
+ mFeatureFlags = featureFlags;
mActiveUnlockConfig.setKeyguardUpdateMonitor(this);
mFaceAcquiredInfoIgnoreList = Arrays.stream(
mContext.getResources().getIntArray(
@@ -2374,6 +2372,12 @@
mConfigFaceAuthSupportedPosture = mContext.getResources().getInteger(
R.integer.config_face_auth_supported_posture);
mFaceWakeUpTriggersConfig = faceWakeUpTriggersConfig;
+ mAllowFingerprintOnOccludingActivitiesFromPackage = Arrays.stream(
+ mContext.getResources().getStringArray(
+ R.array.config_fingerprint_listen_on_occluding_activity_packages))
+ .collect(Collectors.toSet());
+ mTaskStackChangeListeners = taskStackChangeListeners;
+ mActivityTaskManager = activityTaskManagerService;
mHandler = new Handler(mainLooper) {
@Override
@@ -2589,7 +2593,7 @@
}
updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE, FACE_AUTH_UPDATED_ON_KEYGUARD_INIT);
- TaskStackChangeListeners.getInstance().registerTaskStackListener(mTaskStackListener);
+ mTaskStackChangeListeners.registerTaskStackListener(mTaskStackListener);
mIsSystemUser = mUserManager.isSystemUser();
int user = mUserTracker.getUserId();
mUserIsUnlocked.put(user, mUserManager.isUserUnlocked(user));
@@ -3056,7 +3060,12 @@
|| shouldListenForFingerprintAssistant
|| (mKeyguardOccluded && mIsDreaming)
|| (mKeyguardOccluded && userDoesNotHaveTrust && mKeyguardShowing
- && (mOccludingAppRequestingFp || isUdfps || mAlternateBouncerShowing));
+ && (mOccludingAppRequestingFp
+ || isUdfps
+ || mAlternateBouncerShowing
+ || mAllowFingerprintOnCurrentOccludingActivity
+ )
+ );
// Only listen if this KeyguardUpdateMonitor belongs to the system user. There is an
// instance of KeyguardUpdateMonitor for each user but KeyguardUpdateMonitor is user-aware.
@@ -3097,6 +3106,7 @@
System.currentTimeMillis(),
user,
shouldListen,
+ mAllowFingerprintOnCurrentOccludingActivity,
mAlternateBouncerShowing,
biometricEnabledForUser,
mPrimaryBouncerIsOrWillBeShowing,
@@ -3213,6 +3223,13 @@
|| (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) {
@@ -4118,19 +4135,35 @@
return mSimDatas.get(subId).slotId;
}
- private final TaskStackChangeListener
- mTaskStackListener = new TaskStackChangeListener() {
+ private final TaskStackChangeListener mTaskStackListener = new TaskStackChangeListener() {
@Override
public void onTaskStackChangedBackground() {
try {
- RootTaskInfo info = ActivityTaskManager.getService().getRootTaskInfo(
+ if (mFeatureFlags.isEnabled(Flags.FP_LISTEN_OCCLUDING_APPS)) {
+ RootTaskInfo standardTask = mActivityTaskManager.getRootTaskInfo(
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
+ final boolean previousState = mAllowFingerprintOnCurrentOccludingActivity;
+ mAllowFingerprintOnCurrentOccludingActivity =
+ standardTask.topActivity != null
+ && !TextUtils.isEmpty(standardTask.topActivity.getPackageName())
+ && mAllowFingerprintOnOccludingActivitiesFromPackage.contains(
+ standardTask.topActivity.getPackageName())
+ && standardTask.visible;
+ if (mAllowFingerprintOnCurrentOccludingActivity != previousState) {
+ mLogger.allowFingerprintOnCurrentOccludingActivityChanged(
+ mAllowFingerprintOnCurrentOccludingActivity);
+ updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE);
+ }
+ }
+
+ RootTaskInfo assistantTask = mActivityTaskManager.getRootTaskInfo(
WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_ASSISTANT);
- if (info == null) {
+ if (assistantTask == null) {
return;
}
- mLogger.logTaskStackChangedForAssistant(info.visible);
+ mLogger.logTaskStackChangedForAssistant(assistantTask.visible);
mHandler.sendMessage(mHandler.obtainMessage(MSG_ASSISTANT_STACK_CHANGED,
- info.visible));
+ assistantTask.visible));
} catch (RemoteException e) {
mLogger.logException(e, "unable to check task stack ");
}
@@ -4322,7 +4355,7 @@
mUserTracker.removeCallback(mUserChangedCallback);
- TaskStackChangeListeners.getInstance().unregisterTaskStackListener(mTaskStackListener);
+ mTaskStackChangeListeners.unregisterTaskStackListener(mTaskStackListener);
mBroadcastDispatcher.unregisterReceiver(mBroadcastReceiver);
mBroadcastDispatcher.unregisterReceiver(mBroadcastAllReceiver);
@@ -4381,6 +4414,8 @@
pw.println(" enabledByUser=" + mBiometricEnabledForUser.get(userId));
pw.println(" mKeyguardOccluded=" + mKeyguardOccluded);
pw.println(" mIsDreaming=" + mIsDreaming);
+ pw.println(" mFingerprintListenOnOccludingActivitiesFromPackage="
+ + mAllowFingerprintOnOccludingActivitiesFromPackage);
if (isUdfpsSupported()) {
pw.println(" udfpsEnrolled=" + isUdfpsEnrolled());
pw.println(" shouldListenForUdfps=" + shouldListenForFingerprint(true));
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardViewController.java
index 281067d..bc12aee 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardViewController.java
@@ -119,6 +119,14 @@
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/LockIconView.java b/packages/SystemUI/src/com/android/keyguard/LockIconView.java
index abad0be..d1fffaa 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconView.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconView.java
@@ -16,6 +16,8 @@
package com.android.keyguard;
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+
import android.content.Context;
import android.content.res.ColorStateList;
import android.graphics.Color;
@@ -23,6 +25,7 @@
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
+import android.view.Gravity;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.ImageView;
@@ -68,13 +71,9 @@
public LockIconView(Context context, AttributeSet attrs) {
super(context, attrs);
mSensorRect = new RectF();
- }
- @Override
- public void onFinishInflate() {
- super.onFinishInflate();
- mLockIcon = findViewById(R.id.lock_icon);
- mBgView = findViewById(R.id.lock_icon_bg);
+ addBgImageView(context, attrs);
+ addLockIconImageView(context, attrs);
}
void setDozeAmount(float dozeAmount) {
@@ -184,6 +183,30 @@
mLockIcon.setImageState(getLockIconState(mIconType, mAod), true);
}
+ private void addLockIconImageView(Context context, AttributeSet attrs) {
+ mLockIcon = new ImageView(context, attrs);
+ mLockIcon.setId(R.id.lock_icon);
+ mLockIcon.setScaleType(ImageView.ScaleType.CENTER_CROP);
+ addView(mLockIcon);
+ LayoutParams lp = (LayoutParams) mLockIcon.getLayoutParams();
+ lp.height = MATCH_PARENT;
+ lp.width = MATCH_PARENT;
+ lp.gravity = Gravity.CENTER;
+ mLockIcon.setLayoutParams(lp);
+ }
+
+ private void addBgImageView(Context context, AttributeSet attrs) {
+ mBgView = new ImageView(context, attrs);
+ mBgView.setId(R.id.lock_icon_bg);
+ mBgView.setImageDrawable(context.getDrawable(R.drawable.fingerprint_bg));
+ mBgView.setVisibility(View.INVISIBLE);
+ addView(mBgView);
+ LayoutParams lp = (LayoutParams) mBgView.getLayoutParams();
+ lp.height = MATCH_PARENT;
+ lp.width = MATCH_PARENT;
+ mBgView.setLayoutParams(lp);
+ }
+
private static int[] getLockIconState(@IconType int icon, boolean aod) {
if (icon == ICON_NONE) {
return new int[0];
diff --git a/packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java b/packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java
index 7d76f12..a04a48d 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 ebd234f..3f1741a6 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 e22fc30..edc298c 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 32f06dc..7c129b4 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 500910c..56c0953 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 7517dee..0000000
--- 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 1add1a3..0000000
--- 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 0210e08..0000000
--- 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/ClockInfoModule.java b/packages/SystemUI/src/com/android/keyguard/clock/ClockInfoModule.java
deleted file mode 100644
index 72a44bd..0000000
--- a/packages/SystemUI/src/com/android/keyguard/clock/ClockInfoModule.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.keyguard.clock;
-
-import java.util.List;
-
-import dagger.Module;
-import dagger.Provides;
-
-/**
- * Dagger Module for clock package.
- *
- * @deprecated Migrate to ClockRegistry
- */
-@Module
-@Deprecated
-public abstract class ClockInfoModule {
-
- /** */
- @Provides
- public static List<ClockInfo> provideClockInfoList(ClockManager clockManager) {
- return clockManager.getClockInfos();
- }
-}
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 d44d89e..0000000
--- 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 122c521..0000000
--- 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 b6413cb..0000000
--- 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 5c5493a..0000000
--- 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 3c3f475..0000000
--- 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 c81935a..0000000
--- 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 34c041b..0000000
--- 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 096e943..0000000
--- 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 4e51b98..0000000
--- 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 abd0dd2..0000000
--- a/packages/SystemUI/src/com/android/keyguard/clock/ViewPreviewer.java
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.keyguard.clock;
-
-import android.annotation.Nullable;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.os.Handler;
-import android.os.Looper;
-import android.util.Log;
-import android.view.View;
-import android.view.ViewGroup;
-
-import java.util.concurrent.Callable;
-import java.util.concurrent.FutureTask;
-
-/**
- * Creates a preview image ({@link Bitmap}) of a {@link View} for a custom clock face.
- */
-final class ViewPreviewer {
-
- private static final String TAG = "ViewPreviewer";
-
- /**
- * Handler used to run {@link View#draw(Canvas)} on the main thread.
- */
- private final Handler mMainHandler = new Handler(Looper.getMainLooper());
-
- /**
- * Generate a realistic preview of a clock face.
- *
- * @param view view is used to generate preview image.
- * @param width width of the preview image, should be the same as device width in pixels.
- * @param height height of the preview image, should be the same as device height in pixels.
- * @return bitmap of view.
- */
- @Nullable
- Bitmap createPreview(View view, int width, int height) {
- if (view == null) {
- return null;
- }
- FutureTask<Bitmap> task = new FutureTask<>(new Callable<Bitmap>() {
- @Override
- public Bitmap call() {
- Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
-
- // Draw clock view hierarchy to canvas.
- Canvas canvas = new Canvas(bitmap);
- canvas.drawColor(Color.BLACK);
- dispatchVisibilityAggregated(view, true);
- view.measure(View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY),
- View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY));
- view.layout(0, 0, width, height);
- view.draw(canvas);
-
- return bitmap;
- }
- });
-
- if (Looper.myLooper() == Looper.getMainLooper()) {
- task.run();
- } else {
- mMainHandler.post(task);
- }
-
- try {
- return task.get();
- } catch (Exception e) {
- Log.e(TAG, "Error completing task", e);
- return null;
- }
- }
-
- private void dispatchVisibilityAggregated(View view, boolean isVisible) {
- // Similar to View.dispatchVisibilityAggregated implementation.
- final boolean thisVisible = view.getVisibility() == View.VISIBLE;
- if (thisVisible || !isVisible) {
- view.onVisibilityAggregated(isVisible);
- }
-
- if (view instanceof ViewGroup) {
- isVisible = thisVisible && isVisible;
- ViewGroup vg = (ViewGroup) view;
- int count = vg.getChildCount();
-
- for (int i = 0; i < count; i++) {
- dispatchVisibilityAggregated(vg.getChildAt(i), isVisible);
- }
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java b/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java
index d2b10d6..83fc278 100644
--- a/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java
+++ b/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java
@@ -71,7 +71,8 @@
context.getString(R.string.lockscreen_clock_id_fallback),
logBuffer,
/* keepAllLoaded = */ false,
- /* subTag = */ "System");
+ /* subTag = */ "System",
+ /* isTransitClockEnabled = */ featureFlags.isEnabled(Flags.TRANSIT_CLOCK));
registry.registerListeners();
return registry;
}
diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerComponent.java b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerComponent.java
index 154b0ed..b57c2b5 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 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 38f252a..893239b 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.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/keyguard/logging/KeyguardUpdateMonitorLogger.kt b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
index b596331..a192803 100644
--- a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
+++ b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
@@ -601,6 +601,15 @@
)
}
+ fun allowFingerprintOnCurrentOccludingActivityChanged(allow: Boolean) {
+ logBuffer.log(
+ TAG,
+ VERBOSE,
+ { bool1 = allow },
+ { "allowFingerprintOnCurrentOccludingActivityChanged: $bool1" }
+ )
+ }
+
fun logAssistantVisible(assistantVisible: Boolean) {
logBuffer.log(
TAG,
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index be5bb07..1f1b154 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.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 @@
@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 @@
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/keyguard/data/BouncerViewModule.kt b/packages/SystemUI/src/com/android/systemui/PhoneSystemUIAppComponentFactory.kt
similarity index 63%
copy from packages/SystemUI/src/com/android/systemui/keyguard/data/BouncerViewModule.kt
copy to packages/SystemUI/src/com/android/systemui/PhoneSystemUIAppComponentFactory.kt
index 390c54e..f06cb41 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/BouncerViewModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/PhoneSystemUIAppComponentFactory.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,13 +14,10 @@
* limitations under the License.
*/
-package com.android.systemui.keyguard.data
+package com.android.systemui
-import dagger.Binds
-import dagger.Module
+import android.content.Context
-@Module
-interface BouncerViewModule {
- /** Binds BouncerView to BouncerViewImpl and makes it injectable. */
- @Binds fun bindBouncerView(bouncerViewImpl: BouncerViewImpl): BouncerView
+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 99dd6b6..670c1fa 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorHwcLayer.kt
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorHwcLayer.kt
@@ -49,8 +49,11 @@
* 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 @@
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 @@
override fun onAttachedToWindow() {
super.onAttachedToWindow()
parent.requestTransparentRegion(this)
- if (!DEBUG_COLOR) {
+ if (!debug) {
viewRootImpl.setDisplayDecoration(true)
}
@@ -143,12 +146,12 @@
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 @@
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 ea0f343..67d4a2e 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.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.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 @@
* 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 @@
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 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 ScreenDecorations(Context context,
@Main Executor mainExecutor,
SecureSettings secureSettings,
- TunerService tunerService,
UserTracker userTracker,
DisplayTracker displayTracker,
PrivacyDotViewController dotViewController,
@@ -327,7 +326,6 @@
mContext = context;
mMainExecutor = mainExecutor;
mSecureSettings = secureSettings;
- mTunerService = tunerService;
mUserTracker = userTracker;
mDisplayTracker = displayTracker;
mDotViewController = dotViewController;
@@ -365,16 +363,47 @@
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 @@
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 @@
// - 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 @@
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 @@
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 @@
}
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 @@
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 @@
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 @@
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 @@
if (DEBUG_DISABLE_SCREEN_DECORATIONS) {
return;
}
+ ipw.println("mDebug:" + mDebug);
ipw.println("mIsPrivacyDotEnabled:" + isPrivacyDotEnabled());
ipw.println("shouldOptimizeOverlayVisibility:" + shouldOptimizeVisibility());
@@ -1071,6 +1114,7 @@
}
}
mRoundedCornerResDelegate.dump(pw, args);
+ mDebugRoundedCornerDelegate.dump(pw);
}
@VisibleForTesting
@@ -1093,8 +1137,9 @@
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 @@
}
private boolean hasRoundedCorners() {
- return mRoundedCornerFactory.getHasProviders();
+ return mRoundedCornerFactory.getHasProviders()
+ || mDebugRoundedCornerFactory.getHasProviders();
}
private boolean shouldOptimizeVisibility() {
@@ -1170,41 +1216,17 @@
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 @@
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 @@
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 @@
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 @@
// 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 @@
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 806d1af..3ca74ac 100644
--- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
@@ -81,9 +81,6 @@
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 @@
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 527ce12..0000000
--- 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 70c39df..453d1d1 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
@@ -54,7 +54,7 @@
* 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 @@
*/
private CoreStartable[] mServices;
private boolean mServicesStarted;
- private SystemUIAppComponentFactory.ContextAvailableCallback mContextAvailableCallback;
+ private SystemUIAppComponentFactoryBase.ContextAvailableCallback mContextAvailableCallback;
private SysUIComponent mSysUIComponent;
private SystemUIInitializer mInitializer;
@@ -366,7 +366,7 @@
@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 b9454e8..0000000
--- 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 50e0399..6cf9eff 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.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 @@
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 @@
// 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 c3bb423..fd3c158 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 @@
}
/**
- * 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 4158390..2f6a68c 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 @@
// 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 @@
}
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 e2b85fa..2a14dc8 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 @@
mModeSwitchesController.setClickListenerDelegate(
displayId -> mHandler.post(() -> {
- showMagnificationSettingsPanel(displayId);
+ toggleSettingsPanelVisibility(displayId);
}));
}
@@ -254,11 +254,11 @@
}
@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 @@
@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 a67f706..e7eab7e 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 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 @@
// 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 @@
private boolean mAllowDiagonalScrolling = false;
private boolean mEditSizeEnable = false;
+ private boolean mSettingsPanelVisibility = false;
@Nullable
private final MirrorWindowControl mMirrorWindowControl;
@@ -1399,6 +1403,8 @@
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 @@
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 3b1d695..31b0f056 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 @@
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 @@
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 @@
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 @@
});
}
};
+ 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 @@
// Unregister observer before removing view
mSecureSettings.unregisterContentObserver(mMagnificationCapabilityObserver);
+ mSecureSettings.unregisterContentObserver(mMagnificationScaleObserver);
mWindowManager.removeView(mSettingView);
mIsVisible = false;
if (resetPosition) {
@@ -311,6 +328,14 @@
mCallback.onSettingsPanelVisibilityChanged(/* shown= */ false);
}
+ public void toggleSettingsPanelVisibility() {
+ if (!mIsVisible) {
+ showSettingPanel();
+ } else {
+ hideSettingPanel();
+ }
+ }
+
public void showSettingPanel() {
showSettingPanel(true);
}
@@ -320,7 +345,13 @@
}
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 @@
private void showSettingPanel(boolean resetPosition) {
if (!mIsVisible) {
updateUIControlsIfNeeded();
+ setScaleSeekbar(getMagnificationScale());
if (resetPosition) {
mDraggableWindowBounds.set(getDraggableWindowBounds());
mParams.x = mDraggableWindowBounds.right;
@@ -349,6 +381,10 @@
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 @@
}
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 @@
}
};
- 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 @@
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 @@
// 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 @@
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 @@
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 @@
@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 7f4e7844..225a2f7 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.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 3f41a76..6ba40d6 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.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 @@
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 e79b3f4..a910ab5 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.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 @@
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 @@
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 @@
@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.putString(
+ secureSettings.getStringForUser(
Settings.Secure.ACCESSIBILITY_FONT_SCALING_HAS_BEEN_CHANGED,
- ON
+ userTracker.userId
+ ) != ON
+ ) {
+ secureSettings.putStringForUser(
+ Settings.Secure.ACCESSIBILITY_FONT_SCALING_HAS_BEEN_CHANGED,
+ 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 665a398..2b83e6b 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 @@
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 c684dc5..0530aed 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 @@
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 3984627..20e82f7 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 @@
/**
* 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 @@
/**
* 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 6f008c3..1016b6b 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 @@
/** 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 0000000..3c74bf4
--- /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/battery/BatteryMeterView.java b/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterView.java
index 263df33..c1238d9 100644
--- a/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterView.java
+++ b/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterView.java
@@ -348,7 +348,7 @@
updatePercentText();
addView(mBatteryPercentView, new LayoutParams(
LayoutParams.WRAP_CONTENT,
- LayoutParams.MATCH_PARENT));
+ LayoutParams.WRAP_CONTENT));
}
} else {
if (showing) {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index 5b746f1b..76e48e9 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 @@
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 @@
}
}
+ 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;
+ mCurrentDialog.dismissWithoutCallback(true /* animate */);
+ mCurrentDialog = null;
- for (Callback cb : mCallbacks) {
- cb.onBiometricPromptDismissed();
- }
+ for (Callback cb : mCallbacks) {
+ cb.onBiometricPromptDismissed();
+ }
- if (mReceiver != null) {
- mReceiver.onDialogDismissed(
- BiometricPrompt.DISMISSED_REASON_USER_CANCEL,
- null /* credentialAttestation */);
- mReceiver = null;
- }
- }
+ 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 @@
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 6db266f..9d8dcc1 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 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.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.dagger.SysUISingleton;
import com.android.systemui.statusbar.policy.KeyguardStateController;
-
import java.util.Optional;
import javax.inject.Inject;
@@ -69,6 +73,8 @@
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 @@
}
};
+ 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 @@
mBroadcastReceiver = biometricNotificationBroadcastReceiver;
mFingerprintReEnrollNotification = fingerprintReEnrollNotification.orElse(
new FingerprintReEnrollNotificationImpl());
+ mFingerprintManager = fingerprintManager;
+ mFaceManager = faceManager;
}
@Override
@@ -148,9 +171,16 @@
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 @@
}
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 d48b9c33..9d0cde1 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.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 2eb5330..0dc7974 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.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 @@
@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 @@
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 @@
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 @@
@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 @@
mSecureSettings = secureSettings;
mUdfpsUtils = udfpsUtils;
mInputManager = inputManager;
+ mUdfpsKeyguardAccessibilityDelegate = udfpsKeyguardAccessibilityDelegate;
mTouchProcessor = mFeatureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION)
? singlePointerTouchProcessor : null;
@@ -952,6 +957,10 @@
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 @@
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 cabe900..e542147 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.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 @@
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 @@
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 0000000..fb7b56e
--- /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 3fc3e82..9bafeec 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.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.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 @@
featureFlags: FeatureFlags,
private val primaryBouncerInteractor: PrimaryBouncerInteractor,
private val alternateBouncerInteractor: AlternateBouncerInteractor,
+ private val udfpsKeyguardAccessibilityDelegate: UdfpsKeyguardAccessibilityDelegate,
) :
UdfpsAnimationViewController<UdfpsKeyguardViewLegacy>(
view,
@@ -300,7 +302,10 @@
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 056d692..b916810c 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 @@
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 @@
.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 @@
frameInfo -> new PorterDuffColorFilter(mTextColorPrimary,
PorterDuff.Mode.SRC_ATOP)
);
+ mOnFinishInflateRunnable.run();
}
};
}
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/data/factory/BouncerMessageFactory.kt b/packages/SystemUI/src/com/android/systemui/bouncer/data/factory/BouncerMessageFactory.kt
new file mode 100644
index 0000000..3206c00
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/data/factory/BouncerMessageFactory.kt
@@ -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 com.android.systemui.bouncer.data.factory
+
+import android.annotation.IntDef
+import com.android.keyguard.KeyguardSecurityModel
+import com.android.keyguard.KeyguardSecurityModel.SecurityMode
+import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_AFTER_LOCKOUT
+import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_DEFAULT
+import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_DEVICE_ADMIN
+import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_FACE_LOCKED_OUT
+import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_FINGERPRINT_LOCKED_OUT
+import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_INCORRECT_FACE_INPUT
+import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_INCORRECT_FINGERPRINT_INPUT
+import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_INCORRECT_PRIMARY_AUTH_INPUT
+import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_NONE
+import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_NON_STRONG_BIOMETRIC_TIMEOUT
+import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_PREPARE_FOR_UPDATE
+import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_PRIMARY_AUTH_LOCKED_OUT
+import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_RESTART
+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 javax.inject.Inject
+
+@SysUISingleton
+class BouncerMessageFactory
+@Inject
+constructor(
+ private val updateMonitor: KeyguardUpdateMonitor,
+ private val securityModel: KeyguardSecurityModel,
+) {
+
+ fun createFromPromptReason(
+ @BouncerPromptReason reason: Int,
+ userId: Int,
+ ): BouncerMessageModel? {
+ val pair =
+ getBouncerMessage(
+ reason,
+ securityModel.getSecurityMode(userId),
+ updateMonitor.isFingerprintAllowedInBouncer
+ )
+ return pair?.let {
+ BouncerMessageModel(
+ message = Message(messageResId = pair.first),
+ secondaryMessage = Message(messageResId = pair.second)
+ )
+ }
+ }
+
+ fun createFromString(
+ primaryMsg: String? = null,
+ secondaryMsg: String? = null
+ ): BouncerMessageModel =
+ BouncerMessageModel(
+ message = primaryMsg?.let { Message(message = it) },
+ secondaryMessage = secondaryMsg?.let { Message(message = it) },
+ )
+
+ /**
+ * Helper method that provides the relevant bouncer message that should be shown for different
+ * scenarios indicated by [reason]. [securityMode] & [fpAllowedInBouncer] parameters are used to
+ * provide a more specific message.
+ */
+ private fun getBouncerMessage(
+ @BouncerPromptReason reason: Int,
+ securityMode: SecurityMode,
+ fpAllowedInBouncer: Boolean = false
+ ): Pair<Int, Int>? {
+ return when (reason) {
+ PROMPT_REASON_RESTART -> authRequiredAfterReboot(securityMode)
+ PROMPT_REASON_TIMEOUT -> authRequiredAfterPrimaryAuthTimeout(securityMode)
+ PROMPT_REASON_DEVICE_ADMIN -> authRequiredAfterAdminLockdown(securityMode)
+ PROMPT_REASON_USER_REQUEST -> authRequiredAfterUserLockdown(securityMode)
+ PROMPT_REASON_AFTER_LOCKOUT -> biometricLockout(securityMode)
+ PROMPT_REASON_PREPARE_FOR_UPDATE -> authRequiredForUnattendedUpdate(securityMode)
+ PROMPT_REASON_FINGERPRINT_LOCKED_OUT -> fingerprintUnlockUnavailable(securityMode)
+ PROMPT_REASON_FACE_LOCKED_OUT -> faceUnlockUnavailable(securityMode)
+ PROMPT_REASON_INCORRECT_PRIMARY_AUTH_INPUT ->
+ if (fpAllowedInBouncer) incorrectSecurityInputWithFingerprint(securityMode)
+ else incorrectSecurityInput(securityMode)
+ PROMPT_REASON_NON_STRONG_BIOMETRIC_TIMEOUT ->
+ if (fpAllowedInBouncer) nonStrongAuthTimeoutWithFingerprintAllowed(securityMode)
+ else nonStrongAuthTimeout(securityMode)
+ PROMPT_REASON_TRUSTAGENT_EXPIRED ->
+ if (fpAllowedInBouncer) trustAgentDisabledWithFingerprintAllowed(securityMode)
+ else trustAgentDisabled(securityMode)
+ PROMPT_REASON_INCORRECT_FACE_INPUT ->
+ if (fpAllowedInBouncer) incorrectFaceInputWithFingerprintAllowed(securityMode)
+ else incorrectFaceInput(securityMode)
+ PROMPT_REASON_INCORRECT_FINGERPRINT_INPUT -> incorrectFingerprintInput(securityMode)
+ PROMPT_REASON_DEFAULT ->
+ if (fpAllowedInBouncer) defaultMessageWithFingerprint(securityMode)
+ else defaultMessage(securityMode)
+ PROMPT_REASON_PRIMARY_AUTH_LOCKED_OUT -> primaryAuthLockedOut(securityMode)
+ else -> null
+ }
+ }
+}
+
+@Retention(AnnotationRetention.SOURCE)
+@IntDef(
+ PROMPT_REASON_TIMEOUT,
+ PROMPT_REASON_DEVICE_ADMIN,
+ PROMPT_REASON_USER_REQUEST,
+ PROMPT_REASON_AFTER_LOCKOUT,
+ PROMPT_REASON_PREPARE_FOR_UPDATE,
+ PROMPT_REASON_NON_STRONG_BIOMETRIC_TIMEOUT,
+ PROMPT_REASON_TRUSTAGENT_EXPIRED,
+ PROMPT_REASON_INCORRECT_PRIMARY_AUTH_INPUT,
+ PROMPT_REASON_INCORRECT_FACE_INPUT,
+ PROMPT_REASON_INCORRECT_FINGERPRINT_INPUT,
+ PROMPT_REASON_FACE_LOCKED_OUT,
+ PROMPT_REASON_FINGERPRINT_LOCKED_OUT,
+ PROMPT_REASON_DEFAULT,
+ PROMPT_REASON_NONE,
+ PROMPT_REASON_RESTART,
+ PROMPT_REASON_PRIMARY_AUTH_LOCKED_OUT,
+)
+annotation class BouncerPromptReason
+
+private fun defaultMessage(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)
+ else -> Pair(0, 0)
+ }
+}
+
+private fun defaultMessageWithFingerprint(securityMode: SecurityMode): Pair<Int, Int> {
+ return when (securityMode) {
+ 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(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(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(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(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(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, 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, 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, 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, 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, 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, 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, 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(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, 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, 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, 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(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(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
similarity index 98%
rename from packages/SystemUI/src/com/android/systemui/keyguard/bouncer/data/repository/BouncerMessageRepository.kt
rename to packages/SystemUI/src/com/android/systemui/bouncer/data/repository/BouncerMessageRepository.kt
index c4400bc..7e420cf 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_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
similarity index 96%
rename from packages/SystemUI/src/com/android/systemui/bouncer/data/repo/BouncerRepository.kt
rename to packages/SystemUI/src/com/android/systemui/bouncer/data/repository/BouncerRepository.kt
index 49a0a3c..ff896fa 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
similarity index 97%
rename from packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBouncerRepository.kt
rename to packages/SystemUI/src/com/android/systemui/bouncer/data/repository/KeyguardBouncerRepository.kt
index 5d15e69..918e168 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
similarity index 96%
rename from packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/AlternateBouncerInteractor.kt
rename to packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractor.kt
index 148d425..2abdb84 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 1d2fce7..256c635 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 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 @@
* 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 @@
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 @@
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
similarity index 90%
rename from packages/SystemUI/src/com/android/systemui/keyguard/bouncer/domain/interactor/BouncerMessageAuditLogger.kt
rename to packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageAuditLogger.kt
index 56f81fc..497747f 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
similarity index 95%
rename from packages/SystemUI/src/com/android/systemui/keyguard/bouncer/domain/interactor/BouncerMessageInteractor.kt
rename to packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractor.kt
index 1754d93..d06dd8e 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
similarity index 94%
rename from packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerCallbackInteractor.kt
rename to packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerCallbackInteractor.kt
index 3099a49..5fbe99a 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
similarity index 92%
rename from packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractor.kt
rename to packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractor.kt
index 54bc349..c486603 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.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.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 @@
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 @@
}
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 @@
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
similarity index 96%
rename from packages/SystemUI/src/com/android/systemui/keyguard/shared/constants/KeyguardBouncerConstants.kt
rename to packages/SystemUI/src/com/android/systemui/bouncer/shared/constants/KeyguardBouncerConstants.kt
index c45faf0..9f17811 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
similarity index 94%
rename from packages/SystemUI/src/com/android/systemui/keyguard/shared/model/BouncerCallbackActionsModel.kt
rename to packages/SystemUI/src/com/android/systemui/bouncer/shared/model/BouncerCallbackActionsModel.kt
index 81cf5b4..afddd39 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
similarity index 95%
rename from packages/SystemUI/src/com/android/systemui/keyguard/bouncer/shared/model/BouncerMessageModel.kt
rename to packages/SystemUI/src/com/android/systemui/bouncer/shared/model/BouncerMessageModel.kt
index 46e8873..0e9e962 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
similarity index 93%
rename from packages/SystemUI/src/com/android/systemui/keyguard/shared/model/BouncerShowMessageModel.kt
rename to packages/SystemUI/src/com/android/systemui/bouncer/shared/model/BouncerShowMessageModel.kt
index 05cdeaa..1878572 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
similarity index 97%
rename from packages/SystemUI/src/com/android/systemui/keyguard/bouncer/ui/BouncerMessageView.kt
rename to packages/SystemUI/src/com/android/systemui/bouncer/ui/BouncerMessageView.kt
index 4dc52ff..47fac2b 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
similarity index 97%
rename from packages/SystemUI/src/com/android/systemui/keyguard/data/BouncerView.kt
rename to packages/SystemUI/src/com/android/systemui/bouncer/ui/BouncerView.kt
index 871a3ff..dc00321 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
similarity index 94%
rename from packages/SystemUI/src/com/android/systemui/keyguard/data/BouncerViewModule.kt
rename to packages/SystemUI/src/com/android/systemui/bouncer/ui/BouncerViewModule.kt
index 390c54e..0cbfb68 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 0000000..5a59012
--- /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
similarity index 88%
rename from packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt
rename to packages/SystemUI/src/com/android/systemui/bouncer/ui/binder/KeyguardBouncerViewBinder.kt
index c1aefc7..34e934b 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.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 @@
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 @@
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 984d9ab..527fe6e 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 @@
.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 @@
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
similarity index 92%
rename from packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBouncerViewModel.kt
rename to packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModel.kt
index 9602888..6ba8439 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 55929b5..0146e40 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 @@
/** 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 d9ef75d..700703e 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 @@
/** 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 5c0fd92..1944c74 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 @@
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.WhileSubscribed(),
- initialValue = 0 to 0,
+ started = SharingStarted.Eagerly,
+ initialValue = computeHintedPinLength(interactor.authenticationMethod.value),
)
- private var resetPinJob: Job? = null
+
+ /** Appearance of the backspace button. */
+ val backspaceButtonAppearance: StateFlow<ActionButtonAppearance> =
+ combine(interactor.authenticationMethod, mutablePinEntries) { authMethod, enteredPin ->
+ computeBackspaceButtonAppearance(authMethod, enteredPin)
+ }
+ .stateIn(
+ scope = applicationScope,
+ started = SharingStarted.Eagerly,
+ initialValue =
+ computeBackspaceButtonAppearance(
+ interactor.authenticationMethod.value,
+ mutablePinEntries.value
+ ),
+ )
+
+ /** Appearance of the confirm button. */
+ val confirmButtonAppearance: StateFlow<ActionButtonAppearance> =
+ interactor.authenticationMethod
+ .map { authMethod -> computeConfirmButtonAppearance(authMethod) }
+ .stateIn(
+ scope = applicationScope,
+ started = SharingStarted.Eagerly,
+ initialValue =
+ computeConfirmButtonAppearance(interactor.authenticationMethod.value),
+ )
/** Notifies that the UI has been shown to the user. */
fun onShown() {
@@ -62,47 +82,108 @@
/** 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()
}
- companion object {
- @VisibleForTesting const val BACKSPACE_LONG_PRESS_DELAY_MS = 80L
+ private fun isAutoConfirmEnabled(authMethodModel: AuthenticationMethodModel): Boolean {
+ return (authMethodModel as? AuthenticationMethodModel.Pin)?.autoConfirm == true
}
+
+ private fun autoConfirmPinLength(authMethodModel: AuthenticationMethodModel): Int? {
+ if (!isAutoConfirmEnabled(authMethodModel)) return null
+
+ return (authMethodModel as? AuthenticationMethodModel.Pin)?.code?.size
+ }
+
+ private fun computeHintedPinLength(authMethodModel: AuthenticationMethodModel): Int? {
+ // Hinting is enabled for 6-digit codes only
+ return autoConfirmPinLength(authMethodModel).takeIf { it == HINTING_PASSCODE_LENGTH }
+ }
+
+ private fun computeBackspaceButtonAppearance(
+ authMethodModel: AuthenticationMethodModel,
+ enteredPin: List<EnteredKey>
+ ): ActionButtonAppearance {
+ val isAutoConfirmEnabled = isAutoConfirmEnabled(authMethodModel)
+ val isEmpty = enteredPin.isEmpty()
+
+ return when {
+ isAutoConfirmEnabled && isEmpty -> ActionButtonAppearance.Hidden
+ isAutoConfirmEnabled -> ActionButtonAppearance.Subtle
+ else -> ActionButtonAppearance.Shown
+ }
+ }
+ private fun computeConfirmButtonAppearance(
+ authMethodModel: AuthenticationMethodModel
+ ): ActionButtonAppearance {
+ return if (isAutoConfirmEnabled(authMethodModel)) {
+ ActionButtonAppearance.Hidden
+ } else {
+ ActionButtonAppearance.Shown
+ }
+ }
+}
+
+/** Appearance of pin-pad action buttons. */
+enum class ActionButtonAppearance {
+ /** Button must not be shown. */
+ Hidden,
+ /** Button is shown, but with no background to make it less prominent. */
+ Subtle,
+ /** Button is shown. */
+ Shown,
+}
+
+/** Auto-confirm passcodes of exactly 6 digits show a length hint, see http://shortn/_IXlmSNbDh6 */
+private const val HINTING_PASSCODE_LENGTH = 6
+
+private var nextSequenceNumber = 1
+
+/**
+ * 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 b2bcb05..25ccc16 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 @@
|| 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 d6c85fb..809d5b2 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 @@
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 @@
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 757ebf4..ffd836b 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 @@
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.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.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.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 @@
/**
* 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 @@
private final FeatureFlags mFeatureFlags;
private final Executor mBgExecutor;
private final ClipboardImageLoader mClipboardImageLoader;
+ private final ClipboardTransitionExecutor mTransitionExecutor;
private final ClipboardOverlayView mView;
@@ -179,10 +185,12 @@
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 @@
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 @@
});
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 @@
@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 @@
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 @@
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 @@
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 @@
}
}
});
- 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 @@
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 28c57d3..a76d2ea 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 @@
});
}
+ 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 @@
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 0000000..0b8e83e
--- /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 de3a990..4538a6c 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.LinearLayout;
import android.widget.SeekBar;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.R;
/**
@@ -37,12 +38,14 @@
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 @@
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 @@
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 @@
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 @@
}
/**
+ * 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 @@
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 4173bdc..b15c60e 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 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 @@
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 18bd467..ccbde01 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 @@
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 2262d8a..f68bd49 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.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 @@
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 52355f3..d82bf58 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 @@
/**
* 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 25634f0..76002d3 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.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 @@
@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 5bcf32a..9ab9e7a 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 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.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.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.footer.dagger.FooterActionsModule;
import com.android.systemui.recents.Recents;
import com.android.systemui.retail.dagger.RetailModeModule;
+import com.android.systemui.scene.SceneContainerFrameworkModule;
import com.android.systemui.screenrecord.ScreenRecordModule;
import com.android.systemui.screenshot.dagger.ScreenshotModule;
import com.android.systemui.security.data.repository.SecurityRepositoryModule;
@@ -88,6 +88,7 @@
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.connectivity.ConnectivityModule;
+import com.android.systemui.statusbar.disableflags.dagger.DisableFlagsModule;
import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
import com.android.systemui.statusbar.notification.NotifPipelineFlags;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
@@ -160,7 +161,6 @@
BiometricsModule.class,
BouncerViewModule.class,
ClipboardOverlayModule.class,
- ClockInfoModule.class,
ClockRegistryModule.class,
CommonRepositoryModule.class,
ConnectivityModule.class,
@@ -168,6 +168,7 @@
DreamModule.class,
ControlsModule.class,
DemoModeModule.class,
+ DisableFlagsModule.class,
FalsingModule.class,
FlagsModule.class,
SystemPropertiesFlagsModule.class,
@@ -186,6 +187,7 @@
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 b71871e..d70c57f 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 @@
/**
* 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 0000000..4069bc7d
--- /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 8b4aeef..c64766a 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.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 @@
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 @@
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 ff07bb6..78ac453 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.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 d509015..553405f 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 @@
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 b141db1..c2421dc 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 @@
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 @@
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 @@
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 @@
} 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 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 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 @@
* @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 @@
* @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 @@
* @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 @@
* @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 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 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 7c1bfed..1865c38 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 @@
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 c954f98..3a28408 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 @@
*/
@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 @@
private final Executor mMainExecutor;
private final List<DreamOverlayStatusBarItemsProvider.StatusBarItem> mExtraStatusBarItems =
new ArrayList<>();
+ private final DreamLogger mLogger;
private boolean mIsAttached;
@@ -157,7 +160,8 @@
StatusBarWindowStateController statusBarWindowStateController,
DreamOverlayStatusBarItemsProvider statusBarItemsProvider,
DreamOverlayStateController dreamOverlayStateController,
- UserTracker userTracker) {
+ UserTracker userTracker,
+ DreamLogger dreamLogger) {
super(view);
mResources = resources;
mMainExecutor = mainExecutor;
@@ -173,6 +177,7 @@
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 @@
@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 570132e1..1cd3774 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 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 @@
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 @@
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 @@
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 @@
mCentralSurfaces = centralSurfaces;
mScrimManager = scrimManager;
mNotificationShadeWindowController = notificationShadeWindowController;
+ mLockPatternUtils = lockPatternUtils;
+ mUserTracker = userTracker;
mBouncerZoneScreenPercentage = swipeRegionPercentage;
mFlingAnimationUtils = flingAnimationUtils;
mFlingAnimationUtilsClosing = flingAnimationUtilsClosing;
@@ -347,6 +360,11 @@
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 4b03fd3..75284fc 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.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 @@
*
* 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 @@
* # 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 @@
* $ <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 @@
"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 @@
}
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)
+ val targets = dumpManager.getDumpables()
+ for (target in targets) {
+ if (target.priority == DumpPriority.NORMAL) {
+ dumpDumpable(target, 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 dumpDumpables(pw: PrintWriter, args: ParsedArgs) {
- if (args.listOnly) {
- dumpManager.listDumpables(pw)
- } else {
- dumpManager.dumpDumpables(pw, args.rawArgs)
+ 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 dumpBuffers(pw: PrintWriter, args: ParsedArgs) {
- if (args.listOnly) {
- dumpManager.listBuffers(pw)
- } else {
- dumpManager.dumpBuffers(pw, args.tailLength)
- }
- }
-
- 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 @@
}
}
- 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 @@
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 @@
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 @@
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 @@
}
}
+ 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 2d57633..c924df6 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 @@
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 @@
@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 @@
* 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 @@
throw IllegalArgumentException("'$name' is already registered")
}
- dumpables[name] = RegisteredDumpable(name, module, priority)
+ dumpables[name] = DumpableEntry(module, name, priority)
}
/**
@@ -110,217 +113,62 @@
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")
}
+ buffers[name] = LogBufferEntry(buffer, name)
+ }
+
+ /** Register a [TableLogBuffer] to be dumped during a bugreport */
+ @Synchronized
+ fun registerTableLogBuffer(name: String, buffer: TableLogBuffer) {
+ 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)
+ tableLogBuffers[name] = TableLogBufferEntry(buffer, name)
}
- /**
- * 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 getDumpables(): Collection<DumpableEntry> = dumpables.values.toList()
+
+ @Synchronized fun getLogBuffers(): Collection<LogBufferEntry> = buffers.values.toList()
@Synchronized
- fun dumpProtoTarget(
- target: String,
- protoDump: SystemUIProtoDump,
- args: Array<String>
- ) {
- findBestProtoTargetMatch(dumpables, target)?.let {
- dumpProtoDumpable(it, protoDump, args)
- }
- }
-
- @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)
- }
- }
-
- /**
- * Dump all the instances of [Dumpable].
- */
- @Synchronized
- fun dumpDumpables(pw: PrintWriter, args: Array<String>) {
- for (module in dumpables.values) {
- dumpDumpable(module, pw, args)
- }
- }
-
- /**
- * Dumps the names of all registered dumpables (one per line)
- */
- @Synchronized
- fun listDumpables(pw: PrintWriter) {
- for (module in dumpables.values) {
- pw.println(module.name)
- }
- }
-
- /**
- * Dumps all registered [LogBuffer]s to [pw]
- */
- @Synchronized
- fun dumpBuffers(pw: PrintWriter, tailLength: Int) {
- for (buffer in buffers.values) {
- dumpBuffer(buffer, pw, tailLength)
- }
- }
-
- /**
- * 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 0000000..cd3e1bb
--- /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 2d5c9ae..bd43302 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.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 @@
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 @@
* 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 @@
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 @@
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 @@
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 @@
}
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 @@
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 31f7e88..b50c6de 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 @@
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,20 @@
/** 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)
+
+
+ /** Keyguard Migration */
/** Migrate the indication area to the new keyguard root view. */
// TODO(b/280067944): Tracking bug.
@JvmField
- val MIGRATE_INDICATION_AREA = unreleasedFlag(236, "migrate_indication_area")
+ val MIGRATE_INDICATION_AREA = unreleasedFlag(236, "migrate_indication_area", teamfood = true)
+
+ /** Migrate the lock icon view to the new keyguard root view. */
+ // TODO(b/286552209): Tracking bug.
+ @JvmField
+ val MIGRATE_LOCK_ICON = unreleasedFlag(238, "migrate_lock_icon")
/** Whether to listen for fingerprint authentication over keyguard occluding activities. */
// TODO(b/283260512): Tracking bug.
@@ -268,6 +272,10 @@
@JvmField
val KEYGUARD_TALKBACK_FIX = releasedFlag(238, "keyguard_talkback_fix")
+ // TODO(b/287268101): Tracking bug.
+ @JvmField
+ val TRANSIT_CLOCK = unreleasedFlag(239, "lockscreen_custom_transit_clock")
+
// 300 - power menu
// TODO(b/254512600): Tracking Bug
@JvmField val POWER_MENU_LITE = releasedFlag(300, "power_menu_lite")
@@ -364,7 +372,7 @@
// 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 +454,6 @@
// 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 +490,7 @@
@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 +548,7 @@
// 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 +583,7 @@
// 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 +642,9 @@
// 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")
@@ -671,8 +679,7 @@
@JvmField
val ENABLE_USI_BATTERY_NOTIFICATIONS =
releasedFlag(2302, "enable_usi_battery_notifications")
- @JvmField val ENABLE_STYLUS_EDUCATION =
- unreleasedFlag(2303, "enable_stylus_education", teamfood = true)
+ @JvmField val ENABLE_STYLUS_EDUCATION = releasedFlag(2303, "enable_stylus_education")
// 2400 - performance tools and debugging info
// TODO(b/238923086): Tracking Bug
@@ -711,23 +718,17 @@
// 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 76322ad..040ee79 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.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 @@
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 @@
@Main Handler handler,
PackageManager packageManager,
Optional<CentralSurfaces> centralSurfacesOptional,
+ ShadeController shadeController,
KeyguardUpdateMonitor keyguardUpdateMonitor,
DialogLaunchAnimator dialogLaunchAnimator) {
mContext = context;
@@ -394,6 +397,7 @@
mSmallestScreenWidthDp = resources.getConfiguration().smallestScreenWidthDp;
mOrientation = resources.getConfiguration().orientation;
mCentralSurfacesOptional = centralSurfacesOptional;
+ mShadeController = shadeController;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mDialogLaunchAnimator = dialogLaunchAnimator;
@@ -702,7 +706,9 @@
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 @@
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 @@
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 @@
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 @@
mKeyguardShowing = keyguardShowing;
mUiEventLogger = uiEventLogger;
mCentralSurfacesOptional = centralSurfacesOptional;
+ mShadeController = shadeController;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mLockPatternUtils = lockPatternUtils;
mGestureDetector = new GestureDetector(mContext, mGestureListener);
@@ -2348,12 +2357,10 @@
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 757afb6..67c85bd 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 @@
*/
@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 @@
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;
-
- @Inject
- public KeyguardLifecyclesDispatcher(ScreenLifecycle screenLifecycle,
- WakefulnessLifecycle wakefulnessLifecycle) {
- mScreenLifecycle = screenLifecycle;
- mWakefulnessLifecycle = wakefulnessLifecycle;
+ @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 {
}
- void dispatch(int what) {
+ 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(
+ @Main Looper mainLooper,
+ ScreenLifecycle screenLifecycle,
+ WakefulnessLifecycle wakefulnessLifecycle) {
+ mHandler = new KeyguardLifecycleHandler(mainLooper, screenLifecycle, wakefulnessLifecycle);
+ }
+
+ void dispatch(@KeyguardLifecycleMessageType int what) {
mHandler.obtainMessage(what).sendToTarget();
}
@@ -60,7 +94,7 @@
* @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 @@
* @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) {
+ 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:
- 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);
+ 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 573de97..0511314 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.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 @@
/**
* 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 @@
protected boolean mDozing;
private int mStatusBarState;
private boolean mMediaIsVisible;
- private SystemUIAppComponentFactory.ContextAvailableCallback mContextAvailableCallback;
+ private SystemUIAppComponentFactoryBase.ContextAvailableCallback mContextAvailableCallback;
@Inject
WakeLockLogger mWakeLockLogger;
@@ -533,7 +533,7 @@
@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 68e72c5..3646144 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 @@
}
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 0000000..e8881a4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.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.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() {
+ val notificationPanel =
+ notificationShadeWindowView.requireViewById(R.id.notification_panel) as ViewGroup
+ bindIndicationArea(notificationPanel)
+ bindLockIconView(notificationPanel)
+ }
+
+ 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
+ )
+ }
+
+ private fun bindLockIconView(legacyParent: ViewGroup) {
+ if (featureFlags.isEnabled(Flags.MIGRATE_LOCK_ICON)) {
+ legacyParent.requireViewById<View>(R.id.lock_icon_view).let {
+ legacyParent.removeView(it)
+ }
+ } else {
+ keyguardRootView.findViewById<View?>(R.id.lock_icon_view)?.let {
+ keyguardRootView.removeView(it)
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index bc41ab31..c629ebf 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.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.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 @@
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 @@
* 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 @@
/** 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 @@
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 @@
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 void userActivity() {
- mPM.userActivity(SystemClock.uptimeMillis(), false);
+ mPM.userActivity(mSystemClock.uptimeMillis(), false);
}
private void setupLocked() {
@@ -1584,7 +1597,7 @@
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 @@
// 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 @@
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 @@
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 @@
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 @@
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 @@
// 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 @@
// 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 */);
});
@@ -2753,8 +2764,8 @@
// 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,
+ if (mDreamOverlayShowing || mUpdateMonitor.isDreaming()) {
+ mPM.wakeUp(mSystemClock.uptimeMillis(), PowerManager.WAKE_REASON_GESTURE,
"com.android.systemui:UNLOCK_DREAMING");
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/bouncer/data/factory/BouncerMessageFactory.kt b/packages/SystemUI/src/com/android/systemui/keyguard/bouncer/data/factory/BouncerMessageFactory.kt
deleted file mode 100644
index 11bc88a..0000000
--- a/packages/SystemUI/src/com/android/systemui/keyguard/bouncer/data/factory/BouncerMessageFactory.kt
+++ /dev/null
@@ -1,322 +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.keyguard.bouncer.data.factory
-
-import android.annotation.IntDef
-import com.android.keyguard.KeyguardSecurityModel
-import com.android.keyguard.KeyguardSecurityModel.SecurityMode
-import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_AFTER_LOCKOUT
-import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_DEFAULT
-import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_DEVICE_ADMIN
-import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_FACE_LOCKED_OUT
-import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_FINGERPRINT_LOCKED_OUT
-import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_INCORRECT_FACE_INPUT
-import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_INCORRECT_FINGERPRINT_INPUT
-import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_INCORRECT_PRIMARY_AUTH_INPUT
-import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_NONE
-import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_NON_STRONG_BIOMETRIC_TIMEOUT
-import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_PREPARE_FOR_UPDATE
-import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_PRIMARY_AUTH_LOCKED_OUT
-import com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_RESTART
-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.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.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
-class BouncerMessageFactory
-@Inject
-constructor(
- private val updateMonitor: KeyguardUpdateMonitor,
- private val securityModel: KeyguardSecurityModel,
-) {
-
- fun createFromPromptReason(
- @BouncerPromptReason reason: Int,
- userId: Int,
- ): BouncerMessageModel? {
- val pair =
- getBouncerMessage(
- reason,
- securityModel.getSecurityMode(userId),
- updateMonitor.isFingerprintAllowedInBouncer
- )
- return pair?.let {
- BouncerMessageModel(
- message = Message(messageResId = pair.first),
- secondaryMessage = Message(messageResId = pair.second)
- )
- }
- }
-
- fun createFromString(
- primaryMsg: String? = null,
- secondaryMsg: String? = null
- ): BouncerMessageModel =
- BouncerMessageModel(
- message = primaryMsg?.let { Message(message = it) },
- secondaryMessage = secondaryMsg?.let { Message(message = it) },
- )
-
- /**
- * Helper method that provides the relevant bouncer message that should be shown for different
- * scenarios indicated by [reason]. [securityMode] & [fpAllowedInBouncer] parameters are used to
- * provide a more specific message.
- */
- private fun getBouncerMessage(
- @BouncerPromptReason reason: Int,
- securityMode: SecurityMode,
- fpAllowedInBouncer: Boolean = false
- ): Pair<Int, Int>? {
- return when (reason) {
- PROMPT_REASON_RESTART -> authRequiredAfterReboot(securityMode)
- PROMPT_REASON_TIMEOUT -> authRequiredAfterPrimaryAuthTimeout(securityMode)
- PROMPT_REASON_DEVICE_ADMIN -> authRequiredAfterAdminLockdown(securityMode)
- PROMPT_REASON_USER_REQUEST -> authRequiredAfterUserLockdown(securityMode)
- PROMPT_REASON_AFTER_LOCKOUT -> biometricLockout(securityMode)
- PROMPT_REASON_PREPARE_FOR_UPDATE -> authRequiredForUnattendedUpdate(securityMode)
- PROMPT_REASON_FINGERPRINT_LOCKED_OUT -> fingerprintUnlockUnavailable(securityMode)
- PROMPT_REASON_FACE_LOCKED_OUT -> faceUnlockUnavailable(securityMode)
- PROMPT_REASON_INCORRECT_PRIMARY_AUTH_INPUT ->
- if (fpAllowedInBouncer) incorrectSecurityInputWithFingerprint(securityMode)
- else incorrectSecurityInput(securityMode)
- PROMPT_REASON_NON_STRONG_BIOMETRIC_TIMEOUT ->
- if (fpAllowedInBouncer) nonStrongAuthTimeoutWithFingerprintAllowed(securityMode)
- else nonStrongAuthTimeout(securityMode)
- PROMPT_REASON_TRUSTAGENT_EXPIRED ->
- if (fpAllowedInBouncer) trustAgentDisabledWithFingerprintAllowed(securityMode)
- else trustAgentDisabled(securityMode)
- PROMPT_REASON_INCORRECT_FACE_INPUT ->
- if (fpAllowedInBouncer) incorrectFaceInputWithFingerprintAllowed(securityMode)
- else incorrectFaceInput(securityMode)
- PROMPT_REASON_INCORRECT_FINGERPRINT_INPUT -> incorrectFingerprintInput(securityMode)
- PROMPT_REASON_DEFAULT ->
- if (fpAllowedInBouncer) defaultMessageWithFingerprint(securityMode)
- else defaultMessage(securityMode)
- PROMPT_REASON_PRIMARY_AUTH_LOCKED_OUT -> primaryAuthLockedOut(securityMode)
- else -> null
- }
- }
-}
-
-@Retention(AnnotationRetention.SOURCE)
-@IntDef(
- PROMPT_REASON_TIMEOUT,
- PROMPT_REASON_DEVICE_ADMIN,
- PROMPT_REASON_USER_REQUEST,
- PROMPT_REASON_AFTER_LOCKOUT,
- PROMPT_REASON_PREPARE_FOR_UPDATE,
- PROMPT_REASON_NON_STRONG_BIOMETRIC_TIMEOUT,
- PROMPT_REASON_TRUSTAGENT_EXPIRED,
- PROMPT_REASON_INCORRECT_PRIMARY_AUTH_INPUT,
- PROMPT_REASON_INCORRECT_FACE_INPUT,
- PROMPT_REASON_INCORRECT_FINGERPRINT_INPUT,
- PROMPT_REASON_FACE_LOCKED_OUT,
- PROMPT_REASON_FINGERPRINT_LOCKED_OUT,
- PROMPT_REASON_DEFAULT,
- PROMPT_REASON_NONE,
- PROMPT_REASON_RESTART,
- PROMPT_REASON_PRIMARY_AUTH_LOCKED_OUT,
-)
-annotation class BouncerPromptReason
-
-private fun defaultMessage(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)
- else -> Pair(0, 0)
- }
-}
-
-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)
- 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)
- 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)
- 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)
- 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)
- 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)
- 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)
- 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)
- 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)
- 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)
- 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)
- 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)
- 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)
- 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)
- 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)
- 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)
- 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)
- 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)
- 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)
- else -> Pair(0, 0)
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardFaceAuthNotSupportedModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardFaceAuthNotSupportedModule.kt
index a44df7e..d8eb81c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardFaceAuthNotSupportedModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardFaceAuthNotSupportedModule.kt
@@ -16,6 +16,8 @@
package com.android.systemui.keyguard.dagger
+import com.android.systemui.keyguard.data.repository.DeviceEntryFaceAuthRepository
+import com.android.systemui.keyguard.data.repository.NoopDeviceEntryFaceAuthRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor
import com.android.systemui.keyguard.domain.interactor.NoopKeyguardFaceAuthInteractor
import dagger.Binds
@@ -32,4 +34,9 @@
interface KeyguardFaceAuthNotSupportedModule {
@Binds
fun keyguardFaceAuthInteractor(impl: NoopKeyguardFaceAuthInteractor): KeyguardFaceAuthInteractor
+
+ @Binds
+ fun deviceEntryFaceAuthRepository(
+ impl: NoopDeviceEntryFaceAuthRepository
+ ): DeviceEntryFaceAuthRepository
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
index 1f121e9..61bacbda 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.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 @@
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 @@
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 9621f03..128057a 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.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.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
@@ -335,6 +335,11 @@
biometricSettingsRepository.isCurrentUserInLockdown.isFalse(),
"userHasNotLockedDownDevice",
tableLogBuffer
+ ),
+ logAndObserve(
+ keyguardRepository.isKeyguardShowing,
+ "isKeyguardShowing",
+ tableLogBuffer
)
)
.reduce(::and)
@@ -598,7 +603,7 @@
const val DEFAULT_CANCEL_SIGNAL_TIMEOUT = 3000L
/** Number of allowed retries whenever there is a face hardware error */
- const val HAL_ERROR_RETRY_MAX = 20
+ const val HAL_ERROR_RETRY_MAX = 5
/** Timeout before retries whenever there is a HAL error. */
const val HAL_ERROR_RETRY_TIMEOUT = 500L // ms
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
index 742e535..81f62b6 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.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.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 @@
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 @@
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 @@
awaitClose { biometricUnlockController.removeListener(callback) }
}
- override val wakefulness: Flow<WakefulnessModel> = conflatedCallbackFlow {
- val observer =
- object : WakefulnessLifecycle.Observer {
- override fun onStartedWakingUp() {
- dispatchNewState()
- }
+ override val wakefulness: StateFlow<WakefulnessModel> =
+ conflatedCallbackFlow {
+ val observer =
+ object : WakefulnessLifecycle.Observer {
+ override fun onStartedWakingUp() {
+ dispatchNewState()
+ }
- override fun onFinishedWakingUp() {
- dispatchNewState()
- }
+ override fun onFinishedWakingUp() {
+ dispatchNewState()
+ }
- override fun onPostFinishedWakingUp() {
- dispatchNewState()
- }
+ override fun onPostFinishedWakingUp() {
+ dispatchNewState()
+ }
- override fun onStartedGoingToSleep() {
- dispatchNewState()
- }
+ override fun onStartedGoingToSleep() {
+ dispatchNewState()
+ }
- override fun onFinishedGoingToSleep() {
- dispatchNewState()
- }
+ override fun onFinishedGoingToSleep() {
+ dispatchNewState()
+ }
- private fun dispatchNewState() {
- trySendWithFailureLogging(
- WakefulnessModel.fromWakefulnessLifecycle(wakefulnessLifecycle),
- TAG,
- "updated wakefulness state"
- )
- }
+ private fun dispatchNewState() {
+ trySendWithFailureLogging(
+ WakefulnessModel.fromWakefulnessLifecycle(wakefulnessLifecycle),
+ TAG,
+ "updated wakefulness state",
+ )
+ }
+ }
+
+ wakefulnessLifecycle.addObserver(observer)
+ awaitClose { wakefulnessLifecycle.removeObserver(observer) }
}
-
- wakefulnessLifecycle.addObserver(observer)
- trySendWithFailureLogging(
- WakefulnessModel.fromWakefulnessLifecycle(wakefulnessLifecycle),
- TAG,
- "initial wakefulness state"
- )
-
- 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 4055fd0..246ee33 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 358ab01..84cd3ef 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 @@
* 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/data/repository/NoopDeviceEntryFaceAuthRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/NoopDeviceEntryFaceAuthRepository.kt
new file mode 100644
index 0000000..abe59b7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/NoopDeviceEntryFaceAuthRepository.kt
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.data.repository
+
+import com.android.keyguard.FaceAuthUiEvent
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.shared.model.AuthenticationStatus
+import com.android.systemui.keyguard.shared.model.DetectionStatus
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.emptyFlow
+
+/**
+ * Implementation of the repository that noops all face auth operations.
+ *
+ * This is required for SystemUI variants that do not support face authentication but still inject
+ * other SysUI components that depend on [DeviceEntryFaceAuthRepository].
+ */
+@SysUISingleton
+class NoopDeviceEntryFaceAuthRepository @Inject constructor() : DeviceEntryFaceAuthRepository {
+ override val isAuthenticated: Flow<Boolean>
+ get() = emptyFlow()
+
+ private val _canRunFaceAuth = MutableStateFlow(false)
+ override val canRunFaceAuth: StateFlow<Boolean> = _canRunFaceAuth
+
+ override val authenticationStatus: Flow<AuthenticationStatus>
+ get() = emptyFlow()
+
+ override val detectionStatus: Flow<DetectionStatus>
+ get() = emptyFlow()
+
+ private val _isLockedOut = MutableStateFlow(false)
+ override val isLockedOut: StateFlow<Boolean> = _isLockedOut
+
+ private val _isAuthRunning = MutableStateFlow(false)
+ override val isAuthRunning: StateFlow<Boolean> = _isAuthRunning
+
+ override val isBypassEnabled: Flow<Boolean>
+ get() = emptyFlow()
+
+ /**
+ * Trigger face authentication.
+ *
+ * [uiEvent] provided should be logged whenever face authentication runs. Invocation should be
+ * ignored if face authentication is already running. Results should be propagated through
+ * [authenticationStatus]
+ *
+ * Run only face detection when [fallbackToDetection] is true and [canRunFaceAuth] is false.
+ */
+ override suspend fun authenticate(uiEvent: FaceAuthUiEvent, fallbackToDetection: Boolean) {}
+
+ /** Stop currently running face authentication or detection. */
+ override fun cancel() {}
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
index c44435e..228290a 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 @@
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 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 @@
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 @@
}
/** 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 @@
/** 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 6b515da..8b749f0 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 @@
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/TransitionStep.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/TransitionStep.kt
index 767fd58..0fa6f4f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/TransitionStep.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/TransitionStep.kt
@@ -16,7 +16,9 @@
package com.android.systemui.keyguard.shared.model
/** This information will flow from the [KeyguardTransitionRepository] to control the UI layer */
-data class TransitionStep(
+data class TransitionStep
+@JvmOverloads
+constructor(
val from: KeyguardState = KeyguardState.OFF,
val to: KeyguardState = KeyguardState.OFF,
val value: Float = 0f, // constrained [0.0, 1.0]
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 51ce7ff..fb685da 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 @@
/** 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 @@
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 52e15be..cfd9e08 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 @@
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 @@
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 a8d662c..7d14198 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.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 @@
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 @@
}
ambientIndicationArea?.alpha = alpha
- indicationArea.alpha = alpha
}
}
@@ -205,50 +198,23 @@
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 @@
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 @@
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 @@
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 0000000..02e6765
--- /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/preview/KeyguardPreviewRenderer.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
index 1c2e85b..b92d104 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
@@ -346,6 +346,10 @@
?.largeClock
?.events
?.onTargetRegionChanged(KeyguardClockSwitch.getLargeClockRegion(parentView))
+ clockController.clock
+ ?.smallClock
+ ?.events
+ ?.onTargetRegionChanged(KeyguardClockSwitch.getSmallClockRegion(parentView))
}
}
parentView.addOnLayoutChangeListener(layoutChangeListener)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/KeyguardIndicationArea.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/KeyguardIndicationArea.kt
new file mode 100644
index 0000000..890d565
--- /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 0000000..a62f383
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/KeyguardRootView.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.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.keyguard.LockIconView
+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()
+ addLockIconView()
+ }
+
+ 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 addLockIconView() {
+ val view = LockIconView(context, attrs).apply { id = R.id.lock_icon_view }
+ addView(
+ view,
+ LayoutParams(
+ WRAP_CONTENT,
+ WRAP_CONTENT,
+ )
+ )
+ }
+
+ private fun Int.dp(): Int {
+ return context.resources.getDimensionPixelSize(this)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModel.kt
index 62a1a9e..3e6f8e6 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 @@
* 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 @@
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 0000000..389cf76
--- /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 68810f9..44e1fd1 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/keyguard/util/IndicationHelper.kt b/packages/SystemUI/src/com/android/systemui/keyguard/util/IndicationHelper.kt
new file mode 100644
index 0000000..7129001
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/util/IndicationHelper.kt
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.keyguard.util
+
+import android.hardware.biometrics.BiometricFaceConstants
+import android.hardware.biometrics.BiometricFingerprintConstants
+import android.hardware.biometrics.BiometricSourceType
+import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.systemui.dagger.SysUISingleton
+import javax.inject.Inject
+
+@SysUISingleton
+class IndicationHelper
+@Inject
+constructor(
+ val keyguardUpdateMonitor: KeyguardUpdateMonitor,
+) {
+ fun shouldSuppressErrorMsg(biometricSource: BiometricSourceType, msgId: Int): Boolean {
+ return when (biometricSource) {
+ BiometricSourceType.FINGERPRINT ->
+ (isPrimaryAuthRequired() && !isFingerprintLockoutErrorMsg(msgId)) ||
+ msgId == BiometricFingerprintConstants.FINGERPRINT_ERROR_CANCELED ||
+ msgId == BiometricFingerprintConstants.FINGERPRINT_ERROR_USER_CANCELED ||
+ msgId == BiometricFingerprintConstants.BIOMETRIC_ERROR_POWER_PRESSED
+ BiometricSourceType.FACE ->
+ (isPrimaryAuthRequired() && !isFaceLockoutErrorMsg(msgId)) ||
+ msgId == BiometricFaceConstants.FACE_ERROR_CANCELED ||
+ msgId == BiometricFaceConstants.FACE_ERROR_UNABLE_TO_PROCESS
+ else -> false
+ }
+ }
+
+ private fun isFingerprintLockoutErrorMsg(msgId: Int): Boolean {
+ return msgId == BiometricFingerprintConstants.FINGERPRINT_ERROR_LOCKOUT ||
+ msgId == BiometricFingerprintConstants.FINGERPRINT_ERROR_LOCKOUT_PERMANENT
+ }
+
+ fun isFaceLockoutErrorMsg(msgId: Int): Boolean {
+ return msgId == BiometricFaceConstants.FACE_ERROR_LOCKOUT ||
+ msgId == BiometricFaceConstants.FACE_ERROR_LOCKOUT_PERMANENT
+ }
+
+ private fun isPrimaryAuthRequired(): Boolean {
+ // Only checking if unlocking with Biometric is allowed (no matter strong or non-strong
+ // as long as primary auth, i.e. PIN/pattern/password, is required), so it's ok to
+ // pass true for isStrongBiometric to isUnlockingWithBiometricAllowed() to bypass the
+ // check of whether non-strong biometric is allowed since strong biometrics can still be
+ // used.
+ return !keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(true /* isStrongBiometric */)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/log/BouncerLogger.kt b/packages/SystemUI/src/com/android/systemui/log/BouncerLogger.kt
index 3be4499..3226865 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 f727784..27301e9 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.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 @@
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/keyguard/data/BouncerViewModule.kt b/packages/SystemUI/src/com/android/systemui/log/dagger/DisableFlagsRepositoryLog.kt
similarity index 62%
copy from packages/SystemUI/src/com/android/systemui/keyguard/data/BouncerViewModule.kt
copy to packages/SystemUI/src/com/android/systemui/log/dagger/DisableFlagsRepositoryLog.kt
index 390c54e..e141291 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/BouncerViewModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/DisableFlagsRepositoryLog.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,14 +13,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package com.android.systemui.log.dagger
-package com.android.systemui.keyguard.data
+import com.android.systemui.log.LogBuffer
+import javax.inject.Qualifier
-import dagger.Binds
-import dagger.Module
-
-@Module
-interface BouncerViewModule {
- /** Binds BouncerView to BouncerViewImpl and makes it injectable. */
- @Binds fun bindBouncerView(bouncerViewImpl: BouncerViewImpl): BouncerView
-}
+/** 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 0261ee5..3497285 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 @@
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 19e1124..1e2f71f 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 @@
bgDispatcher,
coroutineScope,
)
- dumpManager.registerNormalDumpable(name, tableBuffer)
+ dumpManager.registerTableLogBuffer(name, tableBuffer)
tableBuffer.init()
return tableBuffer
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/resume/MediaResumeListener.kt b/packages/SystemUI/src/com/android/systemui/media/controls/resume/MediaResumeListener.kt
index b0389b5..23ee00d 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/resume/MediaResumeListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/resume/MediaResumeListener.kt
@@ -122,9 +122,9 @@
Log.e(TAG, "Error getting package information", e)
}
- Log.d(TAG, "Adding resume controls $desc")
+ Log.d(TAG, "Adding resume controls for ${browser.userId}: $desc")
mediaDataManager.addResumptionControls(
- currentUserId,
+ browser.userId,
desc,
resumeAction,
token,
@@ -196,7 +196,11 @@
}
resumeComponents.add(component to lastPlayed)
}
- Log.d(TAG, "loaded resume components ${resumeComponents.toArray().contentToString()}")
+ Log.d(
+ TAG,
+ "loaded resume components for $currentUserId: " +
+ "${resumeComponents.toArray().contentToString()}"
+ )
if (needsUpdate) {
// Save any missing times that we had to fill in
@@ -210,11 +214,21 @@
return
}
+ val pm = context.packageManager
val now = systemClock.currentTimeMillis()
resumeComponents.forEach {
if (now.minus(it.second) <= RESUME_MEDIA_TIMEOUT) {
- val browser = mediaBrowserFactory.create(mediaBrowserCallback, it.first)
- browser.findRecentMedia()
+ // Verify that the service exists for this user
+ val intent = Intent(MediaBrowserService.SERVICE_INTERFACE)
+ intent.component = it.first
+ val inf = pm.resolveServiceAsUser(intent, 0, currentUserId)
+ if (inf != null) {
+ val browser =
+ mediaBrowserFactory.create(mediaBrowserCallback, it.first, currentUserId)
+ browser.findRecentMedia()
+ } else {
+ Log.d(TAG, "User $currentUserId does not have component ${it.first}")
+ }
}
}
}
@@ -244,7 +258,7 @@
Log.d(TAG, "Checking for service component for " + data.packageName)
val pm = context.packageManager
val serviceIntent = Intent(MediaBrowserService.SERVICE_INTERFACE)
- val resumeInfo = pm.queryIntentServices(serviceIntent, 0)
+ val resumeInfo = pm.queryIntentServicesAsUser(serviceIntent, 0, currentUserId)
val inf = resumeInfo?.filter { it.serviceInfo.packageName == data.packageName }
if (inf != null && inf.size > 0) {
@@ -280,13 +294,17 @@
browser: ResumeMediaBrowser
) {
// Since this is a test, just save the component for later
- Log.d(TAG, "Can get resumable media from $componentName")
+ Log.d(
+ TAG,
+ "Can get resumable media for ${browser.userId} from $componentName"
+ )
mediaDataManager.setResumeAction(key, getResumeAction(componentName))
updateResumptionList(componentName)
mediaBrowser = null
}
},
- componentName
+ componentName,
+ currentUserId
)
mediaBrowser?.testConnection()
}
@@ -326,7 +344,7 @@
/** Get a runnable which will resume media playback */
private fun getResumeAction(componentName: ComponentName): Runnable {
return Runnable {
- mediaBrowser = mediaBrowserFactory.create(null, componentName)
+ mediaBrowser = mediaBrowserFactory.create(null, componentName, currentUserId)
mediaBrowser?.restart()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/resume/ResumeMediaBrowser.java b/packages/SystemUI/src/com/android/systemui/media/controls/resume/ResumeMediaBrowser.java
index d460b5b..ceaccaf 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/resume/ResumeMediaBrowser.java
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/resume/ResumeMediaBrowser.java
@@ -17,6 +17,7 @@
package com.android.systemui.media.controls.resume;
import android.annotation.Nullable;
+import android.annotation.UserIdInt;
import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Context;
@@ -53,6 +54,7 @@
private final ResumeMediaBrowserLogger mLogger;
private final ComponentName mComponentName;
private final MediaController.Callback mMediaControllerCallback = new SessionDestroyCallback();
+ @UserIdInt private final int mUserId;
private MediaBrowser mMediaBrowser;
@Nullable private MediaController mMediaController;
@@ -62,18 +64,21 @@
* @param context the context
* @param callback used to report media items found
* @param componentName Component name of the MediaBrowserService this browser will connect to
+ * @param userId ID of the current user
*/
public ResumeMediaBrowser(
Context context,
@Nullable Callback callback,
ComponentName componentName,
MediaBrowserFactory browserFactory,
- ResumeMediaBrowserLogger logger) {
+ ResumeMediaBrowserLogger logger,
+ @UserIdInt int userId) {
mContext = context;
mCallback = callback;
mComponentName = componentName;
mBrowserFactory = browserFactory;
mLogger = logger;
+ mUserId = userId;
}
/**
@@ -285,6 +290,14 @@
}
/**
+ * Get the ID of the user associated with this broswer
+ * @return the user ID
+ */
+ public @UserIdInt int getUserId() {
+ return mUserId;
+ }
+
+ /**
* Get the media session token
* @return the token, or null if the MediaBrowser is null or disconnected
*/
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/resume/ResumeMediaBrowserFactory.java b/packages/SystemUI/src/com/android/systemui/media/controls/resume/ResumeMediaBrowserFactory.java
index c558227..e374191 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/resume/ResumeMediaBrowserFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/resume/ResumeMediaBrowserFactory.java
@@ -16,6 +16,7 @@
package com.android.systemui.media.controls.resume;
+import android.annotation.UserIdInt;
import android.content.ComponentName;
import android.content.Context;
@@ -42,10 +43,12 @@
*
* @param callback will be called on connection or error, and addTrack when media item found
* @param componentName component to browse
+ * @param userId ID of the current user
* @return
*/
public ResumeMediaBrowser create(ResumeMediaBrowser.Callback callback,
- ComponentName componentName) {
- return new ResumeMediaBrowser(mContext, callback, componentName, mBrowserFactory, mLogger);
+ ComponentName componentName, @UserIdInt int userId) {
+ return new ResumeMediaBrowser(mContext, callback, componentName, mBrowserFactory, mLogger,
+ userId);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java
index 1da8718..99c591f 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.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.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 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 @@
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 @@
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 @@
* @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 682335e..5bae1cb 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.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 @@
// ----- 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 3b32313e..cecf043 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 @@
}
@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 8b3d7a6..7b86d0a 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.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 @@
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 @@
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 @@
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 @@
}
};
+ 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 @@
ViewConfiguration viewConfiguration,
WindowManager windowManager,
IWindowManager windowManagerService,
+ InputManager inputManager,
Optional<Pip> pipOptional,
Optional<DesktopMode> desktopModeOptional,
FalsingManager falsingManager,
@@ -373,6 +419,7 @@
mContext = context;
mDisplayId = context.getDisplayId();
mMainExecutor = executor;
+ mMainHandler = handler;
mBackgroundExecutor = backgroundExecutor;
mUserTracker = userTracker;
mOverviewProxyService = overviewProxyService;
@@ -384,6 +431,7 @@
mViewConfiguration = viewConfiguration;
mWindowManager = windowManager;
mWindowManagerService = windowManagerService;
+ mInputManager = inputManager;
mPipOptional = pipOptional;
mDesktopModeOptional = desktopModeOptional;
mFalsingManager = falsingManager;
@@ -392,6 +440,8 @@
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 @@
ViewConfiguration.getLongPressTimeout());
mGestureNavigationSettingsObserver = new GestureNavigationSettingsObserver(
- mContext.getMainThreadHandler(), mContext, this::onNavigationSettingsChanged);
+ mMainHandler, mContext, this::onNavigationSettingsChanged);
updateCurrentUserResources();
}
@@ -510,6 +560,13 @@
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 @@
mProtoTracer.remove(this);
mOverviewProxyService.removeCallback(mQuickSwitchListener);
mSysUiState.removeCallback(mSysUiStateCallback);
+ mInputManager.unregisterInputDeviceListener(mInputDeviceListener);
updateIsEnabled();
mUserTracker.removeCallback(mUserChangedCallback);
}
@@ -530,7 +588,8 @@
* @see NavigationModeController.ModeChangedListener#onNavigationModeChanged
*/
public void onNavigationModeChanged(int mode) {
- mIsGesturalModeEnabled = QuickStepContract.isGesturalMode(mode);
+ mUsingThreeButtonNav = QuickStepContract.isLegacyMode(mode);
+ mInGestureNavMode = QuickStepContract.isGesturalMode(mode);
updateIsEnabled();
updateCurrentUserResources();
}
@@ -553,83 +612,83 @@
private void updateIsEnabled() {
try {
Trace.beginSection("EdgeBackGestureHandler#updateIsEnabled");
- updateIsEnabledTraced();
+
+ mIsGestureHandlingEnabled =
+ mInGestureNavMode || (mIsTrackpadGestureFeaturesEnabled && mUsingThreeButtonNav
+ && mIsTrackpadConnected);
+ boolean isEnabled = mIsAttached && mIsGestureHandlingEnabled;
+ 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");
+ }
+ 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);
+ }
+
+ } 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);
+ resetEdgeBackPlugin();
+ mPluginManager.addPluginListener(
+ this, NavigationEdgeBackPlugin.class, /*allowMultiple=*/ false);
+ }
+ // Update the ML model resources.
+ updateMLModelState();
} 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");
- }
- 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);
- }
-
- } 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);
- }
- // Update the ML model resources.
- updateMLModelState();
- }
-
@Override
public void onPluginConnected(NavigationEdgeBackPlugin plugin, Context context) {
setEdgeBackPlugin(plugin);
@@ -704,9 +763,9 @@
}
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 @@
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 @@
(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 @@
// 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
SysUiState sysUiState,
PluginManager pluginManager,
@Main Executor executor,
+ @Main Handler handler,
@Background Executor backgroundExecutor,
UserTracker userTracker,
ProtoTracer protoTracer,
@@ -1263,6 +1347,7 @@
ViewConfiguration viewConfiguration,
WindowManager windowManager,
IWindowManager windowManagerService,
+ InputManager inputManager,
Optional<Pip> pipOptional,
Optional<DesktopMode> desktopModeOptional,
FalsingManager falsingManager,
@@ -1275,6 +1360,7 @@
mSysUiState = sysUiState;
mPluginManager = pluginManager;
mExecutor = executor;
+ mHandler = handler;
mBackgroundExecutor = backgroundExecutor;
mUserTracker = userTracker;
mProtoTracer = protoTracer;
@@ -1283,6 +1369,7 @@
mViewConfiguration = viewConfiguration;
mWindowManager = windowManager;
mWindowManagerService = windowManagerService;
+ mInputManager = inputManager;
mPipOptional = pipOptional;
mDesktopModeOptional = desktopModeOptional;
mFalsingManager = falsingManager;
@@ -1300,6 +1387,7 @@
mSysUiState,
mPluginManager,
mExecutor,
+ mHandler,
mBackgroundExecutor,
mUserTracker,
mProtoTracer,
@@ -1308,6 +1396,7 @@
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 50e8aa7..10a88c8 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 0000000..3e947d9
--- /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 0000000..ec205f8
--- /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 ccfbaf1..48790c2 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.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 @@
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 @@
}
}
- /** 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 @@
userTracker.userProfiles.firstOrNull { userManager.isManagedProfile(it.id) }?.userHandle
?: userTracker.userHandle
} else {
- userTracker.userHandle
+ secureSettings.preferredUser
}
/**
@@ -175,7 +166,19 @@
) {
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 @@
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 @@
}
/**
+ * 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 @@
/** @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 26b35cc..3e352af 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 @@
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 221ff65..fe1034a 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 @@
initializeHandleSystemKey()
initializeOnRoleHoldersChanged()
initializeOnUserUnlocked()
+ initializeUserTracker()
}
/**
@@ -79,7 +83,7 @@
private fun initializeOnRoleHoldersChanged() {
roleManager.addOnRoleHoldersChangedListenerAsUser(
backgroundExecutor,
- controller::onRoleHoldersChanged,
+ callbacks,
UserHandle.ALL,
)
}
@@ -93,18 +97,41 @@
*/
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 109cfee..c0e688f 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.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 @@
@[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 754c365..4d30634 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 @@
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 @@
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 7ef149d..d00a79e 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.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 3259b0d..0000000
--- 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 b2e04bb..69cb611 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.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 @@
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 @@
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 3f799f7..c13476f 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 @@
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 79167f2..c3b5db4 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.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 b8c2fad..8d9475d 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.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 @@
// 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 cd52ec2..ac6aabb 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 @@
},
{
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 59b94b7..d2568ac 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.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.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 @@
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 @@
PluginManager pluginManager,
TunerService tunerService,
Provider<AutoTileManager> autoTiles,
- Optional<CentralSurfaces> centralSurfacesOptional,
+ ShadeController shadeController,
QSLogger qsLogger,
UserTracker userTracker,
SecureSettings secureSettings,
@@ -148,7 +147,7 @@
mUserFileManager = userFileManager;
mFeatureFlags = featureFlags;
- mCentralSurfacesOptional = centralSurfacesOptional;
+ mShadeController = shadeController;
mQsFactories.add(defaultFactory);
pluginManager.addPluginListener(this, QSFactory.class, true);
@@ -209,17 +208,17 @@
@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 260caa7..fa6de8d 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 @@
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 cd69f4e..e541681 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 @@
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 eef4c1d..b5e6a0f 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 @@
}
@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 c013486..423fa80 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.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 @@
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 @@
}
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 ad65845..6e1ef91 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 @@
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 @@
}
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 @@
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 @@
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 abeb5af..ec12580 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 @@
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 @@
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 @@
mKeyguardUpdateMonitor.removeCallback(mKeyguardUpdateCallback);
mConnectivityManager.unregisterNetworkCallback(mConnectivityManagerNetworkCallback);
mConnectedWifiInternetMonitor.unregisterCallback();
+ mCallback = null;
}
@VisibleForTesting
@@ -724,7 +731,7 @@
ActivityLaunchAnimator.Controller controller =
mDialogLaunchAnimator.createActivityLaunchController(view);
- if (controller == null) {
+ if (controller == null && mCallback != null) {
mCallback.dismissDialog();
}
@@ -1095,7 +1102,9 @@
mHasWifiEntries = false;
}
- mCallback.onAccessPointsChanged(wifiEntries, connectedEntry, hasMoreWifiEntries);
+ if (mCallback != null) {
+ mCallback.onAccessPointsChanged(wifiEntries, connectedEntry, hasMoreWifiEntries);
+ }
}
@Override
@@ -1117,34 +1126,46 @@
@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 @@
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 07259c2..e7dde66 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 @@
// 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 @@
}
@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 @@
@Override
public void expandNotificationPanel() {
- verifyCallerAndClearCallingIdentity("expandNotificationPanel",
+ verifyCallerAndClearCallingIdentityPostMain("expandNotificationPanel",
() -> mCommandQueue.handleSystemKey(new KeyEvent(KeyEvent.ACTION_DOWN,
KeyEvent.KEYCODE_SYSTEM_NAVIGATION_DOWN)));
}
@@ -353,7 +360,7 @@
@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 752471d..0a9839e 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 61b162b..1ebeced 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 @@
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 @@
.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
similarity index 90%
rename from packages/SystemUI/src/com/android/systemui/scene/data/model/SceneContainerConfig.kt
rename to packages/SystemUI/src/com/android/systemui/scene/shared/model/SceneContainerConfig.kt
index d0769eb..0327edb 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
similarity index 89%
rename from packages/SystemUI/src/com/android/systemui/scene/data/model/SceneContainerConfigModule.kt
rename to packages/SystemUI/src/com/android/systemui/scene/shared/model/SceneContainerConfigModule.kt
index 0af8094..7562a5a 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 0000000..9b5898f
--- /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 0000000..11e1eb9
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/scene/ui/view/WindowRootView.kt
@@ -0,0 +1,53 @@
+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 {
+ // TODO(b/283300105): remove this check once there's only one subclass of WindowRootView.
+ return parent.let { it !is View || it.id == android.R.id.content }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsCrossProcessHelper.java b/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsCrossProcessHelper.java
index afc8bff..7de22b1 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 @@
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 @@
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 4c01315..4cc2f62 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 @@
/**
* 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 3949492..dce8c81 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.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.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 @@
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 f00803c..6e5cef4 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_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 @@
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;
- mUserTracker = userTracker;
mUiEventLogger = uiEventLogger;
- mUserManager = userManager;
+ 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;
+ mUiEventLogger = uiEventLogger;
+ mBroadcastSender = broadcastSender;
+ mBgExecutor = bgExecutor;
+ mMainExecutor = mainExecutor;
mResultReceiver = createResultReceiver(mainHandler);
}
@@ -127,62 +145,62 @@
return;
}
- if (mUserManager.isManagedProfile()) {
- maybeStartActivityForWPUser();
- finish();
+ mUserHandle = getUser();
+
+ mBgExecutor.execute(() -> {
+ AndroidFuture<Integer> statusCodeFuture = mAppClipsServiceConnector.postForResult(
+ service -> service.canLaunchCaptureContentActivityForNoteInternal(getTaskId()));
+ statusCodeFuture.whenCompleteAsync(this::handleAppClipsStatusCode, mMainExecutor);
+ });
+ }
+
+ @Override
+ protected void onDestroy() {
+ if (isFinishing() && mKillAppClipsBroadcastIntent != null) {
+ mBroadcastSender.sendBroadcast(mKillAppClipsBroadcastIntent, PERMISSION_SELF);
+ }
+
+ super.onDestroy();
+ }
+
+ private void handleAppClipsStatusCode(@CaptureContentForNoteStatusCodes int statusCode,
+ Throwable error) {
+ if (isFinishing()) {
+ // It's too late, trampoline activity is finishing or already finished. Return early.
return;
}
- if (!mFeatureFlags.isEnabled(SCREENSHOT_APP_CLIPS)) {
- finish();
+ if (error != null) {
+ Log.d(TAG, "Error querying app clips service", error);
+ setErrorResultAndFinish(statusCode);
return;
}
- if (mOptionalBubbles.isEmpty()) {
- setErrorResultAndFinish(CAPTURE_CONTENT_FOR_NOTE_FAILED);
- return;
- }
+ switch (statusCode) {
+ case CAPTURE_CONTENT_FOR_NOTE_SUCCESS:
+ launchAppClipsActivity();
+ break;
- if (!mOptionalBubbles.get().isAppBubbleTaskId(getTaskId())) {
- setErrorResultAndFinish(CAPTURE_CONTENT_FOR_NOTE_WINDOW_MODE_UNSUPPORTED);
- return;
+ 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);
}
+ }
- if (mDevicePolicyManager.getScreenCaptureDisabled(null)) {
- setErrorResultAndFinish(CAPTURE_CONTENT_FOR_NOTE_BLOCKED_BY_ADMIN);
- return;
- }
-
- ComponentName componentName;
- try {
- componentName = ComponentName.unflattenFromString(
+ private void launchAppClipsActivity() {
+ ComponentName componentName = ComponentName.unflattenFromString(
getString(R.string.config_screenshotAppClipsActivityComponent));
- } catch (Resources.NotFoundException e) {
- setErrorResultAndFinish(CAPTURE_CONTENT_FOR_NOTE_FAILED);
- return;
- }
-
- if (componentName == null || componentName.getPackageName().isEmpty()
- || componentName.getClassName().isEmpty()) {
- setErrorResultAndFinish(CAPTURE_CONTENT_FOR_NOTE_FAILED);
- return;
- }
-
- 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);
- }
-
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 @@
}
}
- @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 @@
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 @@
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 @@
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 7e0f504..a9b3d0a 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 @@
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 @@
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 @@
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 @@
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 776a90d..2ef9e07 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.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;
@@ -90,7 +90,6 @@
import com.android.app.animation.Interpolators;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.policy.SystemBarUtils;
@@ -114,6 +113,8 @@
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 +126,14 @@
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;
@@ -156,6 +157,7 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
import com.android.systemui.shade.transition.ShadeTransitionController;
+import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.GestureRecorder;
@@ -222,8 +224,6 @@
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 javax.inject.Inject;
import javax.inject.Provider;
+import kotlin.Unit;
+
import kotlinx.coroutines.CoroutineDispatcher;
@CentralSurfacesComponent.CentralSurfacesScope
@@ -347,7 +349,6 @@
private final NotificationGutsManager mGutsManager;
private final AlternateBouncerInteractor mAlternateBouncerInteractor;
private final QuickSettingsController mQsController;
- private final InteractionJankMonitor mInteractionJankMonitor;
private final TouchHandler mTouchHandler = new TouchHandler();
private long mDownTime;
@@ -360,6 +361,7 @@
/** The current squish amount for the predictive back animation */
private float mCurrentBackProgress = 0.0f;
private boolean mTracking;
+ private boolean mIsTrackingExpansionFromStatusBar;
private boolean mHintAnimationRunning;
private KeyguardBottomAreaView mKeyguardBottomArea;
private boolean mExpanding;
@@ -599,6 +601,7 @@
private final KeyguardTransitionInteractor mKeyguardTransitionInteractor;
private final KeyguardInteractor mKeyguardInteractor;
+ private final KeyguardViewConfigurator mKeyguardViewConfigurator;
private final @Nullable MultiShadeInteractor mMultiShadeInteractor;
private final CoroutineDispatcher mMainDispatcher;
private boolean mIsAnyMultiShadeExpanded;
@@ -724,7 +727,6 @@
NotificationStackSizeCalculator notificationStackSizeCalculator,
UnlockedScreenOffAnimationController unlockedScreenOffAnimationController,
ShadeTransitionController shadeTransitionController,
- InteractionJankMonitor interactionJankMonitor,
SystemClock systemClock,
KeyguardBottomAreaViewModel keyguardBottomAreaViewModel,
KeyguardBottomAreaInteractor keyguardBottomAreaInteractor,
@@ -741,8 +743,8 @@
KeyguardLongPressViewModel keyguardLongPressViewModel,
KeyguardInteractor keyguardInteractor,
ActivityStarter activityStarter,
+ KeyguardViewConfigurator keyguardViewConfigurator,
KeyguardFaceAuthInteractor keyguardFaceAuthInteractor) {
- mInteractionJankMonitor = interactionJankMonitor;
keyguardStateController.addCallback(new KeyguardStateController.Callback() {
@Override
public void onKeyguardFadingAwayChanged() {
@@ -763,6 +765,7 @@
mLockscreenToOccludedTransitionViewModel = lockscreenToOccludedTransitionViewModel;
mKeyguardTransitionInteractor = keyguardTransitionInteractor;
mKeyguardInteractor = keyguardInteractor;
+ mKeyguardViewConfigurator = keyguardViewConfigurator;
mView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
@Override
public void onViewAttachedToWindow(View v) {
@@ -1029,7 +1032,7 @@
new NsslHeightChangedListener());
mNotificationStackScrollLayoutController.setOnEmptySpaceClickListener(
mOnEmptySpaceClickListener);
- mQsController.initNotificationStackScrollLayoutController();
+ mQsController.init();
mShadeExpansionStateManager.addQsExpansionListener(this::onQsExpansionChanged);
mShadeHeadsUpTracker.addTrackingHeadsUpListener(
mNotificationStackScrollLayoutController::setTrackingHeadsUp);
@@ -1321,7 +1324,6 @@
mKeyguardBottomArea.initFrom(oldBottomArea);
mView.addView(mKeyguardBottomArea, index);
initBottomArea();
- mKeyguardIndicationController.setIndicationArea(mKeyguardBottomArea);
mStatusBarStateListener.onDozeAmountChanged(mStatusBarStateController.getDozeAmount(),
mStatusBarStateController.getInterpolatedDozeAmount());
@@ -1363,6 +1365,9 @@
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 +1405,6 @@
private void setKeyguardBottomArea(KeyguardBottomAreaView keyguardBottomArea) {
mKeyguardBottomArea = keyguardBottomArea;
- mKeyguardIndicationController.setIndicationArea(mKeyguardBottomArea);
}
void setOpenCloseListener(OpenCloseListener openCloseListener) {
@@ -1816,6 +1820,7 @@
// Set after notifyExpandingStarted, as notifyExpandingStarted resets the closing state.
setClosing(true);
+ mUpdateFlingOnLayout = false;
if (delayed) {
mNextCollapseSpeedUpFactor = speedUpFactor;
this.mView.postDelayed(mFlingCollapseRunnable, 120);
@@ -2148,10 +2153,14 @@
}
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 +2438,7 @@
boolean isExpanded = !isFullyCollapsed() || mExpectingSynthesizedDown;
if (mPanelExpanded != isExpanded) {
mPanelExpanded = isExpanded;
+ updateSystemUiStateFlags();
mShadeExpansionStateManager.onShadeExpansionFullyChanged(isExpanded);
if (!isExpanded) {
mQsController.closeQsCustomizer();
@@ -2436,6 +2446,7 @@
}
}
+ @Override
public boolean isPanelExpanded() {
return mPanelExpanded;
}
@@ -2463,7 +2474,8 @@
alpha = getFadeoutAlpha();
}
if (mBarState == KEYGUARD && !mHintAnimationRunning
- && !mKeyguardBypassController.getBypassEnabled()) {
+ && !mKeyguardBypassController.getBypassEnabled()
+ && !mQsController.getFullyExpanded()) {
alpha *= mClockPositionResult.clockAlpha;
}
mNotificationStackScrollLayoutController.setAlpha(alpha);
@@ -2571,7 +2583,7 @@
this);
return;
}
- if (mCentralSurfaces.getNotificationShadeWindowView()
+ if (mNotificationShadeWindowController.getWindowRootView()
.isVisibleToUser()) {
mView.getViewTreeObserver().removeOnGlobalLayoutListener(
this);
@@ -2652,6 +2664,8 @@
private void onTrackingStopped(boolean expand) {
mFalsingCollector.onTrackingStopped();
mTracking = false;
+ maybeStopTrackingExpansionFromStatusBar(expand);
+
updateExpansionAndVisibility();
if (expand) {
mNotificationStackScrollLayoutController.setOverScrollAmount(0.0f, true /* onTop */,
@@ -2893,9 +2907,7 @@
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 +3038,6 @@
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 +3441,8 @@
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 +3597,10 @@
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();
@@ -4030,6 +4037,42 @@
}
@Override
+ public void startTrackingExpansionFromStatusBar() {
+ mIsTrackingExpansionFromStatusBar = true;
+ InteractionJankMonitorWrapper.begin(
+ mView, InteractionJankMonitorWrapper.CUJ_SHADE_EXPAND_FROM_STATUS_BAR);
+ }
+
+ /**
+ * Stops tracking an expansion that originated from the status bar (if we had started tracking
+ * it).
+ *
+ * @param expand the expand boolean passed to {@link #onTrackingStopped(boolean)}.
+ */
+ private void maybeStopTrackingExpansionFromStatusBar(boolean expand) {
+ if (!mIsTrackingExpansionFromStatusBar) {
+ return;
+ }
+ mIsTrackingExpansionFromStatusBar = false;
+
+ // Determine whether the shade actually expanded due to the status bar touch:
+ // - If the user just taps on the status bar, then #isExpanded is false but
+ // #onTrackingStopped is called with `true`.
+ // - If the user drags down on the status bar but doesn't drag down far enough, then
+ // #onTrackingStopped is called with `false` but #isExpanded is true.
+ // So, we need *both* #onTrackingStopped called with `true` *and* #isExpanded to be true in
+ // order to confirm that the shade successfully opened.
+ boolean shadeExpansionFromStatusBarSucceeded = expand && isExpanded();
+ if (shadeExpansionFromStatusBarSucceeded) {
+ InteractionJankMonitorWrapper.end(
+ InteractionJankMonitorWrapper.CUJ_SHADE_EXPAND_FROM_STATUS_BAR);
+ } else {
+ InteractionJankMonitorWrapper.cancel(
+ InteractionJankMonitorWrapper.CUJ_SHADE_EXPAND_FROM_STATUS_BAR);
+ }
+ }
+
+ @Override
public void updateTouchableRegion() {
//A layout will ensure that onComputeInternalInsets will be called and after that we can
// resize the layout. Make sure that the window stays small for one frame until the
@@ -4660,6 +4703,9 @@
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 +4738,7 @@
addMovement(event);
break;
case MotionEvent.ACTION_POINTER_UP:
- if (isTrackpadMotionEvent(event)) {
+ if (isTrackpadTwoOrThreeFingerSwipe) {
break;
}
final int upPointer = event.getPointerId(event.getActionIndex());
@@ -4708,7 +4754,7 @@
mShadeLog.logMotionEventStatusBarState(event,
mStatusBarStateController.getState(),
"onInterceptTouchEvent: pointer down action");
- if (!isTrackpadMotionEvent(event)
+ if (!isTrackpadTwoOrThreeFingerSwipe
&& mStatusBarStateController.getState() == StatusBarState.KEYGUARD) {
mMotionAborted = true;
mVelocityTracker.clear();
@@ -4858,9 +4904,13 @@
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 +4970,7 @@
break;
case MotionEvent.ACTION_POINTER_UP:
- if (isTrackpadMotionEvent(event)) {
+ if (isTrackpadTwoOrThreeFingerSwipe) {
break;
}
final int upPointer = event.getPointerId(event.getActionIndex());
@@ -4939,7 +4989,7 @@
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 +5063,6 @@
}
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 0c800d4..8105a145 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 @@
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 @@
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 @@
}
@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 @@
}
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
@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 d75190e..2b62b7d 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 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 @@
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 2f7644e..5c41d57 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 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,12 +53,12 @@
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;
+import com.android.systemui.power.domain.interactor.PowerInteractor;
import com.android.systemui.shared.animation.DisableSubpixelTextTransitionListener;
import com.android.systemui.statusbar.DragDownHelper;
import com.android.systemui.statusbar.LockscreenShadeTransitionController;
@@ -108,6 +113,8 @@
private NotificationStackScrollLayout mStackScrollLayout;
private PhoneStatusBarViewController mStatusBarViewController;
private final CentralSurfaces mService;
+ private final BackActionInteractor mBackActionInteractor;
+ private final PowerInteractor mPowerInteractor;
private final NotificationShadeWindowController mNotificationShadeWindowController;
private DragDownHelper mDragDownHelper;
private boolean mExpandingBelowNotch;
@@ -141,6 +148,8 @@
StatusBarWindowStateController statusBarWindowStateController,
LockIconViewController lockIconViewController,
CentralSurfaces centralSurfaces,
+ BackActionInteractor backActionInteractor,
+ PowerInteractor powerInteractor,
NotificationShadeWindowController controller,
Optional<UnfoldTransitionProgressProvider> unfoldTransitionProgressProvider,
KeyguardUnlockAnimationController keyguardUnlockAnimationController,
@@ -149,12 +158,15 @@
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,8 +179,10 @@
mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
mStatusBarWindowStateController = statusBarWindowStateController;
mLockIconViewController = lockIconViewController;
+ mBackActionInteractor = backActionInteractor;
mLockIconViewController.init();
mService = centralSurfaces;
+ mPowerInteractor = powerInteractor;
mNotificationShadeWindowController = controller;
mKeyguardUnlockAnimationController = keyguardUnlockAnimationController;
mAmbientState = ambientState;
@@ -183,7 +197,11 @@
mView.findViewById(R.id.keyguard_bouncer_container),
keyguardBouncerViewModel,
primaryBouncerToGoneTransitionViewModel,
- keyguardBouncerComponentFactory);
+ keyguardBouncerComponentFactory,
+ messageAreaControllerFactory,
+ bouncerMessageInteractor,
+ bouncerLogger,
+ featureFlags);
collectFlow(mView, keyguardTransitionInteractor.getLockscreenToDreamingTransition(),
mLockscreenToDreamingTransition);
@@ -297,8 +315,7 @@
/* onGestureDetectedRunnable */
() -> {
mService.userActivity();
- mService.wakeUpIfDozing(
- mClock.uptimeMillis(),
+ mPowerInteractor.wakeUpIfDozing(
"LOCK_ICON_TOUCH",
PowerManager.WAKE_REASON_GESTURE);
}
@@ -433,7 +450,7 @@
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 81fe3ca..fba0120 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.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 @@
@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 fd82e2f..ee4e98e 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 @@
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 @@
* 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 @@
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 @@
!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 7d66a0b..1361c9f 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
@@ -78,6 +78,7 @@
import com.android.systemui.plugins.qs.QS;
import com.android.systemui.screenrecord.RecordingController;
import com.android.systemui.shade.data.repository.ShadeRepository;
+import com.android.systemui.shade.domain.interactor.ShadeInteractor;
import com.android.systemui.shade.transition.ShadeTransitionController;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.statusbar.LockscreenShadeTransitionController;
@@ -101,6 +102,7 @@
import com.android.systemui.statusbar.policy.CastController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.LargeScreenUtils;
+import com.android.systemui.util.kotlin.JavaAdapter;
import dagger.Lazy;
@@ -153,6 +155,8 @@
private final FeatureFlags mFeatureFlags;
private final InteractionJankMonitor mInteractionJankMonitor;
private final ShadeRepository mShadeRepository;
+ private final ShadeInteractor mShadeInteractor;
+ private final JavaAdapter mJavaAdapter;
private final FalsingManager mFalsingManager;
private final AccessibilityManager mAccessibilityManager;
private final MetricsLogger mMetricsLogger;
@@ -342,6 +346,8 @@
DumpManager dumpManager,
KeyguardFaceAuthInteractor keyguardFaceAuthInteractor,
ShadeRepository shadeRepository,
+ ShadeInteractor shadeInteractor,
+ JavaAdapter javaAdapter,
CastController castController
) {
mPanelViewControllerLazy = panelViewControllerLazy;
@@ -387,6 +393,8 @@
mFeatureFlags = featureFlags;
mInteractionJankMonitor = interactionJankMonitor;
mShadeRepository = shadeRepository;
+ mShadeInteractor = shadeInteractor;
+ mJavaAdapter = javaAdapter;
mLockscreenShadeTransitionController.addCallback(new LockscreenShadeTransitionCallback());
dumpManager.registerDumpable(this);
@@ -459,7 +467,13 @@
}
// TODO (b/265054088): move this and others to a CoreStartable
- void initNotificationStackScrollLayoutController() {
+ void init() {
+ initNotificationStackScrollLayoutController();
+ mJavaAdapter.alwaysCollectFlow(
+ mShadeInteractor.isExpandToQsEnabled(), this::setExpansionEnabledPolicy);
+ }
+
+ private void initNotificationStackScrollLayoutController() {
mNotificationStackScrollLayoutController.setOverscrollTopChangedListener(
new NsslOverscrollTopChangedListener());
mNotificationStackScrollLayoutController.setOnStackYChanged(this::onStackYChanged);
@@ -780,7 +794,6 @@
/** update Qs height state */
public void setExpansionHeight(float height) {
- checkCorrectSplitShadeState(height);
int maxHeight = getMaxExpansionHeight();
height = Math.min(Math.max(
height, getMinExpansionHeight()), maxHeight);
@@ -802,14 +815,6 @@
}
}
- /** 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()) {
@@ -892,7 +897,7 @@
}
/** */
- public void setExpansionEnabledPolicy(boolean expansionEnabledPolicy) {
+ private void setExpansionEnabledPolicy(boolean expansionEnabledPolicy) {
mExpansionEnabledPolicy = expansionEnabledPolicy;
if (mQs != null) {
mQs.setHeaderClickable(isExpansionEnabled());
@@ -1888,7 +1893,7 @@
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 d0a3cbb..9ed0e9a 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 @@
/** 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 @@
*/
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 @@
/** 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 d00dab6..c9338b3 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 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.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 @@
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 @@
@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 @@
@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 @@
}
@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 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 boolean collapseShade() {
if (!mNotificationPanelViewController.isFullyCollapsed()) {
// close the shade if it was open
- animateCollapseShadeDelayed();
+ animateCollapseShadeForcedDelayed();
notifyVisibilityChanged(false);
return true;
@@ -227,6 +270,26 @@
}
@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 @@
}
}
- @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 @@
}
@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 b7551cf..a2b9351 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.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.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 @@
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 @@
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 @@
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 @@
// 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 5ac36bf..8d5c30b 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 @@
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 @@
/** 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 f75047c..3d9fcf9 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 @@
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 @@
* 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 @@
/** 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 @@
/** 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)
@@ -214,6 +231,9 @@
/** Sends an external (e.g. Status Bar) touch event to the Shade touch handler. */
fun handleExternalTouch(event: MotionEvent): Boolean
+ /** Starts tracking a shade expansion gesture that originated from the status bar. */
+ fun startTrackingExpansionFromStatusBar()
+
// ******* End Keyguard Section *********
/** Returns the ShadeHeadsUpTracker. */
@@ -226,6 +246,16 @@
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/domain/interactor/ShadeInteractor.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt
new file mode 100644
index 0000000..eceda84
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.shade.domain.interactor
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.data.repository.KeyguardRepository
+import com.android.systemui.statusbar.disableflags.data.repository.DisableFlagsRepository
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.UserSetupRepository
+import com.android.systemui.statusbar.policy.DeviceProvisionedController
+import com.android.systemui.user.domain.interactor.UserInteractor
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
+
+/** Business logic for shade interactions. */
+@SysUISingleton
+class ShadeInteractor
+@Inject
+constructor(
+ disableFlagsRepository: DisableFlagsRepository,
+ keyguardRepository: KeyguardRepository,
+ userSetupRepository: UserSetupRepository,
+ deviceProvisionedController: DeviceProvisionedController,
+ userInteractor: UserInteractor,
+) {
+ /** Emits true if the shade can be expanded from QQS to QS and false otherwise. */
+ val isExpandToQsEnabled: Flow<Boolean> =
+ combine(
+ disableFlagsRepository.disableFlags,
+ keyguardRepository.isDozing,
+ userSetupRepository.isUserSetupFlow,
+ ) { disableFlags, isDozing, isUserSetup ->
+ deviceProvisionedController.isDeviceProvisioned &&
+ // Disallow QS during setup if it's a simple user switcher. (The user intends to
+ // use the lock screen user switcher, QS is not needed.)
+ (isUserSetup || !userInteractor.isSimpleUserSwitcher) &&
+ disableFlags.isShadeEnabled() &&
+ disableFlags.isQuickSettingsEnabled() &&
+ !isDozing
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/transition/ScrimShadeTransitionController.kt b/packages/SystemUI/src/com/android/systemui/shade/transition/ScrimShadeTransitionController.kt
index 4e1c272..abb69f6 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 @@
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 @@
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 a532195..6c2c0cf 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 @@
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.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 @@
* @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 @@
}
@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 @@
}
}
- 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 @@
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 06f43f1..3918144 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.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 @@
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 @@
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 43fbc7c..a3fd82e9 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.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 @@
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 @@
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 @@
@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 b847fb6..9692482 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -56,7 +56,6 @@
import android.graphics.Color;
import android.hardware.biometrics.BiometricSourceType;
import android.hardware.face.FaceManager;
-import android.hardware.fingerprint.FingerprintManager;
import android.os.BatteryManager;
import android.os.Handler;
import android.os.Looper;
@@ -86,6 +85,8 @@
import com.android.systemui.R;
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.biometrics.FaceHelpMessageDeferral;
+import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor;
+import com.android.systemui.bouncer.domain.interactor.BouncerMessageInteractor;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Background;
@@ -95,7 +96,7 @@
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.keyguard.util.IndicationHelper;
import com.android.systemui.log.LogLevel;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -148,6 +149,7 @@
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;
@@ -225,7 +227,8 @@
// triggered while the device is asleep
private final AlarmTimeout mHideTransientMessageHandler;
private final AlarmTimeout mHideBiometricMessageHandler;
- private FeatureFlags mFeatureFlags;
+ private final FeatureFlags mFeatureFlags;
+ private final IndicationHelper mIndicationHelper;
/**
* Creates a new KeyguardIndicationController and registers callbacks.
@@ -256,7 +259,9 @@
AlternateBouncerInteractor alternateBouncerInteractor,
AlarmManager alarmManager,
UserTracker userTracker,
- FeatureFlags flags
+ BouncerMessageInteractor bouncerMessageInteractor,
+ FeatureFlags flags,
+ IndicationHelper indicationHelper
) {
mContext = context;
mBroadcastDispatcher = broadcastDispatcher;
@@ -281,7 +286,9 @@
mScreenLifecycle.addObserver(mScreenObserver);
mAlternateBouncerInteractor = alternateBouncerInteractor;
mUserTracker = userTracker;
+ mBouncerMessageInteractor = bouncerMessageInteractor;
mFeatureFlags = flags;
+ mIndicationHelper = indicationHelper;
mFaceAcquiredMessageDeferral = faceHelpMessageDeferral;
mCoExFaceAcquisitionMsgIdsToShow = new HashSet<>();
@@ -1156,6 +1163,11 @@
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 +1223,8 @@
if (biometricSourceType == FACE) {
mFaceAcquiredMessageDeferral.reset();
}
+ mBouncerMessageInteractor.setFaceAcquisitionMessage(null);
+ mBouncerMessageInteractor.setFingerprintAcquisitionMessage(null);
}
@Override
@@ -1231,18 +1245,20 @@
} else if (biometricSourceType == FINGERPRINT) {
onFingerprintAuthError(msgId, errString);
}
+ mBouncerMessageInteractor.setFaceAcquisitionMessage(null);
+ mBouncerMessageInteractor.setFingerprintAcquisitionMessage(null);
}
private void onFaceAuthError(int msgId, String errString) {
CharSequence deferredFaceMessage = mFaceAcquiredMessageDeferral.getDeferredMessage();
mFaceAcquiredMessageDeferral.reset();
- if (shouldSuppressFaceError(msgId)) {
- mKeyguardLogger.logBiometricMessage("suppressingFaceError", msgId, errString);
+ if (mIndicationHelper.shouldSuppressErrorMsg(FACE, msgId)) {
+ mKeyguardLogger.logBiometricMessage("KIC suppressingFaceError", msgId, errString);
return;
}
if (msgId == FaceManager.FACE_ERROR_TIMEOUT) {
handleFaceAuthTimeoutError(deferredFaceMessage);
- } else if (isLockoutError(msgId)) {
+ } else if (mIndicationHelper.isFaceLockoutErrorMsg(msgId)) {
handleFaceLockoutError(errString);
} else {
showErrorMessageNowOrLater(errString, null);
@@ -1250,8 +1266,8 @@
}
private void onFingerprintAuthError(int msgId, String errString) {
- if (shouldSuppressFingerprintError(msgId)) {
- mKeyguardLogger.logBiometricMessage("suppressingFingerprintError",
+ if (mIndicationHelper.shouldSuppressErrorMsg(FINGERPRINT, msgId)) {
+ mKeyguardLogger.logBiometricMessage("KIC suppressingFingerprintError",
msgId,
errString);
} else {
@@ -1259,19 +1275,6 @@
}
}
- private boolean shouldSuppressFingerprintError(int msgId) {
- return ((isPrimaryAuthRequired() && !isLockoutError(msgId))
- || msgId == FingerprintManager.FINGERPRINT_ERROR_CANCELED
- || msgId == FingerprintManager.FINGERPRINT_ERROR_USER_CANCELED
- || msgId == FingerprintManager.BIOMETRIC_ERROR_POWER_PRESSED);
- }
-
- private boolean shouldSuppressFaceError(int msgId) {
- return ((isPrimaryAuthRequired() && msgId != FaceManager.FACE_ERROR_LOCKOUT_PERMANENT)
- || msgId == FaceManager.FACE_ERROR_CANCELED
- || msgId == FaceManager.FACE_ERROR_UNABLE_TO_PROCESS);
- }
-
@Override
public void onTrustChanged(int userId) {
if (!isCurrentUser(userId)) return;
@@ -1315,6 +1318,8 @@
showActionToUnlock();
}
}
+ mBouncerMessageInteractor.setFaceAcquisitionMessage(null);
+ mBouncerMessageInteractor.setFingerprintAcquisitionMessage(null);
}
@Override
@@ -1393,11 +1398,6 @@
return mContext.getString(followupMsgId);
}
- private static boolean isLockoutError(int msgId) {
- return msgId == FaceManager.FACE_ERROR_LOCKOUT_PERMANENT
- || msgId == FaceManager.FACE_ERROR_LOCKOUT;
- }
-
private void handleFaceAuthTimeoutError(@Nullable CharSequence deferredFaceMessage) {
mKeyguardLogger.logBiometricMessage("deferred message after face auth timeout",
null, String.valueOf(deferredFaceMessage));
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LegacyNotificationShelfControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/LegacyNotificationShelfControllerImpl.java
index dcd9dc3..4ec5f46 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 @@
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 c098f45..e2d2ac0 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.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.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 @@
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 @@
// 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 1c9bc60..5dcf6d1 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 @@
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 c519115..da84afe 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
@@ -27,7 +27,6 @@
import android.os.PowerManager;
import android.os.RemoteException;
import android.os.ServiceManager;
-import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UserManager;
import android.service.notification.StatusBarNotification;
@@ -52,6 +51,7 @@
import com.android.systemui.R;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.power.domain.interactor.PowerInteractor;
import com.android.systemui.statusbar.dagger.CentralSurfacesDependenciesModule;
import com.android.systemui.statusbar.notification.NotifPipelineFlags;
import com.android.systemui.statusbar.notification.RemoteInputControllerLogger;
@@ -60,19 +60,15 @@
import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
-import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.policy.RemoteInputUriController;
import com.android.systemui.statusbar.policy.RemoteInputView;
import com.android.systemui.util.DumpUtilsKt;
import com.android.systemui.util.ListenerSet;
-import dagger.Lazy;
-
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
-import java.util.Optional;
import java.util.function.Consumer;
/**
@@ -95,10 +91,8 @@
private final NotificationLockscreenUserManager mLockscreenUserManager;
private final SmartReplyController mSmartReplyController;
private final NotificationVisibilityProvider mVisibilityProvider;
+ private final PowerInteractor mPowerInteractor;
private final ActionClickLogger mLogger;
-
- private final Lazy<Optional<CentralSurfaces>> mCentralSurfacesOptionalLazy;
-
protected final Context mContext;
protected final NotifPipelineFlags mNotifPipelineFlags;
private final UserManager mUserManager;
@@ -122,10 +116,8 @@
@Override
public boolean onInteraction(
View view, PendingIntent pendingIntent, RemoteViews.RemoteResponse response) {
- mCentralSurfacesOptionalLazy.get().ifPresent(
- centralSurfaces -> centralSurfaces.wakeUpIfDozing(
- SystemClock.uptimeMillis(), "NOTIFICATION_CLICK",
- PowerManager.WAKE_REASON_GESTURE));
+ mPowerInteractor.wakeUpIfDozing(
+ "NOTIFICATION_CLICK", PowerManager.WAKE_REASON_GESTURE);
final NotificationEntry entry = getNotificationForParent(view.getParent());
mLogger.logInitialClick(entry, pendingIntent);
@@ -259,7 +251,7 @@
NotificationLockscreenUserManager lockscreenUserManager,
SmartReplyController smartReplyController,
NotificationVisibilityProvider visibilityProvider,
- Lazy<Optional<CentralSurfaces>> centralSurfacesOptionalLazy,
+ PowerInteractor powerInteractor,
StatusBarStateController statusBarStateController,
RemoteInputUriController remoteInputUriController,
RemoteInputControllerLogger remoteInputControllerLogger,
@@ -271,7 +263,7 @@
mLockscreenUserManager = lockscreenUserManager;
mSmartReplyController = smartReplyController;
mVisibilityProvider = visibilityProvider;
- mCentralSurfacesOptionalLazy = centralSurfacesOptionalLazy;
+ mPowerInteractor = powerInteractor;
mLogger = logger;
mBarService = IStatusBarService.Stub.asInterface(
ServiceManager.getService(Context.STATUS_BAR_SERVICE));
@@ -402,7 +394,7 @@
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 2ca0b00..47a4641 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 @@
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 9d7f3be..25a1dc6 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.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 @@
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 @@
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 @@
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 @@
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 @@
// 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 @@
* 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 f6c9a5c..075b41b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
@@ -28,6 +28,7 @@
import com.android.systemui.animation.ActivityLaunchAnimator;
import com.android.systemui.animation.AnimationFeatureFlags;
import com.android.systemui.animation.DialogLaunchAnimator;
+import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
@@ -35,10 +36,10 @@
import com.android.systemui.dump.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.media.controls.pipeline.MediaDataManager;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.power.domain.interactor.PowerInteractor;
import com.android.systemui.settings.DisplayTracker;
import com.android.systemui.shade.carrier.ShadeCarrierGroupController;
import com.android.systemui.statusbar.ActionClickLogger;
@@ -79,14 +80,14 @@
import com.android.systemui.util.concurrency.DelayableExecutor;
import com.android.systemui.util.time.SystemClock;
-import java.util.Optional;
-import java.util.concurrent.Executor;
-
import dagger.Binds;
import dagger.Lazy;
import dagger.Module;
import dagger.Provides;
+import java.util.Optional;
+import java.util.concurrent.Executor;
+
/**
* This module provides instances needed to construct {@link CentralSurfacesImpl}. These are moved to
* this separate from {@link CentralSurfacesModule} module so that components that wish to build
@@ -104,7 +105,7 @@
NotificationLockscreenUserManager lockscreenUserManager,
SmartReplyController smartReplyController,
NotificationVisibilityProvider visibilityProvider,
- Lazy<Optional<CentralSurfaces>> centralSurfacesOptionalLazy,
+ PowerInteractor powerInteractor,
StatusBarStateController statusBarStateController,
RemoteInputUriController remoteInputUriController,
RemoteInputControllerLogger remoteInputControllerLogger,
@@ -117,7 +118,7 @@
lockscreenUserManager,
smartReplyController,
visibilityProvider,
- centralSurfacesOptionalLazy,
+ powerInteractor,
statusBarStateController,
remoteInputUriController,
remoteInputControllerLogger,
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 f04159c..dc86869 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 @@
}
/**
- * 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 @@
* 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/dagger/DisableFlagsModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/disableflags/dagger/DisableFlagsModule.kt
new file mode 100644
index 0000000..3d5f8df
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/disableflags/dagger/DisableFlagsModule.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.disableflags.dagger
+
+import com.android.systemui.statusbar.disableflags.data.repository.DisableFlagsRepository
+import com.android.systemui.statusbar.disableflags.data.repository.DisableFlagsRepositoryImpl
+import dagger.Binds
+import dagger.Module
+
+/** Provides information related to disable flags. */
+@Module
+abstract class DisableFlagsModule {
+ @Binds
+ abstract fun bindDisableFlagsRepo(impl: DisableFlagsRepositoryImpl): DisableFlagsRepository
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/disableflags/data/model/DisableFlagsModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/disableflags/data/model/DisableFlagsModel.kt
new file mode 100644
index 0000000..ac05248
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/disableflags/data/model/DisableFlagsModel.kt
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.systemui.statusbar.disableflags.data.model
+
+import android.app.StatusBarManager.DISABLE2_NONE
+import android.app.StatusBarManager.DISABLE2_NOTIFICATION_SHADE
+import android.app.StatusBarManager.DISABLE2_QUICK_SETTINGS
+import android.app.StatusBarManager.DISABLE_NONE
+import android.app.StatusBarManager.DISABLE_NOTIFICATION_ALERTS
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.LogLevel
+import com.android.systemui.statusbar.disableflags.DisableFlagsLogger
+
+/**
+ * Model for the disable flags that come from [IStatusBar].
+ *
+ * For clients of the disable flags: do *not* refer to the disable integers directly. Instead,
+ * re-use or define a helper method that internally processes the flags. (We want to hide the
+ * bitwise logic here so no one else has to worry about it.)
+ */
+data class DisableFlagsModel(
+ private val disable1: Int = DISABLE_NONE,
+ private val disable2: Int = DISABLE2_NONE,
+) {
+ /** Returns true if notification alerts are allowed based on the flags. */
+ fun areNotificationAlertsEnabled(): Boolean {
+ return (disable1 and DISABLE_NOTIFICATION_ALERTS) == 0
+ }
+
+ /** Returns true if the shade is allowed based on the flags. */
+ fun isShadeEnabled(): Boolean {
+ return (disable2 and DISABLE2_NOTIFICATION_SHADE) == 0
+ }
+
+ /** Returns true if full quick settings are allowed based on the flags. */
+ fun isQuickSettingsEnabled(): Boolean {
+ return (disable2 and DISABLE2_QUICK_SETTINGS) == 0
+ }
+
+ /** Logs the change to the provided buffer. */
+ fun logChange(buffer: LogBuffer, disableFlagsLogger: DisableFlagsLogger) {
+ buffer.log(
+ TAG,
+ LogLevel.INFO,
+ {
+ int1 = disable1
+ int2 = disable2
+ },
+ {
+ disableFlagsLogger.getDisableFlagsString(
+ new = DisableFlagsLogger.DisableState(int1, int2),
+ )
+ }
+ )
+ }
+
+ private companion object {
+ const val TAG = "DisableFlagsModel"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/disableflags/data/repository/DisableFlagsRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/disableflags/data/repository/DisableFlagsRepository.kt
new file mode 100644
index 0000000..13b74b4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/disableflags/data/repository/DisableFlagsRepository.kt
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.systemui.statusbar.disableflags.data.repository
+
+import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.DisplayId
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.dagger.DisableFlagsRepositoryLog
+import com.android.systemui.statusbar.CommandQueue
+import com.android.systemui.statusbar.disableflags.DisableFlagsLogger
+import com.android.systemui.statusbar.disableflags.data.model.DisableFlagsModel
+import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.flow.stateIn
+
+/** Repository for the disable flags received from external systems. See [IStatusBar.disable]. */
+interface DisableFlagsRepository {
+ /** A model of the disable flags last received from [IStatusBar]. */
+ val disableFlags: StateFlow<DisableFlagsModel>
+}
+
+@SysUISingleton
+class DisableFlagsRepositoryImpl
+@Inject
+constructor(
+ commandQueue: CommandQueue,
+ @DisplayId private val thisDisplayId: Int,
+ @Application scope: CoroutineScope,
+ remoteInputQuickSettingsDisabler: RemoteInputQuickSettingsDisabler,
+ @DisableFlagsRepositoryLog private val logBuffer: LogBuffer,
+ private val disableFlagsLogger: DisableFlagsLogger,
+) : DisableFlagsRepository {
+ override val disableFlags: StateFlow<DisableFlagsModel> =
+ conflatedCallbackFlow {
+ val callback =
+ object : CommandQueue.Callbacks {
+ override fun disable(
+ displayId: Int,
+ state1: Int,
+ state2: Int,
+ animate: Boolean,
+ ) {
+ if (displayId != thisDisplayId) {
+ return
+ }
+ trySend(
+ DisableFlagsModel(
+ state1,
+ // Ideally, [RemoteInputQuickSettingsDisabler] should instead
+ // expose a flow that gets `combine`d with this [disableFlags]
+ // flow in a [DisableFlagsInteractor] or
+ // [QuickSettingsInteractor]-type class. However, that's out of
+ // scope for the CentralSurfaces removal project.
+ remoteInputQuickSettingsDisabler.adjustDisableFlags(state2),
+ )
+ )
+ }
+ }
+ commandQueue.addCallback(callback)
+ awaitClose { commandQueue.removeCallback(callback) }
+ }
+ .distinctUntilChanged()
+ .onEach { it.logChange(logBuffer, disableFlagsLogger) }
+ // Use Eagerly because we always need to know about disable flags
+ .stateIn(scope, SharingStarted.Eagerly, DisableFlagsModel())
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
index 518825c..8778463 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 @@
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) {
@@ -163,7 +176,16 @@
now.isBefore(Instant.ofEpochMilli(t.expiryTimeMillis))
}
if (weatherTarget != null) {
- val weatherData = WeatherData.fromBundle(weatherTarget.baseAction.extras)
+ val clickIntent = weatherTarget.headerAction?.intent
+ val weatherData = WeatherData.fromBundle(weatherTarget.baseAction.extras, { v ->
+ if (!falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
+ activityStarter.startActivity(
+ clickIntent,
+ true, /* dismissShade */
+ null,
+ false)
+ }
+ })
if (weatherData != null) {
keyguardUpdateMonitor.sendWeatherData(weatherData)
}
@@ -171,23 +193,6 @@
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 bab553e..d10fac6 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 @@
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 @@
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 @@
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 @@
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 @@
/** 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 8aeefee..5c72731 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.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 @@
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 @@
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 @@
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 @@
}
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 @@
*/
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 @@
// 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 @@
* @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 @@
}
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);
}
}
}
@@ -840,6 +875,7 @@
&& !hasFlag(entry, Notification.FLAG_ONGOING_EVENT)
&& !hasFlag(entry, Notification.FLAG_BUBBLE)
&& !hasFlag(entry, Notification.FLAG_NO_CLEAR)
+ && (entry.getChannel() == null || !entry.getChannel().isImportantConversation())
&& entry.getDismissState() != 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 bdb206b..1896080 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 @@
private NotificationPresenter mPresenter;
private NotificationListContainer mListContainer;
- private BindRowCallback mBindRowCallback;
private NotificationClicker mNotificationClicker;
private FeatureFlags mFeatureFlags;
@@ -103,11 +102,9 @@
* 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();
}
@@ -143,7 +140,6 @@
.expandableNotificationRow(row)
.notificationEntry(entry)
.onExpandClickListener(mPresenter)
- .listContainer(mListContainer)
.build();
ExpandableNotificationRowController rowController =
component.getExpandableNotificationRowController();
@@ -179,7 +175,7 @@
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 +231,4 @@
}
});
}
-
- /** 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 20de785..73227ab 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 @@
})
}
- 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 @@
})
}
- 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 @@
})
}
- 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 @@
"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/dagger/NotificationsModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
index b100d44..31d4ab9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
@@ -63,8 +63,12 @@
import com.android.systemui.statusbar.notification.logging.NotificationPanelLoggerImpl;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.notification.row.OnUserInteractionCallback;
+import com.android.systemui.statusbar.notification.row.ui.viewmodel.ActivatableNotificationViewModelModule;
+import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager;
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
import com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm;
+import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationListViewModelModule;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import dagger.Binds;
@@ -85,6 +89,8 @@
ShadeEventsModule.class,
NotifPipelineChoreographerModule.class,
NotificationSectionHeadersModule.class,
+ NotificationListViewModelModule.class,
+ ActivatableNotificationViewModelModule.class,
})
public interface NotificationsModule {
@Binds
@@ -159,6 +165,14 @@
}
}
+ /** Provides the container for the notification list. */
+ @Provides
+ @SysUISingleton
+ static NotificationListContainer provideListContainer(
+ NotificationStackScrollLayoutController nsslController) {
+ return nsslController.getNotificationListContainer();
+ }
+
/**
* Provide the active notification collection managing the notifications to render.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsInteractor.kt
new file mode 100644
index 0000000..8f7e269
--- /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 a5278c3d..76e228b 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 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 @@
*/
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 0ed4175..f7bd177 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.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 @@
) : NotificationsController {
override fun initialize(
- centralSurfaces: CentralSurfaces,
presenter: NotificationPresenter,
listContainer: NotificationListContainer,
stackController: NotifStackController,
notificationActivityStarter: NotificationActivityStarter,
- bindRowCallback: NotificationRowBinderImpl.BindRowCallback
) {
notificationListener.registerAsSystemService()
@@ -94,13 +91,8 @@
})
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 14856da..65ba6de 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.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 @@
) : 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 417d4ac..8af488e 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 @@
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 9f397fe..30747db 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -53,6 +53,7 @@
import android.view.NotificationHeaderView;
import android.view.View;
import android.view.ViewGroup;
+import android.view.ViewParent;
import android.view.ViewStub;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
@@ -103,6 +104,8 @@
import com.android.systemui.statusbar.notification.stack.AnimationProperties;
import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainer;
+import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainerLogger;
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.notification.stack.SwipeableView;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
@@ -170,6 +173,7 @@
private PeopleNotificationIdentifier mPeopleNotificationIdentifier;
private Optional<BubblesManager> mBubblesManagerOptional;
private MetricsLogger mMetricsLogger;
+ private NotificationChildrenContainerLogger mChildrenContainerLogger;
private NotificationDismissibilityProvider mDismissibilityProvider;
private FeatureFlags mFeatureFlags;
private int mIconTransformContentShift;
@@ -1627,6 +1631,51 @@
NotificationEntry child,
NotificationEntry newParent
);
+
+ /**
+ * Called when an ExpandableNotificationRow transient view is removed from the
+ * NotificationChildrenContainer
+ */
+ void logRemoveTransientFromContainer(
+ NotificationEntry childEntry,
+ NotificationEntry containerEntry
+ );
+
+ /**
+ * Called when an ExpandableNotificationRow transient view is removed from the
+ * NotificationStackScrollLayout
+ */
+ void logRemoveTransientFromNssl(
+ NotificationEntry childEntry
+ );
+
+ /**
+ * Called when an ExpandableNotificationRow transient view is removed from a ViewGroup that
+ * is not NotificationChildrenContainer or NotificationStackScrollLayout
+ */
+ void logRemoveTransientFromViewGroup(
+ NotificationEntry childEntry,
+ ViewGroup containerView
+ );
+
+ /**
+ * Called when an ExpandableNotificationRow transient view is added to this
+ * ExpandableNotificationRow
+ */
+ void logAddTransientRow(
+ NotificationEntry childEntry,
+ NotificationEntry containerEntry,
+ int index
+ );
+
+ /**
+ * Called when an ExpandableNotificationRow transient view is removed from this
+ * ExpandableNotificationRow
+ */
+ void logRemoveTransientRow(
+ NotificationEntry childEntry,
+ NotificationEntry containerEntry
+ );
}
/**
@@ -1668,6 +1717,7 @@
NotificationGutsManager gutsManager,
NotificationDismissibilityProvider dismissibilityProvider,
MetricsLogger metricsLogger,
+ NotificationChildrenContainerLogger childrenContainerLogger,
SmartReplyConstants smartReplyConstants,
SmartReplyController smartReplyController,
FeatureFlags featureFlags,
@@ -1706,6 +1756,7 @@
mBubblesManagerOptional = bubblesManagerOptional;
mNotificationGutsManager = gutsManager;
mMetricsLogger = metricsLogger;
+ mChildrenContainerLogger = childrenContainerLogger;
mDismissibilityProvider = dismissibilityProvider;
mFeatureFlags = featureFlags;
}
@@ -1874,6 +1925,7 @@
mChildrenContainer.setIsLowPriority(mIsLowPriority);
mChildrenContainer.setContainingNotification(ExpandableNotificationRow.this);
mChildrenContainer.onNotificationUpdated();
+ mChildrenContainer.setLogger(mChildrenContainerLogger);
mTranslateableViews.add(mChildrenContainer);
});
@@ -2673,7 +2725,12 @@
}
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 +3677,8 @@
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();
@@ -3707,4 +3766,73 @@
public boolean getShowSnooze() {
return mShowSnooze;
}
+
+ @Override
+ public void removeFromTransientContainer() {
+ final ViewGroup transientContainer = getTransientContainer();
+ final ViewParent parent = getParent();
+ // Only log when there is real removal of transient views
+ if (transientContainer == null || transientContainer != parent) {
+ super.removeFromTransientContainer();
+ return;
+ }
+ logRemoveFromTransientContainer(transientContainer);
+ super.removeFromTransientContainer();
+ }
+
+ /**
+ * Log calls to removeFromTransientContainer when the container is NotificationChildrenContainer
+ * or NotificationStackScrollLayout.
+ */
+ public void logRemoveFromTransientContainer(ViewGroup transientContainer) {
+ if (mLogger == null) {
+ return;
+ }
+ if (transientContainer instanceof NotificationChildrenContainer) {
+ mLogger.logRemoveTransientFromContainer(
+ /* childEntry = */ getEntry(),
+ /* containerEntry = */ ((NotificationChildrenContainer) transientContainer)
+ .getContainingNotification().getEntry()
+ );
+ } else if (transientContainer instanceof NotificationStackScrollLayout) {
+ mLogger.logRemoveTransientFromNssl(
+ /* childEntry = */ getEntry()
+ );
+ } else {
+ mLogger.logRemoveTransientFromViewGroup(
+ /* childEntry = */ getEntry(),
+ /* containerView = */ transientContainer
+ );
+ }
+ }
+
+ @Override
+ public void addTransientView(View view, int index) {
+ if (view instanceof ExpandableNotificationRow) {
+ logAddTransientRow((ExpandableNotificationRow) view, index);
+ }
+ super.addTransientView(view, index);
+ }
+
+ private void logAddTransientRow(ExpandableNotificationRow row, int index) {
+ if (mLogger == null) {
+ return;
+ }
+ mLogger.logAddTransientRow(row.getEntry(), getEntry(), index);
+ }
+
+ @Override
+ public void removeTransientView(View view) {
+ if (view instanceof ExpandableNotificationRow) {
+ logRemoveTransientRow((ExpandableNotificationRow) view);
+ }
+ super.removeTransientView(view);
+ }
+
+ private void logRemoveTransientRow(ExpandableNotificationRow row) {
+ if (mLogger == null) {
+ return;
+ }
+ mLogger.logRemoveTransientRow(row.getEntry(), getEntry());
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
index 1acc9f9..a4e8c2e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
@@ -50,6 +50,7 @@
import com.android.systemui.statusbar.notification.row.dagger.AppName;
import com.android.systemui.statusbar.notification.row.dagger.NotificationKey;
import com.android.systemui.statusbar.notification.row.dagger.NotificationRowScope;
+import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainerLogger;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
@@ -88,6 +89,7 @@
private final ExpandableNotificationRow.OnExpandClickListener mOnExpandClickListener;
private final StatusBarStateController mStatusBarStateController;
private final MetricsLogger mMetricsLogger;
+ private final NotificationChildrenContainerLogger mChildrenContainerLogger;
private final ExpandableNotificationRow.CoordinateOnClickListener mOnFeedbackClickListener;
private final NotificationGutsManager mNotificationGutsManager;
private final OnUserInteractionCallback mOnUserInteractionCallback;
@@ -125,6 +127,46 @@
) {
mLogBufferLogger.logSkipAttachingKeepInParentChild(child, newParent);
}
+
+ @Override
+ public void logRemoveTransientFromContainer(
+ NotificationEntry childEntry,
+ NotificationEntry containerEntry
+ ) {
+ mLogBufferLogger.logRemoveTransientFromContainer(childEntry, containerEntry);
+ }
+
+ @Override
+ public void logRemoveTransientFromNssl(
+ NotificationEntry childEntry
+ ) {
+ mLogBufferLogger.logRemoveTransientFromNssl(childEntry);
+ }
+
+ @Override
+ public void logRemoveTransientFromViewGroup(
+ NotificationEntry childEntry,
+ ViewGroup containerView
+ ) {
+ mLogBufferLogger.logRemoveTransientFromViewGroup(childEntry, containerView);
+ }
+
+ @Override
+ public void logAddTransientRow(
+ NotificationEntry childEntry,
+ NotificationEntry containerEntry,
+ int index
+ ) {
+ mLogBufferLogger.logAddTransientRow(childEntry, containerEntry, index);
+ }
+
+ @Override
+ public void logRemoveTransientRow(
+ NotificationEntry childEntry,
+ NotificationEntry containerEntry
+ ) {
+ mLogBufferLogger.logRemoveTransientRow(childEntry, containerEntry);
+ }
};
@@ -135,6 +177,7 @@
RemoteInputViewSubcomponent.Factory rivSubcomponentFactory,
MetricsLogger metricsLogger,
NotificationRowLogger logBufferLogger,
+ NotificationChildrenContainerLogger childrenContainerLogger,
NotificationListContainer listContainer,
SmartReplyConstants smartReplyConstants,
SmartReplyController smartReplyController,
@@ -188,6 +231,7 @@
mBubblesManagerOptional = bubblesManagerOptional;
mDragController = dragController;
mMetricsLogger = metricsLogger;
+ mChildrenContainerLogger = childrenContainerLogger;
mLogBufferLogger = logBufferLogger;
mSmartReplyConstants = smartReplyConstants;
mSmartReplyController = smartReplyController;
@@ -222,6 +266,7 @@
mNotificationGutsManager,
mDismissibilityProvider,
mMetricsLogger,
+ mChildrenContainerLogger,
mSmartReplyConstants,
mSmartReplyController,
mFeatureFlags,
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 7a2bee9..b950187 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 @@
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 0989df6..6bbeebf 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 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 b4bfded..13d1978 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 @@
parentLayout,
remoteViewClickHandler);
validateView(v, entry, row.getResources());
- v.setIsRootNamespace(true);
applyCallback.setResultView(v);
} else {
newContentView.reapply(
@@ -511,7 +510,6 @@
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 124df8c..20f4429 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 @@
mSmartReplyConstants = smartReplyConstants;
mSmartReplyController = smartReplyController;
mStatusBarService = statusBarService;
+ // We set root namespace so that we avoid searching children for id. Notification might
+ // contain custom view and their ids may clash with ids already existing in shade or
+ // notification panel
+ setIsRootNamespace(true);
+ }
+
+ @Override
+ public View focusSearch(View focused, int direction) {
+ // This implementation is copied from ViewGroup but with removed special handling of
+ // setIsRootNamespace. This view is set as tree root using setIsRootNamespace and it
+ // causes focus to be stuck inside of it. We need to be root to avoid id conflicts
+ // but we don't want to behave like root when it comes to focusing.
+ if (mParent != null) {
+ return mParent.focusSearch(focused, direction);
+ }
+ Log.wtf(TAG, "NotificationContentView doesn't have parent");
+ return null;
}
public void reinflate() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowLogger.kt
index c3dd92a..89338f9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowLogger.kt
@@ -17,14 +17,21 @@
package com.android.systemui.statusbar.notification.row
+import android.view.ViewGroup
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.LogLevel
import com.android.systemui.log.dagger.NotificationLog
+import com.android.systemui.log.dagger.NotificationRenderLog
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.logKey
import javax.inject.Inject
-class NotificationRowLogger @Inject constructor(@NotificationLog private val buffer: LogBuffer) {
+class NotificationRowLogger
+@Inject
+constructor(
+ @NotificationLog private val buffer: LogBuffer,
+ @NotificationRenderLog private val notificationRenderBuffer: LogBuffer
+) {
fun logKeepInParentChildDetached(child: NotificationEntry, oldParent: NotificationEntry?) {
buffer.log(
TAG,
@@ -48,6 +55,79 @@
{ "Skipping to attach $str1 to $str2, because it still flagged to keep in parent" }
)
}
+
+ fun logRemoveTransientFromContainer(
+ childEntry: NotificationEntry,
+ containerEntry: NotificationEntry
+ ) {
+ notificationRenderBuffer.log(
+ TAG,
+ LogLevel.INFO,
+ {
+ str1 = childEntry.logKey
+ str2 = containerEntry.logKey
+ },
+ { "RemoveTransientRow from ChildrenContainer: childKey: $str1 -- containerKey: $str2" }
+ )
+ }
+
+ fun logRemoveTransientFromNssl(
+ childEntry: NotificationEntry,
+ ) {
+ notificationRenderBuffer.log(
+ TAG,
+ LogLevel.INFO,
+ { str1 = childEntry.logKey },
+ { "RemoveTransientRow from Nssl: childKey: $str1" }
+ )
+ }
+
+ fun logRemoveTransientFromViewGroup(
+ childEntry: NotificationEntry,
+ containerView: ViewGroup,
+ ) {
+ notificationRenderBuffer.log(
+ TAG,
+ LogLevel.WARNING,
+ {
+ str1 = childEntry.logKey
+ str2 = containerView.toString()
+ },
+ { "RemoveTransientRow from other ViewGroup: childKey: $str1 -- ViewGroup: $str2" }
+ )
+ }
+
+ fun logAddTransientRow(
+ childEntry: NotificationEntry,
+ containerEntry: NotificationEntry,
+ index: Int
+ ) {
+ notificationRenderBuffer.log(
+ TAG,
+ LogLevel.ERROR,
+ {
+ str1 = childEntry.logKey
+ str2 = containerEntry.logKey
+ int1 = index
+ },
+ { "addTransientRow to row: childKey: $str1 -- containerKey: $str2 -- index: $int1" }
+ )
+ }
+
+ fun logRemoveTransientRow(
+ childEntry: NotificationEntry,
+ containerEntry: NotificationEntry,
+ ) {
+ notificationRenderBuffer.log(
+ TAG,
+ LogLevel.ERROR,
+ {
+ str1 = childEntry.logKey
+ str2 = containerEntry.logKey
+ },
+ { "removeTransientRow from row: childKey: $str1 -- containerKey: $str2" }
+ )
+ }
}
private const val TAG = "NotifRow"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/dagger/ExpandableNotificationRowComponent.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/dagger/ExpandableNotificationRowComponent.java
index 1a7417a..3588621 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/dagger/ExpandableNotificationRowComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/dagger/ExpandableNotificationRowComponent.java
@@ -25,7 +25,6 @@
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRowController;
-import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.statusbar.phone.CentralSurfaces;
import dagger.Binds;
@@ -59,8 +58,6 @@
Builder notificationEntry(NotificationEntry entry);
@BindsInstance
Builder onExpandClickListener(ExpandableNotificationRow.OnExpandClickListener presenter);
- @BindsInstance
- Builder listContainer(NotificationListContainer listContainer);
ExpandableNotificationRowComponent build();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractor.kt
index 014406f..4b89615 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 @@
/** 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 12956ab..23a58d2 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 @@
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.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 @@
* [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 @@
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/shelf/ui/viewmodel/NotificationShelfViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModel.kt
index fb19443..5ca8b53 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModel.kt
@@ -16,16 +16,16 @@
package com.android.systemui.statusbar.notification.shelf.ui.viewmodel
+import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.statusbar.NotificationShelf
import com.android.systemui.statusbar.notification.row.ui.viewmodel.ActivatableNotificationViewModel
import com.android.systemui.statusbar.notification.shelf.domain.interactor.NotificationShelfInteractor
-import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent.CentralSurfacesScope
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
/** ViewModel for [NotificationShelf]. */
-@CentralSurfacesScope
+@SysUISingleton
class NotificationShelfViewModel
@Inject
constructor(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
index f8e374d..dad8064 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
@@ -132,6 +132,8 @@
private boolean mContainingNotificationIsFaded = false;
private RoundableState mRoundableState;
+ private NotificationChildrenContainerLogger mLogger;
+
public NotificationChildrenContainer(Context context) {
this(context, null);
}
@@ -1507,6 +1509,33 @@
return mNotificationHeaderWrapper;
}
+ public void setLogger(NotificationChildrenContainerLogger logger) {
+ mLogger = logger;
+ }
+
+ @Override
+ public void addTransientView(View view, int index) {
+ if (mLogger != null && view instanceof ExpandableNotificationRow) {
+ mLogger.addTransientRow(
+ ((ExpandableNotificationRow) view).getEntry(),
+ getContainingNotification().getEntry(),
+ index
+ );
+ }
+ super.addTransientView(view, index);
+ }
+
+ @Override
+ public void removeTransientView(View view) {
+ if (mLogger != null && view instanceof ExpandableNotificationRow) {
+ mLogger.removeTransientRow(
+ ((ExpandableNotificationRow) view).getEntry(),
+ getContainingNotification().getEntry()
+ );
+ }
+ super.removeTransientView(view);
+ }
+
public String debugString() {
return TAG + " { "
+ "visibility: " + getVisibility()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerLogger.kt
new file mode 100644
index 0000000..6be1ef8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerLogger.kt
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.stack
+
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.LogLevel
+import com.android.systemui.log.dagger.NotificationRenderLog
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.logKey
+import javax.inject.Inject
+
+class NotificationChildrenContainerLogger
+@Inject
+constructor(@NotificationRenderLog private val notificationRenderBuffer: LogBuffer) {
+ fun addTransientRow(
+ childEntry: NotificationEntry,
+ containerEntry: NotificationEntry,
+ index: Int
+ ) {
+ notificationRenderBuffer.log(
+ TAG,
+ LogLevel.INFO,
+ {
+ str1 = childEntry.logKey
+ str2 = containerEntry.logKey
+ int1 = index
+ },
+ { "addTransientRow: childKey: $str1 -- containerKey: $str2 -- index: $int1" }
+ )
+ }
+
+ fun removeTransientRow(
+ childEntry: NotificationEntry,
+ containerEntry: NotificationEntry,
+ ) {
+ notificationRenderBuffer.log(
+ TAG,
+ LogLevel.INFO,
+ {
+ str1 = childEntry.logKey
+ str2 = containerEntry.logKey
+ },
+ { "removeTransientRow: childKey: $str1 -- containerKey: $str2" }
+ )
+ }
+
+ companion object {
+ private const val TAG = "NotifChildrenContainer"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index df6fe3d..479fbdb 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.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 @@
*/
private Set<Integer> mDebugTextUsedYPositions;
private final boolean mDebugRemoveAnimation;
- private final boolean mSimplifiedAppearFraction;
private final boolean mSensitiveRevealAnimEndabled;
private boolean mAnimatedInsets;
@@ -316,7 +315,7 @@
}
};
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 @@
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 @@
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 @@
* 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;
}
@@ -2778,6 +2759,7 @@
boolean animationGenerated = container != null && generateRemoveAnimation(child);
if (animationGenerated) {
if (!mSwipedOutViews.contains(child) || !isFullySwipedOut(child)) {
+ logAddTransientChild(child, container);
container.addTransientView(child, 0);
child.setTransientContainer(container);
}
@@ -2793,6 +2775,46 @@
focusNextViewIfFocused(child);
}
+ private void logAddTransientChild(ExpandableView child, ViewGroup container) {
+ if (mLogger == null) {
+ return;
+ }
+ if (child instanceof ExpandableNotificationRow) {
+ if (container instanceof NotificationChildrenContainer) {
+ mLogger.addTransientChildNotificationToChildContainer(
+ ((ExpandableNotificationRow) child).getEntry(),
+ ((NotificationChildrenContainer) container)
+ .getContainingNotification().getEntry()
+ );
+ } else if (container instanceof NotificationStackScrollLayout) {
+ mLogger.addTransientChildNotificationToNssl(
+ ((ExpandableNotificationRow) child).getEntry()
+ );
+ } else {
+ mLogger.addTransientChildNotificationToViewGroup(
+ ((ExpandableNotificationRow) child).getEntry(),
+ container
+ );
+ }
+ }
+ }
+
+ @Override
+ public void addTransientView(View view, int index) {
+ if (mLogger != null && view instanceof ExpandableNotificationRow) {
+ mLogger.addTransientRow(((ExpandableNotificationRow) view).getEntry(), index);
+ }
+ super.addTransientView(view, index);
+ }
+
+ @Override
+ public void removeTransientView(View view) {
+ if (mLogger != null && view instanceof ExpandableNotificationRow) {
+ mLogger.removeTransientRow(((ExpandableNotificationRow) view).getEntry());
+ }
+ super.removeTransientView(view);
+ }
+
/**
* Has this view been fully swiped out such that it's not visible anymore.
*/
@@ -3052,7 +3074,9 @@
if (!animationsEnabled) {
mSwipedOutViews.clear();
mChildrenToRemoveAnimated.clear();
- clearTemporaryViewsInGroup(this);
+ clearTemporaryViewsInGroup(
+ /* viewGroup = */ this,
+ /* reason = */ "setAnimationsEnabled");
}
}
@@ -4008,7 +4032,7 @@
mAmbientState.setExpansionChanging(false);
if (!mIsExpanded) {
resetScrollPosition();
- mCentralSurfaces.resetUserExpandedStates();
+ mNotificationsController.resetUserExpandedStates();
clearTemporaryViews();
clearUserLockedViews();
resetAllSwipeState();
@@ -4027,26 +4051,48 @@
private void clearTemporaryViews() {
// lets make sure nothing is transient anymore
- clearTemporaryViewsInGroup(this);
+ clearTemporaryViewsInGroup(
+ /* viewGroup = */ this,
+ /* reason = */ "clearTemporaryViews"
+ );
for (int i = 0; i < getChildCount(); i++) {
ExpandableView child = getChildAtIndex(i);
if (child instanceof ExpandableNotificationRow) {
ExpandableNotificationRow row = (ExpandableNotificationRow) child;
- clearTemporaryViewsInGroup(row.getChildrenContainer());
+ clearTemporaryViewsInGroup(
+ /* viewGroup = */ row.getChildrenContainer(),
+ /* reason = */ "clearTemporaryViewsInGroup(row.getChildrenContainer())"
+ );
}
}
}
- private void clearTemporaryViewsInGroup(ViewGroup viewGroup) {
+ private void clearTemporaryViewsInGroup(ViewGroup viewGroup, String reason) {
while (viewGroup != null && viewGroup.getTransientViewCount() != 0) {
final View transientView = viewGroup.getTransientView(0);
viewGroup.removeTransientView(transientView);
if (transientView instanceof ExpandableView) {
((ExpandableView) transientView).setTransientContainer(null);
+ if (transientView instanceof ExpandableNotificationRow) {
+ logTransientNotificationRowTraversalCleaned(
+ (ExpandableNotificationRow) transientView,
+ reason
+ );
+ }
}
}
}
+ private void logTransientNotificationRowTraversalCleaned(
+ ExpandableNotificationRow transientView,
+ String reason
+ ) {
+ if (mLogger == null) {
+ return;
+ }
+ mLogger.transientNotificationRowTraversalCleaned(transientView.getEntry(), reason);
+ }
+
void onPanelTrackingStarted() {
mPanelTracking = true;
mAmbientState.setPanelTracking(true);
@@ -4602,8 +4648,8 @@
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 +5116,9 @@
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 9272c37..7c66bbe5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -29,6 +29,7 @@
import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_HIGH_PRIORITY;
import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.SelectedRows;
import static com.android.systemui.statusbar.phone.NotificationIconAreaController.HIGH_PRIORITY;
+import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow;
import android.content.res.Configuration;
import android.content.res.Resources;
@@ -56,11 +57,18 @@
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.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.keyguard.data.repository.KeyguardTransitionRepository;
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
+import com.android.systemui.keyguard.shared.model.KeyguardState;
+import com.android.systemui.keyguard.shared.model.TransitionStep;
import com.android.systemui.media.controls.ui.KeyguardMediaController;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.FalsingManager;
@@ -69,6 +77,7 @@
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 +107,7 @@
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,14 +117,12 @@
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;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.NotificationIconAreaController;
import com.android.systemui.statusbar.phone.ScrimController;
-import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
@@ -137,13 +145,14 @@
/**
* Controller for {@link NotificationStackScrollLayout}.
*/
-@CentralSurfacesComponent.CentralSurfacesScope
+@SysUISingleton
public class NotificationStackScrollLayoutController {
private static final String TAG = "StackScrollerController";
private static final boolean DEBUG = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.DEBUG);
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 +179,9 @@
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;
@@ -184,13 +193,17 @@
private final GroupExpansionManager mGroupExpansionManager;
private final NotifPipelineFlags mNotifPipelineFlags;
private final SeenNotificationsProvider mSeenNotificationsProvider;
+ private final KeyguardTransitionRepository mKeyguardTransitionRepo;
private NotificationStackScrollLayout mView;
private NotificationSwipeHelper mSwipeHelper;
@Nullable
private Boolean mHistoryEnabled;
private int mBarState;
+ private boolean mIsBouncerShowingFromCentralSurfaces;
private HeadsUpAppearanceController mHeadsUpAppearanceController;
+ private boolean mIsInTransitionToAod = false;
+
private final FeatureFlags mFeatureFlags;
private final NotificationTargetsHelper mNotificationTargetsHelper;
private final SecureSettings mSecureSettings;
@@ -429,7 +442,7 @@
@Override
public void onSnooze(StatusBarNotification sbn,
NotificationSwipeActionHelper.SnoozeOption snoozeOption) {
- mCentralSurfaces.setNotificationSnoozed(sbn, snoozeOption);
+ mNotificationsController.setNotificationSnoozed(sbn, snoozeOption);
}
@Override
@@ -559,7 +572,8 @@
@Override
public float getFalsingThresholdFactor() {
- return mCentralSurfaces.isWakeUpComingFromTouch() ? 1.5f : 1.0f;
+ return ShadeViewController.getFalsingThresholdFactor(
+ mKeyguardInteractor.getWakefulnessModel().getValue());
}
@Override
@@ -613,6 +627,7 @@
NotificationStackScrollLayout view,
@Named(ALLOW_NOTIFICATION_LONG_PRESS_NAME) boolean allowLongPress,
NotificationGutsManager notificationGutsManager,
+ NotificationsController notificationsController,
NotificationVisibilityProvider visibilityProvider,
HeadsUpManagerPhone headsUpManager,
NotificationRoundnessManager notificationRoundnessManager,
@@ -623,6 +638,9 @@
SysuiStatusBarStateController statusBarStateController,
KeyguardMediaController keyguardMediaController,
KeyguardBypassController keyguardBypassController,
+ KeyguardInteractor keyguardInteractor,
+ PrimaryBouncerInteractor primaryBouncerInteractor,
+ KeyguardTransitionRepository keyguardTransitionRepo,
ZenModeController zenModeController,
NotificationLockscreenUserManager lockscreenUserManager,
Optional<NotificationListViewModel> nsslViewModel,
@@ -632,7 +650,6 @@
FalsingManager falsingManager,
@Main Resources resources,
NotificationSwipeHelper.Builder notificationSwipeHelperBuilder,
- CentralSurfaces centralSurfaces,
ScrimController scrimController,
GroupExpansionManager groupManager,
@SilentHeader SectionHeaderController silentHeaderController,
@@ -656,10 +673,12 @@
NotificationDismissibilityProvider dismissibilityProvider,
ActivityStarter activityStarter) {
mView = view;
+ mKeyguardTransitionRepo = keyguardTransitionRepo;
mStackStateLogger = stackLogger;
mLogger = logger;
mAllowLongPress = allowLongPress;
mNotificationGutsManager = notificationGutsManager;
+ mNotificationsController = notificationsController;
mVisibilityProvider = visibilityProvider;
mHeadsUpManager = headsUpManager;
mNotificationRoundnessManager = notificationRoundnessManager;
@@ -670,6 +689,8 @@
mStatusBarStateController = statusBarStateController;
mKeyguardMediaController = keyguardMediaController;
mKeyguardBypassController = keyguardBypassController;
+ mKeyguardInteractor = keyguardInteractor;
+ mPrimaryBouncerInteractor = primaryBouncerInteractor;
mZenModeController = zenModeController;
mLockscreenUserManager = lockscreenUserManager;
mViewModel = nsslViewModel;
@@ -680,7 +701,6 @@
mFalsingManager = falsingManager;
mResources = resources;
mNotificationSwipeHelperBuilder = notificationSwipeHelperBuilder;
- mCentralSurfaces = centralSurfaces;
mScrimController = scrimController;
mJankMonitor = jankMonitor;
mNotificationStackSizeCalculator = notificationStackSizeCalculator;
@@ -709,7 +729,7 @@
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(
@@ -808,6 +828,9 @@
mViewModel.ifPresent(
vm -> NotificationListViewBinder
.bind(mView, vm, mFalsingManager, mFeatureFlags, mNotifIconAreaController));
+
+ collectFlow(mView, mKeyguardTransitionRepo.getTransitions(),
+ this::onKeyguardTransitionChanged);
}
private boolean isInVisibleLocation(NotificationEntry entry) {
@@ -1196,6 +1219,14 @@
}
/**
+ * 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.
@@ -1222,11 +1253,12 @@
final boolean shouldShow = getVisibleNotificationCount() == 0
&& !mView.isQsFullScreen()
- // Hide empty shade view when in transition to Keyguard.
+ // Hide empty shade view when in transition to AOD.
// That avoids "No Notifications" to blink when transitioning to AOD.
// For more details, see: b/228790482
- && !isInTransitionToKeyguard()
- && !mCentralSurfaces.isBouncerShowing();
+ && !mIsInTransitionToAod
+ // Don't show any notification content if the bouncer is showing. See b/267060171.
+ && !isBouncerShowing();
mView.updateEmptyShadeView(shouldShow, mZenModeController.areNotificationsHiddenInShade());
@@ -1234,6 +1266,24 @@
}
/**
+ * 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
@@ -1424,7 +1474,7 @@
return mNotificationRoundnessManager;
}
- NotificationListContainer getNotificationListContainer() {
+ public NotificationListContainer getNotificationListContainer() {
return mNotificationListContainer;
}
@@ -1605,6 +1655,16 @@
return shelf == null ? 0 : shelf.getIntrinsicHeight();
}
+ @VisibleForTesting
+ void onKeyguardTransitionChanged(TransitionStep transitionStep) {
+ boolean isTransitionToAod = transitionStep.getFrom().equals(KeyguardState.GONE)
+ && transitionStep.getTo().equals(KeyguardState.AOD);
+ if (mIsInTransitionToAod != isTransitionToAod) {
+ mIsInTransitionToAod = isTransitionToAod;
+ updateShowEmptyShadeView();
+ }
+ }
+
/**
* Enum for UiEvent logged from this class
*/
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutListContainerModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutListContainerModule.java
deleted file mode 100644
index 3dcaae2..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutListContainerModule.java
+++ /dev/null
@@ -1,32 +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.statusbar.notification.stack;
-
-import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent;
-
-import dagger.Module;
-import dagger.Provides;
-
-@Module
-public abstract class NotificationStackScrollLayoutListContainerModule {
- @Provides
- @CentralSurfacesComponent.CentralSurfacesScope
- static NotificationListContainer provideListContainer(
- NotificationStackScrollLayoutController nsslController) {
- return nsslController.getNotificationListContainer();
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLogger.kt
index 5b0ec1d..9c1bd17 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLogger.kt
@@ -1,9 +1,12 @@
package com.android.systemui.statusbar.notification.stack
+import android.view.ViewGroup
import com.android.systemui.log.dagger.NotificationHeadsUpLog
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.LogLevel.DEBUG
import com.android.systemui.log.LogLevel.INFO
+import com.android.systemui.log.LogLevel.ERROR
+import com.android.systemui.log.dagger.NotificationRenderLog
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.logKey
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_ADD
@@ -15,7 +18,8 @@
import javax.inject.Inject
class NotificationStackScrollLogger @Inject constructor(
- @NotificationHeadsUpLog private val buffer: LogBuffer
+ @NotificationHeadsUpLog private val buffer: LogBuffer,
+ @NotificationRenderLog private val notificationRenderBuffer: LogBuffer
) {
fun hunAnimationSkipped(entry: NotificationEntry, reason: String) {
buffer.log(TAG, INFO, {
@@ -77,6 +81,79 @@
"isTouchBelowNotification: $bool2 motionEvent: $str1"
})
}
+
+ fun transientNotificationRowTraversalCleaned(entry: NotificationEntry, reason: String) {
+ notificationRenderBuffer.log(TAG, INFO, {
+ str1 = entry.logKey
+ str2 = reason
+ }, {
+ "transientNotificationRowTraversalCleaned: key: $str1 reason: $str2"
+ })
+ }
+
+ fun addTransientChildNotificationToChildContainer(
+ childEntry: NotificationEntry,
+ containerEntry: NotificationEntry,
+ ) {
+ notificationRenderBuffer.log(TAG, INFO, {
+ str1 = childEntry.logKey
+ str2 = containerEntry.logKey
+ }, {
+ "addTransientChildToContainer from onViewRemovedInternal: childKey: $str1 " +
+ "-- containerKey: $str2"
+ })
+ }
+
+ fun addTransientChildNotificationToNssl(
+ childEntry: NotificationEntry,
+ ) {
+ notificationRenderBuffer.log(TAG, INFO, {
+ str1 = childEntry.logKey
+ }, {
+ "addTransientRowToNssl from onViewRemovedInternal: childKey: $str1"
+ })
+ }
+
+ fun addTransientChildNotificationToViewGroup(
+ childEntry: NotificationEntry,
+ container: ViewGroup
+ ) {
+ notificationRenderBuffer.log(TAG, ERROR, {
+ str1 = childEntry.logKey
+ str2 = container.toString()
+ }, {
+ "addTransientRowTo unhandled ViewGroup from onViewRemovedInternal: childKey: $str1 " +
+ "-- ViewGroup: $str2"
+ })
+ }
+
+ fun addTransientRow(
+ childEntry: NotificationEntry,
+ index: Int
+ ) {
+ notificationRenderBuffer.log(
+ TAG,
+ INFO,
+ {
+ str1 = childEntry.logKey
+ int1 = index
+ },
+ { "addTransientRow to NSSL: childKey: $str1 -- index: $int1" }
+ )
+ }
+
+ fun removeTransientRow(
+ childEntry: NotificationEntry,
+ ) {
+ notificationRenderBuffer.log(
+ TAG,
+ INFO,
+ {
+ str1 = childEntry.logKey
+ },
+ { "removeTransientRow from NSSL: childKey: $str1" }
+ )
+ }
}
private const val TAG = "NotificationStackScroll"
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
index efb7926..b2d26d9 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.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 @@
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 @@
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 @@
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 @@
}
}
- /**
+ @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/notification/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
index f07dd00..d73919b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
@@ -347,10 +347,12 @@
final ExpandableView changingView = (ExpandableView) event.mChangingView;
boolean loggable = false;
boolean isHeadsUp = false;
+ boolean isGroupChild = false;
String key = null;
if (changingView instanceof ExpandableNotificationRow && mLogger != null) {
loggable = true;
isHeadsUp = ((ExpandableNotificationRow) changingView).isHeadsUp();
+ isGroupChild = changingView.isChildInGroup();
key = ((ExpandableNotificationRow) changingView).getEntry().getKey();
}
if (event.animationType ==
@@ -407,13 +409,21 @@
}
Runnable postAnimation = changingView::removeFromTransientContainer;
- if (loggable && isHeadsUp) {
- mLogger.logHUNViewDisappearingWithRemoveEvent(key);
+ if (loggable) {
String finalKey = key;
- postAnimation = () -> {
- mLogger.disappearAnimationEnded(finalKey);
- changingView.removeFromTransientContainer();
- };
+ if (isHeadsUp) {
+ mLogger.logHUNViewDisappearingWithRemoveEvent(key);
+ postAnimation = () -> {
+ mLogger.disappearAnimationEnded(finalKey);
+ changingView.removeFromTransientContainer();
+ };
+ } else if (isGroupChild) {
+ mLogger.groupChildRemovalEventProcessed(key);
+ postAnimation = () -> {
+ mLogger.groupChildRemovalAnimationEnded(finalKey);
+ changingView.removeFromTransientContainer();
+ };
+ }
}
changingView.performRemoveAnimation(ANIMATION_DURATION_APPEAR_DISAPPEAR,
0 /* delay */, translationDirection, false /* isHeadsUpAppear */,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateLogger.kt
index cca84b3..c7f80f3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateLogger.kt
@@ -3,11 +3,13 @@
import com.android.systemui.log.dagger.NotificationHeadsUpLog
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.LogLevel
+import com.android.systemui.log.dagger.NotificationRenderLog
import com.android.systemui.statusbar.notification.logKey
import javax.inject.Inject
class StackStateLogger @Inject constructor(
- @NotificationHeadsUpLog private val buffer: LogBuffer
+ @NotificationHeadsUpLog private val buffer: LogBuffer,
+ @NotificationRenderLog private val notificationRenderBuffer: LogBuffer
) {
fun logHUNViewDisappearing(key: String) {
buffer.log(TAG, LogLevel.INFO, {
@@ -56,6 +58,21 @@
"Heads up notification appear animation ended $str1 "
})
}
+
+ fun groupChildRemovalEventProcessed(key: String) {
+ notificationRenderBuffer.log(TAG, LogLevel.DEBUG, {
+ str1 = logKey(key)
+ }, {
+ "Group Child Notification removal event processed $str1 for ANIMATION_TYPE_REMOVE"
+ })
+ }
+ fun groupChildRemovalAnimationEnded(key: String) {
+ notificationRenderBuffer.log(TAG, LogLevel.INFO, {
+ str1 = logKey(key)
+ }, {
+ "Group child notification removal animation ended $str1 "
+ })
+ }
}
private const val TAG = "StackScroll"
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModel.kt
index aab1c2b..11f68e0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModel.kt
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.notification.stack.ui.viewmodel
+import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.statusbar.notification.shelf.ui.viewmodel.NotificationShelfViewModel
@@ -33,6 +34,7 @@
object NotificationListViewModelModule {
@JvmStatic
@Provides
+ @SysUISingleton
fun maybeProvideViewModel(
featureFlags: FeatureFlags,
shelfViewModel: Provider<NotificationShelfViewModel>,
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 1827a46..730ef57 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.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 @@
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 @@
}
/**
+ * 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 @@
val animate =
animationController != null &&
!willLaunchResolverActivity &&
- centralSurfaces?.shouldAnimateLaunch(true /* isActivityIntent */) == true
+ shouldAnimateLaunch(isActivityIntent = true)
val animController =
wrapAnimationController(
animationController = animationController,
@@ -536,7 +567,7 @@
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 @@
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 @@
val animate =
animationController != null &&
- centralSurfaces?.shouldAnimateLaunch(
+ shouldAnimateLaunch(
/* isActivityIntent= */ true,
showOverLockscreenWhenLocked
) == true
@@ -713,7 +744,7 @@
} 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 @@
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 @@
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 86bf7cb..0242e91 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
@@ -24,13 +24,11 @@
import android.content.Intent;
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 +37,14 @@
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.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 +62,11 @@
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 +184,6 @@
return contextForUser.getPackageManager();
}
- void animateExpandNotificationsPanel();
-
- void animateExpandSettingsPanel(@Nullable String subpanel);
-
- void collapsePanelOnMainThread();
-
- void togglePanel();
-
void start();
boolean updateIsKeyguard();
@@ -212,15 +194,6 @@
@Override
Lifecycle getLifecycle();
- /**
- * Wakes up the device if the device was dozing.
- */
- void wakeUpIfDozing(long time, String why, @PowerManager.WakeReason int wakeReason);
-
- NotificationShadeWindowView getNotificationShadeWindowView();
-
- NotificationShadeWindowViewController getNotificationShadeWindowViewController();
-
/** */
ShadeViewController getShadeViewController();
@@ -229,46 +202,34 @@
int getStatusBarHeight();
- void updateQsExpansionEnabled();
-
boolean isShadeDisabled();
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 +250,12 @@
@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 +290,11 @@
/** 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 +306,6 @@
void setBouncerShowing(boolean bouncerShowing);
- void setBouncerShowingOverDream(boolean bouncerShowingOverDream);
-
- void collapseShade();
-
int getWakefulnessState();
boolean isScreenFullyOff();
@@ -389,9 +338,6 @@
boolean isDeviceInteractive();
- void setNotificationSnoozed(StatusBarNotification sbn,
- NotificationSwipeActionHelper.SnoozeOption snoozeOption);
-
void awakenDreams();
void clearNotificationEffects();
@@ -448,15 +394,10 @@
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 0ccc819..332be2a 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 @@
@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 @@
Log.d(CentralSurfaces.TAG,
"animateExpand: mExpandedVisible=" + mShadeController.isExpandedVisible());
}
- if (!mCommandQueue.panelsEnabled()) {
- return;
- }
-
- mShadeViewController.expandToNotifications();
+ mShadeController.animateExpandShade();
}
@Override
@@ -231,14 +227,7 @@
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 @@
}
/**
* 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 @@
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,17 +280,12 @@
}
if ((diff1 & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0) {
- if (mCentralSurfaces.areNotificationAlertsDisabled()) {
+ if ((state1 & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0) {
mHeadsUpManager.releaseAllImmediately();
}
}
- if ((diff2 & StatusBarManager.DISABLE2_QUICK_SETTINGS) != 0) {
- mCentralSurfaces.updateQsExpansionEnabled();
- }
-
if ((diff2 & StatusBarManager.DISABLE2_NOTIFICATION_SHADE) != 0) {
- mCentralSurfaces.updateQsExpansionEnabled();
if ((state2 & StatusBarManager.DISABLE2_NOTIFICATION_SHADE) != 0) {
mShadeController.animateCollapseShade();
}
@@ -556,10 +543,10 @@
@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 648ece5..4ae4c52 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.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.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.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.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.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;
@@ -311,7 +312,7 @@
private final DeviceStateManager mDeviceStateManager;
private CentralSurfacesCommandQueueCallbacks mCommandQueueCallbacks;
private float mTransitionToFullShadeProgress = 0f;
- private NotificationListContainer mNotifListContainer;
+ private final NotificationListContainer mNotifListContainer;
private boolean mIsShortcutListSearchEnabled;
private final KeyguardStateController.Callback mKeyguardStateControllerCallback =
@@ -411,23 +412,6 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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,8 @@
private final Optional<StartingSurface> mStartingSurfaceOptional;
private final ActivityIntentHelper mActivityIntentHelper;
- private NotificationStackScrollLayoutController mStackScrollerController;
+
+ private final NotificationStackScrollLayoutController mStackScrollerController;
private final ColorExtractor.OnColorsChangedListener mOnColorsChangedListener =
(extractor, which) -> updateTheme();
@@ -685,17 +668,7 @@
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 +678,12 @@
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 +737,7 @@
SysuiColorExtractor colorExtractor,
ScreenLifecycle screenLifecycle,
WakefulnessLifecycle wakefulnessLifecycle,
+ PowerInteractor powerInteractor,
SysuiStatusBarStateController statusBarStateController,
Optional<Bubbles> bubblesOptional,
Lazy<NoteTaskController> noteTaskControllerLazy,
@@ -773,12 +747,15 @@
Lazy<AssistManager> assistManagerLazy,
ConfigurationController configurationController,
NotificationShadeWindowController notificationShadeWindowController,
+ NotificationShelfController notificationShelfController,
+ NotificationStackScrollLayoutController notificationStackScrollLayoutController,
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 @@
InitController initController,
@Named(TIME_TICK_HANDLER_NAME) Handler timeTickHandler,
PluginDependencyProvider pluginDependencyProvider,
- KeyguardDismissUtil keyguardDismissUtil,
ExtensionController extensionController,
UserInfoControllerImpl userInfoControllerImpl,
PhoneStatusBarPolicy phoneStatusBarPolicy,
@@ -839,6 +815,7 @@
mKeyguardBypassController = keyguardBypassController;
mKeyguardStateController = keyguardStateController;
mHeadsUpManager = headsUpManagerPhone;
+ mBackActionInteractor = backActionInteractor;
mKeyguardIndicationController = keyguardIndicationController;
mStatusBarTouchableRegionManager = statusBarTouchableRegionManager;
mFalsingCollector = falsingCollector;
@@ -861,6 +838,7 @@
mColorExtractor = colorExtractor;
mScreenLifecycle = screenLifecycle;
mWakefulnessLifecycle = wakefulnessLifecycle;
+ mPowerInteractor = powerInteractor;
mStatusBarStateController = statusBarStateController;
mBubblesOptional = bubblesOptional;
mNoteTaskControllerLazy = noteTaskControllerLazy;
@@ -870,6 +848,10 @@
mAssistManagerLazy = assistManagerLazy;
mConfigurationController = configurationController;
mNotificationShadeWindowController = notificationShadeWindowController;
+ mNotificationShelfController = notificationShelfController;
+ mStackScrollerController = notificationStackScrollLayoutController;
+ mStackScroller = mStackScrollerController.getView();
+ mNotifListContainer = mStackScrollerController.getNotificationListContainer();
mDozeServiceHost = dozeServiceHost;
mPowerManager = powerManager;
mDozeParameters = dozeParameters;
@@ -889,7 +871,6 @@
mKeyguardViewMediatorCallback = viewMediatorCallback;
mInitController = initController;
mPluginDependencyProvider = pluginDependencyProvider;
- mKeyguardDismissUtil = keyguardDismissUtil;
mExtensionController = extensionController;
mUserInfoControllerImpl = userInfoControllerImpl;
mIconPolicy = phoneStatusBarPolicy;
@@ -958,6 +939,12 @@
}
// 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 +1138,8 @@
@Override
public void onPluginConnected(OverlayPlugin plugin, Context pluginContext) {
mMainExecutor.execute(
- () -> plugin.setup(getNotificationShadeWindowView(),
+ () -> plugin.setup(
+ mNotificationShadeWindowController.getWindowRootView(),
getNavigationBarView(),
new Callback(plugin), mDozeParameters));
}
@@ -1551,22 +1539,15 @@
@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");
}
- if (isExpanded && mStatusBarStateController.getState() != StatusBarState.KEYGUARD) {
- if (DEBUG) {
- Log.v(TAG, "clearing notification effects from Height");
- }
- clearNotificationEffects();
- }
+ clearNotificationEffects();
+ }
- if (!isExpanded) {
- mRemoteInputManager.onPanelCollapsed();
- }
+ if (!isExpanded) {
+ mRemoteInputManager.onPanelCollapsed();
}
}
@@ -1604,12 +1585,10 @@
mGutsManager.setNotificationActivityStarter(mNotificationActivityStarter);
mShadeController.setNotificationPresenter(mPresenter);
mNotificationsController.initialize(
- this,
mPresenter,
mNotifListContainer,
mStackScrollerController.getNotifStackController(),
- mNotificationActivityStarter,
- mCentralSurfacesComponent.getBindRowCallback());
+ mNotificationActivityStarter);
}
/**
@@ -1621,22 +1600,6 @@
mCommandQueue.disable(mDisplayId, state1, state2, false /* animate */);
}
- /**
- * Ask the display to wake up if currently dozing, else do nothing
- *
- * @param time when to wake up
- * @param why the reason for the wake up
- */
- @Override
- 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();
- }
- }
-
// TODO(b/117478341): This was left such that CarStatusBar can override this method.
// Try to remove this.
protected void createNavigationBar(@Nullable RegisterStatusBarResult result) {
@@ -1665,12 +1628,13 @@
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();
@@ -1678,14 +1642,10 @@
mShadeController.setNotificationPanelViewController(npvc);
mShadeController.setNotificationShadeWindowViewController(
mNotificationShadeWindowViewController);
- mStackScrollerController =
- mCentralSurfacesComponent.getNotificationStackScrollLayoutController();
mQsController = mCentralSurfacesComponent.getQuickSettingsController();
- mStackScroller = mStackScrollerController.getView();
- mNotifListContainer = mCentralSurfacesComponent.getNotificationListContainer();
+ mBackActionInteractor.setup(mQsController, mShadeSurface);
mPresenter = mCentralSurfacesComponent.getNotificationPresenter();
mNotificationActivityStarter = mCentralSurfacesComponent.getNotificationActivityStarter();
- mNotificationShelfController = mCentralSurfacesComponent.getNotificationShelfController();
mHeadsUpManager.addListener(mCentralSurfacesComponent.getStatusBarHeadsUpChangeListener());
@@ -1746,21 +1706,10 @@
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;
}
@@ -1775,23 +1724,6 @@
return mStatusBarWindowController.getStatusBarHeight();
}
- /**
- * Disable QS if device not provisioned.
- * If the user switcher is simple then disable QS during setup because
- * the user intends to use the lock screen user switcher, QS in not needed.
- */
- @Override
- public void updateQsExpansionEnabled() {
- final boolean expandEnabled = mDeviceProvisionedController.isDeviceProvisioned()
- && (mUserSetup || mUserSwitcherController == null
- || !mUserSwitcherController.isSimpleUserSwitcher())
- && !isShadeDisabled()
- && ((mDisabled2 & StatusBarManager.DISABLE2_QUICK_SETTINGS) == 0)
- && !mDozing;
- mQsController.setExpansionEnabledPolicy(expandEnabled);
- Log.d(TAG, "updateQsExpansionEnabled - QS Expand enabled: " + expandEnabled);
- }
-
@Override
public boolean isShadeDisabled() {
return (mDisabled2 & StatusBarManager.DISABLE2_NOTIFICATION_SHADE) != 0;
@@ -1805,11 +1737,6 @@
&& 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 +1746,6 @@
return mIsLaunchingActivityOverLockscreen;
}
- @Override
- public boolean isWakeUpComingFromTouch() {
- return mWakeUpComingFromTouch;
- }
-
/**
* To be called when there's a state change in StatusBarKeyguardViewManager.
*/
@@ -1833,11 +1755,6 @@
}
@Override
- public ViewGroup getNotificationScrollLayout() {
- return mStackScroller;
- }
-
- @Override
public boolean isPulsing() {
return mDozeServiceHost.isPulsing();
}
@@ -1853,58 +1770,6 @@
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 +1822,9 @@
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 +1842,8 @@
}
@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 +2097,7 @@
+ CameraIntents.getOverrideCameraPackage(mContext));
}
- @Override
- public void createAndAddWindows(@Nullable RegisterStatusBarResult result) {
+ private void createAndAddWindows(@Nullable RegisterStatusBarResult result) {
makeStatusBarView(result);
mNotificationShadeWindowController.attach();
mStatusBarWindowController.attach();
@@ -2341,7 +2181,7 @@
mNotificationShadeWindowController.setNotTouchable(false);
}
finishBarAnimations();
- resetUserExpandedStates();
+ mNotificationsController.resetUserExpandedStates();
}
Trace.endSection();
}
@@ -2360,20 +2200,6 @@
}
};
- @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
*
@@ -2860,7 +2686,6 @@
|| (mDozing && mDozeParameters.shouldControlScreenOff() && keyguardVisibleOrWillBe);
mShadeSurface.setDozing(mDozing, animate);
- updateQsExpansionEnabled();
Trace.endSection();
}
@@ -2889,7 +2714,7 @@
case KeyEvent.KEYCODE_BACK:
if (mState == StatusBarState.KEYGUARD
&& mStatusBarKeyguardViewManager.dispatchBackKeyEventPreIme()) {
- return onBackPressed();
+ return mBackActionInteractor.onBackRequested();
}
}
return false;
@@ -2929,34 +2754,6 @@
}
@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 +2796,6 @@
}
/**
- * 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 +2831,6 @@
}
}
- @Override
- public LightRevealScrim getLightRevealScrim() {
- return mLightRevealScrim;
- }
-
// TODO: Figure out way to remove these.
@Override
public NavigationBarView getNavigationBarView() {
@@ -3074,9 +2853,8 @@
}
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,12 +2865,12 @@
mBouncerShowing = bouncerShowing;
mKeyguardBypassController.setBouncerShowing(bouncerShowing);
mPulseExpansionHandler.setBouncerShowing(bouncerShowing);
+ mStackScrollerController.setBouncerShowingFromCentralSurfaces(bouncerShowing);
setBouncerShowingForStatusBarComponents(bouncerShowing);
mStatusBarHideIconsForBouncerManager.setBouncerShowingAndTriggerUpdate(bouncerShowing);
mCommandQueue.recomputeDisableFlags(mDisplayId, true /* animate */);
if (mBouncerShowing) {
- wakeUpIfDozing(SystemClock.uptimeMillis(), "BOUNCER_VISIBLE",
- PowerManager.WAKE_REASON_GESTURE);
+ mPowerInteractor.wakeUpIfDozing("BOUNCER_VISIBLE", PowerManager.WAKE_REASON_GESTURE);
}
updateScrimController();
if (!mBouncerShowing) {
@@ -3101,17 +2879,6 @@
}
/**
- * 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 +2895,6 @@
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 +2903,6 @@
releaseGestureWakeLock();
mLaunchCameraWhenFinishedWaking = false;
mDeviceInteractive = false;
- mWakeUpComingFromTouch = false;
updateVisibleToUser();
updateNotificationPanelTouchState();
@@ -3505,7 +3258,7 @@
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);
@@ -3572,7 +3325,7 @@
protected IStatusBarService mBarService;
// all notifications
- protected NotificationStackScrollLayout mStackScroller;
+ private final NotificationStackScrollLayout mStackScroller;
protected AccessibilityManager mAccessibilityManager;
@@ -3602,7 +3355,7 @@
protected Display mDisplay;
private int mDisplayId;
- protected NotificationShelfController mNotificationShelfController;
+ private final NotificationShelfController mNotificationShelfController;
private final Lazy<AssistManager> mAssistManagerLazy;
@@ -3635,12 +3388,6 @@
};
@Override
- public void setNotificationSnoozed(StatusBarNotification sbn, SnoozeOption snoozeOption) {
- mNotificationsController.setNotificationSnoozed(sbn, snoozeOption);
- }
-
-
- @Override
public void awakenDreams() {
mUiBgExecutor.execute(() -> {
try {
@@ -3841,10 +3588,10 @@
if (userSetup != mUserSetup) {
mUserSetup = userSetup;
- if (!mUserSetup) {
- animateCollapseQuickSettings();
+ if (!mUserSetup && mState == StatusBarState.SHADE) {
+ mShadeSurface.collapse(true /* animate */, false /* delayed */,
+ 1.0f /* speedUpFactor */);
}
- updateQsExpansionEnabled();
}
}
};
@@ -3979,7 +3726,6 @@
// reset them again when we're waking up
mShadeSurface.resetViews(dozingAnimated && isDozing);
- updateQsExpansionEnabled();
mKeyguardViewMediator.setDozing(mDozing);
updateDozingState();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardDismissUtil.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardDismissUtil.java
index 27b68f2..1c90c0d 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;
+
+ private final ActivityStarter mActivityStarter;
@Inject
- public KeyguardDismissUtil() {
+ public KeyguardDismissUtil(KeyguardStateController keyguardStateController,
+ SysuiStatusBarStateController statusBarStateController,
+ ActivityStarter activityStarter) {
+ mKeyguardStateController = keyguardStateController;
+ mStatusBarStateController = statusBarStateController;
+ mActivityStarter = activityStarter;
}
- /** Sets the actual {@link KeyguardDismissHandler} implementation. */
- public void setDismissHandler(KeyguardDismissHandler dismissHandler) {
- mDismissHandler = dismissHandler;
- }
-
- /**
- * 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/LetterboxAppearanceCalculator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LetterboxAppearanceCalculator.kt
index f742645..a61914a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LetterboxAppearanceCalculator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LetterboxAppearanceCalculator.kt
@@ -209,7 +209,7 @@
if (this.contains(other) || other.contains(this)) {
return false
}
- return this.intersect(other)
+ return this.intersects(other.left, other.top, other.right, other.bottom)
}
override fun dump(pw: PrintWriter, args: Array<out String>) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
index 313410a..2fd244e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
@@ -240,7 +240,7 @@
private void reloadDimens(Context context) {
Resources res = context.getResources();
mIconSize = res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_icon_size);
- mIconHPadding = res.getDimensionPixelSize(R.dimen.status_bar_icon_padding);
+ mIconHPadding = res.getDimensionPixelSize(R.dimen.status_bar_icon_horizontal_margin);
mAodIconAppearTranslation = res.getDimensionPixelSize(
R.dimen.shelf_appear_translation);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
index e6cb68f..f0fc143 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
@@ -176,6 +176,7 @@
shadeLogger.logMotionEvent(event, "top edge touch ignored")
return true
}
+ centralSurfaces.shadeViewController.startTrackingExpansionFromStatusBar()
}
return centralSurfaces.shadeViewController.handleExternalTouch(event)
}
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 25ecf1a..47c4023 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.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 @@
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/StatusBarIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
index a8a834f..b14fe90 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
@@ -173,7 +173,7 @@
*/
class DarkIconManager extends IconManager {
private final DarkIconDispatcher mDarkIconDispatcher;
- private int mIconHPadding;
+ private final int mIconHorizontalMargin;
public DarkIconManager(
LinearLayout linearLayout,
@@ -189,8 +189,8 @@
wifiUiAdapter,
mobileUiAdapter,
mobileContextProvider);
- mIconHPadding = mContext.getResources().getDimensionPixelSize(
- R.dimen.status_bar_icon_padding);
+ mIconHorizontalMargin = mContext.getResources().getDimensionPixelSize(
+ R.dimen.status_bar_icon_horizontal_margin);
mDarkIconDispatcher = darkIconDispatcher;
}
@@ -205,7 +205,7 @@
protected LayoutParams onCreateLayoutParams() {
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT, mIconSize);
- lp.setMargins(mIconHPadding, 0, mIconHPadding, 0);
+ lp.setMargins(mIconHorizontalMargin, 0, mIconHorizontalMargin, 0);
return lp;
}
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 1bf63be..e63875b 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 @@
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.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 @@
// 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 @@
@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 @@
@Override
public void onStartedWakingUp() {
- mCentralSurfaces.getNotificationShadeWindowView().getWindowInsetsController()
+ mNotificationShadeWindowController.getWindowRootView().getWindowInsetsController()
.setAnimationsDisabled(false);
NavigationBarView navBarView = mCentralSurfaces.getNavigationBarView();
if (navBarView != null) {
@@ -764,7 +765,7 @@
@Override
public void onStartedGoingToSleep() {
- mCentralSurfaces.getNotificationShadeWindowView().getWindowInsetsController()
+ mNotificationShadeWindowController.getWindowRootView().getWindowInsetsController()
.setAnimationsDisabled(true);
NavigationBarView navBarView = mCentralSurfaces.getNavigationBarView();
if (navBarView != null) {
@@ -823,6 +824,11 @@
}
@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 @@
if (view != null) {
view.setVisibility(View.VISIBLE);
}
- mCentralSurfaces.getNotificationShadeWindowView().getWindowInsetsController()
+ mNotificationShadeWindowController.getWindowRootView().getWindowInsetsController()
.show(navigationBars());
}
};
@@ -1192,7 +1198,7 @@
}
} else {
mNotificationContainer.removeCallbacks(mMakeNavigationBarVisibleRunnable);
- mCentralSurfaces.getNotificationShadeWindowView().getWindowInsetsController()
+ mNotificationShadeWindowController.getWindowRootView().getWindowInsetsController()
.hide(navigationBars());
}
}
@@ -1312,7 +1318,7 @@
@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 9f69db9..b67ec58 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 @@
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 @@
*/
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 @@
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 7bbb03b..f79a081 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.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 @@
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 @@
OnUserInteractionCallback onUserInteractionCallback,
CentralSurfaces centralSurfaces,
NotificationPresenter presenter,
- ShadeViewController panel,
+ ShadeViewController shadeViewController,
+ NotificationShadeWindowController notificationShadeWindowController,
ActivityLaunchAnimator activityLaunchAnimator,
NotificationLaunchAnimatorControllerProvider notificationAnimationProvider,
LaunchFullScreenIntentProvider launchFullScreenIntentProvider,
@@ -182,6 +185,7 @@
mLockPatternUtils = lockPatternUtils;
mStatusBarRemoteInputCallback = remoteInputCallback;
mActivityIntentHelper = activityIntentHelper;
+ mNotificationShadeWindowController = notificationShadeWindowController;
mFeatureFlags = featureFlags;
mMetricsLogger = metricsLogger;
mLogger = logger;
@@ -189,7 +193,7 @@
// 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 @@
&& 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 @@
}
// Always defer the keyguard dismiss when animating.
- return animate || !mNotificationPanel.isFullyCollapsed();
+ return animate || !mShadeViewController.isFullyCollapsed();
}
private void handleNotificationClickAfterPanelCollapsed(
@@ -319,7 +323,7 @@
removeHunAfterClick(row);
// Show work challenge, do not run PendingIntent and
// remove notification
- collapseOnMainThread();
+ mShadeController.collapseOnMainThread();
return;
}
}
@@ -436,7 +440,9 @@
ActivityLaunchAnimator.Controller animationController =
new StatusBarLaunchAnimatorController(
mNotificationAnimationProvider.getAnimatorController(row, null),
- mCentralSurfaces,
+ mShadeViewController,
+ mShadeController,
+ mNotificationShadeWindowController,
isActivityIntent);
mActivityLaunchAnimator.startPendingIntentWithAnimation(
animationController,
@@ -467,7 +473,7 @@
@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 @@
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 @@
@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 @@
);
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 @@
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 dfaee4c..35285b2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
@@ -22,7 +22,6 @@
import android.os.PowerManager;
import android.os.RemoteException;
import android.os.ServiceManager;
-import android.os.SystemClock;
import android.service.notification.StatusBarNotification;
import android.service.vr.IVrManager;
import android.service.vr.IVrStateCallbacks;
@@ -36,11 +35,11 @@
import com.android.systemui.R;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
+import com.android.systemui.power.domain.interactor.PowerInteractor;
import com.android.systemui.shade.NotificationShadeWindowView;
import com.android.systemui.shade.QuickSettingsController;
import com.android.systemui.shade.ShadeViewController;
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.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 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,9 +83,10 @@
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 PowerInteractor mPowerInteractor;
private final CommandQueue mCommandQueue;
private final AccessibilityManager mAccessibilityManager;
@@ -115,9 +113,10 @@
NotificationShadeWindowController notificationShadeWindowController,
DynamicPrivacyController dynamicPrivacyController,
KeyguardStateController keyguardStateController,
- KeyguardIndicationController keyguardIndicationController,
CentralSurfaces centralSurfaces,
+ NotificationsInteractor notificationsInteractor,
LockscreenShadeTransitionController shadeTransitionController,
+ PowerInteractor powerInteractor,
CommandQueue commandQueue,
NotificationLockscreenUserManager lockscreenUserManager,
SysuiStatusBarStateController sysuiStatusBarStateController,
@@ -137,10 +136,11 @@
mQsController = quickSettingsController;
mHeadsUpManager = headsUp;
mDynamicPrivacyController = dynamicPrivacyController;
- mKeyguardIndicationController = keyguardIndicationController;
// TODO: use KeyguardStateController#isOccluded to remove this dependency
mCentralSurfaces = centralSurfaces;
+ mNotificationsInteractor = notificationsInteractor;
mShadeTransitionController = shadeTransitionController;
+ mPowerInteractor = powerInteractor;
mCommandQueue = commandQueue;
mLockscreenUserManager = lockscreenUserManager;
mStatusBarStateController = sysuiStatusBarStateController;
@@ -173,7 +173,6 @@
mNotificationPanel.getShadeNotificationPresenter().createRemoteInputDelegate());
initController.addPostInitTask(() -> {
- mKeyguardIndicationController.init();
mNotifShadeEventSource.setShadeEmptiedCallback(this::maybeClosePanelForShadeEmptied);
mNotifShadeEventSource.setNotifRemovedByUserCallback(this::maybeEndAmbientPulse);
notificationInterruptStateProvider.addSuppressor(mInterruptSuppressor);
@@ -242,9 +241,7 @@
public void onExpandClicked(NotificationEntry clickedEntry, View clickedView,
boolean nowExpanded) {
mHeadsUpManager.setExpanded(clickedEntry, nowExpanded);
- mCentralSurfaces.wakeUpIfDozing(
- SystemClock.uptimeMillis(), "NOTIFICATION_CLICK",
- PowerManager.WAKE_REASON_GESTURE);
+ mPowerInteractor.wakeUpIfDozing("NOTIFICATION_CLICK", PowerManager.WAKE_REASON_GESTURE);
if (nowExpanded) {
if (mStatusBarStateController.getState() == StatusBarState.KEYGUARD) {
mShadeTransitionController.goToLockedShade(clickedEntry.getRow());
@@ -341,7 +338,7 @@
@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 26c483c..d94e434 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 @@
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/StatusIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java
index 26c1767..c332280 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java
@@ -145,8 +145,8 @@
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
mMeasureViews.clear();
- int mode = MeasureSpec.getMode(widthMeasureSpec);
- final int width = MeasureSpec.getSize(widthMeasureSpec);
+ int widthMode = MeasureSpec.getMode(widthMeasureSpec);
+ final int specWidth = MeasureSpec.getSize(widthMeasureSpec);
final int count = getChildCount();
// Collect all of the views which want to be laid out
for (int i = 0; i < count; i++) {
@@ -163,7 +163,7 @@
boolean trackWidth = true;
// Measure all children so that they report the correct width
- int childWidthSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.UNSPECIFIED);
+ int childWidthSpec = MeasureSpec.makeMeasureSpec(specWidth, MeasureSpec.UNSPECIFIED);
mNeedsUnderflow = mShouldRestrictIcons && visibleCount > MAX_ICONS;
for (int i = 0; i < visibleCount; i++) {
// Walking backwards
@@ -182,18 +182,35 @@
totalWidth += getViewTotalMeasuredWidth(child) + spacing;
}
}
+ setMeasuredDimension(
+ getMeasuredWidth(widthMode, specWidth, totalWidth),
+ getMeasuredHeight(heightMeasureSpec, mMeasureViews));
+ }
- if (mode == MeasureSpec.EXACTLY) {
- if (!mNeedsUnderflow && totalWidth > width) {
- mNeedsUnderflow = true;
- }
- setMeasuredDimension(width, MeasureSpec.getSize(heightMeasureSpec));
+ private int getMeasuredHeight(int heightMeasureSpec, List<View> measuredChildren) {
+ if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.EXACTLY) {
+ return MeasureSpec.getSize(heightMeasureSpec);
} else {
- if (mode == MeasureSpec.AT_MOST && totalWidth > width) {
- mNeedsUnderflow = true;
- totalWidth = width;
+ int highest = 0;
+ for (View child : measuredChildren) {
+ highest = Math.max(child.getMeasuredHeight(), highest);
}
- setMeasuredDimension(totalWidth, MeasureSpec.getSize(heightMeasureSpec));
+ return highest + getPaddingTop() + getPaddingBottom();
+ }
+ }
+
+ private int getMeasuredWidth(int widthMode, int specWidth, int totalWidth) {
+ if (widthMode == MeasureSpec.EXACTLY) {
+ if (!mNeedsUnderflow && totalWidth > specWidth) {
+ mNeedsUnderflow = true;
+ }
+ return specWidth;
+ } else {
+ if (widthMode == MeasureSpec.AT_MOST && totalWidth > specWidth) {
+ mNeedsUnderflow = true;
+ totalWidth = specWidth;
+ }
+ return totalWidth;
}
}
@@ -280,34 +297,6 @@
}
/**
- * Sets the list of ignored icon slots clearing the current list.
- * @param slots names of the icons to ignore
- */
- public void setIgnoredSlots(List<String> slots) {
- mIgnoredSlots.clear();
- addIgnoredSlots(slots);
- }
-
- /**
- * Returns the view corresponding to a particular slot.
- *
- * Use it solely to manipulate how it is presented.
- * @param slot name of the slot to find. Names are defined in
- * {@link com.android.internal.R.config_statusBarIcons}
- * @return a view for the slot if this container has it, else {@code null}
- */
- public View getViewForSlot(String slot) {
- for (int i = 0; i < getChildCount(); i++) {
- View child = getChildAt(i);
- if (child instanceof StatusIconDisplayable
- && ((StatusIconDisplayable) child).getSlot().equals(slot)) {
- return child;
- }
- }
- return null;
- }
-
- /**
* Layout is happening from end -> start
*/
private void calculateIconTranslations() {
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 592d1aa..96a4d90 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.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 @@
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 @@
override fun onAnimationStart(animation: Animator?) {
interactionJankMonitor.begin(
- mCentralSurfaces.notificationShadeWindowView, CUJ_SCREEN_OFF)
+ notifShadeWindowControllerLazy.get().windowRootView, CUJ_SCREEN_OFF)
}
})
}
@@ -218,7 +220,7 @@
.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 273e783..c618be8 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,18 +20,14 @@
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;
import com.android.systemui.statusbar.phone.CentralSurfacesCommandQueueCallbacks;
import com.android.systemui.statusbar.phone.CentralSurfacesImpl;
import com.android.systemui.statusbar.phone.StatusBarHeadsUpChangeListener;
@@ -57,7 +53,6 @@
* outside the component. Should more items be moved *into* this component to avoid so many getters?
*/
@Subcomponent(modules = {
- NotificationStackScrollLayoutListContainerModule.class,
StatusBarViewModule.class,
StatusBarNotificationActivityStarterModule.class,
StatusBarNotificationPresenterModule.class,
@@ -80,17 +75,14 @@
@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();
-
/**
* Creates a NotificationShadeWindowViewController.
*/
@@ -129,8 +121,4 @@
NotificationActivityStarter getNotificationActivityStarter();
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 4ccbc5a..6b0746f 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,25 +23,14 @@
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;
import com.android.systemui.statusbar.phone.StatusBarBoundsProvider;
@@ -68,68 +57,25 @@
import java.util.concurrent.Executor;
import javax.inject.Named;
-import javax.inject.Provider;
-@Module(subcomponents = StatusBarFragmentComponent.class,
- includes = {
- ActivatableNotificationViewModelModule.class,
- NotificationListViewModelModule.class,
- })
+/**
+ * A module for {@link CentralSurfacesComponent.CentralSurfacesScope} components.
+ *
+ * @deprecated CentralSurfacesScope will be removed shortly (b/277762009). Classes should be
+ * annotated with @SysUISingleton instead.
+ */
+@Module(subcomponents = StatusBarFragmentComponent.class)
+@Deprecated
public abstract class StatusBarViewModule {
public static final String STATUS_BAR_FRAGMENT = "status_bar_fragment";
/** */
- @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 8c19fb4..f4ab408 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 @@
* 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 @@
},
{
disableFlagsLogger.getDisableFlagsString(
- old = null,
- new = DisableFlagsLogger.DisableState(int1, int2),
+ DisableFlagsLogger.DisableState(int1, int2),
)
}
)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt
index a47f95d..74352d29 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt
@@ -57,7 +57,6 @@
import kotlinx.coroutines.asExecutor
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
@@ -205,9 +204,6 @@
}
.stateIn(scope, SharingStarted.WhileSubscribed(), null)
- private val defaultDataSubIdChangeEvent: MutableSharedFlow<Unit> =
- MutableSharedFlow(extraBufferCapacity = 1)
-
override val defaultDataSubId: StateFlow<Int> =
broadcastDispatcher
.broadcastFlow(
@@ -223,7 +219,6 @@
initialValue = INVALID_SUBSCRIPTION_ID,
)
.onStart { emit(subscriptionManagerProxy.getDefaultDataSubscriptionId()) }
- .onEach { defaultDataSubIdChangeEvent.tryEmit(Unit) }
.stateIn(scope, SharingStarted.WhileSubscribed(), INVALID_SUBSCRIPTION_ID)
private val carrierConfigChangedEvent =
@@ -232,7 +227,7 @@
.onEach { logger.logActionCarrierConfigChanged() }
override val defaultDataSubRatConfig: StateFlow<Config> =
- merge(defaultDataSubIdChangeEvent, carrierConfigChangedEvent)
+ merge(defaultDataSubId, carrierConfigChangedEvent)
.onStart { emit(Unit) }
.mapLatest { Config.readConfig(context) }
.distinctUntilChanged()
diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/JavaAdapter.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/JavaAdapter.kt
index d6b3b22..12d7b4d 100644
--- a/packages/SystemUI/src/com/android/systemui/util/kotlin/JavaAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/JavaAdapter.kt
@@ -19,12 +19,44 @@
import android.view.View
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.lifecycle.repeatWhenAttached
import java.util.function.Consumer
+import javax.inject.Inject
import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.EmptyCoroutineContext
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.collect
+import kotlinx.coroutines.launch
+
+/** A class allowing Java classes to collect on Kotlin flows. */
+@SysUISingleton
+class JavaAdapter
+@Inject
+constructor(
+ @Application private val scope: CoroutineScope,
+) {
+ /**
+ * Collect information for the given [flow], calling [consumer] for each emitted event.
+ *
+ * Important: This will immediately start collection and *never* stop it. This should only be
+ * used by classes that *need* to always be collecting a value and processing it. Whenever
+ * possible, please use [collectFlow] instead; that method will stop the collection when a view
+ * has disappeared, which will ensure that we don't perform unnecessary work.
+ *
+ * Do *not* call this method in a class's constructor. Instead, call it in
+ * [com.android.systemui.CoreStartable.start] or similar method.
+ */
+ fun <T> alwaysCollectFlow(
+ flow: Flow<T>,
+ consumer: Consumer<T>,
+ ): Job {
+ return scope.launch { flow.collect { consumer.accept(it) } }
+ }
+}
/**
* Collect information for the given [flow], calling [consumer] for each emitted event. Defaults to
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
index 5144d19..4c7e6b0 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 @@
}
@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 080be6d..12da17f 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 f5cd0ca..319a02d 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 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 @@
@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 e9a9f3a..0dcd404 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 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.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.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 @@
commandQueue = commandQueue,
featureFlags = featureFlags,
bouncerRepository = bouncerRepository,
+ configurationRepository = FakeConfigurationRepository(),
),
KeyguardTransitionInteractor(transitionRepository, TestScope().backgroundScope),
broadcastDispatcher,
@@ -156,9 +159,8 @@
@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 061340e..65c677e 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 @@
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 @@
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 @@
// 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 @@
// 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/KeyguardPatternViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
index 71a57c7..2eea9eb 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
@@ -18,10 +18,12 @@
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
+import android.view.View
+import androidx.constraintlayout.widget.ConstraintLayout
+import androidx.constraintlayout.widget.ConstraintSet
import androidx.test.filters.SmallTest
import com.android.internal.util.LatencyTracker
import com.android.internal.widget.LockPatternUtils
-import com.android.internal.widget.LockPatternView
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingCollector
@@ -29,6 +31,10 @@
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.statusbar.policy.DevicePostureController
+import com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_HALF_OPENED
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -45,7 +51,7 @@
@RunWith(AndroidTestingRunner::class)
@TestableLooper.RunWithLooper
class KeyguardPatternViewControllerTest : SysuiTestCase() {
- @Mock private lateinit var mKeyguardPatternView: KeyguardPatternView
+ private lateinit var mKeyguardPatternView: KeyguardPatternView
@Mock private lateinit var mKeyguardUpdateMonitor: KeyguardUpdateMonitor
@@ -63,54 +69,70 @@
@Mock
private lateinit var mKeyguardMessageAreaControllerFactory: KeyguardMessageAreaController.Factory
- @Mock private lateinit var mKeyguardMessageArea: BouncerKeyguardMessageArea
-
@Mock
private lateinit var mKeyguardMessageAreaController:
KeyguardMessageAreaController<BouncerKeyguardMessageArea>
- @Mock private lateinit var mLockPatternView: LockPatternView
-
- @Mock private lateinit var mPostureController: DevicePostureController
+ @Mock private lateinit var mPostureController: DevicePostureController
private lateinit var mKeyguardPatternViewController: KeyguardPatternViewController
private lateinit var fakeFeatureFlags: FakeFeatureFlags
- @Before
- fun setup() {
- MockitoAnnotations.initMocks(this)
- `when`(mKeyguardPatternView.isAttachedToWindow).thenReturn(true)
- `when`(
- mKeyguardPatternView.requireViewById<BouncerKeyguardMessageArea>(
- R.id.bouncer_message_area))
- .thenReturn(mKeyguardMessageArea)
- `when`(mKeyguardPatternView.findViewById<LockPatternView>(R.id.lockPatternView))
- .thenReturn(mLockPatternView)
- `when`(mKeyguardMessageAreaControllerFactory.create(mKeyguardMessageArea))
- .thenReturn(mKeyguardMessageAreaController)
- `when`(mKeyguardPatternView.resources).thenReturn(context.resources)
- fakeFeatureFlags = FakeFeatureFlags()
- fakeFeatureFlags.set(Flags.REVAMPED_BOUNCER_MESSAGES, false)
- mKeyguardPatternViewController =
- KeyguardPatternViewController(
- mKeyguardPatternView,
- mKeyguardUpdateMonitor,
- mSecurityMode,
- mLockPatternUtils,
- mKeyguardSecurityCallback,
- mLatencyTracker,
- mFalsingCollector,
- mEmergencyButtonController,
- mKeyguardMessageAreaControllerFactory,
- mPostureController,
- fakeFeatureFlags)
- }
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+ whenever(mKeyguardMessageAreaControllerFactory.create(any()))
+ .thenReturn(mKeyguardMessageAreaController)
+ fakeFeatureFlags = FakeFeatureFlags()
+ fakeFeatureFlags.set(Flags.REVAMPED_BOUNCER_MESSAGES, false)
+ mKeyguardPatternView = View.inflate(mContext, R.layout.keyguard_pattern_view, null)
+ as KeyguardPatternView
+
+
+ mKeyguardPatternViewController =
+ KeyguardPatternViewController(
+ mKeyguardPatternView,
+ mKeyguardUpdateMonitor,
+ mSecurityMode,
+ mLockPatternUtils,
+ mKeyguardSecurityCallback,
+ mLatencyTracker,
+ mFalsingCollector,
+ mEmergencyButtonController,
+ mKeyguardMessageAreaControllerFactory,
+ mPostureController,
+ fakeFeatureFlags
+ )
+ mKeyguardPatternView.onAttachedToWindow()
+ }
+
+ @Test
+ fun tabletopPostureIsDetectedFromStart() {
+ overrideResource(R.dimen.half_opened_bouncer_height_ratio, 0.5f)
+ whenever(mPostureController.devicePosture).thenReturn(DEVICE_POSTURE_HALF_OPENED)
+
+ mKeyguardPatternViewController.onViewAttached()
+
+ assertThat(getPatternTopGuideline()).isEqualTo(getExpectedTopGuideline())
+ }
+
+ private fun getPatternTopGuideline(): Float {
+ val cs = ConstraintSet()
+ val container =
+ mKeyguardPatternView.findViewById(R.id.pattern_container) as ConstraintLayout
+ cs.clone(container)
+ return cs.getConstraint(R.id.pattern_top_guideline).layout.guidePercent
+ }
+
+ private fun getExpectedTopGuideline(): Float {
+ return mContext.resources.getFloat(R.dimen.half_opened_bouncer_height_ratio)
+ }
@Test
fun withFeatureFlagOn_oldMessage_isHidden() {
fakeFeatureFlags.set(Flags.REVAMPED_BOUNCER_MESSAGES, true)
- mKeyguardPatternViewController.init()
+ mKeyguardPatternViewController.onViewAttached()
verify<KeyguardMessageAreaController<*>>(mKeyguardMessageAreaController).disable()
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
index 2f20f76..e561f1f 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 @@
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.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 @@
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 1e675f8..901c3fb 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -17,6 +17,7 @@
package com.android.keyguard;
import static android.app.StatusBarManager.SESSION_KEYGUARD;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ERROR_LOCKOUT;
import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ERROR_LOCKOUT_PERMANENT;
@@ -38,6 +39,7 @@
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;
@@ -64,6 +66,8 @@
import static org.mockito.Mockito.when;
import android.app.Activity;
+import android.app.ActivityTaskManager;
+import android.app.IActivityTaskManager;
import android.app.admin.DevicePolicyManager;
import android.app.trust.IStrongAuthTracker;
import android.app.trust.TrustManager;
@@ -115,6 +119,7 @@
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import android.text.TextUtils;
import androidx.annotation.NonNull;
@@ -134,9 +139,12 @@
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;
+import com.android.systemui.shared.system.TaskStackChangeListener;
+import com.android.systemui.shared.system.TaskStackChangeListeners;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.policy.DevicePostureController;
@@ -173,6 +181,8 @@
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
public class KeyguardUpdateMonitorTest extends SysuiTestCase {
+ private static final String PKG_ALLOWING_FP_LISTEN_ON_OCCLUDING_ACTIVITY =
+ "test_app_fp_listen_on_occluding_activity";
private static final String TEST_CARRIER = "TEST_CARRIER";
private static final String TEST_CARRIER_2 = "TEST_CARRIER_2";
private static final int TEST_CARRIER_ID = 1;
@@ -262,6 +272,10 @@
private UsbPortStatus mUsbPortStatus;
@Mock
private Uri mURI;
+ @Mock
+ private TaskStackChangeListeners mTaskStackChangeListeners;
+ @Mock
+ private IActivityTaskManager mActivityTaskManager;
private List<FaceSensorPropertiesInternal> mFaceSensorProperties;
private List<FingerprintSensorPropertiesInternal> mFingerprintSensorProperties;
@@ -278,6 +292,7 @@
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;
@@ -324,8 +339,14 @@
mDumpManager
);
+ mContext.getOrCreateTestableResources().addOverride(com.android.systemui
+ .R.array.config_fingerprint_listen_on_occluding_activity_packages,
+ new String[]{ PKG_ALLOWING_FP_LISTEN_ON_OCCLUDING_ACTIVITY });
+
mTestableLooper = TestableLooper.get(this);
allowTestableLooperAsMainThread();
+ mFeatureFlags = new FakeFeatureFlags();
+ mFeatureFlags.set(FP_LISTEN_OCCLUDING_APPS, false);
when(mSecureSettings.getUriFor(anyString())).thenReturn(mURI);
@@ -1555,6 +1576,50 @@
}
@Test
+ public void listenForFingerprint_whenOccludingAppPkgOnAllowlist()
+ throws RemoteException {
+ 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);
+
+ // GIVEN the top activity is from a package that allows fingerprint listening over its
+ // occluding activities
+ setTopStandardActivity(PKG_ALLOWING_FP_LISTEN_ON_OCCLUDING_ACTIVITY);
+ onTaskStackChanged();
+
+ // THEN we SHOULD listen for non-UDFPS fingerprint
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFingerprint(false)).isEqualTo(true);
+
+ // THEN we should listen for udfps (hiding mechanism to actually auth is
+ // controlled by UdfpsKeyguardViewController)
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFingerprint(true)).isEqualTo(true);
+ }
+
+ @Test
+ public void doNotListenForFingerprint_whenOccludingAppPkgNotOnAllowlist()
+ throws RemoteException {
+ 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);
+
+ // GIVEN top activity is not in the allowlist for listening to fp over occluding activities
+ setTopStandardActivity("notInAllowList");
+
+ // THEN we should not listen for non-UDFPS fingerprint
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFingerprint(false)).isEqualTo(false);
+
+ // THEN we should listen for udfps (hiding 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);
@@ -3245,6 +3310,23 @@
BatteryManager.CHARGING_POLICY_ADAPTIVE_LONGLIFE);
}
+ private void setTopStandardActivity(String pkgName) throws RemoteException {
+ final ActivityTaskManager.RootTaskInfo taskInfo = new ActivityTaskManager.RootTaskInfo();
+ taskInfo.visible = true;
+ taskInfo.topActivity = TextUtils.isEmpty(pkgName)
+ ? null : new ComponentName(pkgName, "testClass");
+ when(mActivityTaskManager.getRootTaskInfo(anyInt(), eq(ACTIVITY_TYPE_STANDARD)))
+ .thenReturn(taskInfo);
+ }
+
+ private void onTaskStackChanged() {
+ ArgumentCaptor<TaskStackChangeListener> taskStackChangeListenerCaptor =
+ ArgumentCaptor.forClass(TaskStackChangeListener.class);
+ verify(mTaskStackChangeListeners).registerTaskStackListener(
+ taskStackChangeListenerCaptor.capture());
+ taskStackChangeListenerCaptor.getValue().onTaskStackChangedBackground();
+ }
+
private class TestableKeyguardUpdateMonitor extends KeyguardUpdateMonitor {
AtomicBoolean mSimStateChanged = new AtomicBoolean(false);
AtomicInteger mCachedSimState = new AtomicInteger(-1);
@@ -3262,7 +3344,8 @@
mDreamManager, mDevicePolicyManager, mSensorPrivacyManager, mTelephonyManager,
mPackageManager, mFaceManager, mFingerprintManager, mBiometricManager,
mFaceWakeUpTriggersConfig, mDevicePostureController,
- Optional.of(mInteractiveToAuthProvider));
+ Optional.of(mInteractiveToAuthProvider), mFeatureFlags,
+ mTaskStackChangeListeners, mActivityTaskManager);
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 84e58be..c88c4d6 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 @@
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.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 @@
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 @@
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 @@
mResources,
new KeyguardTransitionInteractor(mTransitionRepository,
TestScopeProvider.getTestScope().getBackgroundScope()),
- new KeyguardInteractor(
- new FakeKeyguardRepository(),
- mCommandQueue,
- mFeatureFlags,
- new FakeKeyguardBouncerRepository()
- ),
+ KeyguardInteractorFactory.create(mFeatureFlags).getKeyguardInteractor(),
mFeatureFlags
);
}
@@ -227,7 +221,7 @@
}
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 ef3af8a..0000000
--- 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 b56986e..0000000
--- 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 4c0890a..0000000
--- 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 7a5b772..0000000
--- 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 d2832fb9..0000000
--- 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 347b26d..0000000
--- 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 fd7657f..0000000
--- 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 573581d..0000000
--- 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 3a27e35..0000000
--- 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 5ece6ef..0000000
--- 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 a0fdc8f..24a5e80 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 @@
@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 8207fa6..d500b5a 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 @@
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 4cf5a4b..79c87cf 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.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.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.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.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 @@
@Mock
private Display mDisplay;
@Mock
- private TunerService mTunerService;
- @Mock
private UserTracker mUserTracker;
@Mock
private PrivacyDotViewController mDotViewController;
@@ -234,7 +232,7 @@
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 @@
}
@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 @@
}
});
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 @@
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 @@
// 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 @@
// 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 @@
// 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 @@
}
@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 @@
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 @@
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 @@
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 665246b..62a176c9 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 @@
@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 9c36af3..31c09b8 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.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 @@
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 @@
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 ce96708..275723b 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.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.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.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.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 @@
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 @@
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 @@
}
@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 @@
}
@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 @@
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 @@
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 38ecec0..db58074 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 @@
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 @@
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 f6ca938..fd258e3 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 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 @@
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 d4efbe4..98be49f 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.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 @@
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 f10c21b..a10f5dd 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 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.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 @@
.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 @@
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 @@
systemSettings,
secureSettings,
systemClock,
+ userTracker,
mainHandler,
backgroundDelayableExecutor
)
@@ -93,7 +97,12 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 0798d73..d1ac0e8 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.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 1990c8f..ac2d492 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 @@
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 @@
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 @@
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 @@
}
@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 @@
}
@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 0000000..88c710a
--- /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 b9f92a0..5cae23c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
@@ -492,6 +492,50 @@
}
@Test
+ public void testOnAuthenticationFailedInvoked_whenFaceAuthRejected() throws RemoteException {
+ final int modality = BiometricAuthenticator.TYPE_FACE;
+ final int userId = 0;
+
+ enrollFingerprintAndFace(userId);
+
+ showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */);
+
+ mAuthController.onBiometricError(modality,
+ BiometricConstants.BIOMETRIC_PAUSED_REJECTED,
+ 0 /* vendorCode */);
+
+ ArgumentCaptor<Integer> modalityCaptor = ArgumentCaptor.forClass(Integer.class);
+ ArgumentCaptor<String> messageCaptor = ArgumentCaptor.forClass(String.class);
+ verify(mDialog1).onAuthenticationFailed(modalityCaptor.capture(), messageCaptor.capture());
+
+ assertEquals(modalityCaptor.getValue().intValue(), modality);
+ assertEquals(messageCaptor.getValue(),
+ mContext.getString(R.string.biometric_face_not_recognized));
+ }
+
+ @Test
+ public void testOnAuthenticationFailedInvoked_whenFingerprintAuthRejected() {
+ final int modality = BiometricAuthenticator.TYPE_FINGERPRINT;
+ final int userId = 0;
+
+ enrollFingerprintAndFace(userId);
+
+ showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */);
+
+ mAuthController.onBiometricError(modality,
+ BiometricConstants.BIOMETRIC_PAUSED_REJECTED,
+ 0 /* vendorCode */);
+
+ ArgumentCaptor<Integer> modalityCaptor = ArgumentCaptor.forClass(Integer.class);
+ ArgumentCaptor<String> messageCaptor = ArgumentCaptor.forClass(String.class);
+ verify(mDialog1).onAuthenticationFailed(modalityCaptor.capture(), messageCaptor.capture());
+
+ assertEquals(modalityCaptor.getValue().intValue(), modality);
+ assertEquals(messageCaptor.getValue(),
+ mContext.getString(R.string.fingerprint_error_not_match));
+ }
+
+ @Test
public void testOnAuthenticationFailedInvoked_whenBiometricTimedOut() {
showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */);
final int modality = BiometricAuthenticator.TYPE_FACE;
@@ -954,6 +998,25 @@
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 */,
@@ -998,6 +1061,31 @@
return HAT;
}
+ private void enrollFingerprintAndFace(final int userId) {
+
+ // Enroll fingerprint
+ verify(mFingerprintManager).registerBiometricStateListener(
+ mBiometricStateCaptor.capture());
+ assertFalse(mAuthController.isFingerprintEnrolled(userId));
+
+ mBiometricStateCaptor.getValue().onEnrollmentsChanged(userId,
+ 1 /* sensorId */, true /* hasEnrollments */);
+ waitForIdleSync();
+
+ assertTrue(mAuthController.isFingerprintEnrolled(userId));
+
+ // Enroll face
+ verify(mFaceManager).registerBiometricStateListener(
+ mBiometricStateCaptor.capture());
+ assertFalse(mAuthController.isFaceAuthEnrolled(userId));
+
+ mBiometricStateCaptor.getValue().onEnrollmentsChanged(userId,
+ 2 /* sensorId */, true /* hasEnrollments */);
+ waitForIdleSync();
+
+ assertTrue(mAuthController.isFaceAuthEnrolled(userId));
+ }
+
private final class TestableAuthController extends AuthController {
private int mBuildCount = 0;
private PromptInfo mLastBiometricPromptInfo;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricNotificationServiceTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricNotificationServiceTest.java
index 38c9caf..9cb3b1a 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.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 @@
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 @@
private TestableLooper mLooper;
private KeyguardUpdateMonitorCallback mKeyguardUpdateMonitorCallback;
private KeyguardStateController.Callback mKeyguardStateControllerCallback;
+ private BiometricStateListener mFaceStateListener;
+ private BiometricStateListener mFingerprintStateListener;
@Before
public void setUp() {
@@ -99,25 +109,37 @@
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 @@
.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 @@
.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 adb10f0..5420c37 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 2908e75..d022653 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.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 9dcdc46..2248755 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.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 @@
@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 @@
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 b2ccd60..821c2cb 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.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 @@
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 @@
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 @@
}
@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 @@
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 0000000..921ff09
--- /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 0d3b394..032753a 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.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 @@
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 @@
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 ffad326..9df06dc 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.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
similarity index 96%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/bouncer/data/factory/BouncerMessageFactoryTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/bouncer/data/factory/BouncerMessageFactoryTest.kt
index 0ef77e8..992ee1a 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_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.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 @@
@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
similarity index 97%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/bouncer/data/repository/BouncerMessageRepositoryTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/bouncer/data/repo/BouncerMessageRepositoryTest.kt
index a729de5..de712da 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_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.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 @@
@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
similarity index 64%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardBouncerRepositoryTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/bouncer/data/repository/KeyguardBouncerRepositoryTest.kt
index b3104b7..a049191 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.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 @@
@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
similarity index 96%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/AlternateBouncerInteractorTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/AlternateBouncerInteractorTest.kt
index ca6b8d5..37b9ca4 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 374c28d6..9483667 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 @@
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 @@
}
@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 @@
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 @@
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
similarity index 96%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/bouncer/domain/interactor/BouncerMessageInteractorTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerMessageInteractorTest.kt
index 3a87680..8e5256e 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.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.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 @@
@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
similarity index 97%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerCallbackInteractorTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerCallbackInteractorTest.kt
index 2b135cc..a81ca86 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
similarity index 92%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractorTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorTest.kt
index b5cb44a..f892453 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.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 @@
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 @@
}
@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
similarity index 95%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractorWithCoroutinesTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorWithCoroutinesTest.kt
index b288fbc..665456d 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.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 1642410..b53e034 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 @@
@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 e8c946c..c607496 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 @@
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 @@
}
)
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 @@
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 @@
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
similarity index 92%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBouncerViewModelTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/KeyguardBouncerViewModelTest.kt
index 15707c9..8236165 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.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 3bdaf05..7e358d2 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.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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 292fdff..f770a38 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 @@
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 9254617..4da151c 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 @@
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 @@
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 @@
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 7e06680..0353f87 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 @@
}
@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 39fb7b4..9671966 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.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 @@
@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 @@
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 @@
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 @@
mClipboardUtils,
mExecutor,
mClipboardImageLoader,
+ mClipboardTransitionExecutor,
mUiEventLogger);
verify(mClipboardOverlayView).setCallbacks(mOverlayCallbacksCaptor.capture());
mCallbacks = mOverlayCallbacksCaptor.getValue();
@@ -148,6 +164,8 @@
@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 @@
@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 @@
@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 @@
@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 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 @@
@Test
public void test_setClipData_invalidImageData() {
+ initController();
+
ClipData clipData = new ClipData("", new String[]{"image/png"},
new ClipData.Item(Uri.parse("")));
@@ -223,6 +250,7 @@
@Test
public void test_setClipData_nonImageUri() {
+ initController();
ClipData clipData = new ClipData("", new String[]{"resource/png"},
new ClipData.Item(Uri.parse("")));
@@ -235,6 +263,7 @@
@Test
public void test_setClipData_textData() {
+ initController();
mOverlayController.setClipData(mSampleClipData, "abc");
verify(mClipboardOverlayView, times(1)).showTextPreview("Test Item", false);
@@ -245,6 +274,7 @@
@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 @@
@Test
public void test_setClipData_repeatedCalls() {
+ initController();
when(mAnimator.isRunning()).thenReturn(true);
mOverlayController.setClipData(mSampleClipData, "");
@@ -268,7 +299,9 @@
}
@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 @@
}
@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 @@
}
@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 @@
@Test
public void test_remoteCopy_withFlagOn() {
+ initController();
when(mClipboardUtils.isRemoteCopy(any(), any(), any())).thenReturn(true);
mOverlayController.setClipData(mSampleClipData, "");
@@ -309,6 +386,7 @@
@Test
public void test_nonRemoteCopy() {
+ initController();
when(mClipboardUtils.isRemoteCopy(any(), any(), any())).thenReturn(false);
mOverlayController.setClipData(mSampleClipData, "");
@@ -318,13 +396,16 @@
@Test
public void test_logsUseLastClipSource() {
- mOverlayController.setClipData(mSampleClipData, "first.package");
- mCallbacks.onDismissButtonTapped();
- mOverlayController.setClipData(mSampleClipData, "second.package");
- mCallbacks.onDismissButtonTapped();
+ initController();
- verify(mUiEventLogger).log(CLIPBOARD_OVERLAY_DISMISS_TAPPED, 0, "first.package");
- verify(mUiEventLogger).log(CLIPBOARD_OVERLAY_DISMISS_TAPPED, 0, "second.package");
+ mOverlayController.setClipData(mSampleClipData, "first.package");
+ mCallbacks.onShareButtonTapped();
+
+ mOverlayController.setClipData(mSampleClipData, "second.package");
+ mCallbacks.onShareButtonTapped();
+
+ 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 @@
@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 @@
@Test
public void test_noInsets_showsExpanded() {
+ initController();
mOverlayController.setClipData(mSampleClipData, "");
verify(mClipboardOverlayView, never()).setMinimized(true);
@@ -366,6 +449,7 @@
@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 @@
@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 ca6282c..461ec65 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.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 @@
private FakeFeatureFlags mFeatureFlags;
@Mock
private Observer mObserver;
+ @Mock
+ private DreamLogger mLogger;
@Before
public void setUp() {
@@ -66,7 +69,8 @@
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 26cbd77..724c9d1 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 @@
@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 @@
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 fcc3589..8f0b193 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 @@
@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 93a1868..4feba7b 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 @@
@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 @@
@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 @@
@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 @@
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 @@
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 @@
}
@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/demomode/DemoModeControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/demomode/DemoModeControllerTest.kt
index 75eec72..68ea7eb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/demomode/DemoModeControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/demomode/DemoModeControllerTest.kt
@@ -94,7 +94,7 @@
intent.putExtra("command", command)
args.forEach { arg -> intent.putExtra(arg.key, arg.value) }
- fakeBroadcastDispatcher.registeredReceivers.forEach { it.onReceive(context, intent) }
+ fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(context, intent)
}
companion object {
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 47b7d49..8786520 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.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 d0d348d..d99f0da 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 @@
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 7b41605..2c1ebe4 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 @@
@Mock
private FeatureFlags mFeatureFlags;
+ @Mock
+ private DreamLogger mLogger;
+
final FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock());
@Before
@@ -405,6 +408,6 @@
}
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 d16b757..5dc0e55 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 @@
DreamOverlayStateController mDreamOverlayStateController;
@Mock
UserTracker mUserTracker;
+ @Mock
+ DreamLogger mLogger;
@Captor
private ArgumentCaptor<DreamOverlayStateController.Callback> mCallbackCaptor;
@@ -146,7 +148,8 @@
mStatusBarWindowStateController,
mDreamOverlayStatusBarItemsProvider,
mDreamOverlayStateController,
- mUserTracker);
+ mUserTracker,
+ mLogger);
}
@Test
@@ -289,7 +292,8 @@
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 1a89076..3f9b198 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 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 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.Mockito;
import org.mockito.MockitoAnnotations;
+import java.util.Collections;
import java.util.Optional;
@SmallTest
@@ -100,21 +104,34 @@
@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 @@
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 @@
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 e8cbdf3..2830476 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.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 @@
@Mock
private lateinit var logBufferEulogizer: LogBufferEulogizer
- @Mock
- private lateinit var exceptionHandlerManager: UncaughtExceptionPreHandlerManager
@Mock
private lateinit var pw: PrintWriter
@@ -70,6 +68,11 @@
@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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
@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 @@
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 02555cf..6d5226f 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 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 @@
@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 @@
}
@Test
- fun testDumpTarget_dumpable() {
- // 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 explicitly
- val args = arrayOf<String>()
- dumpManager.dumpTarget("dumpable2", pw, args, tailLength = 0)
-
- // 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())
- }
-
- @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 a buffer is dumped explicitly
- val args = arrayOf<String>()
- dumpManager.dumpTarget("buffer1", pw, args, tailLength = 14)
-
- // 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())
- }
-
- @Test
- fun testDumpableMatchingIsBasedOnEndOfTag() {
- // GIVEN a dumpable registered to the manager
- dumpManager.registerCriticalDumpable("com.android.foo.bar.dumpable1", dumpable1)
-
- // WHEN that module is dumped
- val args = arrayOf<String>()
- dumpManager.dumpTarget("dumpable1", pw, arrayOf(), tailLength = 14)
-
- // THEN its dump() method is called
- verify(dumpable1).dump(pw, args)
- }
-
- @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())
- }
-
- @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)
- }
-
- @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())
- }
-
- @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<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)
- }
-
- @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
- 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
- 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)
- }
-
- @Test
- fun testCriticalDump() {
- // GIVEN a variety of registered dumpables and buffers
- 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)
-
- // 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())
- }
-
- @Test
- fun testNormalDump() {
- // GIVEN a variety of registered dumpables and buffers
- dumpManager.registerCriticalDumpable("dumpable1", dumpable1)
- dumpManager.registerCriticalDumpable("dumpable2", dumpable2)
- dumpManager.registerNormalDumpable("dumpable3", dumpable3)
- dumpManager.registerBuffer("buffer1", buffer1)
- dumpManager.registerBuffer("buffer2", buffer2)
-
- // WHEN a normal dump is requested
- val args = arrayOf<String>()
- dumpManager.dumpNormal(pw, args, tailLength = 2)
-
- // 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)
- }
-
- @Test
- fun testUnregister() {
- // 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.registerNormalDumpable("dumpable3", dumpable3)
+ // WHEN the collection is requested
+ var dumpables = dumpManager.getDumpables().map { it.dumpable }
+
+ // THEN it contains the registered entries
+ assertThat(dumpables).containsExactly(dumpable1, dumpable2, dumpable3)
+
+ // WHEN the dumpables are unregistered
dumpManager.unregisterDumpable("dumpable2")
dumpManager.unregisterDumpable("dumpable3")
- // WHEN a dumpables dump is requested
- val args = arrayOf<String>()
- dumpManager.dumpDumpables(pw, args)
+ // WHEN the dumpable collection is requests
+ dumpables = dumpManager.getDumpables().map { it.dumpable }
- // 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())
+ // THEN it contains only the currently-registered entry
+ assertThat(dumpables).containsExactly(dumpable1)
+ }
+
+ @Test
+ fun testRegister_buffers() {
+ // GIVEN a set of registered buffers
+ dumpManager.registerBuffer("buffer1", buffer1)
+ dumpManager.registerBuffer("buffer2", buffer2)
+
+ // WHEN the collection is requested
+ val dumpables = dumpManager.getLogBuffers().map { it.buffer }
+
+ // THEN it contains the registered entries
+ assertThat(dumpables).containsExactly(buffer1, buffer2)
+ }
+
+ @Test
+ fun testRegister_tableLogBuffers() {
+ // GIVEN a set of registered buffers
+ dumpManager.registerTableLogBuffer("table1", table1)
+ dumpManager.registerTableLogBuffer("table2", table2)
+
+ // WHEN the collection is requested
+ val tables = dumpManager.getTableLogBuffers().map { it.table }
+
+ // THEN it contains the registered entries
+ assertThat(tables).containsExactly(table1, table2)
+ }
+
+ @Test
+ fun registerDumpable_throwsWhenNameCannotBeAssigned() {
+ // GIVEN dumpable1 and buffer1 and table1 are registered
+ dumpManager.registerCriticalDumpable("dumpable1", dumpable1)
+ dumpManager.registerBuffer("buffer1", buffer1)
+ 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 registerDumpable_doesNotThrowWhenReRegistering() {
+ // GIVEN dumpable1 and buffer1 are registered
+ dumpManager.registerCriticalDumpable("dumpable1", dumpable1)
+ dumpManager.registerBuffer("buffer1", buffer1)
+
+ // THEN no exception is thrown when trying to re-register a new dumpable under the same key
+ dumpManager.registerCriticalDumpable("dumpable1", dumpable1)
+ dumpManager.registerBuffer("buffer1", buffer1)
+
+ // No exception thrown
+ }
+
+ @Test
+ fun getDumpables_returnsSafeCollection() {
+ // GIVEN a variety of registered dumpables
+ dumpManager.registerCriticalDumpable("dumpable1", dumpable1)
+ dumpManager.registerCriticalDumpable("dumpable2", dumpable2)
+ dumpManager.registerNormalDumpable("dumpable3", dumpable3)
+
+ // WHEN the collection is retrieved
+ val dumpables = dumpManager.getDumpables()
+
+ // 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 getBuffers_returnsSafeCollection() {
+ // GIVEN a set of registered buffers
+ dumpManager.registerBuffer("buffer1", buffer1)
+ dumpManager.registerBuffer("buffer2", buffer2)
+
+ // WHEN the collection is requested
+ val buffers = dumpManager.getLogBuffers()
+
+ // WHEN the collection changes
+ dumpManager.registerBuffer("buffer3", buffer1)
+
+ // THEN the new entry is represented
+ assertThat(dumpManager.getLogBuffers()).hasSize(3)
+
+ // AND the previous collection is unchanged
+ assertThat(buffers).hasSize(2)
+ }
+
+ @Test
+ fun getTableBuffers_returnsSafeCollection() {
+ // GIVEN a set of registered buffers
+ dumpManager.registerTableLogBuffer("table1", table1)
+ dumpManager.registerTableLogBuffer("table2", table2)
+
+ // WHEN the collection is requested
+ val tables = dumpManager.getTableLogBuffers()
+
+ // WHEN the collection changes
+ dumpManager.registerTableLogBuffer("table3", table1)
+
+ // THEN the new entry is represented
+ assertThat(dumpManager.getTableLogBuffers()).hasSize(3)
+
+ // 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 cb38846..3ff7202 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 @@
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.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 @@
@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 @@
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 @@
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 @@
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 c9ee1e8..6aa5a00 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.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 @@
@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 @@
mHandler,
mPackageManager,
Optional.of(mCentralSurfaces),
+ mShadeController,
mKeyguardUpdateMonitor,
mDialogLaunchAnimator);
mGlobalActionsDialogLite.setZeroDialogPressDelayForTesting();
@@ -317,7 +320,7 @@
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 @@
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 cdc99af3..3383516 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.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.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 @@
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 2230841..9a2936e 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 @@
// 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 c280538..12a9f94 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;
@@ -31,6 +36,7 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.mock;
@@ -38,9 +44,12 @@
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 +108,12 @@
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 +178,28 @@
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 +217,12 @@
DejankUtils.setImmediate(true);
createAndStartViewMediator();
+ mInitialUserId = KeyguardUpdateMonitor.getCurrentUser();
+ }
+
+ @After
+ public void teardown() {
+ KeyguardUpdateMonitor.setCurrentUser(mInitialUserId);
}
@Test
@@ -296,6 +321,27 @@
@Test
@TestableLooper.RunWithLooper(setAsMainLooper = true)
+ public void wakeupFromDreamingWhenKeyguardHides() {
+ mViewMediator.onSystemReady();
+ TestableLooper.get(this).processAllMessages();
+
+ // Given device is dreaming
+ when(mUpdateMonitor.isDreaming()).thenReturn(true);
+
+ // When keyguard is going away
+ mKeyguardStateController.notifyKeyguardGoingAway(true);
+
+ // And keyguard is disabled which will call #handleHide
+ mViewMediator.setKeyguardEnabled(false);
+ TestableLooper.get(this).processAllMessages();
+
+ // Then dream should wake up
+ verify(mPowerManager).wakeUp(anyLong(), anyInt(),
+ eq("com.android.systemui:UNLOCK_DREAMING"));
+ }
+
+ @Test
+ @TestableLooper.RunWithLooper(setAsMainLooper = true)
public void restoreBouncerWhenSimLockedAndKeyguardIsGoingAway() {
// When showing and provisioned
mViewMediator.onSystemReady();
@@ -438,6 +484,49 @@
}
@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 +815,9 @@
() -> 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 6447368..b4bd473 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 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 @@
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/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManagerTest.kt
index b2528c5..edaff44 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManagerTest.kt
@@ -25,6 +25,7 @@
import com.android.systemui.R
import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.backup.BackupHelper
import com.android.systemui.settings.FakeUserTracker
import com.android.systemui.settings.UserFileManager
import com.android.systemui.util.FakeSharedPreferences
@@ -360,7 +361,10 @@
}
clearInvocations(userFileManager)
- fakeBroadcastDispatcher.registeredReceivers.firstOrNull()?.onReceive(context, Intent())
+ fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(
+ context,
+ Intent(BackupHelper.ACTION_RESTORE_FINISHED),
+ )
verify(userFileManager, atLeastOnce()).getSharedPreferences(anyString(), anyInt(), anyInt())
job.cancel()
@@ -374,12 +378,13 @@
arrayOf("leftTest:testShortcut1", "rightTest:testShortcut2")
)
- assertThat(underTest.getSelections()).isEqualTo(
- mapOf(
- "leftTest" to listOf("testShortcut1"),
- "rightTest" to listOf("testShortcut2"),
+ assertThat(underTest.getSelections())
+ .isEqualTo(
+ mapOf(
+ "leftTest" to listOf("testShortcut1"),
+ "rightTest" to listOf("testShortcut2"),
+ )
)
- )
}
private fun assertSelections(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt
index b925aeb..f9070b3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt
@@ -461,12 +461,10 @@
}
private fun broadcastDPMStateChange() {
- fakeBroadcastDispatcher.registeredReceivers.forEach { receiver ->
- receiver.onReceive(
- context,
- Intent(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED)
- )
- }
+ fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(
+ context,
+ Intent(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED),
+ )
}
companion object {
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 1090c15..5922cbf 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.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 @@
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,
@@ -506,6 +507,18 @@
}
@Test
+ fun authenticateDoesNotRunIfKeyguardIsNotShowing() =
+ testScope.runTest {
+ testGatingCheckForFaceAuth { keyguardRepository.setKeyguardShowing(false) }
+ }
+
+ @Test
+ fun detectDoesNotRunIfKeyguardIsNotShowing() =
+ testScope.runTest {
+ testGatingCheckForDetect { keyguardRepository.setKeyguardShowing(false) }
+ }
+
+ @Test
fun authenticateDoesNotRunWhenFpIsLockedOut() =
testScope.runTest {
testGatingCheckForFaceAuth { deviceEntryFingerprintAuthRepository.setLockedOut(true) }
@@ -564,6 +577,8 @@
testScope.runTest {
testGatingCheckForFaceAuth {
bouncerRepository.setAlternateVisible(false)
+ // Keyguard is occluded when secure camera is active.
+ keyguardRepository.setKeyguardOccluded(true)
fakeCommandQueue.doForEachCallback {
it.onCameraLaunchGestureDetected(CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP)
}
@@ -773,6 +788,8 @@
testScope.runTest {
testGatingCheckForDetect {
bouncerRepository.setAlternateVisible(false)
+ // Keyguard is occluded when secure camera is active.
+ keyguardRepository.setKeyguardOccluded(true)
fakeCommandQueue.doForEachCallback {
it.onCameraLaunchGestureDetected(CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP)
}
@@ -1010,6 +1027,7 @@
fakeUserRepository.setSelectedUserInfo(primaryUser)
biometricSettingsRepository.setIsFaceAuthSupportedInCurrentPosture(true)
bouncerRepository.setAlternateVisible(true)
+ keyguardRepository.setKeyguardShowing(true)
runCurrent()
}
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 4b797cb..953d618 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 @@
dozeParameters,
authController,
dreamOverlayCallbackController,
- mainDispatcher
+ mainDispatcher,
+ testScope.backgroundScope,
)
}
@@ -343,8 +344,6 @@
)
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 71e73cbd..80700e5 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.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 0d695aa..4b09468 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.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 @@
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 @@
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/KeyguardLongPressInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractorTest.kt
index f63be61..e9c22f9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractorTest.kt
@@ -194,9 +194,10 @@
underTest.onLongPress()
assertThat(isMenuVisible).isTrue()
- fakeBroadcastDispatcher.registeredReceivers.forEach { broadcastReceiver ->
- broadcastReceiver.onReceive(context, Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS))
- }
+ fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(
+ context,
+ Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS),
+ )
assertThat(isMenuVisible).isFalse()
}
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 2c90d53..8540bf7 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.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.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 @@
@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 @@
underTest =
KeyguardQuickAffordanceInteractor(
keyguardInteractor =
- KeyguardInteractor(
- repository = FakeKeyguardRepository(),
- commandQueue = commandQueue,
- featureFlags = featureFlags,
- bouncerRepository = FakeKeyguardBouncerRepository(),
- ),
+ KeyguardInteractorFactory.create(
+ featureFlags = featureFlags,
+ )
+ .keyguardInteractor,
registry =
FakeKeyguardQuickAffordanceRegistry(
mapOf(
@@ -372,11 +366,11 @@
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 7cab680..a0c5a75 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.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.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 @@
@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 @@
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 30dba23..50075b5 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 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.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 @@
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 @@
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 @@
fromDreamingTransitionInteractor =
FromDreamingTransitionInteractor(
scope = testScope,
- keyguardInteractor = createKeyguardInteractor(featureFlags),
+ keyguardInteractor = createKeyguardInteractor(),
keyguardTransitionRepository = mockTransitionRepository,
keyguardTransitionInteractor =
KeyguardTransitionInteractor(transitionRepository, testScope.backgroundScope),
@@ -128,7 +126,7 @@
fromAodTransitionInteractor =
FromAodTransitionInteractor(
scope = testScope,
- keyguardInteractor = createKeyguardInteractor(featureFlags),
+ keyguardInteractor = createKeyguardInteractor(),
keyguardTransitionRepository = mockTransitionRepository,
keyguardTransitionInteractor =
KeyguardTransitionInteractor(transitionRepository, testScope.backgroundScope),
@@ -138,7 +136,7 @@
fromGoneTransitionInteractor =
FromGoneTransitionInteractor(
scope = testScope,
- keyguardInteractor = createKeyguardInteractor(featureFlags),
+ keyguardInteractor = createKeyguardInteractor(),
keyguardTransitionRepository = mockTransitionRepository,
keyguardTransitionInteractor =
KeyguardTransitionInteractor(transitionRepository, testScope.backgroundScope),
@@ -148,7 +146,7 @@
fromDozingTransitionInteractor =
FromDozingTransitionInteractor(
scope = testScope,
- keyguardInteractor = createKeyguardInteractor(featureFlags),
+ keyguardInteractor = createKeyguardInteractor(),
keyguardTransitionRepository = mockTransitionRepository,
keyguardTransitionInteractor =
KeyguardTransitionInteractor(transitionRepository, testScope.backgroundScope),
@@ -158,7 +156,7 @@
fromOccludedTransitionInteractor =
FromOccludedTransitionInteractor(
scope = testScope,
- keyguardInteractor = createKeyguardInteractor(featureFlags),
+ keyguardInteractor = createKeyguardInteractor(),
keyguardTransitionRepository = mockTransitionRepository,
keyguardTransitionInteractor =
KeyguardTransitionInteractor(transitionRepository, testScope.backgroundScope),
@@ -168,7 +166,7 @@
fromAlternateBouncerTransitionInteractor =
FromAlternateBouncerTransitionInteractor(
scope = testScope,
- keyguardInteractor = createKeyguardInteractor(featureFlags),
+ keyguardInteractor = createKeyguardInteractor(),
keyguardTransitionRepository = mockTransitionRepository,
keyguardTransitionInteractor =
KeyguardTransitionInteractor(transitionRepository, testScope.backgroundScope),
@@ -178,7 +176,7 @@
fromPrimaryBouncerTransitionInteractor =
FromPrimaryBouncerTransitionInteractor(
scope = testScope,
- keyguardInteractor = createKeyguardInteractor(featureFlags),
+ keyguardInteractor = createKeyguardInteractor(),
keyguardTransitionRepository = mockTransitionRepository,
keyguardTransitionInteractor =
KeyguardTransitionInteractor(transitionRepository, testScope.backgroundScope),
@@ -855,13 +853,13 @@
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 d622f1c..65781c4 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 @@
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 @@
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 @@
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 b6368cf..29886d5 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.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.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 @@
@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 @@
),
),
)
- repository = FakeKeyguardRepository()
val featureFlags =
FakeFeatureFlags().apply {
set(Flags.CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES, false)
@@ -152,13 +148,10 @@
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 @@
}
@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 @@
)
}
- 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 0000000..dff0f29
--- /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 8ba3f0f..f0ea007 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 @@
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 @@
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 @@
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 @@
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 @@
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 65b2ef7..f88b71d 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.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/keyguard/util/IndicationHelperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/util/IndicationHelperTest.kt
new file mode 100644
index 0000000..fd0ff9b
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/util/IndicationHelperTest.kt
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.util
+
+import android.hardware.biometrics.BiometricFaceConstants.BIOMETRIC_ERROR_POWER_PRESSED
+import android.hardware.biometrics.BiometricFaceConstants.FACE_ERROR_CANCELED
+import android.hardware.biometrics.BiometricFaceConstants.FACE_ERROR_LOCKOUT
+import android.hardware.biometrics.BiometricFaceConstants.FACE_ERROR_LOCKOUT_PERMANENT
+import android.hardware.biometrics.BiometricFaceConstants.FACE_ERROR_TIMEOUT
+import android.hardware.biometrics.BiometricFaceConstants.FACE_ERROR_UNABLE_TO_PROCESS
+import android.hardware.biometrics.BiometricFaceConstants.FACE_ERROR_VENDOR
+import android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ERROR_CANCELED
+import android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ERROR_LOCKOUT
+import android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ERROR_LOCKOUT_PERMANENT
+import android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ERROR_TIMEOUT
+import android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ERROR_USER_CANCELED
+import android.hardware.biometrics.BiometricSourceType
+import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.util.mockito.whenever
+import junit.framework.Assert.assertFalse
+import junit.framework.Assert.assertTrue
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyBoolean
+import org.mockito.Mock
+import org.mockito.junit.MockitoJUnit
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+@TestableLooper.RunWithLooper
+class IndicationHelperTest : SysuiTestCase() {
+
+ @JvmField @Rule var mockitoRule = MockitoJUnit.rule()
+
+ @Mock lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
+ private lateinit var underTest: IndicationHelper
+
+ @Before
+ fun setup() {
+ underTest =
+ IndicationHelper(
+ keyguardUpdateMonitor,
+ )
+ }
+
+ @Test
+ fun suppressErrorMsg_faceErrorCancelled() {
+ givenPrimaryAuthNotRequired()
+ assertTrue(underTest.shouldSuppressErrorMsg(BiometricSourceType.FACE, FACE_ERROR_CANCELED))
+ }
+
+ @Test
+ fun suppressErrorMsg_faceErrorUnableToProcess() {
+ givenPrimaryAuthNotRequired()
+ assertTrue(
+ underTest.shouldSuppressErrorMsg(BiometricSourceType.FACE, FACE_ERROR_UNABLE_TO_PROCESS)
+ )
+ }
+
+ @Test
+ fun suppressErrorMsg_facePrimaryAuthRequired() {
+ givenPrimaryAuthRequired()
+ assertTrue(underTest.shouldSuppressErrorMsg(BiometricSourceType.FACE, FACE_ERROR_TIMEOUT))
+ }
+
+ @Test
+ fun doNotSuppressErrorMsg_facePrimaryAuthRequired_faceLockout() {
+ givenPrimaryAuthRequired()
+ assertFalse(underTest.shouldSuppressErrorMsg(BiometricSourceType.FACE, FACE_ERROR_LOCKOUT))
+ assertFalse(
+ underTest.shouldSuppressErrorMsg(BiometricSourceType.FACE, FACE_ERROR_LOCKOUT_PERMANENT)
+ )
+ }
+
+ @Test
+ fun suppressErrorMsg_fingerprintErrorCancelled() {
+ givenPrimaryAuthNotRequired()
+ assertTrue(
+ underTest.shouldSuppressErrorMsg(
+ BiometricSourceType.FINGERPRINT,
+ FINGERPRINT_ERROR_CANCELED
+ )
+ )
+ }
+
+ @Test
+ fun suppressErrorMsg_fingerprintErrorUserCancelled() {
+ givenPrimaryAuthNotRequired()
+ assertTrue(
+ underTest.shouldSuppressErrorMsg(
+ BiometricSourceType.FINGERPRINT,
+ FINGERPRINT_ERROR_USER_CANCELED
+ )
+ )
+ }
+
+ @Test
+ fun suppressErrorMsg_fingerprintErrorPowerPressed() {
+ givenPrimaryAuthNotRequired()
+ assertTrue(
+ underTest.shouldSuppressErrorMsg(
+ BiometricSourceType.FINGERPRINT,
+ BIOMETRIC_ERROR_POWER_PRESSED
+ )
+ )
+ }
+
+ @Test
+ fun suppressErrorMsg_fingerprintPrimaryAuthRequired() {
+ givenPrimaryAuthRequired()
+ assertTrue(
+ underTest.shouldSuppressErrorMsg(BiometricSourceType.FACE, FINGERPRINT_ERROR_TIMEOUT)
+ )
+ }
+
+ @Test
+ fun doNotSuppressErrorMsg_fingerprintPrimaryAuthRequired_fingerprintLockout() {
+ givenPrimaryAuthRequired()
+ assertFalse(
+ underTest.shouldSuppressErrorMsg(
+ BiometricSourceType.FINGERPRINT,
+ FINGERPRINT_ERROR_LOCKOUT
+ )
+ )
+ assertFalse(
+ underTest.shouldSuppressErrorMsg(
+ BiometricSourceType.FACE,
+ FINGERPRINT_ERROR_LOCKOUT_PERMANENT
+ )
+ )
+ }
+
+ @Test
+ fun isFaceLockoutErrorMsgId() {
+ givenPrimaryAuthRequired()
+ assertTrue(underTest.isFaceLockoutErrorMsg(FACE_ERROR_LOCKOUT))
+ assertTrue(underTest.isFaceLockoutErrorMsg(FACE_ERROR_LOCKOUT_PERMANENT))
+ assertFalse(underTest.isFaceLockoutErrorMsg(FACE_ERROR_TIMEOUT))
+ assertFalse(underTest.isFaceLockoutErrorMsg(FACE_ERROR_CANCELED))
+ assertFalse(underTest.isFaceLockoutErrorMsg(FACE_ERROR_UNABLE_TO_PROCESS))
+ assertFalse(underTest.isFaceLockoutErrorMsg(FACE_ERROR_VENDOR))
+ }
+
+ private fun givenPrimaryAuthNotRequired() {
+ whenever(keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean()))
+ .thenReturn(true)
+ }
+
+ private fun givenPrimaryAuthRequired() {
+ whenever(keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean()))
+ .thenReturn(false)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/resume/MediaResumeListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/resume/MediaResumeListenerTest.kt
index 9ab7289..530b86e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/resume/MediaResumeListenerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/resume/MediaResumeListenerTest.kt
@@ -98,6 +98,8 @@
@Captor lateinit var callbackCaptor: ArgumentCaptor<ResumeMediaBrowser.Callback>
@Captor lateinit var actionCaptor: ArgumentCaptor<Runnable>
@Captor lateinit var componentCaptor: ArgumentCaptor<String>
+ @Captor lateinit var userIdCaptor: ArgumentCaptor<Int>
+ @Captor lateinit var userCallbackCaptor: ArgumentCaptor<UserTracker.Callback>
private lateinit var executor: FakeExecutor
private lateinit var data: MediaData
@@ -124,7 +126,7 @@
)
Settings.Secure.putInt(context.contentResolver, Settings.Secure.MEDIA_CONTROLS_RESUME, 1)
- whenever(resumeBrowserFactory.create(capture(callbackCaptor), any()))
+ whenever(resumeBrowserFactory.create(capture(callbackCaptor), any(), capture(userIdCaptor)))
.thenReturn(resumeBrowser)
// resume components are stored in sharedpreferences
@@ -334,6 +336,7 @@
@Test
fun testOnUserUnlock_loadsTracks() {
// Set up mock service to successfully find valid media
+ setUpMbsWithValidResolveInfo()
val description = MediaDescription.Builder().setTitle(TITLE).build()
val component = ComponentName(PACKAGE_NAME, CLASS_NAME)
whenever(resumeBrowser.token).thenReturn(token)
@@ -417,6 +420,7 @@
@Test
fun testLoadComponents_recentlyPlayed_adds() {
// Set up browser to return successfully
+ setUpMbsWithValidResolveInfo()
val description = MediaDescription.Builder().setTitle(TITLE).build()
val component = ComponentName(PACKAGE_NAME, CLASS_NAME)
whenever(resumeBrowser.token).thenReturn(token)
@@ -600,7 +604,7 @@
// Set up our factory to return a new browser so we can verify we disconnected the old one
val newResumeBrowser = mock(ResumeMediaBrowser::class.java)
- whenever(resumeBrowserFactory.create(capture(callbackCaptor), any()))
+ whenever(resumeBrowserFactory.create(capture(callbackCaptor), any(), anyInt()))
.thenReturn(newResumeBrowser)
// When the resume action is run
@@ -610,6 +614,66 @@
verify(resumeBrowser).disconnect()
}
+ @Test
+ fun testUserUnlocked_userChangeWhileQuerying() {
+ val firstUserId = 1
+ val secondUserId = 2
+ val description = MediaDescription.Builder().setTitle(TITLE).build()
+ val component = ComponentName(PACKAGE_NAME, CLASS_NAME)
+
+ setUpMbsWithValidResolveInfo()
+ whenever(resumeBrowser.token).thenReturn(token)
+ whenever(resumeBrowser.appIntent).thenReturn(pendingIntent)
+
+ val unlockIntent =
+ Intent(Intent.ACTION_USER_UNLOCKED).apply {
+ putExtra(Intent.EXTRA_USER_HANDLE, firstUserId)
+ }
+ verify(userTracker).addCallback(capture(userCallbackCaptor), any())
+
+ // When the first user unlocks and we query their recent media
+ userCallbackCaptor.value.onUserChanged(firstUserId, context)
+ resumeListener.userUnlockReceiver.onReceive(context, unlockIntent)
+ whenever(resumeBrowser.userId).thenReturn(userIdCaptor.value)
+ verify(resumeBrowser, times(3)).findRecentMedia()
+
+ // And the user changes before the MBS response is received
+ userCallbackCaptor.value.onUserChanged(secondUserId, context)
+ callbackCaptor.value.addTrack(description, component, resumeBrowser)
+
+ // Then the loaded media is correctly associated with the first user
+ verify(mediaDataManager)
+ .addResumptionControls(
+ eq(firstUserId),
+ eq(description),
+ any(),
+ eq(token),
+ eq(PACKAGE_NAME),
+ eq(pendingIntent),
+ eq(PACKAGE_NAME)
+ )
+ }
+
+ @Test
+ fun testUserUnlocked_noComponent_doesNotQuery() {
+ // Set up a valid MBS, but user does not have the service available
+ setUpMbsWithValidResolveInfo()
+ val pm = mock(PackageManager::class.java)
+ whenever(mockContext.packageManager).thenReturn(pm)
+ whenever(pm.resolveServiceAsUser(any(), anyInt(), anyInt())).thenReturn(null)
+
+ val unlockIntent =
+ Intent(Intent.ACTION_USER_UNLOCKED).apply {
+ putExtra(Intent.EXTRA_USER_HANDLE, context.userId)
+ }
+
+ // When the user is unlocked, but does not have the component installed
+ resumeListener.userUnlockReceiver.onReceive(context, unlockIntent)
+
+ // Then we never attempt to connect to it
+ verify(resumeBrowser, never()).findRecentMedia()
+ }
+
/** Sets up mocks to successfully find a MBS that returns valid media. */
private fun setUpMbsWithValidResolveInfo() {
val pm = mock(PackageManager::class.java)
@@ -620,6 +684,8 @@
resolveInfo.serviceInfo = serviceInfo
resolveInfo.serviceInfo.name = CLASS_NAME
val resumeInfo = listOf(resolveInfo)
- whenever(pm.queryIntentServices(any(), anyInt())).thenReturn(resumeInfo)
+ whenever(pm.queryIntentServicesAsUser(any(), anyInt(), anyInt())).thenReturn(resumeInfo)
+ whenever(pm.resolveServiceAsUser(any(), anyInt(), anyInt())).thenReturn(resolveInfo)
+ whenever(pm.getApplicationLabel(any())).thenReturn(PACKAGE_NAME)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/resume/ResumeMediaBrowserTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/resume/ResumeMediaBrowserTest.kt
index a04cfd4..b45e66b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/resume/ResumeMediaBrowserTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/resume/ResumeMediaBrowserTest.kt
@@ -93,7 +93,8 @@
component,
browserFactory,
logger,
- mediaController
+ mediaController,
+ context.userId,
)
}
@@ -381,8 +382,9 @@
componentName: ComponentName,
browserFactory: MediaBrowserFactory,
logger: ResumeMediaBrowserLogger,
- private val fakeController: MediaController
- ) : ResumeMediaBrowser(context, callback, componentName, browserFactory, logger) {
+ private val fakeController: MediaController,
+ userId: Int,
+ ) : ResumeMediaBrowser(context, callback, componentName, browserFactory, logger, userId) {
override fun createMediaController(token: MediaSession.Token): MediaController {
return fakeController
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 9b8605d..8d306cce 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.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 @@
EdgeBackGestureHandler mEdgeBackGestureHandler;
@Mock
EdgeBackGestureHandler.Factory mEdgeBackGestureHandlerFactory;
+ @Mock
+ NotificationShadeWindowController mNotificationShadeWindowController;
+
private AccessibilityManager.AccessibilityServicesStateChangeListener
mAccessibilityServicesStateChangeListener;
@@ -136,7 +140,7 @@
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 f062ec7..697d1a3 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_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.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 @@
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 @@
() -> 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 @@
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 @@
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 @@
@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 0000000..450aadd
--- /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 0000000..baac9e0
--- /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 0954f6f..a76af8e 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.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.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.MockitoAnnotations
/** atest SystemUITests:NoteTaskControllerTest */
+@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(AndroidJUnit4::class)
internal class NoteTaskControllerTest : SysuiTestCase() {
@@ -98,7 +104,10 @@
@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 @@
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 @@
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 @@
context = context,
resolver = resolver,
eventLogger = eventLogger,
- optionalBubbles = Optional.ofNullable(bubbles),
userManager = userManager,
keyguardManager = keyguardManager,
isEnabled = isEnabled,
@@ -142,6 +155,10 @@
roleManager = roleManager,
shortcutManager = shortcutManager,
activityManager = activityManager,
+ secureSettings = secureSettings,
+ noteTaskBubblesController =
+ FakeNoteTaskBubbleController(context, testDispatcher, Optional.ofNullable(bubbles)),
+ applicationScope = testScope,
)
// region onBubbleExpandChanged
@@ -157,7 +174,7 @@
)
verify(eventLogger).logNoteTaskOpened(expectedInfo)
- verifyZeroInteractions(context, bubbles, keyguardManager, userManager)
+ verifyZeroInteractions(bubbles, keyguardManager, userManager)
}
@Test
@@ -172,7 +189,7 @@
)
verify(eventLogger).logNoteTaskClosed(expectedInfo)
- verifyZeroInteractions(context, bubbles, keyguardManager, userManager)
+ verifyZeroInteractions(bubbles, keyguardManager, userManager)
}
@Test
@@ -186,7 +203,7 @@
key = Bubble.getAppBubbleKeyForApp(expectedInfo.packageName, expectedInfo.user),
)
- verifyZeroInteractions(context, bubbles, keyguardManager, userManager, eventLogger)
+ verifyZeroInteractions(bubbles, keyguardManager, userManager, eventLogger)
}
@Test
@@ -200,7 +217,7 @@
key = Bubble.getAppBubbleKeyForApp(expectedInfo.packageName, expectedInfo.user),
)
- verifyZeroInteractions(context, bubbles, keyguardManager, userManager, eventLogger)
+ verifyZeroInteractions(bubbles, keyguardManager, userManager, eventLogger)
}
@Test
@@ -211,7 +228,7 @@
key = "any other key",
)
- verifyZeroInteractions(context, bubbles, keyguardManager, userManager, eventLogger)
+ verifyZeroInteractions(bubbles, keyguardManager, userManager, eventLogger)
}
@Test
@@ -222,16 +239,19 @@
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 @@
}
@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 @@
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 @@
createNoteTaskController().showNoteTask(entryPoint = TAIL_BUTTON)
- verifyZeroInteractions(context, bubbles, eventLogger)
+ verifyZeroInteractions(bubbles, eventLogger)
}
@Test
@@ -483,7 +541,7 @@
createNoteTaskController().showNoteTask(entryPoint = QUICK_AFFORDANCE)
- verifyZeroInteractions(context, bubbles, eventLogger)
+ verifyZeroInteractions(bubbles, eventLogger)
}
@Test
@@ -499,7 +557,7 @@
createNoteTaskController().showNoteTask(entryPoint = QUICK_AFFORDANCE)
- verifyZeroInteractions(context, bubbles, eventLogger)
+ verifyZeroInteractions(bubbles, eventLogger)
}
@Test
@@ -611,11 +669,11 @@
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 @@
verify(controller).updateNoteTaskAsUser(user)
}
-
- @Test
- fun onRoleHoldersChanged_notesRole_differentUser_shouldUpdateShortcutsInUserProcess() {
- // FakeUserTracker will default to UserHandle.SYSTEM.
- val user = UserHandle.CURRENT
-
- createNoteTaskController(isEnabled = true).onRoleHoldersChanged(ROLE_NOTES, user)
-
- verify(context).startServiceAsUser(any(), eq(user))
- }
// endregion
// region updateNoteTaskAsUser
@Test
- fun updateNoteTaskAsUser_withNotesRole_withShortcuts_shouldUpdateShortcuts() {
- createNoteTaskController(isEnabled = true).updateNoteTaskAsUser(userTracker.userHandle)
+ 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())
+
+ controller.updateNoteTaskAsUser(user)
+
+ verify(controller, never()).updateNoteTaskAsUserInternal(any())
+ val intent = withArgCaptor { verify(context).startServiceAsUser(capture(), eq(user)) }
+ assertThat(intent).hasComponentClass(NoteTaskControllerUpdateService::class.java)
+ }
+ // endregion
+
+ // region internalUpdateNoteTaskAsUser
+ @Test
+ fun updateNoteTaskAsUserInternal_withNotesRole_withShortcuts_shouldUpdateShortcuts() {
+ createNoteTaskController(isEnabled = true)
+ .updateNoteTaskAsUserInternal(userTracker.userHandle)
val actualComponent = argumentCaptor<ComponentName>()
verify(context.packageManager)
@@ -652,27 +729,29 @@
.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 @@
}
@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 @@
}
// 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 @@
// 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 4e85b6c..95bb3e0 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.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 @@
}
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 @@
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 @@
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 @@
underTest.initialize()
verifyZeroInteractions(
- commandQueue,
- bubbles,
- controller,
- roleManager,
- userManager,
- keyguardMonitor,
+ commandQueue,
+ bubbles,
+ controller,
+ roleManager,
+ userManager,
+ keyguardMonitor,
)
}
@@ -132,12 +136,12 @@
underTest.initialize()
verifyZeroInteractions(
- commandQueue,
- bubbles,
- controller,
- roleManager,
- userManager,
- keyguardMonitor,
+ commandQueue,
+ bubbles,
+ controller,
+ roleManager,
+ userManager,
+ keyguardMonitor,
)
}
@@ -146,7 +150,7 @@
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 @@
}
@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 a0c376f..627c4a8 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.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 @@
class LaunchNoteTaskActivityTest : SysuiTestCase() {
@Mock lateinit var noteTaskController: NoteTaskController
- @Mock lateinit var userManager: UserManager
- private val userTracker: FakeUserTracker = FakeUserTracker()
@Rule
@JvmField
@@ -60,8 +52,6 @@
override fun create(intent: Intent?) =
LaunchNoteTaskActivity(
controller = noteTaskController,
- userManager = userManager,
- userTracker = userTracker
)
},
/* initialTouchMode= */ false,
@@ -71,7 +61,6 @@
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
- whenever(userManager.isManagedProfile(eq(workProfileUser.id))).thenReturn(true)
}
@After
@@ -83,36 +72,7 @@
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 6347c34..0000000
--- 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 bb3b3f7..a01394f 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 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 @@
@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 @@
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 @@
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 31d4512..023ed06 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.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 @@
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 @@
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 aacc695..93f316e 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 @@
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 87ca9df..fcda5f5 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 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 @@
}
@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 @@
}
@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 810ab34..d98bcee 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.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 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 @@
@Mock
private Provider<AutoTileManager> mAutoTiles;
@Mock
- private CentralSurfaces mCentralSurfaces;
+ private ShadeController mShadeController;
@Mock
private QSLogger mQSLogger;
@Mock
@@ -161,7 +160,7 @@
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 @@
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 198ed4a..41240e5 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 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 45783ab..6556cfd 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 @@
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 @@
@SmallTest
class PanelInteractorImplTest : SysuiTestCase() {
- @Mock private lateinit var centralSurfaces: CentralSurfaces
+ @Mock private lateinit var shadeController: ShadeController
@Before
fun setup() {
@@ -40,37 +39,28 @@
@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 f55d262..180fed9 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 @@
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 ddbfca5..cbc3553 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.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.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 @@
@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 @@
val featureFlags = FakeFeatureFlags()
+ @Captor private lateinit var argumentCaptor: ArgumentCaptor<Runnable>
+
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
@@ -88,11 +97,13 @@
statusBarStateController,
activityStarter,
qsLogger,
+ keyguardStateController,
dialogLaunchAnimator,
FakeSettings(),
FakeSettings(),
FakeSystemClock(),
featureFlags,
+ userTracker,
backgroundDelayableExecutor,
)
fontScalingTile.initialize()
@@ -124,15 +135,45 @@
}
@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 6614392..5b30687 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 @@
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 @@
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 105387d..05a1699 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 @@
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 @@
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 8744aa3..59b5953 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.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.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 @@
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 @@
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 @@
mRecordingService.onStopped();
- verify(mController).updateState(false);
+ assertUpdateState(false);
}
@Test
@@ -235,8 +257,21 @@
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 67b1099..d8897e9 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.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.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 @@
@Mock private Optional<Bubbles> mOptionalBubbles;
@Mock private Bubbles mBubbles;
@Mock private DevicePolicyManager mDevicePolicyManager;
- @Mock private UserManager mUserManager;
private AppClipsService mAppClipsService;
@@ -81,51 +80,84 @@
}
@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 @@
}
@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);
-
- verify(mAppClipsService.mProxyConnectorToMainProfile).postForResult(any());
+ assertThat(getInterfaceWithRealContext()
+ .canLaunchCaptureContentActivityForNoteInternal(FAKE_TASK_ID))
+ .isEqualTo(CAPTURE_CONTENT_FOR_NOTE_SUCCESS);
}
- @Test
- public void isManagedProfile_noMainUser_shouldReturnFalse() {
- when(mUserManager.isManagedProfile()).thenReturn(true);
- when(mUserManager.getMainUser()).thenReturn(null);
-
- getInterfaceWithRealContext();
-
- assertThat(mAppClipsService.mProxyConnectorToMainProfile).isNull();
+ private void mockForEmptyBubbles() {
+ when(mFeatureFlags.isEnabled(SCREENSHOT_APP_CLIPS)).thenReturn(true);
+ when(mOptionalBubbles.isEmpty()).thenReturn(true);
}
+ 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);
+ }
+
+ 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);
+ }
+
+ 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 @@
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 e9007ff..7fad972 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_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.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.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 @@
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 @@
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 @@
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 @@
}
@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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 3706859..0a1eca6 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 fe89a14..ef7c7bc 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.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;
@@ -116,6 +117,7 @@
import com.android.systemui.qs.QSFragment;
import com.android.systemui.screenrecord.RecordingController;
import com.android.systemui.shade.data.repository.ShadeRepository;
+import com.android.systemui.shade.domain.interactor.ShadeInteractor;
import com.android.systemui.shade.transition.ShadeTransitionController;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.KeyguardIndicationController;
@@ -167,6 +169,7 @@
import com.android.systemui.statusbar.policy.KeyguardUserSwitcherView;
import com.android.systemui.statusbar.window.StatusBarWindowStateController;
import com.android.systemui.unfold.SysUIUnfoldComponent;
+import com.android.systemui.util.kotlin.JavaAdapter;
import com.android.systemui.util.time.FakeSystemClock;
import com.android.systemui.util.time.SystemClock;
import com.android.wm.shell.animation.FlingAnimationUtils;
@@ -253,6 +256,7 @@
@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;
@@ -309,10 +313,13 @@
@Mock protected ActivityStarter mActivityStarter;
@Mock protected KeyguardFaceAuthInteractor mKeyguardFaceAuthInteractor;
@Mock protected ShadeRepository mShadeRepository;
+ @Mock private ShadeInteractor mShadeInteractor;
+ @Mock private JavaAdapter mJavaAdapter;
@Mock private CastController mCastController;
protected final int mMaxUdfpsBurnInOffsetY = 5;
protected KeyguardBottomAreaInteractor mKeyguardBottomAreaInteractor;
+ protected FakeKeyguardRepository mFakeKeyguardRepository;
protected KeyguardInteractor mKeyguardInteractor;
protected NotificationPanelViewController.TouchHandler mTouchHandler;
protected ConfigurationController mConfigurationController;
@@ -340,10 +347,12 @@
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);
@@ -594,7 +603,6 @@
mNotificationStackSizeCalculator,
mUnlockedScreenOffAnimationController,
mShadeTransitionController,
- mInteractionJankMonitor,
systemClock,
mKeyguardBottomAreaViewModel,
mKeyguardBottomAreaInteractor,
@@ -611,6 +619,7 @@
mKeyuardLongPressViewModel,
mKeyguardInteractor,
mActivityStarter,
+ mKeyguardViewConfigurator,
mKeyguardFaceAuthInteractor);
mNotificationPanelViewController.initDependencies(
mCentralSurfaces,
@@ -682,6 +691,8 @@
mDumpManager,
mKeyguardFaceAuthInteractor,
mShadeRepository,
+ mShadeInteractor,
+ mJavaAdapter,
mCastController
);
}
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 8f2ee91..5802eb3 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.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 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 @@
}
@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 @@
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 526dc8d..cde6ac0 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 @@
}
};
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 3995e71..2a9b403 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
@@ -21,26 +21,35 @@
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
import com.android.systemui.multishade.domain.interactor.MultiShadeMotionEventInteractor
+import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.shade.NotificationShadeWindowView.InteractionEventHandler
import com.android.systemui.statusbar.LockscreenShadeTransitionController
import com.android.systemui.statusbar.NotificationInsetsController
@@ -54,6 +63,7 @@
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 +93,8 @@
@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 powerInteractor: PowerInteractor
@Mock private lateinit var dockManager: DockManager
@Mock private lateinit var notificationPanelViewController: NotificationPanelViewController
@Mock private lateinit var notificationShadeDepthController: NotificationShadeDepthController
@@ -134,6 +146,7 @@
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 +175,8 @@
statusBarWindowStateController,
lockIconViewController,
centralSurfaces,
+ backActionInteractor,
+ powerInteractor,
notificationShadeWindowController,
unfoldTransitionProgressProvider,
keyguardUnlockAnimationController,
@@ -170,6 +185,7 @@
pulsingGestureListener,
keyguardBouncerViewModel,
keyguardBouncerComponentFactory,
+ mock(KeyguardMessageAreaController.Factory::class.java),
keyguardTransitionInteractor,
primaryBouncerToGoneTransitionViewModel,
featureFlags,
@@ -190,6 +206,10 @@
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 fe95742..252a03b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
@@ -21,25 +21,34 @@
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
import com.android.systemui.multishade.domain.interactor.MultiShadeMotionEventInteractor
+import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.shade.NotificationShadeWindowView.InteractionEventHandler
import com.android.systemui.statusbar.DragDownHelper
import com.android.systemui.statusbar.LockscreenShadeTransitionController
@@ -54,6 +63,7 @@
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 +80,7 @@
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 +95,8 @@
@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 powerInteractor: PowerInteractor
@Mock private lateinit var dockManager: DockManager
@Mock private lateinit var notificationPanelViewController: NotificationPanelViewController
@Mock private lateinit var notificationStackScrollLayout: NotificationStackScrollLayout
@@ -147,6 +160,7 @@
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 +188,8 @@
statusBarWindowStateController,
lockIconViewController,
centralSurfaces,
+ backActionInteractor,
+ powerInteractor,
notificationShadeWindowController,
unfoldTransitionProgressProvider,
keyguardUnlockAnimationController,
@@ -182,6 +198,7 @@
pulsingGestureListener,
keyguardBouncerViewModel,
keyguardBouncerComponentFactory,
+ Mockito.mock(KeyguardMessageAreaController.Factory::class.java),
keyguardTransitionInteractor,
primaryBouncerToGoneTransitionViewModel,
featureFlags,
@@ -202,6 +219,14 @@
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 d7c06a7..a4fab1d 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.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 @@
@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 @@
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 @@
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 @@
dumpManager
)
whenever(dockManager.isDocked).thenReturn(false)
+ whenever(screenOffAnimationController.allowWakeUpIfDozing()).thenReturn(true)
}
@Test
@@ -110,8 +120,8 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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/QuickSettingsControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java
new file mode 100644
index 0000000..c72f4e7
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java
@@ -0,0 +1,265 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.shade;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.content.res.Resources;
+import android.os.Handler;
+import android.os.Looper;
+import android.view.ViewGroup;
+import android.view.ViewParent;
+import android.view.accessibility.AccessibilityManager;
+
+import com.android.internal.jank.InteractionJankMonitor;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.UiEventLogger;
+import com.android.keyguard.KeyguardStatusView;
+import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.keyguard.TestScopeProvider;
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.classifier.FalsingCollector;
+import com.android.systemui.dump.DumpManager;
+import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.fragments.FragmentHostManager;
+import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository;
+import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor;
+import com.android.systemui.media.controls.pipeline.MediaDataManager;
+import com.android.systemui.media.controls.ui.MediaHierarchyManager;
+import com.android.systemui.plugins.FalsingManager;
+import com.android.systemui.plugins.qs.QS;
+import com.android.systemui.qs.QSFragment;
+import com.android.systemui.screenrecord.RecordingController;
+import com.android.systemui.shade.data.repository.ShadeRepository;
+import com.android.systemui.shade.domain.interactor.ShadeInteractor;
+import com.android.systemui.shade.transition.ShadeTransitionController;
+import com.android.systemui.statusbar.LockscreenShadeTransitionController;
+import com.android.systemui.statusbar.NotificationRemoteInputManager;
+import com.android.systemui.statusbar.NotificationShadeDepthController;
+import com.android.systemui.statusbar.PulseExpansionHandler;
+import com.android.systemui.statusbar.QsFrameTranslateController;
+import com.android.systemui.statusbar.StatusBarStateControllerImpl;
+import com.android.systemui.statusbar.SysuiStatusBarStateController;
+import com.android.systemui.statusbar.disableflags.data.repository.FakeDisableFlagsRepository;
+import com.android.systemui.statusbar.notification.stack.AmbientState;
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
+import com.android.systemui.statusbar.phone.KeyguardBottomAreaView;
+import com.android.systemui.statusbar.phone.KeyguardBypassController;
+import com.android.systemui.statusbar.phone.KeyguardStatusBarView;
+import com.android.systemui.statusbar.phone.LightBarController;
+import com.android.systemui.statusbar.phone.LockscreenGestureLogger;
+import com.android.systemui.statusbar.phone.ScrimController;
+import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
+import com.android.systemui.statusbar.phone.StatusBarTouchableRegionManager;
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeUserSetupRepository;
+import com.android.systemui.statusbar.policy.CastController;
+import com.android.systemui.statusbar.policy.DeviceProvisionedController;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.user.domain.interactor.UserInteractor;
+import com.android.systemui.util.kotlin.JavaAdapter;
+
+import dagger.Lazy;
+
+import org.junit.After;
+import org.junit.Before;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import kotlinx.coroutines.test.TestScope;
+
+public class QuickSettingsControllerBaseTest extends SysuiTestCase {
+ protected static final float QS_FRAME_START_X = 0f;
+ protected static final int QS_FRAME_WIDTH = 1000;
+ protected static final int QS_FRAME_TOP = 0;
+ protected static final int QS_FRAME_BOTTOM = 1000;
+ protected static final int DEFAULT_HEIGHT = 1000;
+ // In split shade min = max
+ protected static final int DEFAULT_MIN_HEIGHT_SPLIT_SHADE = DEFAULT_HEIGHT;
+ protected static final int DEFAULT_MIN_HEIGHT = 300;
+
+ protected QuickSettingsController mQsController;
+
+ protected TestScope mTestScope = TestScopeProvider.getTestScope();
+
+ @Mock
+ protected Resources mResources;
+ @Mock protected KeyguardBottomAreaView mQsFrame;
+ @Mock protected KeyguardStatusBarView mKeyguardStatusBar;
+ @Mock protected QS mQs;
+ @Mock protected QSFragment mQSFragment;
+ @Mock protected Lazy<NotificationPanelViewController> mPanelViewControllerLazy;
+ @Mock protected NotificationPanelViewController mNotificationPanelViewController;
+ @Mock protected NotificationPanelView mPanelView;
+ @Mock protected ViewGroup mQsHeader;
+ @Mock protected ViewParent mPanelViewParent;
+ @Mock protected QsFrameTranslateController mQsFrameTranslateController;
+ @Mock protected ShadeTransitionController mShadeTransitionController;
+ @Mock protected PulseExpansionHandler mPulseExpansionHandler;
+ @Mock protected NotificationRemoteInputManager mNotificationRemoteInputManager;
+ @Mock protected StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
+ @Mock protected LightBarController mLightBarController;
+ @Mock protected NotificationStackScrollLayoutController
+ mNotificationStackScrollLayoutController;
+ @Mock protected LockscreenShadeTransitionController mLockscreenShadeTransitionController;
+ @Mock protected NotificationShadeDepthController mNotificationShadeDepthController;
+ @Mock protected ShadeHeaderController mShadeHeaderController;
+ @Mock protected StatusBarTouchableRegionManager mStatusBarTouchableRegionManager;
+ @Mock protected KeyguardStateController mKeyguardStateController;
+ @Mock protected KeyguardBypassController mKeyguardBypassController;
+ @Mock protected KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ @Mock protected ScrimController mScrimController;
+ @Mock protected MediaDataManager mMediaDataManager;
+ @Mock protected MediaHierarchyManager mMediaHierarchyManager;
+ @Mock protected AmbientState mAmbientState;
+ @Mock protected RecordingController mRecordingController;
+ @Mock protected FalsingManager mFalsingManager;
+ @Mock protected FalsingCollector mFalsingCollector;
+ @Mock protected AccessibilityManager mAccessibilityManager;
+ @Mock protected LockscreenGestureLogger mLockscreenGestureLogger;
+ @Mock protected MetricsLogger mMetricsLogger;
+ @Mock protected FeatureFlags mFeatureFlags;
+ @Mock protected InteractionJankMonitor mInteractionJankMonitor;
+ @Mock protected ShadeLogger mShadeLogger;
+ @Mock protected DumpManager mDumpManager;
+ @Mock protected UiEventLogger mUiEventLogger;
+ @Mock protected CastController mCastController;
+ @Mock protected DeviceProvisionedController mDeviceProvisionedController;
+ @Mock protected UserInteractor mUserInteractor;
+ protected FakeDisableFlagsRepository mDisableFlagsRepository =
+ new FakeDisableFlagsRepository();
+ protected FakeKeyguardRepository mKeyguardRepository = new FakeKeyguardRepository();
+
+ protected SysuiStatusBarStateController mStatusBarStateController;
+ protected ShadeInteractor mShadeInteractor;
+
+ protected Handler mMainHandler;
+ protected LockscreenShadeTransitionController.Callback mLockscreenShadeTransitionCallback;
+
+ protected final ShadeExpansionStateManager mShadeExpansionStateManager =
+ new ShadeExpansionStateManager();
+
+ protected FragmentHostManager.FragmentListener mFragmentListener;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ when(mPanelViewControllerLazy.get()).thenReturn(mNotificationPanelViewController);
+ mStatusBarStateController = new StatusBarStateControllerImpl(mUiEventLogger, mDumpManager,
+ mInteractionJankMonitor, mShadeExpansionStateManager);
+
+ when(mDeviceProvisionedController.isDeviceProvisioned()).thenReturn(true);
+ mShadeInteractor =
+ new ShadeInteractor(
+ mDisableFlagsRepository,
+ mKeyguardRepository,
+ new FakeUserSetupRepository(),
+ mDeviceProvisionedController,
+ mUserInteractor
+ );
+
+ KeyguardStatusView keyguardStatusView = new KeyguardStatusView(mContext);
+ keyguardStatusView.setId(R.id.keyguard_status_view);
+
+ when(mResources.getDimensionPixelSize(
+ R.dimen.lockscreen_shade_qs_transition_distance)).thenReturn(DEFAULT_HEIGHT);
+ when(mPanelView.getResources()).thenReturn(mResources);
+ when(mPanelView.getContext()).thenReturn(getContext());
+ when(mPanelView.findViewById(R.id.keyguard_header)).thenReturn(mKeyguardStatusBar);
+ when(mNotificationStackScrollLayoutController.getHeight()).thenReturn(1000);
+ when(mPanelView.findViewById(R.id.qs_frame)).thenReturn(mQsFrame);
+ when(mQsFrame.getX()).thenReturn(QS_FRAME_START_X);
+ when(mQsFrame.getWidth()).thenReturn(QS_FRAME_WIDTH);
+ when(mQsHeader.getTop()).thenReturn(QS_FRAME_TOP);
+ when(mQsHeader.getBottom()).thenReturn(QS_FRAME_BOTTOM);
+ when(mPanelView.getY()).thenReturn((float) QS_FRAME_TOP);
+ when(mPanelView.getHeight()).thenReturn(QS_FRAME_BOTTOM);
+ when(mPanelView.findViewById(R.id.keyguard_status_view))
+ .thenReturn(mock(KeyguardStatusView.class));
+ when(mQs.getView()).thenReturn(mPanelView);
+ when(mQSFragment.getView()).thenReturn(mPanelView);
+
+ when(mNotificationRemoteInputManager.isRemoteInputActive())
+ .thenReturn(false);
+ when(mInteractionJankMonitor.begin(any(), anyInt()))
+ .thenReturn(true);
+ when(mInteractionJankMonitor.end(anyInt()))
+ .thenReturn(true);
+
+ when(mPanelView.getParent()).thenReturn(mPanelViewParent);
+ when(mQs.getHeader()).thenReturn(mQsHeader);
+
+ doAnswer(invocation -> {
+ mLockscreenShadeTransitionCallback = invocation.getArgument(0);
+ return null;
+ }).when(mLockscreenShadeTransitionController).addCallback(any());
+
+
+ mMainHandler = new Handler(Looper.getMainLooper());
+
+ mQsController = new QuickSettingsController(
+ mPanelViewControllerLazy,
+ mPanelView,
+ mQsFrameTranslateController,
+ mShadeTransitionController,
+ mPulseExpansionHandler,
+ mNotificationRemoteInputManager,
+ mShadeExpansionStateManager,
+ mStatusBarKeyguardViewManager,
+ mLightBarController,
+ mNotificationStackScrollLayoutController,
+ mLockscreenShadeTransitionController,
+ mNotificationShadeDepthController,
+ mShadeHeaderController,
+ mStatusBarTouchableRegionManager,
+ mKeyguardStateController,
+ mKeyguardBypassController,
+ mKeyguardUpdateMonitor,
+ mScrimController,
+ mMediaDataManager,
+ mMediaHierarchyManager,
+ mAmbientState,
+ mRecordingController,
+ mFalsingManager,
+ mFalsingCollector,
+ mAccessibilityManager,
+ mLockscreenGestureLogger,
+ mMetricsLogger,
+ mFeatureFlags,
+ mInteractionJankMonitor,
+ mShadeLogger,
+ mDumpManager,
+ mock(KeyguardFaceAuthInteractor.class),
+ mock(ShadeRepository.class),
+ mShadeInteractor,
+ new JavaAdapter(mTestScope.getBackgroundScope()),
+ mCastController
+ );
+ mQsController.init();
+
+ mFragmentListener = mQsController.getQsFragmentListener();
+ }
+
+ @After
+ public void tearDown() {
+ mMainHandler.removeCallbacksAndMessages(null);
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerTest.java
index ff047aa..6d288e1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerTest.java
@@ -28,238 +28,32 @@
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyFloat;
-import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.atLeastOnce;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import android.content.res.Resources;
-import android.os.Handler;
-import android.os.Looper;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.MotionEvent;
-import android.view.ViewGroup;
-import android.view.ViewParent;
-import android.view.accessibility.AccessibilityManager;
import androidx.test.filters.SmallTest;
-import com.android.internal.jank.InteractionJankMonitor;
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.UiEventLogger;
-import com.android.keyguard.KeyguardStatusView;
-import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.R;
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.classifier.FalsingCollector;
-import com.android.systemui.dump.DumpManager;
-import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.fragments.FragmentHostManager;
-import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor;
-import com.android.systemui.media.controls.pipeline.MediaDataManager;
-import com.android.systemui.media.controls.ui.MediaHierarchyManager;
-import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.qs.QS;
-import com.android.systemui.qs.QSFragment;
-import com.android.systemui.screenrecord.RecordingController;
-import com.android.systemui.shade.data.repository.ShadeRepository;
-import com.android.systemui.shade.transition.ShadeTransitionController;
-import com.android.systemui.statusbar.LockscreenShadeTransitionController;
-import com.android.systemui.statusbar.NotificationRemoteInputManager;
-import com.android.systemui.statusbar.NotificationShadeDepthController;
-import com.android.systemui.statusbar.PulseExpansionHandler;
-import com.android.systemui.statusbar.QsFrameTranslateController;
-import com.android.systemui.statusbar.StatusBarStateControllerImpl;
-import com.android.systemui.statusbar.SysuiStatusBarStateController;
-import com.android.systemui.statusbar.notification.stack.AmbientState;
-import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
-import com.android.systemui.statusbar.phone.KeyguardBottomAreaView;
-import com.android.systemui.statusbar.phone.KeyguardBypassController;
-import com.android.systemui.statusbar.phone.KeyguardStatusBarView;
-import com.android.systemui.statusbar.phone.LightBarController;
-import com.android.systemui.statusbar.phone.LockscreenGestureLogger;
-import com.android.systemui.statusbar.phone.ScrimController;
-import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
-import com.android.systemui.statusbar.phone.StatusBarTouchableRegionManager;
-import com.android.systemui.statusbar.policy.CastController;
-import com.android.systemui.statusbar.policy.KeyguardStateController;
-import dagger.Lazy;
-
-import org.junit.After;
-import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
import java.util.List;
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
-public class QuickSettingsControllerTest extends SysuiTestCase {
-
- private static final float QS_FRAME_START_X = 0f;
- private static final int QS_FRAME_WIDTH = 1000;
- private static final int QS_FRAME_TOP = 0;
- private static final int QS_FRAME_BOTTOM = 1000;
- private static final int DEFAULT_HEIGHT = 1000;
- // In split shade min = max
- private static final int DEFAULT_MIN_HEIGHT_SPLIT_SHADE = DEFAULT_HEIGHT;
- private static final int DEFAULT_MIN_HEIGHT = 300;
-
- private QuickSettingsController mQsController;
-
- @Mock private Resources mResources;
- @Mock private KeyguardBottomAreaView mQsFrame;
- @Mock private KeyguardStatusBarView mKeyguardStatusBar;
- @Mock private QS mQs;
- @Mock private QSFragment mQSFragment;
- @Mock private Lazy<NotificationPanelViewController> mPanelViewControllerLazy;
- @Mock private NotificationPanelViewController mNotificationPanelViewController;
- @Mock private NotificationPanelView mPanelView;
- @Mock private ViewGroup mQsHeader;
- @Mock private ViewParent mPanelViewParent;
- @Mock private QsFrameTranslateController mQsFrameTranslateController;
- @Mock private ShadeTransitionController mShadeTransitionController;
- @Mock private PulseExpansionHandler mPulseExpansionHandler;
- @Mock private NotificationRemoteInputManager mNotificationRemoteInputManager;
- @Mock private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
- @Mock private LightBarController mLightBarController;
- @Mock private NotificationStackScrollLayoutController mNotificationStackScrollLayoutController;
- @Mock private LockscreenShadeTransitionController mLockscreenShadeTransitionController;
- @Mock private NotificationShadeDepthController mNotificationShadeDepthController;
- @Mock private ShadeHeaderController mShadeHeaderController;
- @Mock private StatusBarTouchableRegionManager mStatusBarTouchableRegionManager;
- @Mock private KeyguardStateController mKeyguardStateController;
- @Mock private KeyguardBypassController mKeyguardBypassController;
- @Mock private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
- @Mock private ScrimController mScrimController;
- @Mock private MediaDataManager mMediaDataManager;
- @Mock private MediaHierarchyManager mMediaHierarchyManager;
- @Mock private AmbientState mAmbientState;
- @Mock private RecordingController mRecordingController;
- @Mock private FalsingManager mFalsingManager;
- @Mock private FalsingCollector mFalsingCollector;
- @Mock private AccessibilityManager mAccessibilityManager;
- @Mock private LockscreenGestureLogger mLockscreenGestureLogger;
- @Mock private MetricsLogger mMetricsLogger;
- @Mock private FeatureFlags mFeatureFlags;
- @Mock private InteractionJankMonitor mInteractionJankMonitor;
- @Mock private ShadeLogger mShadeLogger;
- @Mock private DumpManager mDumpManager;
- @Mock private UiEventLogger mUiEventLogger;
- @Mock private CastController mCastController;
-
- private SysuiStatusBarStateController mStatusBarStateController;
-
- private Handler mMainHandler;
- private LockscreenShadeTransitionController.Callback mLockscreenShadeTransitionCallback;
-
- private final ShadeExpansionStateManager mShadeExpansionStateManager =
- new ShadeExpansionStateManager();
-
- private FragmentHostManager.FragmentListener mFragmentListener;
-
- @Before
- public void setup() {
- MockitoAnnotations.initMocks(this);
- when(mPanelViewControllerLazy.get()).thenReturn(mNotificationPanelViewController);
- mStatusBarStateController = new StatusBarStateControllerImpl(mUiEventLogger, mDumpManager,
- mInteractionJankMonitor, mShadeExpansionStateManager);
-
- KeyguardStatusView keyguardStatusView = new KeyguardStatusView(mContext);
- keyguardStatusView.setId(R.id.keyguard_status_view);
-
- when(mResources.getDimensionPixelSize(
- R.dimen.lockscreen_shade_qs_transition_distance)).thenReturn(DEFAULT_HEIGHT);
- when(mPanelView.getResources()).thenReturn(mResources);
- when(mPanelView.getContext()).thenReturn(getContext());
- when(mPanelView.findViewById(R.id.keyguard_header)).thenReturn(mKeyguardStatusBar);
- when(mNotificationStackScrollLayoutController.getHeight()).thenReturn(1000);
- when(mPanelView.findViewById(R.id.qs_frame)).thenReturn(mQsFrame);
- when(mQsFrame.getX()).thenReturn(QS_FRAME_START_X);
- when(mQsFrame.getWidth()).thenReturn(QS_FRAME_WIDTH);
- when(mQsHeader.getTop()).thenReturn(QS_FRAME_TOP);
- when(mQsHeader.getBottom()).thenReturn(QS_FRAME_BOTTOM);
- when(mPanelView.getY()).thenReturn((float) QS_FRAME_TOP);
- when(mPanelView.getHeight()).thenReturn(QS_FRAME_BOTTOM);
- when(mPanelView.findViewById(R.id.keyguard_status_view))
- .thenReturn(mock(KeyguardStatusView.class));
- when(mQs.getView()).thenReturn(mPanelView);
- when(mQSFragment.getView()).thenReturn(mPanelView);
-
- when(mNotificationRemoteInputManager.isRemoteInputActive())
- .thenReturn(false);
- when(mInteractionJankMonitor.begin(any(), anyInt()))
- .thenReturn(true);
- when(mInteractionJankMonitor.end(anyInt()))
- .thenReturn(true);
-
- when(mPanelView.getParent()).thenReturn(mPanelViewParent);
- when(mQs.getHeader()).thenReturn(mQsHeader);
-
- doAnswer(invocation -> {
- mLockscreenShadeTransitionCallback = invocation.getArgument(0);
- return null;
- }).when(mLockscreenShadeTransitionController).addCallback(any());
-
-
- mMainHandler = new Handler(Looper.getMainLooper());
-
- mQsController = new QuickSettingsController(
- mPanelViewControllerLazy,
- mPanelView,
- mQsFrameTranslateController,
- mShadeTransitionController,
- mPulseExpansionHandler,
- mNotificationRemoteInputManager,
- mShadeExpansionStateManager,
- mStatusBarKeyguardViewManager,
- mLightBarController,
- mNotificationStackScrollLayoutController,
- mLockscreenShadeTransitionController,
- mNotificationShadeDepthController,
- mShadeHeaderController,
- mStatusBarTouchableRegionManager,
- mKeyguardStateController,
- mKeyguardBypassController,
- mKeyguardUpdateMonitor,
- mScrimController,
- mMediaDataManager,
- mMediaHierarchyManager,
- mAmbientState,
- mRecordingController,
- mFalsingManager,
- mFalsingCollector,
- mAccessibilityManager,
- mLockscreenGestureLogger,
- mMetricsLogger,
- mFeatureFlags,
- mInteractionJankMonitor,
- mShadeLogger,
- mDumpManager,
- mock(KeyguardFaceAuthInteractor.class),
- mock(ShadeRepository.class),
- mCastController
- );
-
- mFragmentListener = mQsController.getQsFragmentListener();
- }
-
- @After
- public void tearDown() {
- mMainHandler.removeCallbacksAndMessages(null);
- }
+public class QuickSettingsControllerTest extends QuickSettingsControllerBaseTest {
@Test
public void testCloseQsSideEffects() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerWithCoroutinesTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerWithCoroutinesTest.kt
new file mode 100644
index 0000000..cc4a063
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerWithCoroutinesTest.kt
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.shade
+
+import android.app.StatusBarManager
+import androidx.test.filters.SmallTest
+import com.android.systemui.statusbar.disableflags.data.model.DisableFlagsModel
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+
+@SmallTest
+@OptIn(ExperimentalCoroutinesApi::class)
+class QuickSettingsControllerWithCoroutinesTest : QuickSettingsControllerBaseTest() {
+
+ @Test
+ fun isExpansionEnabled_dozing_false() =
+ mTestScope.runTest {
+ mKeyguardRepository.setIsDozing(true)
+ runCurrent()
+
+ assertThat(mQsController.isExpansionEnabled).isFalse()
+ }
+
+ @Test
+ fun isExpansionEnabled_notDozing_true() =
+ mTestScope.runTest {
+ mKeyguardRepository.setIsDozing(false)
+ runCurrent()
+
+ assertThat(mQsController.isExpansionEnabled).isTrue()
+ }
+
+ @Test
+ fun isExpansionEnabled_qsDisabled_false() =
+ mTestScope.runTest {
+ mDisableFlagsRepository.disableFlags.value =
+ DisableFlagsModel(
+ StatusBarManager.DISABLE_NONE,
+ StatusBarManager.DISABLE2_QUICK_SETTINGS
+ )
+ runCurrent()
+
+ assertThat(mQsController.isExpansionEnabled).isFalse()
+ }
+
+ @Test
+ fun isExpansionEnabled_shadeDisabled_false() =
+ mTestScope.runTest {
+ mDisableFlagsRepository.disableFlags.value =
+ DisableFlagsModel(
+ StatusBarManager.DISABLE_NONE,
+ StatusBarManager.DISABLE2_NOTIFICATION_SHADE
+ )
+ runCurrent()
+
+ assertThat(mQsController.isExpansionEnabled).isFalse()
+ }
+
+ @Test
+ fun isExpansionEnabled_qsAndShadeEnabled_true() =
+ mTestScope.runTest {
+ mDisableFlagsRepository.disableFlags.value =
+ DisableFlagsModel(StatusBarManager.DISABLE_NONE, StatusBarManager.DISABLE2_NONE)
+ runCurrent()
+
+ assertThat(mQsController.isExpansionEnabled).isTrue()
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeControllerImplTest.kt
new file mode 100644
index 0000000..00a0567
--- /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/data/repository/ShadeInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt
new file mode 100644
index 0000000..7392a94
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt
@@ -0,0 +1,321 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.shade.data.repository
+
+import android.app.ActivityManager
+import android.app.StatusBarManager.DISABLE2_NONE
+import android.app.StatusBarManager.DISABLE2_NOTIFICATION_SHADE
+import android.app.StatusBarManager.DISABLE2_QUICK_SETTINGS
+import android.os.UserManager
+import androidx.test.filters.SmallTest
+import com.android.internal.logging.UiEventLogger
+import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.flags.FakeFeatureFlags
+import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
+import com.android.systemui.statusbar.disableflags.data.model.DisableFlagsModel
+import com.android.systemui.statusbar.disableflags.data.repository.FakeDisableFlagsRepository
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeUserSetupRepository
+import com.android.systemui.statusbar.policy.DeviceProvisionedController
+import com.android.systemui.telephony.data.repository.FakeTelephonyRepository
+import com.android.systemui.telephony.domain.interactor.TelephonyInteractor
+import com.android.systemui.user.data.model.UserSwitcherSettingsModel
+import com.android.systemui.user.data.repository.FakeUserRepository
+import com.android.systemui.user.domain.interactor.GuestUserInteractor
+import com.android.systemui.user.domain.interactor.HeadlessSystemUserMode
+import com.android.systemui.user.domain.interactor.RefreshUsersScheduler
+import com.android.systemui.user.domain.interactor.UserInteractor
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@OptIn(ExperimentalCoroutinesApi::class)
+class ShadeInteractorTest : SysuiTestCase() {
+ private lateinit var underTest: ShadeInteractor
+
+ private val testDispatcher = StandardTestDispatcher()
+ private val testScope = TestScope(testDispatcher)
+ private val featureFlags = FakeFeatureFlags()
+ private val userSetupRepository = FakeUserSetupRepository()
+ private val userRepository = FakeUserRepository()
+ private val disableFlagsRepository = FakeDisableFlagsRepository()
+ private val keyguardRepository = FakeKeyguardRepository()
+
+ @Mock private lateinit var manager: UserManager
+ @Mock private lateinit var headlessSystemUserMode: HeadlessSystemUserMode
+ @Mock private lateinit var deviceProvisionedController: DeviceProvisionedController
+ @Mock private lateinit var activityStarter: ActivityStarter
+ @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
+ @Mock private lateinit var activityManager: ActivityManager
+ @Mock private lateinit var uiEventLogger: UiEventLogger
+ @Mock private lateinit var guestInteractor: GuestUserInteractor
+
+ private lateinit var userInteractor: UserInteractor
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+
+ featureFlags.set(Flags.FACE_AUTH_REFACTOR, false)
+
+ val refreshUsersScheduler =
+ RefreshUsersScheduler(
+ applicationScope = testScope.backgroundScope,
+ mainDispatcher = testDispatcher,
+ repository = userRepository,
+ )
+ userInteractor =
+ UserInteractor(
+ applicationContext = context,
+ repository = userRepository,
+ activityStarter = activityStarter,
+ keyguardInteractor =
+ KeyguardInteractorFactory.create(featureFlags = featureFlags)
+ .keyguardInteractor,
+ featureFlags = featureFlags,
+ manager = manager,
+ headlessSystemUserMode = headlessSystemUserMode,
+ applicationScope = testScope.backgroundScope,
+ telephonyInteractor =
+ TelephonyInteractor(
+ repository = FakeTelephonyRepository(),
+ ),
+ broadcastDispatcher = fakeBroadcastDispatcher,
+ keyguardUpdateMonitor = keyguardUpdateMonitor,
+ backgroundDispatcher = testDispatcher,
+ activityManager = activityManager,
+ refreshUsersScheduler = refreshUsersScheduler,
+ guestUserInteractor = guestInteractor,
+ uiEventLogger = uiEventLogger,
+ )
+ underTest =
+ ShadeInteractor(
+ disableFlagsRepository,
+ keyguardRepository,
+ userSetupRepository,
+ deviceProvisionedController,
+ userInteractor,
+ )
+ }
+
+ @Test
+ fun isExpandToQsEnabled_deviceNotProvisioned_false() =
+ testScope.runTest {
+ whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(false)
+
+ val actual by collectLastValue(underTest.isExpandToQsEnabled)
+
+ assertThat(actual).isFalse()
+ }
+
+ @Test
+ fun isExpandToQsEnabled_userNotSetupAndSimpleUserSwitcher_false() =
+ testScope.runTest {
+ whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(true)
+
+ userSetupRepository.setUserSetup(false)
+ userRepository.setSettings(UserSwitcherSettingsModel(isSimpleUserSwitcher = true))
+
+ val actual by collectLastValue(underTest.isExpandToQsEnabled)
+
+ assertThat(actual).isFalse()
+ }
+
+ @Test
+ fun isExpandToQsEnabled_shadeNotEnabled_false() =
+ testScope.runTest {
+ whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(true)
+ userSetupRepository.setUserSetup(true)
+
+ disableFlagsRepository.disableFlags.value =
+ DisableFlagsModel(
+ disable2 = DISABLE2_NOTIFICATION_SHADE,
+ )
+
+ val actual by collectLastValue(underTest.isExpandToQsEnabled)
+
+ assertThat(actual).isFalse()
+ }
+
+ @Test
+ fun isExpandToQsEnabled_quickSettingsNotEnabled_false() =
+ testScope.runTest {
+ whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(true)
+ userSetupRepository.setUserSetup(true)
+
+ disableFlagsRepository.disableFlags.value =
+ DisableFlagsModel(
+ disable2 = DISABLE2_QUICK_SETTINGS,
+ )
+ val actual by collectLastValue(underTest.isExpandToQsEnabled)
+
+ assertThat(actual).isFalse()
+ }
+
+ @Test
+ fun isExpandToQsEnabled_dozing_false() =
+ testScope.runTest {
+ whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(true)
+ userSetupRepository.setUserSetup(true)
+ disableFlagsRepository.disableFlags.value =
+ DisableFlagsModel(
+ disable2 = DISABLE2_NONE,
+ )
+
+ keyguardRepository.setIsDozing(true)
+
+ val actual by collectLastValue(underTest.isExpandToQsEnabled)
+
+ assertThat(actual).isFalse()
+ }
+
+ @Test
+ fun isExpandToQsEnabled_userSetup_true() =
+ testScope.runTest {
+ whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(true)
+ keyguardRepository.setIsDozing(false)
+ disableFlagsRepository.disableFlags.value =
+ DisableFlagsModel(
+ disable2 = DISABLE2_NONE,
+ )
+
+ userSetupRepository.setUserSetup(true)
+
+ val actual by collectLastValue(underTest.isExpandToQsEnabled)
+
+ assertThat(actual).isTrue()
+ }
+
+ @Test
+ fun isExpandToQsEnabled_notSimpleUserSwitcher_true() =
+ testScope.runTest {
+ whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(true)
+ keyguardRepository.setIsDozing(false)
+ disableFlagsRepository.disableFlags.value =
+ DisableFlagsModel(
+ disable2 = DISABLE2_NONE,
+ )
+
+ userRepository.setSettings(UserSwitcherSettingsModel(isSimpleUserSwitcher = false))
+
+ val actual by collectLastValue(underTest.isExpandToQsEnabled)
+
+ assertThat(actual).isTrue()
+ }
+
+ @Test
+ fun isExpandToQsEnabled_respondsToDozingUpdates() =
+ testScope.runTest {
+ whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(true)
+ keyguardRepository.setIsDozing(false)
+ disableFlagsRepository.disableFlags.value =
+ DisableFlagsModel(
+ disable2 = DISABLE2_NONE,
+ )
+ userSetupRepository.setUserSetup(true)
+
+ val actual by collectLastValue(underTest.isExpandToQsEnabled)
+
+ assertThat(actual).isTrue()
+
+ // WHEN dozing starts
+ keyguardRepository.setIsDozing(true)
+
+ // THEN expand is disabled
+ assertThat(actual).isFalse()
+
+ // WHEN dozing stops
+ keyguardRepository.setIsDozing(false)
+
+ // THEN expand is enabled
+ assertThat(actual).isTrue()
+ }
+
+ @Test
+ fun isExpandToQsEnabled_respondsToDisableUpdates() =
+ testScope.runTest {
+ whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(true)
+ keyguardRepository.setIsDozing(false)
+ disableFlagsRepository.disableFlags.value =
+ DisableFlagsModel(
+ disable2 = DISABLE2_NONE,
+ )
+ userSetupRepository.setUserSetup(true)
+
+ val actual by collectLastValue(underTest.isExpandToQsEnabled)
+
+ assertThat(actual).isTrue()
+
+ // WHEN QS is disabled
+ disableFlagsRepository.disableFlags.value =
+ DisableFlagsModel(
+ disable2 = DISABLE2_QUICK_SETTINGS,
+ )
+ // THEN expand is disabled
+ assertThat(actual).isFalse()
+
+ // WHEN QS is enabled
+ disableFlagsRepository.disableFlags.value =
+ DisableFlagsModel(
+ disable2 = DISABLE2_NONE,
+ )
+ // THEN expand is enabled
+ assertThat(actual).isTrue()
+ }
+
+ @Test
+ fun isExpandToQsEnabled_respondsToUserUpdates() =
+ testScope.runTest {
+ whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(true)
+ keyguardRepository.setIsDozing(false)
+ disableFlagsRepository.disableFlags.value =
+ DisableFlagsModel(
+ disable2 = DISABLE2_NONE,
+ )
+ userSetupRepository.setUserSetup(true)
+
+ val actual by collectLastValue(underTest.isExpandToQsEnabled)
+
+ assertThat(actual).isTrue()
+
+ // WHEN the user is no longer setup
+ userSetupRepository.setUserSetup(false)
+ userRepository.setSettings(UserSwitcherSettingsModel(isSimpleUserSwitcher = true))
+
+ // THEN expand is disabled
+ assertThat(actual).isFalse()
+
+ // WHEN the user is setup again
+ userSetupRepository.setUserSetup(true)
+
+ // THEN expand is enabled
+ assertThat(actual).isTrue()
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/transition/ScrimShadeTransitionControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/transition/ScrimShadeTransitionControllerTest.kt
index cbf5485..a5bd2ae 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 @@
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 @@
@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 @@
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)
-
+ fun onPanelExpansionChanged_setsFractionEqualToEventFraction() {
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)
-
- 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 69d03d9..f8e1a9d 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 @@
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 @@
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 @@
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 @@
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/clocks/ClockRegistryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
index 04c93cb..9495fdd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
@@ -21,6 +21,8 @@
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.flags.FakeFeatureFlags
+import com.android.systemui.flags.Flags.TRANSIT_CLOCK
import com.android.systemui.plugins.ClockController
import com.android.systemui.plugins.ClockId
import com.android.systemui.plugins.ClockMetadata
@@ -68,6 +70,7 @@
private lateinit var fakeDefaultProvider: FakeClockPlugin
private lateinit var pluginListener: PluginListener<ClockProviderPlugin>
private lateinit var registry: ClockRegistry
+ private val featureFlags = FakeFeatureFlags()
companion object {
private fun failFactory(clockId: ClockId): ClockController {
@@ -448,4 +451,44 @@
val actual = ClockSettings.serialize(ClockSettings("ID", null))
assertEquals(expected, actual)
}
+
+ @Test
+ fun testTransitClockEnabled_hasTransitClock() {
+ testTransitClockFlag(true)
+ }
+
+ @Test
+ fun testTransitClockDisabled_noTransitClock() {
+ testTransitClockFlag(false)
+ }
+
+ private fun testTransitClockFlag(flag: Boolean) {
+ featureFlags.set(TRANSIT_CLOCK, flag)
+ registry.isTransitClockEnabled = featureFlags.isEnabled(TRANSIT_CLOCK)
+ val mockPluginLifecycle = mock<PluginLifecycleManager<ClockProviderPlugin>>()
+ val plugin = FakeClockPlugin()
+ .addClock("clock_1", "clock 1")
+ .addClock("DIGITAL_CLOCK_METRO", "metro clock")
+ pluginListener.onPluginLoaded(plugin, mockContext, mockPluginLifecycle)
+
+ val list = registry.getClocks()
+ if (flag) {
+ assertEquals(
+ setOf(
+ ClockMetadata(DEFAULT_CLOCK_ID, DEFAULT_CLOCK_NAME),
+ ClockMetadata("clock_1", "clock 1"),
+ ClockMetadata("DIGITAL_CLOCK_METRO", "metro clock")
+ ),
+ list.toSet()
+ )
+ } else {
+ assertEquals(
+ setOf(
+ ClockMetadata(DEFAULT_CLOCK_ID, DEFAULT_CLOCK_NAME),
+ ClockMetadata("clock_1", "clock 1")
+ ),
+ list.toSet()
+ )
+ }
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/condition/ConditionMonitorTest.java b/packages/SystemUI/tests/src/com/android/systemui/shared/condition/ConditionMonitorTest.java
index b724175..0b1753f 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 @@
}
/**
- * 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 1643e17..385d556 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 @@
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 @@
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 109f185..22c9e45 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 @@
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 ea822aa..a3ecde0 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 @@
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 cd0c1352..b1f5dde 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
@@ -72,6 +72,7 @@
import android.content.pm.UserInfo;
import android.graphics.Color;
import android.hardware.biometrics.BiometricFaceConstants;
+import android.hardware.biometrics.BiometricFingerprintConstants;
import android.hardware.biometrics.BiometricSourceType;
import android.hardware.fingerprint.FingerprintManager;
import android.os.BatteryManager;
@@ -98,13 +99,15 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.biometrics.FaceHelpMessageDeferral;
+import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor;
+import com.android.systemui.bouncer.domain.interactor.BouncerMessageInteractor;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dock.DockManager;
import com.android.systemui.flags.FakeFeatureFlags;
import com.android.systemui.keyguard.KeyguardIndication;
import com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController;
import com.android.systemui.keyguard.ScreenLifecycle;
-import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor;
+import com.android.systemui.keyguard.util.IndicationHelper;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.settings.UserTracker;
@@ -212,6 +215,7 @@
private StatusBarStateController.StateListener mStatusBarStateListener;
private ScreenLifecycle.Observer mScreenObserver;
private BroadcastReceiver mBroadcastReceiver;
+ private IndicationHelper mIndicationHelper;
private FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock());
private TestableLooper mTestableLooper;
private final int mCurrentUserId = 1;
@@ -261,13 +265,14 @@
when(mDevicePolicyManager.getDeviceOwnerType(DEVICE_OWNER_COMPONENT))
.thenReturn(DEVICE_OWNER_TYPE_DEFAULT);
-
when(mDevicePolicyResourcesManager.getString(anyString(), any()))
.thenReturn(mDisclosureGeneric);
when(mDevicePolicyResourcesManager.getString(anyString(), any(), anyString()))
.thenReturn(mDisclosureWithOrganization);
when(mUserTracker.getUserId()).thenReturn(mCurrentUserId);
+ mIndicationHelper = new IndicationHelper(mKeyguardUpdateMonitor);
+
mWakeLock = new WakeLockFake();
mWakeLockBuilder = new WakeLockFake.Builder(mContext);
mWakeLockBuilder.setWakeLock(mWakeLock);
@@ -302,7 +307,9 @@
mAlternateBouncerInteractor,
mAlarmManager,
mUserTracker,
- flags
+ mock(BouncerMessageInteractor.class),
+ flags,
+ mIndicationHelper
);
mController.init();
mController.setIndicationArea(mIndicationArea);
@@ -803,36 +810,22 @@
}
@Test
- public void transientIndication_visibleWhenDozing_ignoresFingerprintCancellation() {
+ public void transientIndication_visibleWhenDozing_ignoresFingerprintErrorMsg() {
createController();
-
mController.setVisible(true);
reset(mRotateTextViewController);
+
+ // WHEN a fingerprint error user cancelled message is received
mController.getKeyguardCallback().onBiometricError(
- FingerprintManager.FINGERPRINT_ERROR_USER_CANCELED, "foo",
- BiometricSourceType.FINGERPRINT);
- mController.getKeyguardCallback().onBiometricError(
- FingerprintManager.FINGERPRINT_ERROR_CANCELED, "bar",
+ BiometricFingerprintConstants.FINGERPRINT_ERROR_USER_CANCELED, "foo",
BiometricSourceType.FINGERPRINT);
+ // THEN no message is shown
verifyNoMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE);
verifyNoMessage(INDICATION_TYPE_TRANSIENT);
}
@Test
- public void transientIndication_visibleWhenDozing_ignoresPowerPressed() {
- createController();
-
- mController.setVisible(true);
- reset(mRotateTextViewController);
- mController.getKeyguardCallback().onBiometricError(
- FingerprintManager.BIOMETRIC_ERROR_POWER_PRESSED, "foo",
- BiometricSourceType.FINGERPRINT);
-
- verifyNoMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE);
- }
-
- @Test
public void transientIndication_swipeUpToRetry() {
createController();
String message = mContext.getString(R.string.keyguard_retry);
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 2351f760..21e0f68 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.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.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 @@
@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 @@
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/NotificationRemoteInputManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
index ced0734..305f48b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
@@ -33,25 +33,21 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.power.domain.interactor.PowerInteractor;
import com.android.systemui.statusbar.notification.NotifPipelineFlags;
import com.android.systemui.statusbar.notification.RemoteInputControllerLogger;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
-import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.policy.RemoteInputUriController;
-import dagger.Lazy;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import java.util.Optional;
-
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@@ -69,6 +65,7 @@
@Mock private RemoteInputUriController mRemoteInputUriController;
@Mock private NotificationClickNotifier mClickNotifier;
@Mock private NotificationLockscreenUserManager mLockscreenUserManager;
+ @Mock private PowerInteractor mPowerInteractor;
private TestableNotificationRemoteInputManager mRemoteInputManager;
private NotificationEntry mEntry;
@@ -82,7 +79,7 @@
mLockscreenUserManager,
mSmartReplyController,
mVisibilityProvider,
- () -> Optional.of(mock(CentralSurfaces.class)),
+ mPowerInteractor,
mStateController,
mRemoteInputUriController,
mock(RemoteInputControllerLogger.class),
@@ -140,7 +137,7 @@
NotificationLockscreenUserManager lockscreenUserManager,
SmartReplyController smartReplyController,
NotificationVisibilityProvider visibilityProvider,
- Lazy<Optional<CentralSurfaces>> centralSurfacesOptionalLazy,
+ PowerInteractor powerInteractor,
StatusBarStateController statusBarStateController,
RemoteInputUriController remoteInputUriController,
RemoteInputControllerLogger remoteInputControllerLogger,
@@ -153,7 +150,7 @@
lockscreenUserManager,
smartReplyController,
visibilityProvider,
- centralSurfacesOptionalLazy,
+ powerInteractor,
statusBarStateController,
remoteInputUriController,
remoteInputControllerLogger,
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 4b5d0a3..f1c7956 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 @@
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 @@
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 @@
@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 @@
}
@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 0000000..580463a
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/disableflags/data/repository/DisableFlagsRepositoryTest.kt
@@ -0,0 +1,274 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.systemui.statusbar.disableflags.data.repository
+
+import android.app.StatusBarManager.DISABLE2_NONE
+import android.app.StatusBarManager.DISABLE2_NOTIFICATION_SHADE
+import android.app.StatusBarManager.DISABLE2_QUICK_SETTINGS
+import android.app.StatusBarManager.DISABLE_CLOCK
+import android.app.StatusBarManager.DISABLE_NONE
+import android.app.StatusBarManager.DISABLE_NOTIFICATION_ALERTS
+import android.content.res.Configuration
+import androidx.test.filters.SmallTest
+import com.android.systemui.R
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.log.LogBufferFactory
+import com.android.systemui.statusbar.CommandQueue
+import com.android.systemui.statusbar.disableflags.DisableFlagsLogger
+import com.android.systemui.statusbar.disableflags.data.model.DisableFlagsModel
+import com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.argumentCaptor
+import com.android.systemui.util.mockito.mock
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.mockito.Mockito.verify
+
+@SmallTest
+@OptIn(ExperimentalCoroutinesApi::class)
+class DisableFlagsRepositoryTest : SysuiTestCase() {
+
+ private lateinit var underTest: DisableFlagsRepository
+
+ private val testScope = TestScope(UnconfinedTestDispatcher())
+ private val commandQueue: CommandQueue = mock()
+ private val configurationController: ConfigurationController = mock()
+ private val remoteInputQuickSettingsDisabler =
+ RemoteInputQuickSettingsDisabler(
+ context,
+ commandQueue,
+ configurationController,
+ )
+ private val logBuffer = LogBufferFactory(DumpManager(), mock()).create("buffer", 10)
+ private val disableFlagsLogger = DisableFlagsLogger()
+
+ @Before
+ fun setUp() {
+ underTest =
+ DisableFlagsRepositoryImpl(
+ commandQueue,
+ DISPLAY_ID,
+ testScope.backgroundScope,
+ remoteInputQuickSettingsDisabler,
+ logBuffer,
+ disableFlagsLogger,
+ )
+ }
+
+ @Test
+ fun disableFlags_initialValue_none() {
+ assertThat(underTest.disableFlags.value)
+ .isEqualTo(DisableFlagsModel(DISABLE_NONE, DISABLE2_NONE))
+ }
+
+ @Test
+ fun disableFlags_noSubscribers_callbackStillRegistered() =
+ testScope.runTest { verify(commandQueue).addCallback(any()) }
+
+ @Test
+ fun disableFlags_notifAlertsNotDisabled_notifAlertsEnabledTrue() =
+ testScope.runTest {
+ getCommandQueueCallback()
+ .disable(DISPLAY_ID, DISABLE_NONE, DISABLE2_NONE, /* animate= */ false)
+
+ assertThat(underTest.disableFlags.value.areNotificationAlertsEnabled()).isTrue()
+ }
+
+ @Test
+ fun disableFlags_notifAlertsDisabled_notifAlertsEnabledFalse() =
+ testScope.runTest {
+ getCommandQueueCallback()
+ .disable(
+ DISPLAY_ID,
+ DISABLE_NOTIFICATION_ALERTS,
+ DISABLE2_NONE,
+ /* animate= */ false,
+ )
+
+ assertThat(underTest.disableFlags.value.areNotificationAlertsEnabled()).isFalse()
+ }
+
+ @Test
+ fun disableFlags_notifAlertsDisabled_differentDisplay_notifAlertsEnabledTrue() =
+ testScope.runTest {
+ val wrongDisplayId = DISPLAY_ID + 10
+
+ getCommandQueueCallback()
+ .disable(
+ wrongDisplayId,
+ DISABLE_NOTIFICATION_ALERTS,
+ DISABLE2_NONE,
+ /* animate= */ false,
+ )
+
+ // THEN our repo reports them as still enabled
+ assertThat(underTest.disableFlags.value.areNotificationAlertsEnabled()).isTrue()
+ }
+
+ @Test
+ fun disableFlags_shadeNotDisabled_shadeEnabledTrue() =
+ testScope.runTest {
+ getCommandQueueCallback()
+ .disable(DISPLAY_ID, DISABLE_NONE, DISABLE2_NONE, /* animate= */ false)
+
+ assertThat(underTest.disableFlags.value.isShadeEnabled()).isTrue()
+ }
+
+ @Test
+ fun disableFlags_shadeDisabled_shadeEnabledFalse() =
+ testScope.runTest {
+ getCommandQueueCallback()
+ .disable(
+ DISPLAY_ID,
+ DISABLE_NONE,
+ DISABLE2_NOTIFICATION_SHADE,
+ /* animate= */ false,
+ )
+
+ assertThat(underTest.disableFlags.value.isShadeEnabled()).isFalse()
+ }
+
+ @Test
+ fun disableFlags_shadeDisabled_differentDisplay_shadeEnabledTrue() =
+ testScope.runTest {
+ val wrongDisplayId = DISPLAY_ID + 10
+
+ getCommandQueueCallback()
+ .disable(
+ wrongDisplayId,
+ DISABLE_NONE,
+ DISABLE2_NOTIFICATION_SHADE,
+ /* animate= */ false,
+ )
+
+ // THEN our repo reports them as still enabled
+ assertThat(underTest.disableFlags.value.isShadeEnabled()).isTrue()
+ }
+
+ @Test
+ fun disableFlags_quickSettingsNotDisabled_quickSettingsEnabledTrue() =
+ testScope.runTest {
+ getCommandQueueCallback()
+ .disable(DISPLAY_ID, DISABLE_NONE, DISABLE2_NONE, /* animate= */ false)
+
+ assertThat(underTest.disableFlags.value.isQuickSettingsEnabled()).isTrue()
+ }
+
+ @Test
+ fun disableFlags_quickSettingsDisabled_quickSettingsEnabledFalse() =
+ testScope.runTest {
+ getCommandQueueCallback()
+ .disable(
+ DISPLAY_ID,
+ DISABLE_NONE,
+ DISABLE2_QUICK_SETTINGS,
+ /* animate= */ false,
+ )
+
+ assertThat(underTest.disableFlags.value.isQuickSettingsEnabled()).isFalse()
+ }
+
+ @Test
+ fun disableFlags_quickSettingsDisabled_differentDisplay_quickSettingsEnabledTrue() =
+ testScope.runTest {
+ val wrongDisplayId = DISPLAY_ID + 10
+
+ getCommandQueueCallback()
+ .disable(
+ wrongDisplayId,
+ DISABLE_NONE,
+ DISABLE2_QUICK_SETTINGS,
+ /* animate= */ false,
+ )
+
+ // THEN our repo reports them as still enabled
+ assertThat(underTest.disableFlags.value.isQuickSettingsEnabled()).isTrue()
+ }
+
+ @Test
+ fun disableFlags_remoteInputActive_quickSettingsEnabledFalse() =
+ testScope.runTest {
+ // WHEN remote input is set up to be active
+ val configuration = Configuration(mContext.resources.configuration)
+ configuration.orientation = Configuration.ORIENTATION_LANDSCAPE
+ mContext.orCreateTestableResources.addOverride(
+ R.bool.config_use_split_notification_shade,
+ /* value= */ false
+ )
+ remoteInputQuickSettingsDisabler.setRemoteInputActive(true)
+ remoteInputQuickSettingsDisabler.onConfigChanged(configuration)
+
+ getCommandQueueCallback()
+ .disable(
+ DISPLAY_ID,
+ DISABLE_NONE,
+ DISABLE2_NONE,
+ /* animate= */ false,
+ )
+
+ // THEN quick settings is disabled (even if the disable flags don't say so)
+ assertThat(underTest.disableFlags.value.isQuickSettingsEnabled()).isFalse()
+ }
+
+ @Test
+ fun disableFlags_reactsToChanges() =
+ testScope.runTest {
+ getCommandQueueCallback()
+ .disable(
+ DISPLAY_ID,
+ DISABLE_NOTIFICATION_ALERTS,
+ DISABLE2_NONE,
+ /* animate= */ false,
+ )
+ assertThat(underTest.disableFlags.value.areNotificationAlertsEnabled()).isFalse()
+
+ getCommandQueueCallback()
+ .disable(
+ DISPLAY_ID,
+ DISABLE_CLOCK, // Unrelated to notifications
+ DISABLE2_NONE,
+ /* animate= */ false,
+ )
+ assertThat(underTest.disableFlags.value.areNotificationAlertsEnabled()).isTrue()
+
+ getCommandQueueCallback()
+ .disable(
+ DISPLAY_ID,
+ DISABLE_NOTIFICATION_ALERTS,
+ DISABLE2_QUICK_SETTINGS or DISABLE2_NOTIFICATION_SHADE,
+ /* animate= */ false,
+ )
+ assertThat(underTest.disableFlags.value.areNotificationAlertsEnabled()).isFalse()
+ assertThat(underTest.disableFlags.value.isShadeEnabled()).isFalse()
+ assertThat(underTest.disableFlags.value.isQuickSettingsEnabled()).isFalse()
+ }
+
+ private fun getCommandQueueCallback(): CommandQueue.Callbacks {
+ val callbackCaptor = argumentCaptor<CommandQueue.Callbacks>()
+ verify(commandQueue).addCallback(callbackCaptor.capture())
+ return callbackCaptor.value
+ }
+
+ private companion object {
+ const val DISPLAY_ID = 1
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/disableflags/data/repository/FakeDisableFlagsRepository.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/disableflags/data/repository/FakeDisableFlagsRepository.kt
new file mode 100644
index 0000000..b66231c
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/disableflags/data/repository/FakeDisableFlagsRepository.kt
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.systemui.statusbar.disableflags.data.repository
+
+import com.android.systemui.statusbar.disableflags.data.model.DisableFlagsModel
+import kotlinx.coroutines.flow.MutableStateFlow
+
+class FakeDisableFlagsRepository : DisableFlagsRepository {
+ override val disableFlags = MutableStateFlow(DisableFlagsModel())
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
index 540bda6..9037df8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
@@ -1675,11 +1675,21 @@
}
@Test
+ public void testCanDismissOtherNotificationChildren() {
+ // GIVEN an ongoing notification
+ final NotificationEntry container = new NotificationEntryBuilder()
+ .setGroup(mContext, "group")
+ .build();
+
+ // THEN its children are dismissible
+ assertTrue(mCollection.shouldAutoDismissChildren(
+ container, container.getSbn().getGroupKey()));
+ }
+
+ @Test
public void testCannotDismissOngoingNotificationChildren() {
// GIVEN an ongoing notification
final NotificationEntry container = new NotificationEntryBuilder()
- .setPkg(TEST_PACKAGE)
- .setId(47)
.setGroup(mContext, "group")
.setFlag(mContext, FLAG_ONGOING_EVENT, true)
.build();
@@ -1693,6 +1703,7 @@
public void testCannotDismissNoClearNotifications() {
// GIVEN an no-clear notification
final NotificationEntry container = new NotificationEntryBuilder()
+ .setGroup(mContext, "group")
.setFlag(mContext, FLAG_NO_CLEAR, true)
.build();
@@ -1702,11 +1713,25 @@
}
@Test
+ public void testCannotDismissPriorityConversations() {
+ // GIVEN an no-clear notification
+ NotificationChannel channel =
+ new NotificationChannel("foo", "Foo", NotificationManager.IMPORTANCE_HIGH);
+ channel.setImportantConversation(true);
+ final NotificationEntry container = new NotificationEntryBuilder()
+ .setGroup(mContext, "group")
+ .setChannel(channel)
+ .build();
+
+ // THEN its children are not dismissible
+ assertFalse(mCollection.shouldAutoDismissChildren(
+ container, container.getSbn().getGroupKey()));
+ }
+
+ @Test
public void testCanDismissFgsNotificationChildren() {
// GIVEN an FGS but not ongoing notification
final NotificationEntry container = new NotificationEntryBuilder()
- .setPkg(TEST_PACKAGE)
- .setId(47)
.setGroup(mContext, "group")
.setFlag(mContext, FLAG_FOREGROUND_SERVICE, true)
.build();
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 0000000..fe49016
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/domain/interactor/NotificationsInteractorTest.kt
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.domain.interactor
+
+import android.app.StatusBarManager
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.log.LogBufferFactory
+import com.android.systemui.statusbar.CommandQueue
+import com.android.systemui.statusbar.disableflags.DisableFlagsLogger
+import com.android.systemui.statusbar.disableflags.data.repository.DisableFlagsRepository
+import com.android.systemui.statusbar.disableflags.data.repository.DisableFlagsRepositoryImpl
+import com.android.systemui.util.mockito.argumentCaptor
+import com.android.systemui.util.mockito.mock
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import org.junit.Before
+import org.junit.Test
+import org.mockito.Mockito.verify
+
+@SmallTest
+@OptIn(ExperimentalCoroutinesApi::class)
+class NotificationsInteractorTest : SysuiTestCase() {
+
+ private lateinit var underTest: NotificationsInteractor
+
+ private val testScope = TestScope(UnconfinedTestDispatcher())
+ private val commandQueue: CommandQueue = mock()
+ private val logBuffer = LogBufferFactory(DumpManager(), mock()).create("buffer", 10)
+ private val disableFlagsLogger = DisableFlagsLogger()
+ private lateinit var disableFlagsRepository: DisableFlagsRepository
+
+ @Before
+ fun setUp() {
+ disableFlagsRepository =
+ DisableFlagsRepositoryImpl(
+ commandQueue,
+ DISPLAY_ID,
+ testScope.backgroundScope,
+ mock(),
+ logBuffer,
+ disableFlagsLogger,
+ )
+ underTest = NotificationsInteractor(disableFlagsRepository)
+ }
+
+ @Test
+ fun disableFlags_notifAlertsNotDisabled_notifAlertsEnabledTrue() {
+ val callback = getCommandQueueCallback()
+
+ callback.disable(
+ DISPLAY_ID,
+ StatusBarManager.DISABLE_NONE,
+ StatusBarManager.DISABLE2_NONE,
+ /* animate= */ false
+ )
+
+ assertThat(underTest.areNotificationAlertsEnabled()).isTrue()
+ }
+
+ @Test
+ fun disableFlags_notifAlertsDisabled_notifAlertsEnabledFalse() {
+ val callback = getCommandQueueCallback()
+
+ callback.disable(
+ DISPLAY_ID,
+ StatusBarManager.DISABLE_NOTIFICATION_ALERTS,
+ StatusBarManager.DISABLE2_NONE,
+ /* animate= */ false
+ )
+
+ assertThat(underTest.areNotificationAlertsEnabled()).isFalse()
+ }
+
+ private fun getCommandQueueCallback(): CommandQueue.Callbacks {
+ val callbackCaptor = argumentCaptor<CommandQueue.Callbacks>()
+ verify(commandQueue).addCallback(callbackCaptor.capture())
+ return callbackCaptor.value
+ }
+
+ private companion object {
+ const val DISPLAY_ID = 1
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/StackStateLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/StackStateLoggerTest.kt
new file mode 100644
index 0000000..7707a7e
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/StackStateLoggerTest.kt
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.logging
+
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.LogLevel
+import com.android.systemui.log.LogcatEchoTracker
+import com.android.systemui.statusbar.notification.stack.StackStateLogger
+import com.google.common.truth.Truth
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class StackStateLoggerTest : SysuiTestCase() {
+ private val logBufferCounter = LogBufferCounter()
+ private lateinit var logger: StackStateLogger
+
+ @Before
+ fun setup() {
+ logger = StackStateLogger(logBufferCounter.logBuffer, logBufferCounter.logBuffer)
+ }
+
+ @Test
+ fun groupChildRemovalEvent() {
+ logger.groupChildRemovalEventProcessed(KEY)
+ verifyDidLog(1)
+ logger.groupChildRemovalAnimationEnded(KEY)
+ verifyDidLog(1)
+ }
+
+ class LogBufferCounter {
+ val recentLogs = mutableListOf<Pair<String, LogLevel>>()
+ val tracker =
+ object : LogcatEchoTracker {
+ override val logInBackgroundThread: Boolean = false
+ override fun isBufferLoggable(bufferName: String, level: LogLevel): Boolean = false
+ override fun isTagLoggable(tagName: String, level: LogLevel): Boolean {
+ recentLogs.add(tagName to level)
+ return true
+ }
+ }
+ val logBuffer =
+ LogBuffer(name = "test", maxSize = 1, logcatEchoTracker = tracker, systrace = false)
+
+ fun verifyDidLog(times: Int) {
+ Truth.assertThat(recentLogs).hasSize(times)
+ recentLogs.clear()
+ }
+ }
+
+ private fun verifyDidLog(times: Int) {
+ logBufferCounter.verifyDidLog(times)
+ }
+
+ companion object {
+ private val KEY = "PACKAGE_NAME"
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt
index c2f1f61..2e68cec 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt
@@ -36,6 +36,7 @@
import com.android.systemui.statusbar.notification.logging.NotificationLogger
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier
import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainer
+import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainerLogger
import com.android.systemui.statusbar.notification.stack.NotificationListContainer
import com.android.systemui.statusbar.phone.KeyguardBypassController
import com.android.systemui.statusbar.policy.HeadsUpManager
@@ -107,6 +108,7 @@
rivSubComponentFactory,
metricsLogger,
logBufferLogger,
+ mock<NotificationChildrenContainerLogger>(),
listContainer,
smartReplyConstants,
smartReplyController,
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 915924f1..bdc8135 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 @@
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 4c83194..608778e 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 @@
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/row/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
index 813bae8..df47071 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
@@ -76,7 +76,7 @@
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow.ExpandableNotificationRowLogger;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow.OnExpandClickListener;
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag;
-import com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm;
+import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainerLogger;
import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.policy.InflatedSmartReplyState;
@@ -581,6 +581,7 @@
mock(NotificationGutsManager.class),
mDismissibilityProvider,
mock(MetricsLogger.class),
+ mock(NotificationChildrenContainerLogger.class),
mock(SmartReplyConstants.class),
mock(SmartReplyController.class),
mFeatureFlags,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractorTest.kt
index 944eb2d..a87dd2d 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.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 @@
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 @@
@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 e9a8f3f..7ae1502 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 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 @@
@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 @@
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 @@
@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 05b70eb..8d751e3 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 @@
@RunWithLooper
class NotificationShelfTest : SysuiTestCase() {
- @Mock private lateinit var largeScreenShadeInterpolator: LargeScreenShadeInterpolator
- @Mock private lateinit var flags: FeatureFlags
+ @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 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))
- }
+ 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 @@
}
@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 @@
}
@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 @@
)
}
+ @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 02666e4..6ae7dca 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
@@ -24,6 +24,7 @@
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.argThat;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.mock;
@@ -32,10 +33,14 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import static kotlinx.coroutines.flow.FlowKt.emptyFlow;
+
import android.content.res.Resources;
import android.metrics.LogMaker;
import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
import android.view.View;
+import android.view.ViewTreeObserver;
import androidx.test.filters.SmallTest;
@@ -44,10 +49,17 @@
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.FakeFeatureFlags;
import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.Flags;
+import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository;
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
+import com.android.systemui.keyguard.shared.model.KeyguardState;
+import com.android.systemui.keyguard.shared.model.TransitionStep;
import com.android.systemui.media.controls.ui.KeyguardMediaController;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
@@ -71,11 +83,11 @@
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;
@@ -102,10 +114,12 @@
* Tests for {@link NotificationStackScrollLayoutController}.
*/
@SmallTest
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
@RunWith(AndroidTestingRunner.class)
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 +132,8 @@
@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 +141,6 @@
@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,11 +156,12 @@
@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;
@Mock private ActivityStarter mActivityStarter;
+ @Mock private KeyguardTransitionRepository mKeyguardTransitionRepo;
@Captor
private ArgumentCaptor<StatusBarStateController.StateListener> mStateListenerArgumentCaptor;
@@ -157,9 +173,13 @@
@Before
public void setUp() {
+ allowTestableLooperAsMainThread();
MockitoAnnotations.initMocks(this);
+ mFeatureFlags.set(Flags.USE_REPOS_FOR_BOUNCER_SHOWING, false);
+
when(mNotificationSwipeHelperBuilder.build()).thenReturn(mNotificationSwipeHelper);
+ when(mKeyguardTransitionRepo.getTransitions()).thenReturn(emptyFlow());
}
@Test
@@ -258,28 +278,84 @@
}
@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);
@@ -509,17 +585,33 @@
.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
}
+ @Test
+ public void updateEmptyShadeView_onKeyguardTransitionToAod_hidesView() {
+ initController(/* viewIsAttached= */ true);
+ mController.onKeyguardTransitionChanged(
+ new TransitionStep(
+ /* from= */ KeyguardState.GONE,
+ /* to= */ KeyguardState.AOD));
+ verify(mNotificationStackScrollLayout).updateEmptyShadeView(eq(false), anyBoolean());
+ }
+
private LogMaker logMatcher(int category, int type) {
return argThat(new LogMatcher(category, type));
}
private void setupShowEmptyShadeViewState(boolean toShow) {
if (toShow) {
- when(mSysuiStatusBarStateController.getCurrentOrUpcomingState()).thenReturn(SHADE);
+ mController.onKeyguardTransitionChanged(
+ new TransitionStep(
+ /* from= */ KeyguardState.LOCKSCREEN,
+ /* to= */ KeyguardState.GONE));
mController.setQsFullScreen(false);
mController.getView().removeAllViews();
} else {
- when(mSysuiStatusBarStateController.getCurrentOrUpcomingState()).thenReturn(KEYGUARD);
+ mController.onKeyguardTransitionChanged(
+ new TransitionStep(
+ /* from= */ KeyguardState.GONE,
+ /* to= */ KeyguardState.AOD));
mController.setQsFullScreen(true);
mController.getView().addContainerView(mock(ExpandableNotificationRow.class));
}
@@ -527,11 +619,14 @@
private void initController(boolean viewIsAttached) {
when(mNotificationStackScrollLayout.isAttachedToWindow()).thenReturn(viewIsAttached);
-
+ ViewTreeObserver viewTreeObserver = mock(ViewTreeObserver.class);
+ when(mNotificationStackScrollLayout.getViewTreeObserver())
+ .thenReturn(viewTreeObserver);
mController = new NotificationStackScrollLayoutController(
mNotificationStackScrollLayout,
true,
mNotificationGutsManager,
+ mNotificationsController,
mVisibilityProvider,
mHeadsUpManager,
mNotificationRoundnessManager,
@@ -542,6 +637,9 @@
mSysuiStatusBarStateController,
mKeyguardMediaController,
mKeyguardBypassController,
+ mKeyguardInteractor,
+ mPrimaryBouncerInteractor,
+ mKeyguardTransitionRepo,
mZenModeController,
mNotificationLockscreenUserManager,
Optional.<NotificationListViewModel>empty(),
@@ -551,7 +649,6 @@
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 40e7e8d..8ad271b 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.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 @@
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 @@
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 88b1eb3..4c97d20 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.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 @@
}
@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 @@
}
@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 @@
)
}
+ // 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 4a30800..442ba09 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.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 @@
@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 @@
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 3870d99..036b8be 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 @@
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;
@@ -151,14 +150,7 @@
mSbcqCallbacks.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_NONE,
StatusBarManager.DISABLE2_NOTIFICATION_SHADE, false);
- 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
@@ -169,14 +161,7 @@
when(mCommandQueue.panelsEnabled()).thenReturn(true);
mSbcqCallbacks.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_NONE,
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 7702630..1ffffe4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
@@ -29,7 +29,6 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.clearInvocations;
@@ -98,7 +97,9 @@
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 +114,6 @@
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 +122,7 @@
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 +146,7 @@
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 +261,7 @@
@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 +276,12 @@
@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 +292,6 @@
@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 +439,12 @@
mShadeController = spy(new ShadeControllerImpl(
mCommandQueue,
+ mMainExecutor,
mKeyguardStateController,
mStatusBarStateController,
mStatusBarKeyguardViewManager,
mStatusBarWindowController,
+ mDeviceProvisionedController,
mNotificationShadeWindowController,
mContext.getSystemService(WindowManager.class),
() -> mAssistManager,
@@ -492,6 +498,7 @@
mColorExtractor,
mScreenLifecycle,
mWakefulnessLifecycle,
+ mPowerInteractor,
mStatusBarStateController,
Optional.of(mBubbles),
() -> mNoteTaskController,
@@ -501,13 +508,17 @@
() -> mAssistManager,
configurationController,
mNotificationShadeWindowController,
+ mNotificationShelfController,
+ mStackScrollerController,
mDozeParameters,
mScrimController,
mLockscreenWallpaperLazy,
mBiometricUnlockControllerLazy,
mAuthRippleController,
mDozeServiceHost,
- mPowerManager, mScreenPinningRequest,
+ mBackActionInteractor,
+ mPowerManager,
+ mScreenPinningRequest,
mDozeScrimController,
mVolumeComponent,
mCommandQueue,
@@ -519,7 +530,6 @@
mInitController,
new Handler(TestableLooper.get(this).getLooper()),
mPluginDependencyProvider,
- mKeyguardDismissUtil,
mExtensionController,
mUserInfoControllerImpl,
mPhoneStatusBarPolicy,
@@ -583,7 +593,6 @@
mCentralSurfaces.mPresenter = mNotificationPresenter;
mCentralSurfaces.mKeyguardIndicationController = mKeyguardIndicationController;
mCentralSurfaces.mBarService = mBarService;
- mCentralSurfaces.mStackScroller = mStackScroller;
mCentralSurfaces.mGestureWakeLock = mPowerManager.newWakeLock(
PowerManager.SCREEN_BRIGHT_WAKE_LOCK, "sysui:GestureWakeLock");
mCentralSurfaces.startKeyguard();
@@ -821,9 +830,9 @@
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 +850,7 @@
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 +873,7 @@
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 +1073,7 @@
// 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 +1087,7 @@
// 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 +1156,6 @@
}
@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);
@@ -1266,34 +1251,6 @@
}
@Test
- public void dozing_wakeUp() throws RemoteException {
- // GIVEN can wakeup when dozing & is dozing
- when(mScreenOffAnimationController.allowWakeUpIfDozing()).thenReturn(true);
- setDozing(true);
-
- // WHEN wakeup is requested
- final int wakeReason = PowerManager.WAKE_REASON_TAP;
- mCentralSurfaces.wakeUpIfDozing(0, "", wakeReason);
-
- // THEN power manager receives wakeup
- verify(mPowerManagerService).wakeUp(eq(0L), eq(wakeReason), anyString(), anyString());
- }
-
- @Test
- public void notDozing_noWakeUp() throws RemoteException {
- // GIVEN can wakeup when dozing and NOT dozing
- when(mScreenOffAnimationController.allowWakeUpIfDozing()).thenReturn(true);
- setDozing(false);
-
- // WHEN wakeup is requested
- final int wakeReason = PowerManager.WAKE_REASON_TAP;
- mCentralSurfaces.wakeUpIfDozing(0, "", wakeReason);
-
- // THEN power manager receives wakeup
- verify(mPowerManagerService, never()).wakeUp(anyLong(), anyInt(), anyString(), anyString());
- }
-
- @Test
public void frpLockedDevice_shadeDisabled() {
when(mDeviceProvisionedController.isFrpActive()).thenReturn(true);
when(mDozeServiceHost.isPulsing()).thenReturn(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardDismissUtilTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardDismissUtilTest.java
new file mode 100644
index 0000000..b0aa2d3
--- /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/LetterboxAppearanceCalculatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LetterboxAppearanceCalculatorTest.kt
index c0243dc..b2dc0dc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LetterboxAppearanceCalculatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LetterboxAppearanceCalculatorTest.kt
@@ -105,6 +105,30 @@
expect.that(letterboxAppearance.appearanceRegions).isEqualTo(TEST_APPEARANCE_REGIONS)
}
+ /** Regression test for b/287508741 */
+ @Test
+ fun getLetterboxAppearance_withOverlap_doesNotMutateOriginalBounds() {
+ val statusBarStartSideBounds = Rect(left = 0, top = 0, right = 100, bottom = 100)
+ val statusBarEndSideBounds = Rect(left = 200, top = 0, right = 300, bottom = 100)
+ val letterBoxInnerBounds = Rect(left = 150, top = 50, right = 250, bottom = 150)
+ val statusBarStartSideBoundsCopy = Rect(statusBarStartSideBounds)
+ val statusBarEndSideBoundsCopy = Rect(statusBarEndSideBounds)
+ val letterBoxInnerBoundsCopy = Rect(letterBoxInnerBounds)
+ whenever(statusBarBoundsProvider.visibleStartSideBounds)
+ .thenReturn(statusBarStartSideBounds)
+ whenever(statusBarBoundsProvider.visibleEndSideBounds).thenReturn(statusBarEndSideBounds)
+
+ calculator.getLetterboxAppearance(
+ TEST_APPEARANCE,
+ TEST_APPEARANCE_REGIONS,
+ arrayOf(letterboxWithInnerBounds(letterBoxInnerBounds))
+ )
+
+ expect.that(statusBarStartSideBounds).isEqualTo(statusBarStartSideBoundsCopy)
+ expect.that(statusBarEndSideBounds).isEqualTo(statusBarEndSideBoundsCopy)
+ expect.that(letterBoxInnerBounds).isEqualTo(letterBoxInnerBoundsCopy)
+ }
+
@Test
fun getLetterboxAppearance_noOverlap_BackgroundMultiColor_returnsAppearanceWithScrim() {
whenever(letterboxBackgroundProvider.isLetterboxBackgroundMultiColored).thenReturn(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 a9ed175..ab80158 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.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 @@
}
@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 @@
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 3eea93c..548e1b5 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.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 @@
@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 @@
.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 @@
mDreamOverlayStateController,
mock(NavigationModeController.class),
mock(DockManager.class),
- mock(NotificationShadeWindowController.class),
+ mNotificationShadeWindowController,
mKeyguardStateController,
mock(NotificationMediaManager.class),
mKeyguardMessageAreaFactory,
@@ -646,21 +647,21 @@
@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 d6ae2b7..d44af88 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.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 @@
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 fdfe028..5bd6ff4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
@@ -37,6 +37,7 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.power.domain.interactor.PowerInteractor;
import com.android.systemui.settings.FakeDisplayTracker;
import com.android.systemui.shade.NotificationShadeWindowView;
import com.android.systemui.shade.QuickSettingsController;
@@ -44,7 +45,6 @@
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 +56,7 @@
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 +82,8 @@
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,9 +125,10 @@
mock(NotificationShadeWindowController.class),
mock(DynamicPrivacyController.class),
mKeyguardStateController,
- mock(KeyguardIndicationController.class),
mCentralSurfaces,
+ mNotificationsInteractor,
mock(LockscreenShadeTransitionController.class),
+ mock(PowerInteractor.class),
mCommandQueue,
mock(NotificationLockscreenUserManager.class),
mock(SysuiStatusBarStateController.class),
@@ -235,9 +239,9 @@
.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 ea534bb..2e9a690 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.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 @@
@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 @@
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 9bc49ae..87d813c 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 @@
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/pipeline/mobile/data/repository/CarrierConfigRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/CarrierConfigRepositoryTest.kt
index 6e3af26..932c4a1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/CarrierConfigRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/CarrierConfigRepositoryTest.kt
@@ -152,13 +152,11 @@
}
private fun sendConfig(subId: Int) {
- fakeBroadcastDispatcher.registeredReceivers.forEach { receiver ->
- receiver.onReceive(
- context,
- Intent(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED)
- .putExtra(CarrierConfigManager.EXTRA_SUBSCRIPTION_INDEX, subId)
- )
- }
+ fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(
+ context,
+ Intent(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED)
+ .putExtra(CarrierConfigManager.EXTRA_SUBSCRIPTION_INDEX, subId),
+ )
}
companion object {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt
index d1df6e3..cf832b4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt
@@ -379,9 +379,10 @@
var latest: Int? = null
val job = underTest.carrierId.onEach { latest = it }.launchIn(this)
- fakeBroadcastDispatcher.registeredReceivers.forEach { receiver ->
- receiver.onReceive(context, carrierIdIntent(carrierId = 4321))
- }
+ fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(
+ context,
+ carrierIdIntent(carrierId = 4321),
+ )
assertThat(latest).isEqualTo(4321)
@@ -653,10 +654,7 @@
val job = underTest.networkName.onEach { latest = it }.launchIn(this)
val intent = spnIntent()
-
- fakeBroadcastDispatcher.registeredReceivers.forEach { receiver ->
- receiver.onReceive(context, intent)
- }
+ fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(context, intent)
assertThat(latest).isEqualTo(intent.toNetworkNameModel(SEP))
@@ -670,17 +668,13 @@
val job = underTest.networkName.onEach { latest = it }.launchIn(this)
val intent = spnIntent()
- fakeBroadcastDispatcher.registeredReceivers.forEach { receiver ->
- receiver.onReceive(context, intent)
- }
+ fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(context, intent)
assertThat(latest).isEqualTo(intent.toNetworkNameModel(SEP))
// WHEN an intent with a different subId is sent
val wrongSubIntent = spnIntent(subId = 101)
- fakeBroadcastDispatcher.registeredReceivers.forEach { receiver ->
- receiver.onReceive(context, wrongSubIntent)
- }
+ fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(context, wrongSubIntent)
// THEN the previous intent's name is still used
assertThat(latest).isEqualTo(intent.toNetworkNameModel(SEP))
@@ -695,9 +689,7 @@
val job = underTest.networkName.onEach { latest = it }.launchIn(this)
val intent = spnIntent()
- fakeBroadcastDispatcher.registeredReceivers.forEach { receiver ->
- receiver.onReceive(context, intent)
- }
+ fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(context, intent)
assertThat(latest).isEqualTo(intent.toNetworkNameModel(SEP))
val intentWithoutInfo =
@@ -706,9 +698,7 @@
showPlmn = false,
)
- fakeBroadcastDispatcher.registeredReceivers.forEach { receiver ->
- receiver.onReceive(context, intentWithoutInfo)
- }
+ fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(context, intentWithoutInfo)
assertThat(latest).isEqualTo(DEFAULT_NAME)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
index fd156d8..74bcdee 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
@@ -626,23 +626,17 @@
assertThat(latest).isEqualTo(INVALID_SUBSCRIPTION_ID)
- fakeBroadcastDispatcher.registeredReceivers.forEach { receiver ->
- receiver.onReceive(
- context,
- Intent(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED)
- .putExtra(PhoneConstants.SUBSCRIPTION_KEY, SUB_2_ID)
- )
- }
+ val intent2 =
+ Intent(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED)
+ .putExtra(PhoneConstants.SUBSCRIPTION_KEY, SUB_2_ID)
+ fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(context, intent2)
assertThat(latest).isEqualTo(SUB_2_ID)
- fakeBroadcastDispatcher.registeredReceivers.forEach { receiver ->
- receiver.onReceive(
- context,
- Intent(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED)
- .putExtra(PhoneConstants.SUBSCRIPTION_KEY, SUB_1_ID)
- )
- }
+ val intent1 =
+ Intent(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED)
+ .putExtra(PhoneConstants.SUBSCRIPTION_KEY, SUB_1_ID)
+ fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(context, intent1)
assertThat(latest).isEqualTo(SUB_1_ID)
}
@@ -1074,13 +1068,10 @@
assertThat(configFromContext.showAtLeast3G).isTrue()
// WHEN the change event is fired
- fakeBroadcastDispatcher.registeredReceivers.forEach { receiver ->
- receiver.onReceive(
- context,
- Intent(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED)
- .putExtra(PhoneConstants.SUBSCRIPTION_KEY, SUB_1_ID)
- )
- }
+ val intent =
+ Intent(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED)
+ .putExtra(PhoneConstants.SUBSCRIPTION_KEY, SUB_1_ID)
+ fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(context, intent)
// THEN the config is updated
assertTrue(latest!!.areEqual(configFromContext))
@@ -1099,12 +1090,10 @@
assertThat(configFromContext.showAtLeast3G).isTrue()
// WHEN the change event is fired
- fakeBroadcastDispatcher.registeredReceivers.forEach { receiver ->
- receiver.onReceive(
- context,
- Intent(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED)
- )
- }
+ fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(
+ context,
+ Intent(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED),
+ )
// THEN the config is updated
assertThat(latest!!.areEqual(configFromContext)).isTrue()
@@ -1123,12 +1112,10 @@
assertThat(configFromContext.showAtLeast3G).isTrue()
// WHEN the change event is fired
- fakeBroadcastDispatcher.registeredReceivers.forEach { receiver ->
- receiver.onReceive(
- context,
- Intent(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED)
- )
- }
+ fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(
+ context,
+ Intent(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED),
+ )
// WHEN collection starts AFTER the broadcast is sent out
val latest by collectLastValue(underTest.defaultDataSubRatConfig)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt
index dc68180..5ed3a5c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.pipeline.wifi.data.repository.prod
+import android.content.Intent
import android.net.ConnectivityManager
import android.net.Network
import android.net.NetworkCapabilities
@@ -33,7 +34,6 @@
import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.log.table.TableLogBuffer
import com.android.systemui.statusbar.pipeline.shared.data.model.ConnectivitySlots
import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
@@ -46,13 +46,10 @@
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.mock
-import com.android.systemui.util.mockito.nullable
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
import java.util.concurrent.Executor
import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.flow.MutableSharedFlow
-import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.test.TestScope
@@ -60,7 +57,6 @@
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
-import org.mockito.ArgumentMatchers.anyInt
import org.mockito.Mock
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when` as whenever
@@ -73,7 +69,6 @@
private lateinit var underTest: WifiRepositoryImpl
- @Mock private lateinit var broadcastDispatcher: BroadcastDispatcher
@Mock private lateinit var logger: WifiInputLogger
@Mock private lateinit var tableLogger: TableLogBuffer
@Mock private lateinit var connectivityManager: ConnectivityManager
@@ -86,15 +81,6 @@
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
- whenever(
- broadcastDispatcher.broadcastFlow(
- any(),
- nullable(),
- anyInt(),
- nullable(),
- )
- )
- .thenReturn(flowOf(Unit))
executor = FakeExecutor(FakeSystemClock())
connectivityRepository =
@@ -168,27 +154,23 @@
@Test
fun isWifiEnabled_intentsReceived_valueUpdated() =
testScope.runTest {
- val intentFlow = MutableSharedFlow<Unit>()
- whenever(
- broadcastDispatcher.broadcastFlow(
- any(),
- nullable(),
- anyInt(),
- nullable(),
- )
- )
- .thenReturn(intentFlow)
underTest = createRepo()
val job = underTest.isWifiEnabled.launchIn(this)
whenever(wifiManager.isWifiEnabled).thenReturn(true)
- intentFlow.emit(Unit)
+ fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(
+ context,
+ Intent(WifiManager.WIFI_STATE_CHANGED_ACTION),
+ )
assertThat(underTest.isWifiEnabled.value).isTrue()
whenever(wifiManager.isWifiEnabled).thenReturn(false)
- intentFlow.emit(Unit)
+ fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(
+ context,
+ Intent(WifiManager.WIFI_STATE_CHANGED_ACTION),
+ )
assertThat(underTest.isWifiEnabled.value).isFalse()
@@ -198,23 +180,16 @@
@Test
fun isWifiEnabled_bothIntentAndNetworkUpdates_valueAlwaysUpdated() =
testScope.runTest {
- val intentFlow = MutableSharedFlow<Unit>()
- whenever(
- broadcastDispatcher.broadcastFlow(
- any(),
- nullable(),
- anyInt(),
- nullable(),
- )
- )
- .thenReturn(intentFlow)
underTest = createRepo()
val networkJob = underTest.wifiNetwork.launchIn(this)
val enabledJob = underTest.isWifiEnabled.launchIn(this)
whenever(wifiManager.isWifiEnabled).thenReturn(false)
- intentFlow.emit(Unit)
+ fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(
+ context,
+ Intent(WifiManager.WIFI_STATE_CHANGED_ACTION),
+ )
assertThat(underTest.isWifiEnabled.value).isFalse()
whenever(wifiManager.isWifiEnabled).thenReturn(true)
@@ -227,7 +202,10 @@
assertThat(underTest.isWifiEnabled.value).isFalse()
whenever(wifiManager.isWifiEnabled).thenReturn(true)
- intentFlow.emit(Unit)
+ fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(
+ context,
+ Intent(WifiManager.WIFI_STATE_CHANGED_ACTION),
+ )
assertThat(underTest.isWifiEnabled.value).isTrue()
networkJob.cancel()
@@ -1317,7 +1295,7 @@
private fun createRepo(): WifiRepositoryImpl {
return WifiRepositoryImpl(
- broadcastDispatcher,
+ fakeBroadcastDispatcher,
connectivityManager,
connectivityRepository,
logger,
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 3b0d512..ae38958e 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.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 @@
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 @@
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 @@
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 @@
@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 @@
@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 d9ee081..813597a 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.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 @@
@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 @@
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 ca83d49..89dce61 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.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 @@
@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 @@
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 @@
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,
@@ -641,13 +633,11 @@
userRepository.setSelectedUserInfo(userInfos[1])
runCurrent()
- fakeBroadcastDispatcher.registeredReceivers.forEach {
- it.onReceive(
- context,
- Intent(Intent.ACTION_USER_SWITCHED)
- .putExtra(Intent.EXTRA_USER_HANDLE, userInfos[1].id),
- )
- }
+ fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(
+ context,
+ Intent(Intent.ACTION_USER_SWITCHED)
+ .putExtra(Intent.EXTRA_USER_HANDLE, userInfos[1].id),
+ )
runCurrent()
verify(callback1, atLeastOnce()).onUserStateChanged()
@@ -664,12 +654,10 @@
userRepository.setSelectedUserInfo(userInfos[0])
val refreshUsersCallCount = userRepository.refreshUsersCallCount
- fakeBroadcastDispatcher.registeredReceivers.forEach {
- it.onReceive(
- context,
- Intent(Intent.ACTION_USER_INFO_CHANGED),
- )
- }
+ fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(
+ context,
+ Intent(Intent.ACTION_USER_INFO_CHANGED),
+ )
runCurrent()
@@ -684,13 +672,11 @@
userRepository.setSelectedUserInfo(userInfos[0])
val refreshUsersCallCount = userRepository.refreshUsersCallCount
- fakeBroadcastDispatcher.registeredReceivers.forEach {
- it.onReceive(
- context,
- Intent(Intent.ACTION_USER_UNLOCKED)
- .putExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_SYSTEM),
- )
- }
+ fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(
+ context,
+ Intent(Intent.ACTION_USER_UNLOCKED)
+ .putExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_SYSTEM),
+ )
runCurrent()
assertThat(userRepository.refreshUsersCallCount).isEqualTo(refreshUsersCallCount + 1)
@@ -704,12 +690,10 @@
userRepository.setSelectedUserInfo(userInfos[0])
val refreshUsersCallCount = userRepository.refreshUsersCallCount
- fakeBroadcastDispatcher.registeredReceivers.forEach {
- it.onReceive(
- context,
- Intent(Intent.ACTION_USER_UNLOCKED).putExtra(Intent.EXTRA_USER_HANDLE, 1337),
- )
- }
+ fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(
+ context,
+ Intent(Intent.ACTION_USER_UNLOCKED).putExtra(Intent.EXTRA_USER_HANDLE, 1337),
+ )
assertThat(userRepository.refreshUsersCallCount).isEqualTo(refreshUsersCallCount)
}
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 fd8c6c7..9cb26e0 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.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 @@
@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 @@
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 9155084..e3f9fac 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.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 @@
@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 @@
)
}
- keyguardRepository = FakeKeyguardRepository()
val refreshUsersScheduler =
RefreshUsersScheduler(
applicationScope = testScope.backgroundScope,
@@ -140,38 +136,35 @@
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 @@
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 47a86b1..a09af00 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -35,6 +35,7 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyList;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.atLeastOnce;
@@ -133,15 +134,19 @@
import com.android.wm.shell.bubbles.BubbleDataRepository;
import com.android.wm.shell.bubbles.BubbleEntry;
import com.android.wm.shell.bubbles.BubbleLogger;
+import com.android.wm.shell.bubbles.BubbleOverflow;
import com.android.wm.shell.bubbles.BubbleStackView;
import com.android.wm.shell.bubbles.BubbleViewInfoTask;
+import com.android.wm.shell.bubbles.BubbleViewProvider;
import com.android.wm.shell.bubbles.Bubbles;
import com.android.wm.shell.bubbles.StackEducationViewKt;
+import com.android.wm.shell.bubbles.properties.BubbleProperties;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.FloatingContentCoordinator;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.TaskStackListenerImpl;
+import com.android.wm.shell.common.bubbles.BubbleBarUpdate;
import com.android.wm.shell.draganddrop.DragAndDropController;
import com.android.wm.shell.onehanded.OneHandedController;
import com.android.wm.shell.sysui.ShellCommandHandler;
@@ -151,7 +156,6 @@
import org.junit.After;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
@@ -300,6 +304,8 @@
private UserHandle mUser0;
+ private FakeBubbleProperties mBubbleProperties;
+
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
@@ -320,7 +326,7 @@
mColorExtractor, mDumpManager, mKeyguardStateController,
mScreenOffAnimationController, mAuthController, mShadeExpansionStateManager,
mShadeWindowLogger);
- mNotificationShadeWindowController.setNotificationShadeView(mNotificationShadeWindowView);
+ mNotificationShadeWindowController.setWindowRootView(mNotificationShadeWindowView);
mNotificationShadeWindowController.attach();
mAppBubbleIntent = new Intent(mContext, BubblesTestActivity.class);
@@ -361,6 +367,7 @@
mock(UserTracker.class)
);
when(mShellTaskOrganizer.getExecutor()).thenReturn(syncExecutor);
+ mBubbleProperties = new FakeBubbleProperties();
mBubbleController = new TestableBubbleController(
mContext,
mShellInit,
@@ -385,7 +392,8 @@
mock(Handler.class),
mTaskViewTransitions,
mock(SyncTransactionQueue.class),
- mock(IWindowManager.class));
+ mock(IWindowManager.class),
+ mBubbleProperties);
mBubbleController.setExpandListener(mBubbleExpandListener);
spyOn(mBubbleController);
@@ -476,7 +484,7 @@
public void testAddBubble() {
mBubbleController.updateBubble(mBubbleEntry);
assertTrue(mBubbleController.hasBubbles());
- assertSysuiStates(false /* stackExpanded */, false /* mangeMenuExpanded */);
+ assertSysuiStates(false /* stackExpanded */, false /* manageMenuExpanded */);
}
@Test
@@ -484,7 +492,7 @@
assertFalse(mBubbleController.hasBubbles());
mBubbleController.updateBubble(mBubbleEntry);
assertTrue(mBubbleController.hasBubbles());
- assertSysuiStates(false /* stackExpanded */, false /* mangeMenuExpanded */);
+ assertSysuiStates(false /* stackExpanded */, false /* manageMenuExpanded */);
}
@Test
@@ -499,7 +507,7 @@
assertNull(mBubbleData.getBubbleInStackWithKey(mRow.getKey()));
verify(mNotifCallback, times(2)).invalidateNotifications(anyString());
- assertSysuiStates(false /* stackExpanded */, false /* mangeMenuExpanded */);
+ assertSysuiStates(false /* stackExpanded */, false /* manageMenuExpanded */);
}
@Test
@@ -562,7 +570,7 @@
assertNull(mBubbleData.getBubbleInStackWithKey(mRow.getKey()));
assertNull(mBubbleData.getBubbleInStackWithKey(mRow2.getKey()));
- assertSysuiStates(false /* stackExpanded */, false /* mangeMenuExpanded */);
+ assertSysuiStates(false /* stackExpanded */, false /* manageMenuExpanded */);
}
@Test
@@ -581,7 +589,7 @@
mBubbleData.setExpanded(true);
assertStackExpanded();
verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getKey());
- assertSysuiStates(true /* stackExpanded */, false /* mangeMenuExpanded */);
+ assertSysuiStates(true /* stackExpanded */, false /* manageMenuExpanded */);
// Make sure the notif is suppressed
assertBubbleNotificationSuppressedFromShade(mBubbleEntry);
@@ -590,11 +598,10 @@
mBubbleController.collapseStack();
verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow.getKey());
assertStackCollapsed();
- assertSysuiStates(false /* stackExpanded */, false /* mangeMenuExpanded */);
+ assertSysuiStates(false /* stackExpanded */, false /* manageMenuExpanded */);
}
@Test
- @Ignore("Currently broken.")
public void testCollapseAfterChangingExpandedBubble() {
// Mark it as a bubble and add it explicitly
mEntryListener.onEntryAdded(mRow);
@@ -613,7 +620,7 @@
assertStackExpanded();
verify(mBubbleExpandListener, atLeastOnce()).onBubbleExpandChanged(
true, mRow2.getKey());
- assertSysuiStates(true /* stackExpanded */, false /* mangeMenuExpanded */);
+ assertSysuiStates(true /* stackExpanded */, false /* manageMenuExpanded */);
// Last added is the one that is expanded
assertEquals(mRow2.getKey(), mBubbleData.getSelectedBubble().getKey());
@@ -638,7 +645,7 @@
// Collapse
mBubbleController.collapseStack();
assertStackCollapsed();
- assertSysuiStates(false /* stackExpanded */, false /* mangeMenuExpanded */);
+ assertSysuiStates(false /* stackExpanded */, false /* manageMenuExpanded */);
}
@Test
@@ -658,7 +665,7 @@
mBubbleData.setExpanded(true);
assertStackExpanded();
verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getKey());
- assertSysuiStates(true /* stackExpanded */, false /* mangeMenuExpanded */);
+ assertSysuiStates(true /* stackExpanded */, false /* manageMenuExpanded */);
// Notif is suppressed after expansion
assertBubbleNotificationSuppressedFromShade(mBubbleEntry);
@@ -683,7 +690,7 @@
mBubbleData.setExpanded(true);
assertStackExpanded();
verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getKey());
- assertSysuiStates(true /* stackExpanded */, false /* mangeMenuExpanded */);
+ assertSysuiStates(true /* stackExpanded */, false /* manageMenuExpanded */);
// Notif is suppressed after expansion
assertBubbleNotificationSuppressedFromShade(mBubbleEntry);
@@ -712,7 +719,7 @@
BubbleStackView stackView = mBubbleController.getStackView();
mBubbleData.setExpanded(true);
- assertSysuiStates(true /* stackExpanded */, false /* mangeMenuExpanded */);
+ assertSysuiStates(true /* stackExpanded */, false /* manageMenuExpanded */);
assertStackExpanded();
verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow2.getKey());
@@ -743,7 +750,7 @@
// We should be collapsed
verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow.getKey());
assertFalse(mBubbleController.hasBubbles());
- assertSysuiStates(false /* stackExpanded */, false /* mangeMenuExpanded */);
+ assertSysuiStates(false /* stackExpanded */, false /* manageMenuExpanded */);
}
@Test
@@ -756,7 +763,7 @@
BubbleStackView stackView = mBubbleController.getStackView();
mBubbleData.setExpanded(true);
- assertSysuiStates(true /* stackExpanded */, false /* mangeMenuExpanded */);
+ assertSysuiStates(true /* stackExpanded */, false /* manageMenuExpanded */);
assertStackExpanded();
verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getKey());
@@ -771,7 +778,7 @@
// We should be collapsed
verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow.getKey());
assertFalse(mBubbleController.hasBubbles());
- assertSysuiStates(false /* stackExpanded */, false /* mangeMenuExpanded */);
+ assertSysuiStates(false /* stackExpanded */, false /* manageMenuExpanded */);
}
@@ -789,7 +796,7 @@
verify(mBubbleExpandListener, never()).onBubbleExpandChanged(false /* expanded */,
mRow.getKey());
assertStackCollapsed();
- assertSysuiStates(false /* stackExpanded */, false /* mangeMenuExpanded */);
+ assertSysuiStates(false /* stackExpanded */, false /* manageMenuExpanded */);
}
@Test
@@ -805,7 +812,7 @@
verify(mBubbleExpandListener).onBubbleExpandChanged(true /* expanded */,
mRow.getKey());
assertStackExpanded();
- assertSysuiStates(true /* stackExpanded */, false /* mangeMenuExpanded */);
+ assertSysuiStates(true /* stackExpanded */, false /* manageMenuExpanded */);
}
@Test
@@ -822,7 +829,7 @@
// Dot + flyout is hidden because notif is suppressed
assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getKey()).showDot());
assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getKey()).showFlyout());
- assertSysuiStates(false /* stackExpanded */, false /* mangeMenuExpanded */);
+ assertSysuiStates(false /* stackExpanded */, false /* manageMenuExpanded */);
}
@Test
@@ -844,7 +851,7 @@
// Dot + flyout is hidden because notif is suppressed
assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getKey()).showDot());
assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getKey()).showFlyout());
- assertSysuiStates(false /* stackExpanded */, false /* mangeMenuExpanded */);
+ assertSysuiStates(false /* stackExpanded */, false /* manageMenuExpanded */);
}
@Test
@@ -1125,7 +1132,7 @@
assertThat(mBubbleData.getOverflowBubbleWithKey(mBubbleEntry2User11.getKey())).isNotNull();
// Would have loaded bubbles twice because of user switch
- verify(mDataRepository, times(2)).loadBubbles(anyInt(), any());
+ verify(mDataRepository, times(2)).loadBubbles(anyInt(), anyList(), any());
}
@Test
@@ -1181,7 +1188,7 @@
mEntryListener.onEntryRemoved(mRow2, REASON_APP_CANCEL);
assertThat(mBubbleData.getOverflowBubbles()).isEmpty();
- verify(mDataRepository, times(1)).loadBubbles(anyInt(), any());
+ verify(mDataRepository, times(1)).loadBubbles(anyInt(), anyList(), any());
}
/**
@@ -1249,11 +1256,11 @@
BubbleStackView stackView = mBubbleController.getStackView();
mBubbleData.setExpanded(true);
assertStackExpanded();
- assertSysuiStates(true /* stackExpanded */, false /* mangeMenuExpanded */);
+ assertSysuiStates(true /* stackExpanded */, false /* manageMenuExpanded */);
// Show the menu
stackView.showManageMenu(true);
- assertSysuiStates(true /* stackExpanded */, true /* mangeMenuExpanded */);
+ assertSysuiStates(true /* stackExpanded */, true /* manageMenuExpanded */);
assertTrue(stackView.isManageMenuSettingsVisible());
assertTrue(stackView.isManageMenuDontBubbleVisible());
}
@@ -1267,11 +1274,11 @@
BubbleStackView stackView = mBubbleController.getStackView();
mBubbleData.setExpanded(true);
assertStackExpanded();
- assertSysuiStates(true /* stackExpanded */, false /* mangeMenuExpanded */);
+ assertSysuiStates(true /* stackExpanded */, false /* manageMenuExpanded */);
// Show the menu
stackView.showManageMenu(true);
- assertSysuiStates(true /* stackExpanded */, true /* mangeMenuExpanded */);
+ assertSysuiStates(true /* stackExpanded */, true /* manageMenuExpanded */);
assertFalse(stackView.isManageMenuSettingsVisible());
assertFalse(stackView.isManageMenuDontBubbleVisible());
}
@@ -1285,15 +1292,15 @@
BubbleStackView stackView = mBubbleController.getStackView();
mBubbleData.setExpanded(true);
assertStackExpanded();
- assertSysuiStates(true /* stackExpanded */, false /* mangeMenuExpanded */);
+ assertSysuiStates(true /* stackExpanded */, false /* manageMenuExpanded */);
// Show the menu
stackView.showManageMenu(true);
- assertSysuiStates(true /* stackExpanded */, true /* mangeMenuExpanded */);
+ assertSysuiStates(true /* stackExpanded */, true /* manageMenuExpanded */);
// Hide the menu
stackView.showManageMenu(false);
- assertSysuiStates(true /* stackExpanded */, false /* mangeMenuExpanded */);
+ assertSysuiStates(true /* stackExpanded */, false /* manageMenuExpanded */);
}
@Test
@@ -1305,16 +1312,16 @@
BubbleStackView stackView = mBubbleController.getStackView();
mBubbleData.setExpanded(true);
assertStackExpanded();
- assertSysuiStates(true /* stackExpanded */, false /* mangeMenuExpanded */);
+ assertSysuiStates(true /* stackExpanded */, false /* manageMenuExpanded */);
// Show the menu
stackView.showManageMenu(true);
- assertSysuiStates(true /* stackExpanded */, true /* mangeMenuExpanded */);
+ assertSysuiStates(true /* stackExpanded */, true /* manageMenuExpanded */);
// Collapse the stack
mBubbleData.setExpanded(false);
- assertSysuiStates(false /* stackExpanded */, false /* mangeMenuExpanded */);
+ assertSysuiStates(false /* stackExpanded */, false /* manageMenuExpanded */);
}
@Test
@@ -1576,7 +1583,7 @@
NotificationListenerService.RankingMap rankingMap =
mock(NotificationListenerService.RankingMap.class);
- when(rankingMap.getOrderedKeys()).thenReturn(new String[] { mBubbleEntry.getKey() });
+ when(rankingMap.getOrderedKeys()).thenReturn(new String[]{mBubbleEntry.getKey()});
mBubbleController.onRankingUpdated(rankingMap, entryDataByKey);
// Should no longer be in the stack
@@ -1884,7 +1891,111 @@
);
}
- /** Creates a bubble using the userId and package. */
+ @Test
+ public void registerBubbleBarListener_barDisabled_largeScreen_shouldBeIgnored() {
+ mBubbleProperties.mIsBubbleBarEnabled = false;
+ mPositioner.setIsLargeScreen(true);
+ mEntryListener.onEntryAdded(mRow);
+ mBubbleController.updateBubble(mBubbleEntry);
+ assertTrue(mBubbleController.hasBubbles());
+
+ assertStackMode();
+
+ FakeBubbleStateListener bubbleStateListener = new FakeBubbleStateListener();
+ mBubbleController.registerBubbleStateListener(bubbleStateListener);
+
+ assertStackMode();
+
+ assertThat(mBubbleController.getStackView().getBubbleCount()).isEqualTo(1);
+ }
+
+ @Test
+ public void registerBubbleBarListener_barEnabled_smallScreen_shouldBeIgnored() {
+ mBubbleProperties.mIsBubbleBarEnabled = true;
+ mPositioner.setIsLargeScreen(false);
+ mEntryListener.onEntryAdded(mRow);
+ mBubbleController.updateBubble(mBubbleEntry);
+ assertTrue(mBubbleController.hasBubbles());
+
+ assertStackMode();
+
+ FakeBubbleStateListener bubbleStateListener = new FakeBubbleStateListener();
+ mBubbleController.registerBubbleStateListener(bubbleStateListener);
+
+ assertStackMode();
+
+ assertThat(mBubbleController.getStackView().getBubbleCount()).isEqualTo(1);
+ }
+
+ @Test
+ public void registerBubbleBarListener_switchToBarAndBackToStack() {
+ mBubbleProperties.mIsBubbleBarEnabled = true;
+ mPositioner.setIsLargeScreen(true);
+ mEntryListener.onEntryAdded(mRow);
+ mBubbleController.updateBubble(mBubbleEntry);
+ assertTrue(mBubbleController.hasBubbles());
+
+ assertStackMode();
+
+ assertThat(mBubbleData.getBubbles()).hasSize(1);
+ assertBubbleIsInflatedForStack(mBubbleData.getBubbles().get(0));
+ assertBubbleIsInflatedForStack(mBubbleData.getOverflow());
+
+ FakeBubbleStateListener bubbleStateListener = new FakeBubbleStateListener();
+ mBubbleController.registerBubbleStateListener(bubbleStateListener);
+
+ assertBarMode();
+
+ assertThat(mBubbleData.getBubbles()).hasSize(1);
+ assertBubbleIsInflatedForBar(mBubbleData.getBubbles().get(0));
+ assertBubbleIsInflatedForBar(mBubbleData.getOverflow());
+
+ mBubbleController.unregisterBubbleStateListener();
+
+ assertStackMode();
+
+ assertThat(mBubbleData.getBubbles()).hasSize(1);
+ assertBubbleIsInflatedForStack(mBubbleData.getBubbles().get(0));
+ assertBubbleIsInflatedForStack(mBubbleData.getOverflow());
+ }
+
+ @Test
+ public void switchBetweenBarAndStack_noBubbles_shouldBeIgnored() {
+ mBubbleProperties.mIsBubbleBarEnabled = false;
+ mPositioner.setIsLargeScreen(true);
+ assertFalse(mBubbleController.hasBubbles());
+
+ assertNoBubbleContainerViews();
+
+ FakeBubbleStateListener bubbleStateListener = new FakeBubbleStateListener();
+ mBubbleController.registerBubbleStateListener(bubbleStateListener);
+
+ assertNoBubbleContainerViews();
+
+ mBubbleController.unregisterBubbleStateListener();
+
+ assertNoBubbleContainerViews();
+ }
+
+ @Test
+ public void bubbleBarBubbleExpandedAndCollapsed() {
+ mBubbleProperties.mIsBubbleBarEnabled = true;
+ mPositioner.setIsLargeScreen(true);
+ mEntryListener.onEntryAdded(mRow);
+ mBubbleController.updateBubble(mBubbleEntry);
+
+ FakeBubbleStateListener bubbleStateListener = new FakeBubbleStateListener();
+ mBubbleController.registerBubbleStateListener(bubbleStateListener);
+ mBubbleController.expandStackAndSelectBubbleFromLauncher(mBubbleEntry.getKey(), true);
+
+ assertThat(mBubbleController.getLayerView().isExpanded()).isTrue();
+
+ mBubbleController.collapseStack();
+
+ assertThat(mBubbleController.getLayerView().isExpanded()).isFalse();
+ }
+
+ /** Creates a bubble using the userId and package. */
private Bubble createBubble(int userId, String pkg) {
final UserHandle userHandle = new UserHandle(userId);
NotificationEntry workEntry = new NotificationEntryBuilder()
@@ -2000,6 +2111,44 @@
assertFalse(mBubbleController.getImplCachedState().isStackExpanded());
}
+ /** Asserts that both the bubble stack and bar views don't exist. */
+ private void assertNoBubbleContainerViews() {
+ assertThat(mBubbleController.getStackView()).isNull();
+ assertThat(mBubbleController.getLayerView()).isNull();
+ }
+
+ /** Asserts that the stack is created and the bar is null. */
+ private void assertStackMode() {
+ assertThat(mBubbleController.getStackView()).isNotNull();
+ assertThat(mBubbleController.getLayerView()).isNull();
+ }
+
+ /** Asserts that the given bubble has the stack expanded view inflated. */
+ private void assertBubbleIsInflatedForStack(BubbleViewProvider b) {
+ assertThat(b.getIconView()).isNotNull();
+ assertThat(b.getExpandedView()).isNotNull();
+ assertThat(b.getBubbleBarExpandedView()).isNull();
+ }
+
+ /** Asserts that the bar is created and the stack is null. */
+ private void assertBarMode() {
+ assertThat(mBubbleController.getStackView()).isNull();
+ assertThat(mBubbleController.getLayerView()).isNotNull();
+ }
+
+ /** Asserts that the given bubble has the bar expanded view inflated. */
+ private void assertBubbleIsInflatedForBar(BubbleViewProvider b) {
+ // the icon view should be inflated for the overflow but not for other bubbles when showing
+ // in the bar
+ if (b instanceof Bubble) {
+ assertThat(b.getIconView()).isNull();
+ } else if (b instanceof BubbleOverflow) {
+ assertThat(b.getIconView()).isNotNull();
+ }
+ assertThat(b.getExpandedView()).isNull();
+ assertThat(b.getBubbleBarExpandedView()).isNotNull();
+ }
+
/**
* Asserts that a bubble notification is suppressed from the shade and also validates the cached
* state is updated.
@@ -2029,4 +2178,19 @@
assertThat(mSysUiStateBubblesExpanded).isEqualTo(stackExpanded);
assertThat(mSysUiStateBubblesManageMenuExpanded).isEqualTo(manageMenuExpanded);
}
+
+ private static class FakeBubbleStateListener implements Bubbles.BubbleStateListener {
+ @Override
+ public void onBubbleStateChange(BubbleBarUpdate update) {
+ }
+ }
+
+ private static class FakeBubbleProperties implements BubbleProperties {
+ boolean mIsBubbleBarEnabled = false;
+
+ @Override
+ public boolean isBubbleBarEnabled() {
+ return mIsBubbleBarEnabled;
+ }
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java
index 14c3f3c..5855347 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java
@@ -31,6 +31,7 @@
import com.android.wm.shell.bubbles.BubbleDataRepository;
import com.android.wm.shell.bubbles.BubbleLogger;
import com.android.wm.shell.bubbles.BubblePositioner;
+import com.android.wm.shell.bubbles.properties.BubbleProperties;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.FloatingContentCoordinator;
import com.android.wm.shell.common.ShellExecutor;
@@ -74,13 +75,14 @@
Handler shellMainHandler,
TaskViewTransitions taskViewTransitions,
SyncTransactionQueue syncQueue,
- IWindowManager wmService) {
+ IWindowManager wmService,
+ BubbleProperties bubbleProperties) {
super(context, shellInit, shellCommandHandler, shellController, data, Runnable::run,
floatingContentCoordinator, dataRepository, statusBarService, windowManager,
windowManagerShellWrapper, userManager, launcherApps, bubbleLogger,
taskStackListener, shellTaskOrganizer, positioner, displayController,
oneHandedOptional, dragAndDropController, shellMainExecutor, shellMainHandler,
- new SyncExecutor(), taskViewTransitions, syncQueue, wmService);
+ new SyncExecutor(), taskViewTransitions, syncQueue, wmService, bubbleProperties);
setInflateSynchronously(true);
onInit();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubblePositioner.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubblePositioner.java
index 6edc373..047dc65 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubblePositioner.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubblePositioner.java
@@ -27,6 +27,7 @@
public class TestableBubblePositioner extends BubblePositioner {
private int mMaxBubbles;
+ private boolean mIsLargeScreen = false;
public TestableBubblePositioner(Context context,
WindowManager windowManager) {
@@ -46,4 +47,13 @@
public int getMaxBubbles() {
return mMaxBubbles;
}
+
+ public void setIsLargeScreen(boolean isLargeScreen) {
+ mIsLargeScreen = isLargeScreen;
+ }
+
+ @Override
+ public boolean isLargeScreen() {
+ return mIsLargeScreen;
+ }
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiBaseFragmentTest.java b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiBaseFragmentTest.java
index 9179efc..e470406 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 @@
@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 8bbd58d..de177168 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 @@
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
similarity index 93%
rename from packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeBouncerMessageRepository.kt
rename to packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/data/repository/FakeBouncerMessageRepository.kt
index b03b4ba..d9b926d 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
similarity index 81%
rename from packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardBouncerRepository.kt
rename to packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/data/repository/FakeKeyguardBouncerRepository.kt
index 8a6d2aa..10529e6 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.bouncer.data.repository
-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
+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 @@
_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/utils/src/com/android/systemui/broadcast/FakeBroadcastDispatcher.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/broadcast/FakeBroadcastDispatcher.kt
index af940e4..21a5eb7 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/broadcast/FakeBroadcastDispatcher.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/broadcast/FakeBroadcastDispatcher.kt
@@ -18,6 +18,7 @@
import android.content.BroadcastReceiver
import android.content.Context
+import android.content.Intent
import android.content.IntentFilter
import android.os.Handler
import android.os.Looper
@@ -31,6 +32,14 @@
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.Executor
+/**
+ * A fake instance of [BroadcastDispatcher] for tests.
+ *
+ * Important: The *real* broadcast dispatcher will only send intents to receivers if the intent
+ * matches the [IntentFilter] that the [BroadcastReceiver] was registered with. This fake class
+ * exposes [sendIntentToMatchingReceiversOnly] to get the same matching behavior as the real
+ * broadcast dispatcher.
+ */
class FakeBroadcastDispatcher(
context: SysuiTestableContext,
mainExecutor: Executor,
@@ -52,7 +61,7 @@
PendingRemovalStore(logger)
) {
- val registeredReceivers: MutableSet<BroadcastReceiver> = ConcurrentHashMap.newKeySet()
+ private val receivers: MutableSet<InternalReceiver> = ConcurrentHashMap.newKeySet()
override fun registerReceiverWithHandler(
receiver: BroadcastReceiver,
@@ -62,7 +71,7 @@
@Context.RegisterReceiverFlags flags: Int,
permission: String?
) {
- registeredReceivers.add(receiver)
+ receivers.add(InternalReceiver(receiver, filter))
}
override fun registerReceiver(
@@ -73,24 +82,52 @@
@Context.RegisterReceiverFlags flags: Int,
permission: String?
) {
- registeredReceivers.add(receiver)
+ receivers.add(InternalReceiver(receiver, filter))
}
override fun unregisterReceiver(receiver: BroadcastReceiver) {
- registeredReceivers.remove(receiver)
+ receivers.removeIf { it.receiver == receiver }
}
override fun unregisterReceiverForUser(receiver: BroadcastReceiver, user: UserHandle) {
- registeredReceivers.remove(receiver)
+ receivers.removeIf { it.receiver == receiver }
}
- fun cleanUpReceivers(testName: String) {
- registeredReceivers.forEach {
- Log.i(testName, "Receiver not unregistered from dispatcher: $it")
- if (shouldFailOnLeakedReceiver) {
- throw IllegalStateException("Receiver not unregistered from dispatcher: $it")
+ /**
+ * Sends the given [intent] to *only* the receivers that were registered with an [IntentFilter]
+ * that matches the intent.
+ */
+ fun sendIntentToMatchingReceiversOnly(context: Context, intent: Intent) {
+ receivers.forEach {
+ if (
+ it.filter.match(
+ context.contentResolver,
+ intent,
+ /* resolve= */ false,
+ /* logTag= */ "FakeBroadcastDispatcher",
+ ) > 0
+ ) {
+ it.receiver.onReceive(context, intent)
}
}
- registeredReceivers.clear()
}
+
+ val numReceiversRegistered: Int
+ get() = receivers.size
+
+ fun cleanUpReceivers(testName: String) {
+ receivers.forEach {
+ val receiver = it.receiver
+ Log.i(testName, "Receiver not unregistered from dispatcher: $receiver")
+ if (shouldFailOnLeakedReceiver) {
+ throw IllegalStateException("Receiver not unregistered from dispatcher: $receiver")
+ }
+ }
+ receivers.clear()
+ }
+
+ private data class InternalReceiver(
+ val receiver: BroadcastReceiver,
+ val filter: IntentFilter,
+ )
}
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
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/common/ui/data/repository/FakeConfigurationRepository.kt
rename to 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 b52a768..f6cbb07 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 @@
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 0000000..f13c98d
--- /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 15465f4..3334f3e 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 @@
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 be3d54a..9c4fd94 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.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 @@
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 308e2cf..3406102 100644
--- a/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java
+++ b/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java
@@ -359,11 +359,7 @@
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 @@
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 @@
}
@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 @@
// 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 @@
// And log the success
if ((which & FLAG_SYSTEM) > 0) {
mEventLogger.onSystemImageWallpaperRestored();
- } else {
+ }
+ if ((which & FLAG_LOCK) > 0) {
mEventLogger.onLockImageWallpaperRestored();
}
}
@@ -460,7 +486,8 @@
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 @@
// 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 @@
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 9b07ad4..dc1126e 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 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
@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 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 @@
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 @@
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 @@
@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 8e1aa38..41c3dcb 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 @@
@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 @@
}
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 995e557..d5aee92 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 @@
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 61fc32d..ca1ab9b 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.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.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.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.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 @@
* 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 @@
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 @@
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 @@
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 @@
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 @@
}
}
- 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 @@
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 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 @@
@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 @@
}
}
- 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 @@
}
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 @@
}
}
}
+
+ @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 0000000..a0fd28b
--- /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 0000000..4ebac07
--- /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 0000000..dd5545d
--- /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 27215b2..cbacee6 100644
--- a/services/core/java/com/android/server/DynamicSystemService.java
+++ b/services/core/java/com/android/server/DynamicSystemService.java
@@ -44,7 +44,7 @@
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 @@
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 @@
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 9917892..2812233 100644
--- a/services/core/java/com/android/server/WallpaperUpdateReceiver.java
+++ b/services/core/java/com/android/server/WallpaperUpdateReceiver.java
@@ -88,7 +88,7 @@
} 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/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
index 8c31209..ae24f1e 100644
--- a/services/core/java/com/android/server/am/ActivityManagerConstants.java
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -1347,7 +1347,7 @@
updateForegroundServiceStartsLoggingEnabled();
}
- private void loadDeviceConfigConstants() {
+ void loadDeviceConfigConstants() {
mOnDeviceConfigChangedListener.onPropertiesChanged(
DeviceConfig.getProperties(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER));
mOnDeviceConfigChangedForComponentAliasListener.onPropertiesChanged(
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 476e1b4..7ba720e 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -4579,14 +4579,8 @@
EventLogTags.writeAmProcBound(app.userId, pid, app.processName);
synchronized (mProcLock) {
- app.mState.setCurAdj(ProcessList.INVALID_ADJ);
- app.mState.setSetAdj(ProcessList.INVALID_ADJ);
- app.mState.setVerifiedAdj(ProcessList.INVALID_ADJ);
- mOomAdjuster.setAttachingSchedGroupLSP(app);
- app.mState.setForcingToImportant(null);
+ mOomAdjuster.setAttachingProcessStatesLSP(app);
clearProcessForegroundLocked(app);
- app.mState.setHasShownUi(false);
- app.mState.setCached(false);
app.setDebugging(false);
app.setKilledByAm(false);
app.setKilled(false);
@@ -4753,8 +4747,14 @@
app.makeActive(thread, mProcessStats);
checkTime(startTime, "attachApplicationLocked: immediately after bindApplication");
}
+ app.setPendingFinishAttach(true);
+
updateLruProcessLocked(app, false, null);
checkTime(startTime, "attachApplicationLocked: after updateLruProcessLocked");
+
+ updateOomAdjLocked(app, OOM_ADJ_REASON_PROCESS_BEGIN);
+ checkTime(startTime, "attachApplicationLocked: after updateOomAdjLocked");
+
final long now = SystemClock.uptimeMillis();
synchronized (mAppProfiler.mProfilerLock) {
app.mProfile.setLastRequestedGc(now);
@@ -4770,8 +4770,6 @@
if (!mConstants.mEnableWaitForFinishAttachApplication) {
finishAttachApplicationInner(startSeq, callingUid, pid);
- } else {
- app.setPendingFinishAttach(true);
}
} catch (Exception e) {
// We need kill the process group here. (b/148588589)
@@ -19603,7 +19601,7 @@
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/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 569f55f..6801d27 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -3699,14 +3699,11 @@
if (foregroundActivities) {
try {
int prcState = mIam.getUidProcessState(uid, "android");
- ProcessRecord topApp = mInternal.getTopApp();
- if (topApp == null) {
- mPw.println("No top app found");
+
+ if (prcState == ProcessStateEnum.TOP) {
+ mPw.println("New foreground process: " + pid);
} else {
- int topPid = topApp.getPid();
- if (prcState == ProcessStateEnum.TOP && topPid == pid) {
- mPw.println("New foreground process: " + pid);
- }
+ mPw.println("No top app found");
}
mPw.flush();
} catch (RemoteException e) {
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index f21ad22..e51fc0a 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -1293,12 +1293,19 @@
for (int i = numLru - 1; i >= 0; i--) {
ProcessRecord app = lruList.get(i);
final ProcessStateRecord state = app.mState;
- if (!app.isKilledByAm() && app.getThread() != null && !app.isPendingFinishAttach()) {
+ if (!app.isKilledByAm() && app.getThread() != null) {
// We don't need to apply the update for the process which didn't get computed
if (state.getCompletedAdjSeq() == mAdjSeq) {
applyOomAdjLSP(app, true, now, nowElapsed, oomAdjReason);
}
+ if (app.isPendingFinishAttach()) {
+ // Avoid trimming processes that are still initializing. If they aren't
+ // hosting any components yet because they may be unfairly killed.
+ // We however apply the oom scores set at #setAttachingProcessStatesLSP.
+ continue;
+ }
+
final ProcessServiceRecord psr = app.mServices;
// Count the number of process types.
switch (state.getCurProcState()) {
@@ -2806,6 +2813,18 @@
capability &= ~PROCESS_CAPABILITY_BFSL;
}
+
+ if (app.isPendingFinishAttach()) {
+ // If the app is still starting up. We reset the computations to the
+ // hardcoded values in setAttachingProcessStatesLSP. This ensures that the app keeps
+ // hard-coded default 'startup' oom scores while starting up. When it finishes startup,
+ // we'll recompute oom scores based on it's actual hosted compoenents.
+ setAttachingProcessStatesLSP(app);
+ state.setAdjSeq(mAdjSeq);
+ state.setCompletedAdjSeq(state.getAdjSeq());
+ return false;
+ }
+
// Do final modification to adj. Everything we do between here and applying
// the final setAdj must be done in this function, because we will also use
// it when computing the final cached adj later. Note that we don't need to
@@ -3243,8 +3262,11 @@
}
@GuardedBy({"mService", "mProcLock"})
- void setAttachingSchedGroupLSP(ProcessRecord app) {
+ void setAttachingProcessStatesLSP(ProcessRecord app) {
int initialSchedGroup = SCHED_GROUP_DEFAULT;
+ int initialProcState = PROCESS_STATE_CACHED_EMPTY;
+ int initialCapability = PROCESS_CAPABILITY_NONE;
+ boolean initialCached = true;
final ProcessStateRecord state = app.mState;
// If the process has been marked as foreground, it is starting as the top app (with
// Zygote#START_AS_TOP_APP_ARG), so boost the thread priority of its default UI thread.
@@ -3260,13 +3282,24 @@
setThreadPriority(app.getPid(), THREAD_PRIORITY_TOP_APP_BOOST);
}
initialSchedGroup = SCHED_GROUP_TOP_APP;
+ initialProcState = PROCESS_STATE_TOP;
+ initialCapability = PROCESS_CAPABILITY_ALL;
+ initialCached = false;
} catch (Exception e) {
Slog.w(TAG, "Failed to pre-set top priority to " + app + " " + e);
}
}
- state.setSetSchedGroup(initialSchedGroup);
state.setCurrentSchedulingGroup(initialSchedGroup);
+ state.setCurProcState(initialProcState);
+ state.setCurRawProcState(initialProcState);
+ state.setCurCapability(initialCapability);
+ state.setCached(initialCached);
+
+ state.setCurAdj(ProcessList.FOREGROUND_APP_ADJ);
+ state.setCurRawAdj(ProcessList.FOREGROUND_APP_ADJ);
+ state.setForcingToImportant(null);
+ state.setHasShownUi(false);
}
// ONLY used for unit testing in OomAdjusterTests.java
diff --git a/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCallbackConverter.java b/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCallbackConverter.java
index 2e1a363..1a682a9 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 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 50d375c..35fc43a 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.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 @@
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 a501647..33ed63c 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 @@
@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 @@
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 ece35c5..ea6bb62 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
@@ -913,7 +913,6 @@
}
provider.onPointerDown(requestId, sensorId, pc);
}
-
@android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL)
@Override
@@ -929,17 +928,19 @@
@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) {
@@ -1182,4 +1183,15 @@
}
}
}
+
+ void simulateVhalFingerDown() {
+ if (Utils.isVirtualEnabled(getContext())) {
+ Slog.i(TAG, "Simulate virtual HAL finger down event");
+ final Pair<Integer, ServiceProvider> provider = mRegistry.getSingleProvider();
+ if (provider != null) {
+ provider.second.simulateVhalFingerDown(UserHandle.getCallingUserId(),
+ provider.first);
+ }
+ }
+ }
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintShellCommand.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintShellCommand.java
index 636413f..dc6a63f 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintShellCommand.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintShellCommand.java
@@ -45,6 +45,8 @@
return doHelp();
case "sync":
return doSync();
+ case "fingerdown":
+ return doSimulateVhalFingerDown();
default:
getOutPrintWriter().println("Unrecognized command: " + cmd);
}
@@ -62,6 +64,8 @@
pw.println(" Print this help text.");
pw.println(" sync");
pw.println(" Sync enrollments now (virtualized sensors only).");
+ pw.println(" fingerdown");
+ pw.println(" Simulate finger down event (virtualized sensors only).");
}
private int doHelp() {
@@ -73,4 +77,9 @@
mService.syncEnrollmentsNow();
return 0;
}
+
+ private int doSimulateVhalFingerDown() {
+ mService.simulateVhalFingerDown();
+ return 0;
+ }
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java
index 004af2c..d70ca8c 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 @@
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);
@@ -157,4 +157,11 @@
* @param sensorId sensor ID of the associated operation
*/
default void scheduleWatchdog(int sensorId) {}
+
+ /**
+ * Simulate fingerprint down touch event for virtual HAL
+ * @param userId user ID
+ * @param sensorId sensor ID
+ */
+ default void simulateVhalFingerDown(int userId, int sensorId) {};
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/Udfps.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/Udfps.java
index da7163a..dce0175 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 @@
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 135eccf..ec1eeb1 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 @@
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 2bfc239..3fc36b6 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.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 @@
}
@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 c2ca78e..d35469c 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 @@
}
@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 58ece89..f8d2566 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 @@
@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 @@
mTaskStackListener = new BiometricTaskStackListener();
mBiometricContext = biometricContext;
mAuthSessionCoordinator = mBiometricContext.getAuthSessionCoordinator();
+ mDaemon = daemon;
final List<SensorLocationInternal> workaroundLocations = getWorkaroundSensorProps(context);
@@ -669,14 +681,15 @@
}
@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);
});
}
@@ -844,4 +857,18 @@
}
biometricScheduler.startWatchdog();
}
+
+ @Override
+ public void simulateVhalFingerDown(int userId, int sensorId) {
+ Slog.d(getTag(), "Simulate virtual HAL finger down event");
+ final AidlSession session = mFingerprintSensors.get(sensorId).getSessionForUser(userId);
+ final PointerContext pc = new PointerContext();
+ try {
+ session.getSession().onPointerDownWithContext(pc);
+ session.getSession().onUiReady();
+ session.getSession().onPointerUpWithContext(pc);
+ } catch (RemoteException e) {
+ Slog.e(getTag(), "failed hal operation ", e);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/BiometricTestSessionImpl.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/BiometricTestSessionImpl.java
index 86a9f79..c20a9eb 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 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 9e6f4e4..1cbbf89 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 @@
}
@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 d22aef8..2a62338 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.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 @@
}
@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 362c820..ed0a201 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.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 @@
}
@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 78039ef..c2b7944 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 @@
}
@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 70d7bde..984ad1d 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
}
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 @@
}
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 @@
@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 75709fbb..d647757 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 @@
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 @@
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 @@
darkeningLightDebounceConfig, resetAmbientLuxAfterWarmUpConfig,
ambientBrightnessThresholds, screenBrightnessThresholds,
ambientBrightnessThresholdsIdle, screenBrightnessThresholdsIdle, context,
- hbmController, brightnessThrottler, idleModeBrightnessMapper,
+ brightnessModeController, brightnessThrottler, idleModeBrightnessMapper,
ambientLightHorizonShort, ambientLightHorizonLong, userLux, userBrightness
);
}
@@ -283,7 +284,8 @@
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 @@
mPendingForegroundAppPackageName = null;
mForegroundAppCategory = ApplicationInfo.CATEGORY_UNDEFINED;
mPendingForegroundAppCategory = ApplicationInfo.CATEGORY_UNDEFINED;
- mHbmController = hbmController;
+ mBrightnessRangeController = brightnessModeController;
mBrightnessThrottler = brightnessThrottler;
mInteractiveModeBrightnessMapper = interactiveModeBrightnessMapper;
mIdleModeBrightnessMapper = idleModeBrightnessMapper;
@@ -607,10 +609,11 @@
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 @@
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 @@
// 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 0000000..47cde15
--- /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 de42370..651828b 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 @@
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 @@
@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 @@
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 @@
// 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 7a797dd..d9cb299 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.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.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 @@
* <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>
@@ -495,7 +515,9 @@
private final SensorData mScreenOffBrightnessSensor = new SensorData();
// The details of the proximity sensor associated with this display.
- private final SensorData mProximitySensor = new SensorData();
+ // Is null when no sensor should be used for that display
+ @Nullable
+ private SensorData mProximitySensor = new SensorData();
private final List<RefreshRateLimitation> mRefreshRateLimitations =
new ArrayList<>(2 /*initialCapacity*/);
@@ -693,6 +715,9 @@
private final Map<String, SparseArray<SurfaceControl.RefreshRateRange>>
mRefreshRateThrottlingMap = new HashMap<>();
+ private final Map<BrightnessLimitMapType, Map<Float, Float>>
+ mLuxThrottlingData = new HashMap<>();
+
@Nullable
private HostUsiVersion mHostUsiVersion;
@@ -1314,7 +1339,8 @@
return mScreenOffBrightnessSensor;
}
- SensorData getProximitySensor() {
+ @Nullable
+ public SensorData getProximitySensor() {
return mProximitySensor;
}
@@ -1344,6 +1370,11 @@
return hbmData;
}
+ @NonNull
+ public Map<BrightnessLimitMapType, Map<Float, Float>> getLuxThrottlingData() {
+ return mLuxThrottlingData;
+ }
+
public List<RefreshRateLimitation> getRefreshRateLimitations() {
return mRefreshRateLimitations;
}
@@ -1530,6 +1561,7 @@
+ ", mBrightnessDefault=" + mBrightnessDefault
+ ", mQuirks=" + mQuirks
+ ", isHbmEnabled=" + mIsHighBrightnessModeEnabled
+ + ", mLuxThrottlingData=" + mLuxThrottlingData
+ ", mHbmData=" + mHbmData
+ ", mSdrToHdrRatioSpline=" + mSdrToHdrRatioSpline
+ ", mThermalBrightnessThrottlingDataMapByThrottlingId="
@@ -1676,6 +1708,7 @@
loadBrightnessMap(config);
loadThermalThrottlingConfig(config);
loadHighBrightnessModeData(config);
+ loadLuxThrottling(config);
loadQuirks(config);
loadBrightnessRamps(config);
loadAmbientLightSensorFromDdc(config);
@@ -2428,6 +2461,54 @@
}
}
+ 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)
@@ -2485,46 +2566,51 @@
private void loadAmbientLightSensorFromDdc(DisplayConfiguration config) {
final SensorDetails sensorDetails = config.getLightSensor();
if (sensorDetails != null) {
- mAmbientLightSensor.type = sensorDetails.getType();
- mAmbientLightSensor.name = sensorDetails.getName();
- final RefreshRateRange rr = sensorDetails.getRefreshRate();
- if (rr != null) {
- mAmbientLightSensor.minRefreshRate = rr.getMinimum().floatValue();
- mAmbientLightSensor.maxRefreshRate = rr.getMaximum().floatValue();
- }
+ loadSensorData(sensorDetails, mAmbientLightSensor);
} else {
loadAmbientLightSensorFromConfigXml();
}
}
private void setProxSensorUnspecified() {
- mProximitySensor.name = null;
- mProximitySensor.type = null;
+ mProximitySensor = new SensorData();
}
private void loadScreenOffBrightnessSensorFromDdc(DisplayConfiguration config) {
final SensorDetails sensorDetails = config.getScreenOffBrightnessSensor();
if (sensorDetails != null) {
- mScreenOffBrightnessSensor.type = sensorDetails.getType();
- mScreenOffBrightnessSensor.name = sensorDetails.getName();
+ loadSensorData(sensorDetails, mScreenOffBrightnessSensor);
}
}
private void loadProxSensorFromDdc(DisplayConfiguration config) {
SensorDetails sensorDetails = config.getProxSensor();
if (sensorDetails != null) {
- mProximitySensor.name = sensorDetails.getName();
- mProximitySensor.type = sensorDetails.getType();
- final RefreshRateRange rr = sensorDetails.getRefreshRate();
- if (rr != null) {
- mProximitySensor.minRefreshRate = rr.getMinimum().floatValue();
- mProximitySensor.maxRefreshRate = rr.getMaximum().floatValue();
+ String name = sensorDetails.getName();
+ String type = sensorDetails.getType();
+ if ("".equals(name) && "".equals(type)) {
+ // <proxSensor> with empty values to the config means no sensor should be used
+ mProximitySensor = null;
+ } else {
+ mProximitySensor = new SensorData();
+ loadSensorData(sensorDetails, mProximitySensor);
}
} else {
setProxSensorUnspecified();
}
}
+ private void loadSensorData(@NonNull SensorDetails sensorDetails,
+ @NonNull SensorData sensorData) {
+ sensorData.name = sensorDetails.getName();
+ sensorData.type = sensorDetails.getType();
+ final RefreshRateRange rr = sensorDetails.getRefreshRate();
+ if (rr != null) {
+ sensorData.minRefreshRate = rr.getMinimum().floatValue();
+ sensorData.maxRefreshRate = rr.getMaximum().floatValue();
+ }
+ }
+
private void loadBrightnessChangeThresholdsFromXml() {
loadBrightnessChangeThresholds(/* config= */ null);
}
@@ -3155,4 +3241,19 @@
}
}
}
+
+ 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 da8eb23..858800a 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 @@
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 @@
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.
@@ -4253,6 +4260,13 @@
}
@VisibleForTesting
+ void overrideSensorManager(SensorManager sensorManager) {
+ synchronized (mSyncRoot) {
+ mSensorManager = sensorManager;
+ }
+ }
+
+ @VisibleForTesting
final class LocalService extends DisplayManagerInternal {
@Override
@@ -4505,7 +4519,7 @@
}
final DisplayDeviceConfig config = device.getDisplayDeviceConfig();
SensorData sensorData = config.getProximitySensor();
- if (sensorData.matches(sensorName, sensorType)) {
+ if (sensorData != null && sensorData.matches(sensorName, sensorType)) {
return new RefreshRateRange(sensorData.minRefreshRate,
sensorData.maxRefreshRate);
}
@@ -4621,6 +4635,22 @@
}
@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 9d31572..ffecf2b 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 @@
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 @@
// 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 @@
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();
+ }
+ };
- mHbmController = createHbmControllerLocked();
+ HighBrightnessModeController hbmController = createHbmControllerLocked(modeChangeCallback);
+
+ mBrightnessRangeController = new BrightnessRangeController(hbmController,
+ modeChangeCallback);
mBrightnessThrottler = createBrightnessThrottlerLocked();
@@ -801,8 +815,9 @@
}
@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 @@
mBrightnessToFollow = leadDisplayBrightness;
}
}
+ mBrightnessToFollowSlowChange = slowChange;
sendUpdatePowerState();
}
@@ -831,7 +847,7 @@
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 @@
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 @@
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 @@
darkeningLightDebounce, autoBrightnessResetAmbientLuxAfterWarmUp,
ambientBrightnessThresholds, screenBrightnessThresholds,
ambientBrightnessThresholdsIdle, screenBrightnessThresholdsIdle, mContext,
- mHbmController, mBrightnessThrottler, mIdleModeBrightnessMapper,
+ mBrightnessRangeController, mBrightnessThrottler, mIdleModeBrightnessMapper,
mDisplayDeviceConfig.getAmbientHorizonShort(),
mDisplayDeviceConfig.getAmbientHorizonLong(), userLux, userBrightness);
@@ -1364,7 +1370,7 @@
/** 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 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 @@
if (Float.isNaN(brightnessState) && isValidBrightnessValue(mBrightnessToFollow)) {
brightnessState = mBrightnessToFollow;
+ slowChange = mBrightnessToFollowSlowChange;
mBrightnessReasonTemp.setReason(BrightnessReason.REASON_FOLLOWER);
}
@@ -1582,12 +1590,12 @@
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 @@
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 @@
float rawBrightnessState = brightnessState;
// Apply auto-brightness.
- boolean slowChange = false;
if (Float.isNaN(brightnessState)) {
float newAutoBrightnessAdjustment = autoBrightnessAdjustment;
if (autoBrightnessEnabled) {
@@ -1739,6 +1748,14 @@
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 @@
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 @@
// 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 @@
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 @@
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 @@
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 @@
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 @@
}
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 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 @@
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() {
@@ -2295,29 +2303,23 @@
}
private void loadAmbientLightSensor() {
- DisplayDeviceConfig.SensorData lightSensor = mDisplayDeviceConfig.getAmbientLightSensor();
final int fallbackType = mDisplayId == Display.DEFAULT_DISPLAY
? Sensor.TYPE_LIGHT : SensorUtils.NO_FALLBACK;
- mLightSensor = SensorUtils.findSensor(mSensorManager, lightSensor.type, lightSensor.name,
- fallbackType);
+ mLightSensor = SensorUtils.findSensor(mSensorManager,
+ mDisplayDeviceConfig.getAmbientLightSensor(), fallbackType);
}
private void loadScreenOffBrightnessSensor() {
- DisplayDeviceConfig.SensorData screenOffBrightnessSensor =
- mDisplayDeviceConfig.getScreenOffBrightnessSensor();
mScreenOffBrightnessSensor = SensorUtils.findSensor(mSensorManager,
- screenOffBrightnessSensor.type, screenOffBrightnessSensor.name,
- SensorUtils.NO_FALLBACK);
+ mDisplayDeviceConfig.getScreenOffBrightnessSensor(), SensorUtils.NO_FALLBACK);
}
private void loadProximitySensor() {
if (DEBUG_PRETEND_PROXIMITY_SENSOR_ABSENT || mDisplayId != Display.DEFAULT_DISPLAY) {
return;
}
- final DisplayDeviceConfig.SensorData proxSensor =
- mDisplayDeviceConfig.getProximitySensor();
- mProximitySensor = SensorUtils.findSensor(mSensorManager, proxSensor.type, proxSensor.name,
- Sensor.TYPE_PROXIMITY);
+ mProximitySensor = SensorUtils.findSensor(mSensorManager,
+ mDisplayDeviceConfig.getProximitySensor(), Sensor.TYPE_PROXIMITY);
if (mProximitySensor != null) {
mProximityThreshold = Math.min(mProximitySensor.getMaximumRange(),
TYPICAL_PROXIMITY_THRESHOLD);
@@ -2328,8 +2330,8 @@
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 +2669,10 @@
}
@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 +2952,7 @@
+ 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 +3007,8 @@
mScreenOffBrightnessSensorController.dump(pw);
}
- if (mHbmController != null) {
- mHbmController.dump(pw);
+ if (mBrightnessRangeController != null) {
+ mBrightnessRangeController.dump(pw);
}
if (mBrightnessThrottler != null) {
@@ -3471,7 +3475,8 @@
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 +3485,9 @@
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 +3532,17 @@
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 41e4671d..7043af8 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 @@
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 @@
@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 @@
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 @@
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 @@
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 @@
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 @@
/** 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 @@
// 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 @@
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 @@
&& (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 @@
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 @@
// 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 @@
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 @@
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 @@
// 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 @@
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 @@
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 @@
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 @@
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 @@
}
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 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() {
@@ -1941,27 +1950,23 @@
}
private void loadAmbientLightSensor() {
- DisplayDeviceConfig.SensorData lightSensor = mDisplayDeviceConfig.getAmbientLightSensor();
final int fallbackType = mDisplayId == Display.DEFAULT_DISPLAY
? Sensor.TYPE_LIGHT : SensorUtils.NO_FALLBACK;
- mLightSensor = SensorUtils.findSensor(mSensorManager, lightSensor.type, lightSensor.name,
- fallbackType);
+ mLightSensor = SensorUtils.findSensor(mSensorManager,
+ mDisplayDeviceConfig.getAmbientLightSensor(), fallbackType);
}
private void loadScreenOffBrightnessSensor() {
- DisplayDeviceConfig.SensorData screenOffBrightnessSensor =
- mDisplayDeviceConfig.getScreenOffBrightnessSensor();
mScreenOffBrightnessSensor = SensorUtils.findSensor(mSensorManager,
- screenOffBrightnessSensor.type, screenOffBrightnessSensor.name,
- SensorUtils.NO_FALLBACK);
+ mDisplayDeviceConfig.getScreenOffBrightnessSensor(), SensorUtils.NO_FALLBACK);
}
private float clampScreenBrightness(float value) {
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 +2184,8 @@
}
@Override
- public void setBrightness(float brightnessValue) {
- mDisplayBrightnessController.setBrightness(brightnessValue);
+ public void setBrightness(float brightnessValue, int userSerial) {
+ mDisplayBrightnessController.setBrightness(brightnessValue, userSerial);
}
@Override
@@ -2194,8 +2199,9 @@
}
@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 +2213,7 @@
mDisplayBrightnessController.setBrightnessToFollow(leadDisplayBrightness);
}
}
+ mBrightnessToFollowSlowChange = slowChange;
sendUpdatePowerState();
}
@@ -2266,7 +2273,7 @@
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 +2283,7 @@
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 +2353,7 @@
pw.println(" mReportedToPolicy="
+ reportedToPolicyToString(mReportedScreenStateToPolicy));
pw.println(" mIsRbcActive=" + mIsRbcActive);
+ pw.println(" mBrightnessToFollowSlowChange=" + mBrightnessToFollowSlowChange);
IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
mAutomaticBrightnessStrategy.dump(ipw);
@@ -2374,8 +2382,8 @@
dumpRbcEvents(pw);
- if (mHbmController != null) {
- mHbmController.dump(pw);
+ if (mBrightnessRangeController != null) {
+ mBrightnessRangeController.dump(pw);
}
if (mBrightnessThrottler != null) {
@@ -2840,7 +2848,8 @@
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 +2858,9 @@
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 +2905,17 @@
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 73edb97..e3108c9 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 @@
* 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 @@
* 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 @@
* @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/DisplayPowerProximityStateController.java b/services/core/java/com/android/server/display/DisplayPowerProximityStateController.java
index c074786..882c02f 100644
--- a/services/core/java/com/android/server/display/DisplayPowerProximityStateController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerProximityStateController.java
@@ -358,10 +358,8 @@
if (DEBUG_PRETEND_PROXIMITY_SENSOR_ABSENT || mDisplayId != Display.DEFAULT_DISPLAY) {
return;
}
- final DisplayDeviceConfig.SensorData proxSensor =
- mDisplayDeviceConfig.getProximitySensor();
- mProximitySensor = SensorUtils.findSensor(mSensorManager, proxSensor.type, proxSensor.name,
- Sensor.TYPE_PROXIMITY);
+ mProximitySensor = SensorUtils.findSensor(mSensorManager,
+ mDisplayDeviceConfig.getProximitySensor(), Sensor.TYPE_PROXIMITY);
if (mProximitySensor != null) {
mProximityThreshold = Math.min(mProximitySensor.getMaximumRange(),
TYPICAL_PROXIMITY_THRESHOLD);
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 0000000..dbabc24
--- /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 6d6ed72..2d7792d 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 @@
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 @@
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 @@
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 @@
return false;
}
final DisplayState state = getDisplayState(displayDeviceUniqueId, true);
- if (state.setBrightness(brightness)) {
+ if (state.setBrightness(brightness, userSerial)) {
setDirty();
return true;
}
@@ -611,6 +612,7 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 7574de8..2f52b70 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 @@
* 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 @@
* 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 0e885dc..bcd5259 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 @@
// 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 @@
* 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 @@
return mIsAutoBrightnessEnabled;
}
+ public boolean isAutoBrightnessDisabledDueToDisplayOff() {
+ return mAutoBrightnessDisabledDueToDisplayOff;
+ }
+
/**
* Updates the {@link BrightnessConfiguration} that is currently being used by the associated
* display.
@@ -287,8 +291,6 @@
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 @@
/**
* 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/display/utils/SensorUtils.java b/services/core/java/com/android/server/display/utils/SensorUtils.java
index 48bc46c..56321cd 100644
--- a/services/core/java/com/android/server/display/utils/SensorUtils.java
+++ b/services/core/java/com/android/server/display/utils/SensorUtils.java
@@ -16,10 +16,13 @@
package com.android.server.display.utils;
+import android.annotation.Nullable;
import android.hardware.Sensor;
import android.hardware.SensorManager;
import android.text.TextUtils;
+import com.android.server.display.DisplayDeviceConfig;
+
import java.util.List;
/**
@@ -29,17 +32,26 @@
public static final int NO_FALLBACK = 0;
/**
+ * Finds the specified sensor for SensorData from DisplayDeviceConfig.
+ */
+ @Nullable
+ public static Sensor findSensor(@Nullable SensorManager sensorManager,
+ @Nullable DisplayDeviceConfig.SensorData sensorData, int fallbackType) {
+ if (sensorData == null) {
+ return null;
+ } else {
+ return findSensor(sensorManager, sensorData.type, sensorData.name, fallbackType);
+ }
+ }
+ /**
* Finds the specified sensor by type and name using SensorManager.
*/
- public static Sensor findSensor(SensorManager sensorManager, String sensorType,
- String sensorName, int fallbackType) {
+ @Nullable
+ public static Sensor findSensor(@Nullable SensorManager sensorManager,
+ @Nullable String sensorType, @Nullable String sensorName, int fallbackType) {
if (sensorManager == null) {
return null;
}
-
- if ("".equals(sensorName) && "".equals(sensorType)) {
- return null;
- }
final boolean isNameSpecified = !TextUtils.isEmpty(sensorName);
final boolean isTypeSpecified = !TextUtils.isEmpty(sensorType);
if (isNameSpecified || isTypeSpecified) {
diff --git a/services/core/java/com/android/server/dreams/DreamManagerService.java b/services/core/java/com/android/server/dreams/DreamManagerService.java
index 41651fd..68cf59f 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 @@
@Override
public void onChange(boolean selfChange, Uri uri) {
- synchronized (mLock) {
- updateWhenToDreamSettings();
- }
+ updateWhenToDreamSettings();
}
}
@@ -257,15 +255,7 @@
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 @@
}
}
+ @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 @@
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 0000000..ce86849
--- /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 ff9ce6f..38a0d37 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 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 @@
@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 @@
private BluetoothAdapter.OnMetadataChangedListener mBluetoothMetadataListener;
@Nullable
- private UEventBatteryListener mUEventBatteryListener;
+ private BatteryController.UEventBatteryListener mUEventBatteryListener;
DeviceMonitor(int deviceId) {
mState = new State(deviceId);
@@ -630,7 +630,7 @@
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 @@
}
}
- // 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);
+ 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 addListener(UEventBatteryListener listener, String match) {
- listener.mObserver.startObserving(match);
- }
-
- 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 0000000..a646d1e
--- /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 4cb22db..6a177e0 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.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 @@
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 @@
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 @@
return mLooper;
}
+ UEventManager getUEventManager() {
+ return mUEventManager;
+ }
+
NativeInputManagerService getNativeService(InputManagerService service) {
return new NativeInputManagerService.NativeImpl(service, mLooper.getQueue());
}
@@ -423,7 +423,7 @@
}
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 @@
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 651063e..42591f4 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 @@
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 48c346a..c3205af 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.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 @@
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 @@
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 @@
@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 @@
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 @@
// 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 @@
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 @@
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 @@
}
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 @@
} 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 @@
@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 @@
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 @@
}
}
- 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 @@
}
}
+ 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 @@
@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 @@
}
}
- 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 @@
+ "}";
}
}
+
+ @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 6ec4022..3ac1594 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.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 @@
// 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 @@
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 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 @@
+ 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 @@
@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 @@
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 @@
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 @@
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 @@
}
@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);
}
}
- return layout;
+
+ 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;
+ }
+ }
}
@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 @@
+ "vendor and product Ids. " + inputDevice.getIdentifier()
+ " : " + layout.getDescriptor());
}
- return layout.getDescriptor();
+ return new KeyboardLayoutInfo(layout.getDescriptor(),
+ LAYOUT_SELECTION_CRITERIA_DEVICE);
}
}
@@ -999,7 +1024,7 @@
+ "HW information (Language tag and Layout type). "
+ inputDevice.getIdentifier() + " : " + layoutDesc);
}
- return layoutDesc;
+ return new KeyboardLayoutInfo(layoutDesc, LAYOUT_SELECTION_CRITERIA_DEVICE);
}
}
@@ -1021,7 +1046,10 @@
+ "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 @@
}
}
+ 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 @@
// 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 @@
}
@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 0000000..19fa7a8
--- /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 bce210d..31083fd 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 @@
}
}
+ 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 0000000..17d87e4
--- /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 ba9e280..b0b1d67 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 @@
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 57f8d14..4dbd820 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 @@
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 @@
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 @@
// 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 @@
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 @@
}
@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 @@
@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 @@
}
mImeWindowVis = vis;
mBackDisposition = backDisposition;
- updateSystemUiLocked(vis, backDisposition);
+ updateSystemUiLocked(mImeWindowVis, mBackDisposition);
}
final boolean dismissImeOnBackKeyPressed;
@@ -3167,37 +3166,46 @@
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 @@
} 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 @@
return;
}
mCurPerceptible = perceptible;
- updateSystemUiLocked();
+ updateSystemUiLocked(mImeWindowVis, mBackDisposition);
}
});
}
@@ -5089,8 +5102,11 @@
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 @@
// 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 @@
@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 c212e8e..c2ef83d 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 @@
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 @@
}
}
+ /**
+ * 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 5f78374..93c66a1 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.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 @@
private SensorPrivacyManagerInternal mSensorPrivacyManagerInternal;
+ private UserManager mUserManager = null;
+
private final Map<Integer, AtomicLong> mLastRestartTimestampMap = new HashMap<>();
/**
@@ -491,6 +494,14 @@
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 @@
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 2e62ef4..a96e4ad 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_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.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 @@
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 @@
if (phase == PHASE_ACTIVITY_MANAGER_READY) {
mLockSettingsService.migrateOldDataAfterSystemReady();
mLockSettingsService.loadEscrowData();
+ mLockSettingsService.deleteRepairModePersistentDataIfNeeded();
}
}
@@ -541,7 +543,7 @@
}
public boolean isGsiRunning() {
- return SystemProperties.getInt(GSI_RUNNING_PROP, 0) > 0;
+ return LockPatternUtils.isGsiRunning();
}
public FingerprintManager getFingerprintManager() {
@@ -949,6 +951,16 @@
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 @@
* {@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 @@
}
}
- 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 @@
* 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 @@
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 @@
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 2fa637e..1c5ecb7 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 @@
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 @@
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 @@
}
}
+ @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 @@
}
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 @@
}
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 @@
}
}
+ /**
+ * 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 @@
}
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 66f862a..7349088 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 @@
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 @@
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 @@
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 @@
}
}
+ /**
+ * 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 488745c..5b32273 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 @@
mRankingHandler,
mZenModeHelper,
mPermissionHelper,
+ mPermissionManager,
mNotificationChannelLogger,
mAppOps,
new SysUiStatsEvent.BuilderFactory(),
@@ -6772,6 +6773,8 @@
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 @@
+ " 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 @@
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 @@
* 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 @@
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 @@
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 @@
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 @@
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 c9a6c63..0292a99 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 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.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 @@
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 @@
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 @@
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 0cc4fc4..5ca882c 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.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 @@
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 @@
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 @@
}
/**
- * @param r NotificationRecord
* @return Whether the notification is a non-dismissible notification.
*/
static boolean isNonDismissible(@NonNull NotificationRecord r) {
@@ -567,4 +585,28 @@
}
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 feb75ef..9da0e98 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 @@
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 ffe33a8..e960f4b 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 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 @@
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 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 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 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 @@
}
/**
+ * 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 @@
AggregatedStats[] aggregatedStatsArray = getAggregatedStatsLocked(notification);
for (AggregatedStats stats : aggregatedStatsArray) {
stats.numUpdatedByApp++;
- stats.updateInterarrivalEstimate(SystemClock.elapsedRealtime());
stats.countApiUse(notification);
}
releaseAggregatedStatsLocked(aggregatedStatsArray);
@@ -257,12 +257,12 @@
}
}
- // 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 @@
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 @@
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 4399a3c..838a0fb 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 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.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.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 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 @@
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 @@
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 @@
mZenModeHelper = zenHelper;
mRankingHandler = rankingHandler;
mPermissionHelper = permHelper;
+ mPermissionManager = permManager;
mPm = pm;
mNotificationChannelLogger = notificationChannelLogger;
mAppOps = appOpsManager;
@@ -2027,6 +2036,43 @@
}
/**
+ * @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 @@
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 a2f93dc..eda96ac 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 @@
*
* {@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 @@
}
/** 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 3132c5d..a792b9c 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.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.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.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 @@
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 @@
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 @@
// 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 @@
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 @@
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 @@
@Override public void onChange(boolean selfChange) {
updateSettings();
- updateRotation(false);
}
}
@@ -1095,7 +1103,7 @@
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 @@
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 @@
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 @@
cancelPendingScreenshotChordAction();
}
});
+
if (mHasFeatureWatch) {
mKeyCombinationManager.addRule(
new TwoKeysCombinationRule(KEYCODE_POWER, KEYCODE_STEM_PRIMARY) {
@@ -2903,7 +2918,30 @@
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 @@
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 @@
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 @@
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 efd8b6d..6e9a22c 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 @@
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 @@
* @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 044d30b..719b2d2 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.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 @@
}
@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 @@
}
@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 @@
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 @@
return mDisabled1 == disabled1 && mDisabled2 == disabled2;
}
- private void setImeWindowState(final int vis, final int backDisposition,
+ private void setImeWindowState(@InputMethodService.ImeWindowVisibility final int vis,
+ @InputMethodService.BackDispositionMode final int backDisposition,
final boolean showImeSwitcher, final IBinder token) {
mImeWindowVis = vis;
mImeBackDisposition = backDisposition;
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperCropper.java b/services/core/java/com/android/server/wallpaper/WallpaperCropper.java
index 49b125c..b773ade 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperCropper.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperCropper.java
@@ -81,7 +81,7 @@
if (DEBUG) {
Slog.v(TAG, "Generating crop for new wallpaper(s): 0x"
+ Integer.toHexString(wallpaper.mWhich)
- + " to " + wallpaper.cropFile.getName()
+ + " to " + wallpaper.getCropFile().getName()
+ " crop=(" + cropHint.width() + 'x' + cropHint.height()
+ ") dim=(" + wpData.mWidth + 'x' + wpData.mHeight + ')');
}
@@ -89,7 +89,7 @@
// Analyse the source; needed in multiple cases
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
- BitmapFactory.decodeFile(wallpaper.wallpaperFile.getAbsolutePath(), options);
+ BitmapFactory.decodeFile(wallpaper.getWallpaperFile().getAbsolutePath(), options);
if (options.outWidth <= 0 || options.outHeight <= 0) {
Slog.w(TAG, "Invalid wallpaper data");
success = false;
@@ -154,11 +154,10 @@
// may be we can try to remove this optimized way in the future,
// that means, we will always go into the 'else' block.
- success = FileUtils.copyFile(wallpaper.wallpaperFile, wallpaper.cropFile);
+ success = FileUtils.copyFile(wallpaper.getWallpaperFile(), wallpaper.getCropFile());
if (!success) {
- wallpaper.cropFile.delete();
- // TODO: fall back to default wallpaper in this case
+ wallpaper.getCropFile().delete();
}
if (DEBUG) {
@@ -226,7 +225,7 @@
//Create a record file and will delete if ImageDecoder work well.
final String recordName =
- (wallpaper.wallpaperFile.getName().equals(WALLPAPER)
+ (wallpaper.getWallpaperFile().getName().equals(WALLPAPER)
? RECORD_FILE : RECORD_LOCK_FILE);
final File record = new File(getWallpaperDir(wallpaper.userId), recordName);
record.createNewFile();
@@ -234,7 +233,7 @@
+ ", record name =" + record.getName());
final ImageDecoder.Source srcData =
- ImageDecoder.createSource(wallpaper.wallpaperFile);
+ ImageDecoder.createSource(wallpaper.getWallpaperFile());
final int sampleSize = scale;
Bitmap cropped = ImageDecoder.decodeBitmap(srcData, (decoder, info, src) -> {
decoder.setTargetSampleSize(sampleSize);
@@ -257,7 +256,7 @@
+ " h=" + finalCrop.getHeight());
}
- f = new FileOutputStream(wallpaper.cropFile);
+ f = new FileOutputStream(wallpaper.getCropFile());
bos = new BufferedOutputStream(f, 32 * 1024);
finalCrop.compress(Bitmap.CompressFormat.PNG, 100, bos);
// don't rely on the implicit flush-at-close when noting success
@@ -277,11 +276,11 @@
if (!success) {
Slog.e(TAG, "Unable to apply new wallpaper");
- wallpaper.cropFile.delete();
+ wallpaper.getCropFile().delete();
}
- if (wallpaper.cropFile.exists()) {
- boolean didRestorecon = SELinux.restorecon(wallpaper.cropFile.getAbsoluteFile());
+ if (wallpaper.getCropFile().exists()) {
+ boolean didRestorecon = SELinux.restorecon(wallpaper.getCropFile().getAbsoluteFile());
if (DEBUG) {
Slog.v(TAG, "restorecon() of crop file returned " + didRestorecon);
}
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperData.java b/services/core/java/com/android/server/wallpaper/WallpaperData.java
index d87fca4..b0b66cf 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperData.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperData.java
@@ -40,10 +40,7 @@
*/
class WallpaperData {
- int userId;
-
- final File wallpaperFile; // source image
- final File cropFile; // eventual destination
+ final int userId;
/**
* True while the client is writing a new wallpaper
@@ -133,14 +130,13 @@
*/
final Rect cropHint = new Rect(0, 0, 0, 0);
+ // map of which -> File
+ private final SparseArray<File> mWallpaperFiles = new SparseArray<>();
+ private final SparseArray<File> mCropFiles = new SparseArray<>();
+
WallpaperData(int userId, @SetWallpaperFlags int wallpaperType) {
this.userId = userId;
this.mWhich = wallpaperType;
- File wallpaperDir = getWallpaperDir(userId);
- String wallpaperFileName = (wallpaperType == FLAG_LOCK) ? WALLPAPER_LOCK_ORIG : WALLPAPER;
- String cropFileName = (wallpaperType == FLAG_LOCK) ? WALLPAPER_LOCK_CROP : WALLPAPER_CROP;
- this.wallpaperFile = new File(wallpaperDir, wallpaperFileName);
- this.cropFile = new File(wallpaperDir, cropFileName);
}
/**
@@ -154,8 +150,6 @@
*/
WallpaperData(WallpaperData source) {
this.userId = source.userId;
- this.wallpaperFile = source.wallpaperFile;
- this.cropFile = source.cropFile;
this.wallpaperComponent = source.wallpaperComponent;
this.mWhich = source.mWhich;
this.wallpaperId = source.wallpaperId;
@@ -169,6 +163,25 @@
}
}
+ File getWallpaperFile() {
+ String fileName = mWhich == FLAG_LOCK ? WALLPAPER_LOCK_ORIG : WALLPAPER;
+ return getFile(mWallpaperFiles, fileName);
+ }
+
+ File getCropFile() {
+ String fileName = mWhich == FLAG_LOCK ? WALLPAPER_LOCK_CROP : WALLPAPER_CROP;
+ return getFile(mCropFiles, fileName);
+ }
+
+ private File getFile(SparseArray<File> map, String fileName) {
+ File result = map.get(mWhich);
+ if (result == null) {
+ result = new File(getWallpaperDir(userId), fileName);
+ map.put(userId, result);
+ }
+ return result;
+ }
+
@Override
public String toString() {
StringBuilder out = new StringBuilder(defaultString(this));
@@ -177,7 +190,7 @@
out.append(", which: ");
out.append(mWhich);
out.append(", file mod: ");
- out.append(wallpaperFile != null ? wallpaperFile.lastModified() : "null");
+ out.append(getWallpaperFile() != null ? getWallpaperFile().lastModified() : "null");
if (connection == null) {
out.append(", no connection");
} else {
@@ -202,10 +215,10 @@
// Called during initialization of a given user's wallpaper bookkeeping
boolean cropExists() {
- return cropFile.exists();
+ return getCropFile().exists();
}
boolean sourceExists() {
- return wallpaperFile.exists();
+ return getWallpaperFile().exists();
}
}
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperDataParser.java b/services/core/java/com/android/server/wallpaper/WallpaperDataParser.java
index 3e498d7..c54e3bd 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 @@
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) {
@@ -542,12 +542,12 @@
}
res = r.openRawResource(resId);
- if (wallpaper.wallpaperFile.exists()) {
- wallpaper.wallpaperFile.delete();
- wallpaper.cropFile.delete();
+ if (wallpaper.getWallpaperFile().exists()) {
+ wallpaper.getWallpaperFile().delete();
+ wallpaper.getCropFile().delete();
}
- fos = new FileOutputStream(wallpaper.wallpaperFile);
- cos = new FileOutputStream(wallpaper.cropFile);
+ fos = new FileOutputStream(wallpaper.getWallpaperFile());
+ cos = new FileOutputStream(wallpaper.getCropFile());
byte[] buffer = new byte[32768];
int amt;
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index e31b53c..9b2cdd7 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 @@
if (lockedWallpaper != null) {
detachWallpaperLocked(lockedWallpaper);
}
+ clearWallpaperBitmaps(mWallpaper.userId, FLAG_LOCK);
mLockWallpaperMap.remove(wallpaper.userId);
notifyColorsWhich |= FLAG_LOCK;
}
@@ -669,8 +670,8 @@
// Not having a wallpaperComponent means it's a lock screen wallpaper.
final boolean imageWallpaper = mImageWallpaper.equals(wallpaper.wallpaperComponent)
|| wallpaper.wallpaperComponent == null;
- if (imageWallpaper && wallpaper.cropFile != null && wallpaper.cropFile.exists()) {
- cropFile = wallpaper.cropFile.getAbsolutePath();
+ if (imageWallpaper && wallpaper.getCropFile().exists()) {
+ cropFile = wallpaper.getCropFile().getAbsolutePath();
} else if (imageWallpaper && !wallpaper.cropExists() && !wallpaper.sourceExists()) {
defaultImageWallpaper = true;
}
@@ -1417,6 +1418,10 @@
}
}
+ synchronized (mLock) {
+ saveSettingsLocked(mNewWallpaper.userId);
+ }
+
if (DEBUG) {
Slog.v(TAG, "--- wallpaper changed --");
Slog.v(TAG, "new sysWp: " + mWallpaperMap.get(mCurrentUserId));
@@ -1588,7 +1593,7 @@
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 @@
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 @@
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.getWallpaperFile().delete();
+ if (cropExists) wallpaper.getCropFile().delete();
+ return sourceExists || cropExists;
}
@Override
@@ -1966,10 +1973,7 @@
// 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 @@
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 @@
}
}
- 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 @@
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;
@@ -2331,13 +2394,13 @@
wallpaper.callbacks.register(cb);
}
- File fileToReturn = getCropped ? wallpaper.cropFile : wallpaper.wallpaperFile;
+ File result = getCropped ? wallpaper.getCropFile() : wallpaper.getWallpaperFile();
- if (!fileToReturn.exists()) {
+ if (!result.exists()) {
return null;
}
- return ParcelFileDescriptor.open(fileToReturn, MODE_READ_ONLY);
+ return ParcelFileDescriptor.open(result, MODE_READ_ONLY);
} catch (FileNotFoundException e) {
/* Shouldn't happen as we check to see if the file exists */
Slog.w(TAG, "Error getting wallpaper", e);
@@ -3098,18 +3161,22 @@
// 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.getWallpaperFile().exists()) {
+ Os.rename(sysWP.getWallpaperFile().getAbsolutePath(),
+ lockWP.getWallpaperFile().getAbsolutePath());
+ }
+ if (!mIsLockscreenLiveWallpaperEnabled || sysWP.getCropFile().exists()) {
+ Os.rename(sysWP.getCropFile().getAbsolutePath(),
+ lockWP.getCropFile().getAbsolutePath());
+ }
mLockWallpaperMap.put(userId, lockWP);
if (mIsLockscreenLiveWallpaperEnabled) {
- SELinux.restorecon(lockWP.wallpaperFile);
+ SELinux.restorecon(lockWP.getWallpaperFile());
mLastLockWallpaper = lockWP;
}
} catch (ErrnoException e) {
Slog.e(TAG, "Can't migrate system wallpaper: " + e.getMessage());
- lockWP.wallpaperFile.delete();
- lockWP.cropFile.delete();
- return;
+ clearWallpaperBitmaps(lockWP);
}
}
@@ -3125,11 +3192,11 @@
FileUtils.S_IRWXU|FileUtils.S_IRWXG|FileUtils.S_IXOTH,
-1, -1);
}
- ParcelFileDescriptor fd = ParcelFileDescriptor.open(wallpaper.wallpaperFile,
+ ParcelFileDescriptor fd = ParcelFileDescriptor.open(wallpaper.getWallpaperFile(),
MODE_CREATE|MODE_READ_WRITE|MODE_TRUNCATE);
- if (!SELinux.restorecon(wallpaper.wallpaperFile)) {
+ if (!SELinux.restorecon(wallpaper.getWallpaperFile())) {
Slog.w(TAG, "restorecon failed for wallpaper file: " +
- wallpaper.wallpaperFile.getPath());
+ wallpaper.getWallpaperFile().getPath());
return null;
}
wallpaper.name = name;
@@ -3140,7 +3207,7 @@
// Nullify field to require new computation
wallpaper.primaryColors = null;
Slog.v(TAG, "updateWallpaperBitmapLocked() : id=" + wallpaper.wallpaperId
- + " name=" + name + " file=" + wallpaper.wallpaperFile.getName());
+ + " name=" + name + " file=" + wallpaper.getWallpaperFile().getName());
return fd;
} catch (FileNotFoundException e) {
Slog.w(TAG, "Error setting wallpaper", e);
@@ -3164,16 +3231,17 @@
}
@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 +3252,7 @@
checkPermission(android.Manifest.permission.SET_WALLPAPER_COMPONENT);
boolean shouldNotifyColors = false;
+ boolean bindSuccess;
final WallpaperData newWallpaper;
synchronized (mLock) {
@@ -3232,7 +3301,7 @@
*/
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 +3320,11 @@
});
}
}
+ boolean lockBitmapCleared = false;
+ if (!mImageWallpaper.equals(newWallpaper.wallpaperComponent)) {
+ clearWallpaperBitmaps(newWallpaper);
+ lockBitmapCleared = newWallpaper.mWhich == FLAG_LOCK;
+ }
newWallpaper.wallpaperId = makeWallpaperIdLocked();
notifyCallbacksLocked(newWallpaper);
shouldNotifyColors = true;
@@ -3267,6 +3341,9 @@
updateEngineFlags(newWallpaper);
}
}
+ if (!lockBitmapCleared) {
+ clearWallpaperBitmaps(newWallpaper.userId, FLAG_LOCK);
+ }
mLockWallpaperMap.remove(newWallpaper.userId);
}
}
@@ -3279,6 +3356,7 @@
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 a0a45e6..4262e94e 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 @@
}
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 @@
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 @@
}
// 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 @@
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 @@
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 @@
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 @@
}
}
- // 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 @@
// 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 47b51ac..4a658d6 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 @@
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 0171c20..738797b 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 @@
// 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 @@
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 @@
}
// 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 @@
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 e91c9d4..2b6b62e 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 @@
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 ef19eef..e1132db 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -1065,7 +1065,7 @@
if (obscuredChanged && w.isVisible() && mWallpaperController.isWallpaperTarget(w)) {
// This is the wallpaper target and its obscured state changed... make sure the
// current wallpaper's visibility has been updated accordingly.
- mWallpaperController.updateWallpaperVisibility();
+ mWallpaperController.updateWallpaperTokens(mDisplayContent.isKeyguardLocked());
}
w.handleWindowMovedIfNeeded();
@@ -1214,8 +1214,7 @@
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 95c953a..a02fd11 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 @@
// 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 @@
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 @@
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 @@
} 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 @@
mBottomGestureHost = null;
mTopFullscreenOpaqueWindowState = null;
mNavBarColorWindowCandidate = null;
- mNavBarBackgroundWindow = null;
+ mNavBarBackgroundWindowCandidate = null;
mStatusBarAppearanceRegionList.clear();
mLetterboxDetails.clear();
mStatusBarBackgroundWindows.clear();
@@ -1514,8 +1510,8 @@
mNavBarColorWindowCandidate = win;
addSystemBarColorApp(win);
}
- if (mNavBarBackgroundWindow == null) {
- mNavBarBackgroundWindow = win;
+ if (mNavBarBackgroundWindowCandidate == null) {
+ mNavBarBackgroundWindowCandidate = win;
}
}
@@ -1531,20 +1527,32 @@
}
} 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 @@
&& 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 @@
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 @@
*/
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 @@
}
}
- if (!isFullyTransparentAllowed(mNavBarBackgroundWindow, Type.navigationBars())) {
+ if (!isFullyTransparentAllowed(navBackgroundWin, Type.navigationBars())) {
appearance |= APPEARANCE_SEMI_TRANSPARENT_NAVIGATION_BARS;
}
@@ -2555,11 +2570,26 @@
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;
}
- if (win == getNotificationShade() || win.isActivityTypeDream()) {
+ if (win.mPolicy.getWindowLayerLw(win) > win.mPolicy.getWindowLayerFromTypeLw(
+ WindowManager.LayoutParams.TYPE_STATUS_BAR) || win.isActivityTypeDream()) {
return false;
}
return getInsetsPolicy().hasHiddenSources(Type.navigationBars());
@@ -2727,9 +2757,9 @@
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 2b34bb2..f96f99d 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 @@
* </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 74494dd..de70c4d 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 @@
@NonNull final DisplayRotation displayRotation,
@NonNull final DisplayContent displayContent) {
if (!letterboxConfiguration
- .isDisplayRotationImmersiveAppCompatPolicyEnabled(/* checkDeviceConfig */ false)) {
+ .isDisplayRotationImmersiveAppCompatPolicyEnabledAtBuildTime()) {
return null;
}
@@ -87,8 +87,7 @@
* @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 2583514..4a3d0c1 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 @@
}
} 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 44d6768..98027bb 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_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 @@
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 daa823c..798dc85 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 @@
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 5e2618b..e1c865b 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 @@
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 249ead0..addb521 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 @@
}
}
- 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 @@
// 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 067a18c..fda22ca 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 @@
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,52 @@
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;
+
+ // Whether per-app user aspect ratio override settings is enabled
+ private static final String KEY_ENABLE_USER_ASPECT_RATIO_SETTINGS =
+ "enable_app_compat_user_aspect_ratio_settings";
+
+ // TODO(b/288142656): Enable user aspect ratio settings by default.
+ private static final boolean DEFAULT_VALUE_ENABLE_USER_ASPECT_RATIO_SETTINGS = false;
+
/**
* Override of aspect ratio for fixed orientation letterboxing that is set via ADB with
* set-fixed-orientation-letterbox-aspect-ratio or via {@link
@@ -205,26 +246,16 @@
// 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;
+ // Allows to enable user aspect ratio settings ignoring flags.
+ private boolean mUserAppAspectRatioSettingsOverrideEnabled;
// Whether we should use split screen aspect ratio for the activity when camera compat treatment
// is enabled and activity is connected to the camera in fullscreen.
private final boolean mIsCameraCompatSplitScreenAspectRatioEnabled;
- // 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 +271,8 @@
// 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 +291,6 @@
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 +328,38 @@
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))
+ .addDeviceConfigEntry(KEY_ENABLE_USER_ASPECT_RATIO_SETTINGS,
+ DEFAULT_VALUE_ENABLE_USER_ASPECT_RATIO_SETTINGS,
+ mContext.getResources().getBoolean(
+ R.bool.config_appCompatUserAppAspectRatioSettingsIsEnabled))
+ .build();
}
/**
@@ -342,7 +367,7 @@
* 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 +1074,21 @@
}
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 +1118,7 @@
/** 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 +1138,20 @@
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 +1197,48 @@
/**
* 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);
+ }
+
+ /**
+ * Whether per-app user aspect ratio override settings is enabled
+ */
+ boolean isUserAppAspectRatioSettingsEnabled() {
+ return mUserAppAspectRatioSettingsOverrideEnabled
+ || mDeviceConfig.getFlagValue(KEY_ENABLE_USER_ASPECT_RATIO_SETTINGS);
+ }
+
+ void setUserAppAspectRatioSettingsOverrideEnabled(boolean enabled) {
+ mUserAppAspectRatioSettingsOverrideEnabled = enabled;
+ }
+
+ /**
+ * Resets whether per-app user aspect ratio override settings is enabled
+ * {@code mDeviceConfig.getFlagValue(KEY_ENABLE_USER_ASPECT_RATIO_SETTINGS)}.
+ */
+ void resetUserAppAspectRatioSettingsEnabled() {
+ setUserAppAspectRatioSettingsOverrideEnabled(false);
+ }
}
diff --git a/services/core/java/com/android/server/wm/LetterboxConfigurationDeviceConfig.java b/services/core/java/com/android/server/wm/LetterboxConfigurationDeviceConfig.java
deleted file mode 100644
index a739e42..0000000
--- 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 d83c861..a816838 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 @@
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 @@
boolean shouldRefreshActivityForCameraCompat() {
return shouldEnableWithOptOutOverrideAndProperty(
/* gatingCondition */ () -> mLetterboxConfiguration
- .isCameraCompatTreatmentEnabled(/* checkDeviceConfig */ true),
+ .isCameraCompatTreatmentEnabled(),
mIsOverrideCameraCompatDisableRefreshEnabled,
mBooleanPropertyCameraCompatAllowRefresh);
}
@@ -719,7 +716,7 @@
boolean shouldRefreshActivityViaPauseForCameraCompat() {
return shouldEnableWithOverrideAndProperty(
/* gatingCondition */ () -> mLetterboxConfiguration
- .isCameraCompatTreatmentEnabled(/* checkDeviceConfig */ true),
+ .isCameraCompatTreatmentEnabled(),
mIsOverrideCameraCompatEnableRefreshViaPauseEnabled,
mBooleanPropertyCameraCompatEnableRefreshViaPause);
}
@@ -738,7 +735,7 @@
boolean shouldForceRotateForCameraCompat() {
return shouldEnableWithOptOutOverrideAndProperty(
/* gatingCondition */ () -> mLetterboxConfiguration
- .isCameraCompatTreatmentEnabled(/* checkDeviceConfig */ true),
+ .isCameraCompatTreatmentEnabled(),
mIsOverrideCameraCompatDisableForceRotationEnabled,
mBooleanPropertyCameraCompatAllowForceRotation);
}
@@ -1428,7 +1425,8 @@
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 e47787e..9ef5ed0 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 @@
// 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 @@
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 7b10c63..b49c5fb 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 @@
@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 0000000..c2e819e
--- /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 878b33f..1007357 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_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 @@
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 @@
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 @@
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 ceb80d6..39772dda 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.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 @@
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 @@
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 @@
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 5f2d3ca8..0059f0b 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 @@
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 eb7b8c2..00bedcd 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 @@
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) {
@@ -308,29 +308,12 @@
}
}
- private boolean shouldWallpaperBeVisible(WindowState wallpaperTarget) {
- if (DEBUG_WALLPAPER) {
- Slog.v(TAG, "Wallpaper vis: target " + wallpaperTarget + " prev="
- + mPrevWallpaperTarget);
- }
- return wallpaperTarget != null || mPrevWallpaperTarget != null;
- }
-
boolean isWallpaperTargetAnimating() {
return mWallpaperTarget != null && mWallpaperTarget.isAnimating(TRANSITION | PARENTS)
&& (mWallpaperTarget.mActivityRecord == null
|| !mWallpaperTarget.mActivityRecord.isWaitingForTransitionStart());
}
- void updateWallpaperVisibility() {
- final boolean visible = shouldWallpaperBeVisible(mWallpaperTarget);
-
- for (int curTokenNdx = mWallpaperTokens.size() - 1; curTokenNdx >= 0; curTokenNdx--) {
- final WallpaperWindowToken token = mWallpaperTokens.get(curTokenNdx);
- token.setVisibility(visible);
- }
- }
-
/**
* Make one wallpaper visible, according to {@attr showHome}.
* This is called during the keyguard unlocking transition
@@ -801,11 +784,20 @@
result.setWallpaperTarget(wallpaperTarget);
}
+ public void updateWallpaperTokens(boolean keyguardLocked) {
+ if (DEBUG_WALLPAPER) {
+ Slog.v(TAG, "Wallpaper vis: target " + mWallpaperTarget + " prev="
+ + mPrevWallpaperTarget);
+ }
+ updateWallpaperTokens(mWallpaperTarget != null || mPrevWallpaperTarget != null,
+ keyguardLocked);
+ }
+
/**
* Change the visibility of the top wallpaper to {@param visibility} and hide all the others.
*/
- private void updateWallpaperTokens(boolean visibility, boolean locked) {
- WindowState topWallpaper = mFindResults.getTopWallpaper(locked);
+ private void updateWallpaperTokens(boolean visibility, boolean keyguardLocked) {
+ WindowState topWallpaper = mFindResults.getTopWallpaper(keyguardLocked);
WallpaperWindowToken topWallpaperToken =
topWallpaper == null ? null : topWallpaper.mToken.asWallpaperToken();
for (int curTokenNdx = mWallpaperTokens.size() - 1; curTokenNdx >= 0; curTokenNdx--) {
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 0152666..4bc4c26 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 @@
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 67572bf..bb3d109 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 @@
return;
}
dc.mRemoteInsetsControlTarget.setRequestedVisibleTypes(requestedVisibleTypes);
- dc.getInsetsStateController().onInsetsModified(dc.mRemoteInsetsControlTarget);
+ dc.getInsetsStateController().onRequestedVisibleTypesChanged(
+ dc.mRemoteInsetsControlTarget);
}
} finally {
Binder.restoreCallingIdentity(origId);
@@ -7186,15 +7187,45 @@
@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 @@
@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 @@
@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 @@
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/WindowManagerShellCommand.java b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
index a153708..05e858d 100644
--- a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
+++ b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
@@ -1006,13 +1006,16 @@
runSetBooleanFlag(pw, mLetterboxConfiguration
::setTranslucentLetterboxingOverrideEnabled);
break;
+ case "--isUserAppAspectRatioSettingsEnabled":
+ runSetBooleanFlag(pw, mLetterboxConfiguration
+ ::setUserAppAspectRatioSettingsOverrideEnabled);
+ break;
case "--isCameraCompatRefreshEnabled":
- runSetBooleanFlag(pw, enabled -> mLetterboxConfiguration
- .setCameraCompatRefreshEnabled(enabled));
+ runSetBooleanFlag(pw, mLetterboxConfiguration::setCameraCompatRefreshEnabled);
break;
case "--isCameraCompatRefreshCycleThroughStopEnabled":
- runSetBooleanFlag(pw, enabled -> mLetterboxConfiguration
- .setCameraCompatRefreshCycleThroughStopEnabled(enabled));
+ runSetBooleanFlag(pw,
+ mLetterboxConfiguration::setCameraCompatRefreshCycleThroughStopEnabled);
break;
default:
getErrPrintWriter().println(
@@ -1084,6 +1087,9 @@
case "isTranslucentLetterboxingEnabled":
mLetterboxConfiguration.resetTranslucentLetterboxingEnabled();
break;
+ case "isUserAppAspectRatioSettingsEnabled":
+ mLetterboxConfiguration.resetUserAppAspectRatioSettingsEnabled();
+ break;
case "isCameraCompatRefreshEnabled":
mLetterboxConfiguration.resetCameraCompatRefreshEnabled();
break;
@@ -1194,6 +1200,7 @@
mLetterboxConfiguration.resetIsSplitScreenAspectRatioForUnresizableAppsEnabled();
mLetterboxConfiguration.resetIsDisplayAspectRatioEnabledForFixedOrientationLetterbox();
mLetterboxConfiguration.resetTranslucentLetterboxingEnabled();
+ mLetterboxConfiguration.resetUserAppAspectRatioSettingsEnabled();
mLetterboxConfiguration.resetCameraCompatRefreshEnabled();
mLetterboxConfiguration.resetCameraCompatRefreshCycleThroughStopEnabled();
}
@@ -1249,7 +1256,6 @@
+ mLetterboxConfiguration.isCameraCompatRefreshEnabled());
pw.println(" Refresh using \"stopped -> resumed\" cycle: "
+ mLetterboxConfiguration.isCameraCompatRefreshCycleThroughStopEnabled());
-
pw.println("Background type: "
+ LetterboxConfiguration.letterboxBackgroundTypeToString(
mLetterboxConfiguration.getLetterboxBackgroundType()));
@@ -1259,12 +1265,10 @@
+ mLetterboxConfiguration.getLetterboxBackgroundWallpaperBlurRadius());
pw.println(" Wallpaper dark scrim alpha: "
+ mLetterboxConfiguration.getLetterboxBackgroundWallpaperDarkScrimAlpha());
-
- if (mLetterboxConfiguration.isTranslucentLetterboxingEnabled()) {
- pw.println("Letterboxing for translucent activities: enabled");
- } else {
- pw.println("Letterboxing for translucent activities: disabled");
- }
+ pw.println("Is letterboxing for translucent activities enabled: "
+ + mLetterboxConfiguration.isTranslucentLetterboxingEnabled());
+ pw.println("Is the user aspect ratio settings enabled: "
+ + mLetterboxConfiguration.isUserAppAspectRatioSettingsEnabled());
}
return 0;
}
@@ -1462,6 +1466,8 @@
pw.println(" unresizable apps.");
pw.println(" --isTranslucentLetterboxingEnabled [true|1|false|0]");
pw.println(" Whether letterboxing for translucent activities is enabled.");
+ pw.println(" --isUserAppAspectRatioSettingsEnabled [true|1|false|0]");
+ pw.println(" Whether user aspect ratio settings are enabled.");
pw.println(" --isCameraCompatRefreshEnabled [true|1|false|0]");
pw.println(" Whether camera compatibility refresh is enabled.");
pw.println(" --isCameraCompatRefreshCycleThroughStopEnabled [true|1|false|0]");
@@ -1473,7 +1479,7 @@
pw.println(" |horizontalPositionMultiplier|verticalPositionMultiplier");
pw.println(" |isHorizontalReachabilityEnabled|isVerticalReachabilityEnabled");
pw.println(" |isEducationEnabled||defaultPositionMultiplierForHorizontalReachability");
- pw.println(" |isTranslucentLetterboxingEnabled");
+ pw.println(" |isTranslucentLetterboxingEnabled|isUserAppAspectRatioSettingsEnabled");
pw.println(" |defaultPositionMultiplierForVerticalReachability]");
pw.println(" Resets overrides to default values for specified properties separated");
pw.println(" by space, e.g. 'reset-letterbox-style aspectRatio cornerRadius'.");
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 5de5bab..cf1e51f 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 @@
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 @@
if (didFrameInsetsChange
|| configChanged
|| insetsChanged
+ || dragResizingChanged
|| shouldSendRedrawForSync()
|| attachedFrameChanged) {
ProtoLog.v(WM_DEBUG_RESIZE,
@@ -1478,7 +1481,8 @@
// 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 f0d718a..c065cb5 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -279,7 +279,7 @@
base::Result<std::unique_ptr<InputChannel>> createInputChannel(const std::string& name);
base::Result<std::unique_ptr<InputChannel>> createInputMonitor(int32_t displayId,
const std::string& name,
- int32_t pid);
+ gui::Pid pid);
status_t removeInputChannel(const sp<IBinder>& connectionToken);
status_t pilferPointers(const sp<IBinder>& token);
@@ -330,9 +330,9 @@
void notifyConfigurationChanged(nsecs_t when) override;
// ANR-related callbacks -- start
void notifyNoFocusedWindowAnr(const std::shared_ptr<InputApplicationHandle>& handle) override;
- void notifyWindowUnresponsive(const sp<IBinder>& token, std::optional<int32_t> pid,
+ void notifyWindowUnresponsive(const sp<IBinder>& token, std::optional<gui::Pid> pid,
const std::string& reason) override;
- void notifyWindowResponsive(const sp<IBinder>& token, std::optional<int32_t> pid) override;
+ void notifyWindowResponsive(const sp<IBinder>& token, std::optional<gui::Pid> pid) override;
// ANR-related callbacks -- end
void notifyInputChannelBroken(const sp<IBinder>& token) override;
void notifyFocusChanged(const sp<IBinder>& oldToken, const sp<IBinder>& newToken) override;
@@ -355,6 +355,8 @@
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<gui::Uid>& uids) override;
/* --- PointerControllerPolicyInterface implementation --- */
@@ -539,7 +541,7 @@
}
base::Result<std::unique_ptr<InputChannel>> NativeInputManager::createInputMonitor(
- int32_t displayId, const std::string& name, int32_t pid) {
+ int32_t displayId, const std::string& name, gui::Pid pid) {
ATRACE_CALL();
return mInputManager->getDispatcher().createInputMonitor(displayId, name, pid);
}
@@ -883,7 +885,7 @@
}
void NativeInputManager::notifyWindowUnresponsive(const sp<IBinder>& token,
- std::optional<int32_t> pid,
+ std::optional<gui::Pid> pid,
const std::string& reason) {
#if DEBUG_INPUT_DISPATCHER_POLICY
ALOGD("notifyWindowUnresponsive");
@@ -897,12 +899,12 @@
ScopedLocalRef<jstring> reasonObj(env, env->NewStringUTF(reason.c_str()));
env->CallVoidMethod(mServiceObj, gServiceClassInfo.notifyWindowUnresponsive, tokenObj,
- pid.value_or(0), pid.has_value(), reasonObj.get());
+ pid.value_or(gui::Pid{0}).val(), pid.has_value(), reasonObj.get());
checkAndClearExceptionFromCallback(env, "notifyWindowUnresponsive");
}
void NativeInputManager::notifyWindowResponsive(const sp<IBinder>& token,
- std::optional<int32_t> pid) {
+ std::optional<gui::Pid> pid) {
#if DEBUG_INPUT_DISPATCHER_POLICY
ALOGD("notifyWindowResponsive");
#endif
@@ -914,7 +916,7 @@
jobject tokenObj = javaObjectForIBinder(env, token);
env->CallVoidMethod(mServiceObj, gServiceClassInfo.notifyWindowResponsive, tokenObj,
- pid.value_or(0), pid.has_value());
+ pid.value_or(gui::Pid{0}).val(), pid.has_value());
checkAndClearExceptionFromCallback(env, "notifyWindowResponsive");
}
@@ -966,6 +968,15 @@
checkAndClearExceptionFromCallback(env, "notifyDropWindow");
}
+void NativeInputManager::notifyDeviceInteraction(int32_t deviceId, nsecs_t timestamp,
+ const std::set<gui::Uid>& uids) {
+ static const bool ENABLE_INPUT_DEVICE_USAGE_METRICS =
+ sysprop::InputProperties::enable_input_device_usage_metrics().value_or(true);
+ if (!ENABLE_INPUT_DEVICE_USAGE_METRICS) return;
+
+ 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::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() {
@@ -1808,7 +1819,7 @@
std::string name = nameChars.c_str();
base::Result<std::unique_ptr<InputChannel>> inputChannel =
- im->createInputMonitor(displayId, name, pid);
+ im->createInputMonitor(displayId, name, gui::Pid{pid});
if (!inputChannel.ok()) {
std::string message = inputChannel.error().message();
@@ -1853,7 +1864,8 @@
jint pid, jint uid, jboolean hasPermission, jint displayId) {
NativeInputManager* im = getNativeInputManager(env, nativeImplObj);
- return im->getInputManager()->getDispatcher().setInTouchMode(inTouchMode, pid, uid,
+ return im->getInputManager()->getDispatcher().setInTouchMode(inTouchMode, gui::Pid{pid},
+ gui::Uid{static_cast<uid_t>(uid)},
hasPermission, displayId);
}
@@ -1869,7 +1881,7 @@
jint timeoutMillis, jint policyFlags) {
NativeInputManager* im = getNativeInputManager(env, nativeImplObj);
- const std::optional<int32_t> targetUid = injectIntoUid ? std::make_optional(uid) : std::nullopt;
+ const auto targetUid = injectIntoUid ? std::make_optional<gui::Uid>(uid) : std::nullopt;
// static_cast is safe because the value was already checked at the Java layer
InputEventInjectionSync mode = static_cast<InputEventInjectionSync>(syncMode);
@@ -2214,13 +2226,25 @@
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 +2870,7 @@
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 f96ca58..7104a80 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 ad6434e..507c9dc 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 e44b8cd..417fc39 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 @@
: 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 @@
"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 af84180..991248a 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -3255,6 +3255,12 @@
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 0000000..8b13789
--- /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 0000000..c227083
--- /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 0000000..dadd973
--- /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 0000000..a93d6f7
--- /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 0000000..5121f666
--- /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 0000000..d00491f
--- /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 0000000..56a6051
--- /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 0000000..2bc4ce9
--- /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 0000000..c754f1a
--- /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 0000000..7ed12cf
--- /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 0000000..573541a
--- /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 0000000..8b13789
--- /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 0000000..dadd973
--- /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 0000000..a93d6f7
--- /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 0000000..5121f666
--- /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 0000000..d00491f
--- /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 0000000..56a6051
--- /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 0000000..2bc4ce9
--- /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 0000000..c754f1a
--- /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 0000000..7ed12cf
--- /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 a93d6f7..d17275f 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 c754f1a..79c41c7 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 deebb18..526a717 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 0000000..8b13789
--- /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 0000000..40c7bb2
--- /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/am/AsyncProcessStartTest.java b/services/tests/mockingservicestests/src/com/android/server/am/AsyncProcessStartTest.java
index 7c5d96e..1fd7c4a 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/AsyncProcessStartTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/AsyncProcessStartTest.java
@@ -131,6 +131,7 @@
mRealAms = new ActivityManagerService(
new TestInjector(mContext), mServiceThreadRule.getThread());
+ mRealAms.mConstants.loadDeviceConfigConstants();
mRealAms.mActivityTaskManager = new ActivityTaskManagerService(mContext);
mRealAms.mActivityTaskManager.initialize(null, null, mContext.getMainLooper());
mRealAms.mAtmInternal = mActivityTaskManagerInt;
@@ -195,7 +196,9 @@
Log.v(TAG, "Intercepting bindApplication() for "
+ Arrays.toString(invocation.getArguments()));
if (!wedge) {
- mRealAms.finishAttachApplication(0);
+ if (mRealAms.mConstants.mEnableWaitForFinishAttachApplication) {
+ mRealAms.finishAttachApplication(0);
+ }
}
return null;
}).when(thread).bindApplication(
@@ -237,9 +240,10 @@
*/
@Test
public void testNormal() throws Exception {
- ProcessRecord app = startProcessAndWait(false);
-
- verify(app, never()).killLocked(any(), anyInt(), anyBoolean());
+ if (mRealAms.mConstants.mEnableWaitForFinishAttachApplication) {
+ ProcessRecord app = startProcessAndWait(false);
+ verify(app, never()).killLocked(any(), anyInt(), anyBoolean());
+ }
}
/**
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
index 60e2af5..1f4563f 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
@@ -1971,6 +1971,36 @@
@SuppressWarnings("GuardedBy")
@Test
+ public void testUpdateOomAdj_DoOne_PendingFinishAttach() {
+ ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
+ MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
+ app.setPendingFinishAttach(true);
+ app.mState.setHasForegroundActivities(false);
+
+ sService.mOomAdjuster.setAttachingProcessStatesLSP(app);
+ updateOomAdj(app);
+
+ assertProcStates(app, PROCESS_STATE_CACHED_EMPTY, FOREGROUND_APP_ADJ,
+ SCHED_GROUP_DEFAULT);
+ }
+
+ @SuppressWarnings("GuardedBy")
+ @Test
+ public void testUpdateOomAdj_DoOne_TopApp_PendingFinishAttach() {
+ ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
+ MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
+ app.setPendingFinishAttach(true);
+ app.mState.setHasForegroundActivities(true);
+
+ sService.mOomAdjuster.setAttachingProcessStatesLSP(app);
+ updateOomAdj(app);
+
+ assertProcStates(app, PROCESS_STATE_TOP, FOREGROUND_APP_ADJ,
+ SCHED_GROUP_TOP_APP);
+ }
+
+ @SuppressWarnings("GuardedBy")
+ @Test
public void testUpdateOomAdj_UidIdle_StopService() {
final ProcessRecord app1 = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
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 04f6f8b..2fbe8aa 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 @@
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 @@
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 @@
/* 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 @@
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 @@
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 @@
}
@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 1700760..aaab403 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.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 @@
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 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 @@
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 @@
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 @@
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 @@
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 @@
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;
- when(mHolder.automaticBrightnessController.getRawAutomaticScreenBrightness())
- .thenReturn(brightness);
- when(mHolder.automaticBrightnessController.getAutomaticScreenBrightness())
- .thenReturn(0.3f);
- when(mHolder.automaticBrightnessController.convertToNits(brightness)).thenReturn(nits);
- when(mHolder.automaticBrightnessController.getAmbientLux()).thenReturn(ambientLux);
+ DisplayPowerControllerHolder followerDpc =
+ createDisplayPowerController(FOLLOWER_DISPLAY_ID, FOLLOWER_UNIQUE_ID);
when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON);
- DisplayPowerController2 followerDpc = mock(DisplayPowerController2.class);
+ 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(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.addDisplayBrightnessFollower(followerDpc);
+ mHolder.dpc.addDisplayBrightnessFollower(followerDpc.dpc);
+
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
@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 @@
any(HysteresisLevels.class),
any(HysteresisLevels.class),
eq(mContext),
- any(HighBrightnessModeController.class),
+ any(BrightnessRangeController.class),
any(BrightnessThrottler.class),
isNull(),
anyInt(),
@@ -903,6 +1149,14 @@
});
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 @@
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 @@
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 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 @@
AutomaticBrightnessController automaticBrightnessController,
WakelockController wakelockController,
ScreenOffBrightnessSensorController screenOffBrightnessSensorController,
+ HighBrightnessModeController hbmController,
HighBrightnessModeMetadata hbmMetadata,
BrightnessMappingStrategy brightnessMappingStrategy,
DisplayPowerController2.Injector injector) {
@@ -981,6 +1240,7 @@
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 @@
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 @@
mBrightnessMappingStrategy = brightnessMappingStrategy;
mHysteresisLevels = hysteresisLevels;
mScreenOffBrightnessSensorController = screenOffBrightnessSensorController;
+ mHighBrightnessModeController = highBrightnessModeController;
}
@Override
@@ -1064,7 +1327,7 @@
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 @@
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 5c0810f..7d26913 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.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 @@
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 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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;
- when(mHolder.automaticBrightnessController.getRawAutomaticScreenBrightness())
- .thenReturn(brightness);
- when(mHolder.automaticBrightnessController.getAutomaticScreenBrightness())
- .thenReturn(0.3f);
- when(mHolder.automaticBrightnessController.convertToNits(brightness)).thenReturn(nits);
- when(mHolder.automaticBrightnessController.getAmbientLux()).thenReturn(ambientLux);
+ DisplayPowerControllerHolder followerDpc =
+ createDisplayPowerController(FOLLOWER_DISPLAY_ID, FOLLOWER_UNIQUE_ID);
when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON);
- DisplayPowerController followerDpc = mock(DisplayPowerController.class);
+ 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(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.addDisplayBrightnessFollower(followerDpc);
+ mHolder.dpc.addDisplayBrightnessFollower(followerDpc.dpc);
+
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
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 @@
// 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());
+ 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));
- clearInvocations(mHolder.animator, followerHolder.animator, secondFollowerHolder.animator);
+ 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 @@
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 @@
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 @@
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 @@
}
@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 @@
any(HysteresisLevels.class),
any(HysteresisLevels.class),
eq(mContext),
- any(HighBrightnessModeController.class),
+ any(BrightnessRangeController.class),
any(BrightnessThrottler.class),
isNull(),
anyInt(),
@@ -907,6 +1155,14 @@
});
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 @@
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 @@
return new DisplayPowerControllerHolder(dpc, display, displayPowerState, brightnessSetting,
animator, automaticBrightnessController, screenOffBrightnessSensorController,
- hbmMetadata, brightnessMappingStrategy, injector);
+ hbmController, hbmMetadata, brightnessMappingStrategy, injector);
}
/**
@@ -961,6 +1220,7 @@
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 @@
DualRampAnimator<DisplayPowerState> animator,
AutomaticBrightnessController automaticBrightnessController,
ScreenOffBrightnessSensorController screenOffBrightnessSensorController,
+ HighBrightnessModeController hbmController,
HighBrightnessModeMetadata hbmMetadata,
BrightnessMappingStrategy brightnessMappingStrategy,
DisplayPowerController.Injector injector) {
@@ -980,6 +1241,7 @@
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 @@
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 @@
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 @@
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/DisplayPowerProximityStateControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerProximityStateControllerTest.java
index 5b0b989..534a708 100644
--- a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerProximityStateControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerProximityStateControllerTest.java
@@ -34,8 +34,8 @@
import android.os.test.TestLooper;
import android.view.Display;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
import com.android.server.testutils.OffsettableClock;
@@ -92,7 +92,7 @@
};
mDisplayPowerProximityStateController = new DisplayPowerProximityStateController(
mWakelockController, mDisplayDeviceConfig, mTestLooper.getLooper(),
- mNudgeUpdatePowerState, 0,
+ mNudgeUpdatePowerState, Display.DEFAULT_DISPLAY,
mSensorManager, injector);
mSensorEventListener = mDisplayPowerProximityStateController.getProximitySensorListener();
}
@@ -128,7 +128,7 @@
enableProximitySensor();
emitAndValidatePositiveProximityEvent();
mDisplayPowerProximityStateController.ignoreProximitySensorUntilChangedInternal();
- advanceTime(1);
+ advanceTime();
assertTrue(mDisplayPowerProximityStateController.shouldIgnoreProximityUntilChanged());
verify(mNudgeUpdatePowerState, times(2)).run();
@@ -170,7 +170,7 @@
}
@Test
- public void isProximitySensorAvailableReturnsFalseWhenNotAvailable() {
+ public void isProximitySensorAvailableReturnsFalseWhenNotAvailableAndNoDefault() {
when(mDisplayDeviceConfig.getProximitySensor()).thenReturn(
new DisplayDeviceConfig.SensorData() {
{
@@ -180,12 +180,63 @@
});
mDisplayPowerProximityStateController = new DisplayPowerProximityStateController(
mWakelockController, mDisplayDeviceConfig, mTestLooper.getLooper(),
+ mNudgeUpdatePowerState, Display.DEFAULT_DISPLAY,
+ mSensorManager, null);
+ assertFalse(mDisplayPowerProximityStateController.isProximitySensorAvailable());
+ }
+
+ @Test
+ public void isProximitySensorAvailableReturnsTrueWhenNotAvailableAndHasDefault()
+ throws Exception {
+ when(mDisplayDeviceConfig.getProximitySensor()).thenReturn(
+ new DisplayDeviceConfig.SensorData() {
+ {
+ type = null;
+ name = null;
+ }
+ });
+ when(mSensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY)).thenReturn(
+ TestUtils.createSensor(Sensor.TYPE_PROXIMITY, "proximity"));
+ mDisplayPowerProximityStateController = new DisplayPowerProximityStateController(
+ mWakelockController, mDisplayDeviceConfig, mTestLooper.getLooper(),
+ mNudgeUpdatePowerState, Display.DEFAULT_DISPLAY,
+ mSensorManager, null);
+ assertTrue(mDisplayPowerProximityStateController.isProximitySensorAvailable());
+ }
+
+ @Test
+ public void isProximitySensorAvailableReturnsFalseWhenNotAvailableHasDefaultNonDefaultDisplay()
+ throws Exception {
+ when(mDisplayDeviceConfig.getProximitySensor()).thenReturn(
+ new DisplayDeviceConfig.SensorData() {
+ {
+ type = null;
+ name = null;
+ }
+ });
+ when(mSensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY)).thenReturn(
+ TestUtils.createSensor(Sensor.TYPE_PROXIMITY, "proximity"));
+ mDisplayPowerProximityStateController = new DisplayPowerProximityStateController(
+ mWakelockController, mDisplayDeviceConfig, mTestLooper.getLooper(),
mNudgeUpdatePowerState, 1,
mSensorManager, null);
assertFalse(mDisplayPowerProximityStateController.isProximitySensorAvailable());
}
@Test
+ public void isProximitySensorAvailableReturnsTrueWhenNoSensorConfigured() throws Exception {
+ when(mDisplayDeviceConfig.getProximitySensor()).thenReturn(null);
+ when(mSensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY)).thenReturn(
+ TestUtils.createSensor(Sensor.TYPE_PROXIMITY, Sensor.STRING_TYPE_PROXIMITY));
+
+ mDisplayPowerProximityStateController = new DisplayPowerProximityStateController(
+ mWakelockController, mDisplayDeviceConfig, mTestLooper.getLooper(),
+ mNudgeUpdatePowerState, Display.DEFAULT_DISPLAY,
+ mSensorManager, null);
+ assertFalse(mDisplayPowerProximityStateController.isProximitySensorAvailable());
+ }
+
+ @Test
public void notifyDisplayDeviceChangedReloadsTheProximitySensor() throws Exception {
DisplayDeviceConfig updatedDisplayDeviceConfig = mock(DisplayDeviceConfig.class);
when(updatedDisplayDeviceConfig.getProximitySensor()).thenReturn(
@@ -326,8 +377,8 @@
assertEquals(mDisplayPowerProximityStateController.getPendingProximityDebounceTime(), -1);
}
- private void advanceTime(long timeMs) {
- mClock.fastForward(timeMs);
+ private void advanceTime() {
+ mClock.fastForward(1);
mTestLooper.dispatchAll();
}
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 0000000..c02cbd1
--- /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 206af5b..eefe5af 100644
--- a/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java
@@ -31,7 +31,6 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.server.wallpaper.WallpaperUtils.WALLPAPER;
-import static com.android.server.wallpaper.WallpaperUtils.WALLPAPER_CROP;
import static org.hamcrest.core.IsNot.not;
import static org.junit.Assert.assertEquals;
@@ -92,7 +91,6 @@
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 +173,12 @@
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);
}
@@ -276,10 +274,10 @@
assertEquals(testUserId, newWallpaperData.userId);
WallpaperData wallpaperData = mService.getWallpaperSafeLocked(testUserId, which);
- assertEquals(wallpaperData.cropFile.getAbsolutePath(),
- newWallpaperData.cropFile.getAbsolutePath());
- assertEquals(wallpaperData.wallpaperFile.getAbsolutePath(),
- newWallpaperData.wallpaperFile.getAbsolutePath());
+ assertEquals(wallpaperData.getCropFile().getAbsolutePath(),
+ newWallpaperData.getCropFile().getAbsolutePath());
+ assertEquals(wallpaperData.getWallpaperFile().getAbsolutePath(),
+ newWallpaperData.getWallpaperFile().getAbsolutePath());
}
}
@@ -300,10 +298,8 @@
/**
* 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,
@@ -528,7 +524,8 @@
@Test
public void getWallpaperWithFeature_getCropped_returnsCropFile() throws Exception {
File cropSystemWallpaperFile =
- new File(WallpaperUtils.getWallpaperDir(USER_SYSTEM), WALLPAPER_CROP);
+ new WallpaperData(USER_SYSTEM, FLAG_SYSTEM).getCropFile();
+ cropSystemWallpaperFile.getParentFile().mkdirs();
cropSystemWallpaperFile.createNewFile();
try (FileOutputStream outputStream = new FileOutputStream(cropSystemWallpaperFile)) {
outputStream.write("Crop system wallpaper".getBytes());
@@ -550,7 +547,8 @@
@Test
public void getWallpaperWithFeature_notGetCropped_returnsOriginalFile() throws Exception {
File originalSystemWallpaperFile =
- new File(WallpaperUtils.getWallpaperDir(USER_SYSTEM), WALLPAPER);
+ new WallpaperData(USER_SYSTEM, FLAG_SYSTEM).getWallpaperFile();
+ originalSystemWallpaperFile.getParentFile().mkdirs();
originalSystemWallpaperFile.createNewFile();
try (FileOutputStream outputStream = new FileOutputStream(originalSystemWallpaperFile)) {
outputStream.write("Original system wallpaper".getBytes());
diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp
index 16e1b45..ee3a773 100644
--- a/services/tests/servicestests/Android.bp
+++ b/services/tests/servicestests/Android.bp
@@ -28,6 +28,7 @@
"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 31a58cd..d1d6e9d 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.UserManager;
import android.platform.test.annotations.Presubmit;
-import androidx.annotation.NonNull;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
@@ -73,7 +72,7 @@
private SensorProps[] mSensorProps;
private LockoutResetDispatcher mLockoutResetDispatcher;
- private TestableFaceProvider mFaceProvider;
+ private FaceProvider mFaceProvider;
private static void waitForIdle() {
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
@@ -98,8 +97,9 @@
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 @@
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 @@
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 @@
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 9c01de6..8f6efff 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.UserManager;
import android.platform.test.annotations.Presubmit;
-import androidx.annotation.NonNull;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
@@ -81,7 +80,7 @@
private SensorProps[] mSensorProps;
private LockoutResetDispatcher mLockoutResetDispatcher;
- private TestableFingerprintProvider mFingerprintProvider;
+ private FingerprintProvider mFingerprintProvider;
private static void waitForIdle() {
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
@@ -110,17 +109,13 @@
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 @@
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 @@
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 @@
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 0000000..e457119
--- /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 0000000..ba9956a
--- /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 0000000..7d45ea4
--- /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 0000000..9135ef3
--- /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 962e867..a6acd60 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 @@
@Mock HysteresisLevels mAmbientBrightnessThresholdsIdle;
@Mock HysteresisLevels mScreenBrightnessThresholdsIdle;
@Mock Handler mNoOpHandler;
- @Mock HighBrightnessModeController mHbmController;
+ @Mock BrightnessRangeController mBrightnessRangeController;
@Mock BrightnessThrottler mBrightnessThrottler;
@Before
@@ -134,12 +134,15 @@
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 5837b21..8b04eca 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayDeviceConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayDeviceConfigTest.java
@@ -33,8 +33,8 @@
import android.util.SparseArray;
import android.view.SurfaceControl;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
import com.android.internal.R;
import com.android.server.display.config.ThermalStatus;
@@ -52,6 +52,7 @@
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
+import java.util.Map;
@SmallTest
@RunWith(AndroidJUnit4.class)
@@ -376,6 +377,116 @@
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 @@
}
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 @@
+ "</point>\n"
+ "</sdrHdrRatioMap>\n"
+ "</highBrightnessMode>\n"
+ + brightnessCapConfig
+ "<screenOffBrightnessSensor>\n"
+ "<type>sensor_12345</type>\n"
+ "<name>Sensor 12345</name>\n"
@@ -731,8 +847,12 @@
}
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/DisplayManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
index 0e775d5..5db9d1f 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
@@ -32,6 +32,7 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@@ -60,6 +61,8 @@
import android.content.res.Resources;
import android.graphics.Insets;
import android.graphics.Rect;
+import android.hardware.Sensor;
+import android.hardware.SensorManager;
import android.hardware.display.BrightnessConfiguration;
import android.hardware.display.Curve;
import android.hardware.display.DisplayManager;
@@ -109,8 +112,6 @@
import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges;
import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
-import com.google.common.collect.ImmutableMap;
-
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -125,6 +126,7 @@
import java.time.Duration;
import java.util.Arrays;
+import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
@@ -136,6 +138,9 @@
public class DisplayManagerServiceTest {
private static final int MSG_REGISTER_DEFAULT_DISPLAY_ADAPTERS = 1;
private static final long SHORT_DEFAULT_DISPLAY_TIMEOUT_MILLIS = 10;
+
+ private static final float FLOAT_TOLERANCE = 0.01f;
+
private static final String VIRTUAL_DISPLAY_NAME = "Test Virtual Display";
private static final String PACKAGE_NAME = "com.android.frameworks.servicestests";
private static final long STANDARD_DISPLAY_EVENTS = DisplayManager.EVENT_FLAG_DISPLAY_ADDED
@@ -250,6 +255,10 @@
@Mock IBinder mMockDisplayToken;
@Mock SensorManagerInternal mMockSensorManagerInternal;
+ @Mock SensorManager mSensorManager;
+
+ @Mock DisplayDeviceConfig mMockDisplayDeviceConfig;
+
@Captor ArgumentCaptor<ContentRecordingSession> mContentRecordingSessionCaptor;
@Before
@@ -267,7 +276,7 @@
LocalServices.removeServiceForTest(VirtualDeviceManagerInternal.class);
LocalServices.addService(
VirtualDeviceManagerInternal.class, mMockVirtualDeviceManagerInternal);
-
+ // TODO: b/287945043
mContext = spy(new ContextWrapper(ApplicationProvider.getApplicationContext()));
VirtualDeviceManager vdm = new VirtualDeviceManager(mIVirtualDeviceManager, mContext);
@@ -396,7 +405,7 @@
final int size = displayIds.length;
assertTrue(size > 0);
- Map<Integer, Integer> expectedDisplayTypeToViewPortTypeMapping = ImmutableMap.of(
+ Map<Integer, Integer> expectedDisplayTypeToViewPortTypeMapping = Map.of(
Display.TYPE_INTERNAL, DisplayViewport.VIEWPORT_INTERNAL,
Display.TYPE_EXTERNAL, DisplayViewport.VIEWPORT_EXTERNAL
);
@@ -1934,6 +1943,74 @@
assertEquals(mode, displayManager.getHdrConversionModeInternal());
}
+ @Test
+ public void testReturnsRefreshRateForDisplayAndSensor_proximitySensorSet() {
+ DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector);
+ DisplayManagerInternal localService = displayManager.new LocalService();
+ DisplayManagerService.BinderService displayManagerBinderService =
+ displayManager.new BinderService();
+ displayManager.overrideSensorManager(mSensorManager);
+
+ FakeDisplayDevice displayDevice = createFakeDisplayDevice(displayManager, new float[]{60f});
+ displayDevice.mDisplayDeviceConfig = mMockDisplayDeviceConfig;
+ int displayId = getDisplayIdForDisplayDevice(displayManager, displayManagerBinderService,
+ displayDevice);
+
+ String testSensorName = "testName";
+ String testSensorType = "testType";
+ Sensor testSensor = TestUtils.createSensor(testSensorType, testSensorName);
+
+ DisplayDeviceConfig.SensorData sensorData = new DisplayDeviceConfig.SensorData();
+ sensorData.type = testSensorType;
+ sensorData.name = testSensorName;
+ sensorData.minRefreshRate = 10f;
+ sensorData.maxRefreshRate = 100f;
+
+ when(mMockDisplayDeviceConfig.getProximitySensor()).thenReturn(sensorData);
+ when(mSensorManager.getSensorList(Sensor.TYPE_ALL)).thenReturn(Collections.singletonList(
+ testSensor));
+
+ SurfaceControl.RefreshRateRange result = localService.getRefreshRateForDisplayAndSensor(
+ displayId, testSensorName, testSensorType);
+
+ assertNotNull(result);
+ assertEquals(result.min, sensorData.minRefreshRate, FLOAT_TOLERANCE);
+ assertEquals(result.max, sensorData.maxRefreshRate, FLOAT_TOLERANCE);
+ }
+
+ @Test
+ public void testReturnsRefreshRateForDisplayAndSensor_proximitySensorNotSet() {
+ DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector);
+ DisplayManagerInternal localService = displayManager.new LocalService();
+ DisplayManagerService.BinderService displayManagerBinderService =
+ displayManager.new BinderService();
+ displayManager.overrideSensorManager(mSensorManager);
+
+ FakeDisplayDevice displayDevice = createFakeDisplayDevice(displayManager, new float[]{60f});
+ displayDevice.mDisplayDeviceConfig = mMockDisplayDeviceConfig;
+ int displayId = getDisplayIdForDisplayDevice(displayManager, displayManagerBinderService,
+ displayDevice);
+
+ String testSensorName = "testName";
+ String testSensorType = "testType";
+ Sensor testSensor = TestUtils.createSensor(testSensorType, testSensorName);
+
+ DisplayDeviceConfig.SensorData sensorData = new DisplayDeviceConfig.SensorData();
+ sensorData.type = testSensorType;
+ sensorData.name = testSensorName;
+ sensorData.minRefreshRate = 10f;
+ sensorData.maxRefreshRate = 100f;
+
+ when(mMockDisplayDeviceConfig.getProximitySensor()).thenReturn(null);
+ when(mSensorManager.getSensorList(Sensor.TYPE_ALL)).thenReturn(Collections.singletonList(
+ testSensor));
+
+ SurfaceControl.RefreshRateRange result = localService.getRefreshRateForDisplayAndSensor(
+ displayId, testSensorName, testSensorType);
+
+ assertNull(result);
+ }
+
private void testDisplayInfoFrameRateOverrideModeCompat(boolean compatChangeEnabled)
throws Exception {
DisplayManagerService displayManager =
diff --git a/services/tests/servicestests/src/com/android/server/display/NormalBrightnessModeControllerTest.java b/services/tests/servicestests/src/com/android/server/display/NormalBrightnessModeControllerTest.java
new file mode 100644
index 0000000..c379d6b
--- /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 817b245..642f54c 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 @@
}
@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 @@
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/TestUtils.java b/services/tests/servicestests/src/com/android/server/display/TestUtils.java
index 90d9bae..8b45145 100644
--- a/services/tests/servicestests/src/com/android/server/display/TestUtils.java
+++ b/services/tests/servicestests/src/com/android/server/display/TestUtils.java
@@ -18,6 +18,7 @@
import android.hardware.Sensor;
import android.hardware.SensorEvent;
+import android.hardware.input.InputSensorInfo;
import android.os.Parcel;
import android.os.SystemClock;
import android.view.DisplayAddress;
@@ -75,6 +76,12 @@
return sensor;
}
+ public static Sensor createSensor(String type, String name) {
+ return new Sensor(new InputSensorInfo(
+ name, "vendor", 0, 0, 0, 1f, 1f, 1, 1, 1, 1,
+ type, "", 0, 0, 0));
+ }
+
/**
* Create a custom {@link DisplayAddress} to ensure we're not relying on any specific
* display-address implementation in our code. Intentionally uses default object (reference)
diff --git a/services/tests/servicestests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java b/services/tests/servicestests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java
index e6d3bbc..c4f4838 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 @@
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 @@
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 @@
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 d9cf15b..b652576 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 @@
}
@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_OVERRIDE;
+ 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, 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_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;
- float brightnessState = Float.NaN;
+ 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, 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_DisplayIsInDoze_ConfigDoesAllow() {
+ mAutomaticBrightnessStrategy.setUseAutoBrightness(true);
+ int targetDisplayState = Display.STATE_DOZE;
boolean allowAutoBrightnessWhileDozing = true;
int brightnessReason = BrightnessReason.REASON_DOZE;
int policy = DisplayManagerInternal.DisplayPowerRequest.POLICY_DOZE;
@@ -131,23 +200,24 @@
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 @@
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 @@
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 @@
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/display/utils/SensorUtilsTest.java b/services/tests/servicestests/src/com/android/server/display/utils/SensorUtilsTest.java
new file mode 100644
index 0000000..4494b0c
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/display/utils/SensorUtilsTest.java
@@ -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.display.utils;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.mockito.Mockito.when;
+
+import android.annotation.Nullable;
+import android.hardware.Sensor;
+import android.hardware.SensorManager;
+import android.hardware.input.InputSensorInfo;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.annotations.Keep;
+import com.android.server.display.DisplayDeviceConfig.SensorData;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.Collections;
+import java.util.List;
+
+import junitparams.JUnitParamsRunner;
+import junitparams.Parameters;
+
+@SmallTest
+@RunWith(JUnitParamsRunner.class)
+public class SensorUtilsTest {
+
+ private static final String TEST_SENSOR_NAME = "test_sensor_name";
+ private static final String TEST_SENSOR_TYPE = "test_sensor_type";
+ private static final Sensor TEST_SENSOR = createSensor();
+ @Mock
+ private SensorManager mSensorManager;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ }
+
+ @Test
+ public void testNoSensorData() {
+ Sensor result = SensorUtils.findSensor(mSensorManager, null, Sensor.TYPE_LIGHT);
+ assertNull(result);
+ }
+
+ @Test
+ public void testNoSensorManager() {
+ Sensor result = SensorUtils.findSensor(null, new SensorData(), Sensor.TYPE_LIGHT);
+ assertNull(result);
+ }
+
+ @Keep
+ private static Object[][] findSensorData() {
+ // sensorName, sensorType, fallbackType, allSensors, defaultSensor, expectedResult
+ return new Object[][]{
+ // no data, no default
+ {null, null, Sensor.TYPE_LIGHT,
+ Collections.singletonList(TEST_SENSOR), null, null},
+ // matching name, matching type, no default
+ {TEST_SENSOR_NAME, TEST_SENSOR_TYPE, Sensor.TYPE_LIGHT,
+ Collections.singletonList(TEST_SENSOR), null, TEST_SENSOR},
+ // matching name, no default
+ {TEST_SENSOR_NAME, null, Sensor.TYPE_LIGHT,
+ Collections.singletonList(TEST_SENSOR), null, TEST_SENSOR},
+ // not matching name, no default
+ {"not_matching_name", null, Sensor.TYPE_LIGHT,
+ Collections.singletonList(TEST_SENSOR), null, null},
+ // matching type, no default
+ {null, TEST_SENSOR_TYPE, Sensor.TYPE_LIGHT,
+ Collections.singletonList(TEST_SENSOR), null, TEST_SENSOR},
+ // not matching type, no default
+ {null, "not_matching_type", Sensor.TYPE_LIGHT,
+ Collections.singletonList(TEST_SENSOR), null, null},
+ // not matching type, matching name, no default
+ {TEST_SENSOR_NAME, "not_matching_type", Sensor.TYPE_LIGHT,
+ Collections.singletonList(TEST_SENSOR), null, null},
+ // not matching name, matching type, no default
+ {"not_matching_name", TEST_SENSOR_TYPE, Sensor.TYPE_LIGHT,
+ Collections.singletonList(TEST_SENSOR), null, null},
+ // not matching type, not matching name, no default
+ {"not_matching_name", "not_matching_type", Sensor.TYPE_LIGHT,
+ Collections.singletonList(TEST_SENSOR), null, null},
+ // not matching type, not matching name, with default
+ {"not_matching_name", "not_matching_type", Sensor.TYPE_LIGHT,
+ Collections.singletonList(TEST_SENSOR), TEST_SENSOR, TEST_SENSOR},
+ // no data, with default
+ {null, null, Sensor.TYPE_LIGHT,
+ Collections.singletonList(TEST_SENSOR), TEST_SENSOR, TEST_SENSOR},
+ // empty data, with default
+ {"", "", Sensor.TYPE_LIGHT,
+ Collections.singletonList(TEST_SENSOR), TEST_SENSOR, TEST_SENSOR},
+ // empty data, with default, no fallback
+ {"", "", SensorUtils.NO_FALLBACK,
+ Collections.singletonList(TEST_SENSOR), TEST_SENSOR, null},
+ };
+ }
+
+ @Test
+ @Parameters(method = "findSensorData")
+ public void testFindSensor(@Nullable String sensorName, @Nullable String sensorType,
+ int fallbackType, List<Sensor> allSensors, @Nullable Sensor defaultSensor,
+ @Nullable Sensor expectedResult) {
+ when(mSensorManager.getSensorList(Sensor.TYPE_ALL)).thenReturn(allSensors);
+ when(mSensorManager.getDefaultSensor(fallbackType)).thenReturn(defaultSensor);
+
+ SensorData sensorData = new SensorData();
+ sensorData.name = sensorName;
+ sensorData.type = sensorType;
+
+ Sensor result = SensorUtils.findSensor(mSensorManager, sensorData, fallbackType);
+
+ assertEquals(expectedResult, result);
+ }
+
+ private static Sensor createSensor() {
+ return new Sensor(new InputSensorInfo(
+ TEST_SENSOR_NAME, "vendor", 0, 0, 0, 1f, 1f, 1, 1, 1, 1,
+ TEST_SENSOR_TYPE, "", 0, 0, 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 0000000..98b4628
--- /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 416b1f4..c36122b 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 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 677144c..72066ea 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.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.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.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 @@
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 @@
@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 @@
}
@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 @@
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 64c05dc..3f4a4fb 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.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 @@
.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 @@
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 @@
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 @@
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 @@
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 @@
@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)
+ 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())
+ 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))
+ 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 BRIGHTNESS_VALUE_FOR_LEVEL.size) {
- dataStore.setKeyboardBacklightBrightness(
+ for (level in 1 until DEFAULT_BRIGHTNESS_VALUE_FOR_LEVEL.size) {
+ dataStore.setKeyboardBacklightBrightness(
keyboardWithBacklight.descriptor,
LIGHT_ID,
- BRIGHTNESS_VALUE_FOR_LEVEL[level] - 1
+ 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)
+ }
+ }
+ }
+
+ @Test
+ fun testRestoreBacklightOnInputDeviceChanged() {
+ 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()
+ )
+
+ `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() {
+ 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)
+
+ 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 $maxLevel",
+ maxLevel,
+ lastBacklightState!!.maxBrightnessLevel
+ )
+ assertEquals(
+ "Backlight state isTriggeredByKeyPress should be true",
+ true,
+ lastBacklightState!!.isTriggeredByKeyPress
+ )
+
+ // Unregister listener
+ keyboardBacklightController.unregisterKeyboardBacklightListener(listener, 0)
+
+ lastBacklightState = null
+ incrementKeyboardBacklight(DEVICE_ID)
+
+ assertNull("Listener should not receive any updates", lastBacklightState)
+ }
+ }
+
+ @Test
+ fun testKeyboardBacklight_userActivity() {
+ 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(BRIGHTNESS_VALUE_FOR_LEVEL[level], 0, 0, 0),
- lightColorMap[LIGHT_ID]
+ "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.onInputDeviceRemoved(DEVICE_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 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
- )
-
- 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]
- )
- }
-
- @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)
-
- // Register backlight listener
- val listener = KeyboardBacklightListener()
- keyboardBacklightController.registerKeyboardBacklightListener(listener, 0)
-
- 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
- )
-
- // Unregister listener
- keyboardBacklightController.unregisterKeyboardBacklightListener(listener, 0)
-
- lastBacklightState = null
- incrementKeyboardBacklight(DEVICE_ID)
-
- 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
- )
-
- 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]
- )
- }
-
- @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 @@
)
}
+ @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 @@
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 0000000..c9724a3
--- /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 e960e99..fe2ac17 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 @@
.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 36dc6c5..a029db9 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 @@
}
@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 10ed882..23f14f8 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 @@
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 0000000..70150c5
--- /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 6bcda3f..dc86e5f 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.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 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 workProfileUser = createProfileForUser("Work Profile user",
- UserManager.USER_TYPE_PROFILE_MANAGED,
- parentUser.id);
+ 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);
+ }
+ }
+
+ // 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 cebc540..eaf4838 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.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 @@
@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 @@
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 beab107..0b147c3 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 @@
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.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 @@
// 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 6668f85..a88ab18 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 @@
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 @@
.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 @@
@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 @@
|| 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 48ad86d..47340c1 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 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.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.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 @@
@Mock Context mContext;
@Mock ZenModeHelper mMockZenModeHelper;
@Mock AppOpsManager mAppOpsManager;
+ @Mock PermissionManager mPermissionManager;
private NotificationManager.Policy mTestNotificationPolicy;
@@ -218,6 +222,8 @@
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 @@
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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
+ "</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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 @@
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 @@
@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 @@
@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 @@
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 68aa1dc..131aaa0 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 @@
@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);
+
+ // ... 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);
}
- 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);
- }
-
- /** @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 @@
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 e5371975..5f48f3c 100644
--- a/services/tests/wmtests/Android.bp
+++ b/services/tests/wmtests/Android.bp
@@ -82,7 +82,10 @@
],
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 37e5da5..9c79375 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 5863e9d..6a9f283 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 @@
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 @@
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 676bfb0..2015ae9 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 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 @@
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 0000000..fe8017e
--- /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 d383024..766a88f 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.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.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.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 @@
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 @@
@Mock private StatusBarManagerInternal mStatusBarManagerInternal;
+ @Mock private KeyguardServiceDelegate mKeyguardServiceDelegate;
+
private StaticMockitoSession mMockitoSession;
private HandlerThread mHandlerThread;
private Handler mHandler;
@@ -151,6 +162,10 @@
Supplier<GlobalActions> getGlobalActionsFactory() {
return () -> mGlobalActions;
}
+
+ KeyguardServiceDelegate getKeyguardServiceDelegate() {
+ return mKeyguardServiceDelegate;
+ }
}
TestPhoneWindowManager(Context context) {
@@ -158,12 +173,12 @@
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 @@
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 @@
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 @@
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 @@
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 e881039..3db53eb 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 @@
// 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 @@
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 2b589bf..d179338 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 @@
@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 3a456fb..dc4e47d 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 @@
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 @@
}
@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 b0609dd..dd90e04 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 @@
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 8e015d4..769a309 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 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 @@
@Test
public void testOnScreenRotationAnimationFinished_treatmentNotEnabled_doNotShowToast() {
- when(mLetterboxConfiguration.isCameraCompatTreatmentEnabled(
- /* checkDeviceConfig */ anyBoolean()))
+ when(mLetterboxConfiguration.isCameraCompatTreatmentEnabled())
.thenReturn(false);
spyOn(mDisplayRotationCompatPolicy);
@@ -238,8 +236,7 @@
@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 @@
@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 d29b18f..b105703 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 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 @@
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 @@
@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 a8fc25f..204cbf7 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 @@
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 d7e736d..114796d 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 @@
// 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 @@
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 2b7a06b..0000000
--- 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 7d507e9..81a3794 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_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.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 @@
@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 @@
@Test
public void testShouldRefreshActivityForCameraCompat_flagIsDisabled_returnsFalse() {
doReturn(false).when(mLetterboxConfiguration)
- .isCameraCompatTreatmentEnabled(/* checkDeviceConfig */ true);
+ .isCameraCompatTreatmentEnabled();
assertFalse(mController.shouldRefreshActivityForCameraCompat());
}
@@ -314,7 +316,7 @@
@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 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 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 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 @@
@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 @@
@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 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 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 @@
@Test
public void testShouldForceRotateForCameraCompat_flagIsDisabled_returnsFalse() {
doReturn(false).when(mLetterboxConfiguration)
- .isCameraCompatTreatmentEnabled(/* checkDeviceConfig */ true);
+ .isCameraCompatTreatmentEnabled();
assertFalse(mController.shouldForceRotateForCameraCompat());
}
@@ -415,7 +417,7 @@
@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 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 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 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 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 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 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 @@
@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 @@
@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 @@
@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 0ccb0d0..b02b774 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.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 @@
}
@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 6a89b55..3908947 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.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 @@
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 @@
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 0000000..7d44e11
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/SynchedDeviceConfigTests.java
@@ -0,0 +1,144 @@
+/*
+ * 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.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+
+import android.app.ActivityThread;
+import android.platform.test.annotations.Presubmit;
+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)
+ .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
+ }
+
+ @Test
+ public void testIsEnabled() {
+ assertFlagEnabled(/* key */ "key1", /* expected */ true);
+ assertFlagEnabled(/* key */ "key2", /* expected */ true);
+ assertFlagEnabled(/* key */ "key3", /* expected */ false);
+ assertFlagEnabled(/* key */ "key4", /* expected */ false);
+ }
+
+ @Test
+ public void testWhenUpdated_onlyEnabledChanges() {
+ final CountDownLatch countDownLatch = new CountDownLatch(4);
+ spyOn(mDeviceConfig);
+ doAnswer(invocation -> {
+ invocation.callRealMethod();
+ countDownLatch.countDown();
+ return null;
+ }).when(mDeviceConfig).onPropertiesChanged(any());
+ try {
+ // We update all the keys
+ updateProperty(/* key */ "key1", /* value */ false);
+ 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());
+ }
+ }
+
+ 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 0000000..df11a44
--- /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 d3f6818..197ee92 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 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.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 @@
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 5b1c6b1..dc5b75a 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -9730,6 +9730,27 @@
public static final String KEY_IWLAN_HANDOVER_POLICY_STRING_ARRAY =
"iwlan_handover_policy_string_array";
+ /**
+ * Score table for {@link TelephonyManager#MOBILE_DATA_POLICY_AUTO_DATA_SWITCH}. The score is
+ * used in conjunction with a tolerance value defined in resource config
+ * {@code auto_data_switch_score_tolerance}, greater than which device will switch to the sub
+ * with higher score.
+ * Possible keys are network type name string(also see {@link #KEY_BANDWIDTH_STRING_ARRAY}).
+ * Value should be "score_level_0, score_level_1, score_level_2, score_level_3,score_level_4".
+ * Each network type must have 5 scores correspond to {@link CellSignalStrength}, where score is
+ * a non-negative integer. A score of 0 is treated the same as data out of service.
+ *
+ * For NR (5G), the following network names should be used:
+ * - NR_NSA: NR NSA, sub-6 frequencies
+ * - NR_NSA_MMWAVE: NR NSA, mmwave frequencies
+ * - NR_SA: NR SA, sub-6 frequencies
+ * - NR_SA_MMWAVE: NR SA, mmwave frequencies
+ *
+ * @hide
+ */
+ public static final String KEY_AUTO_DATA_SWITCH_RAT_SIGNAL_SCORE_BUNDLE =
+ "auto_data_switch_rat_signal_score_string_bundle";
+
/** The default value for every variable. */
private static final PersistableBundle sDefaults;
@@ -9876,9 +9897,9 @@
sDefaults.putString(KEY_CARRIER_ERI_FILE_NAME_STRING, "eri.xml");
sDefaults.putInt(KEY_DURATION_BLOCKING_DISABLED_AFTER_EMERGENCY_INT, 7200);
sDefaults.putStringArray(KEY_CARRIER_METERED_APN_TYPES_STRINGS,
- new String[]{"default", "mms", "dun", "supl"});
+ new String[]{"default", "mms", "dun", "supl", "enterprise"});
sDefaults.putStringArray(KEY_CARRIER_METERED_ROAMING_APN_TYPES_STRINGS,
- new String[]{"default", "mms", "dun", "supl"});
+ new String[]{"default", "mms", "dun", "supl", "enterprise"});
sDefaults.putIntArray(KEY_ONLY_SINGLE_DC_ALLOWED_INT_ARRAY,
new int[] {TelephonyManager.NETWORK_TYPE_CDMA, TelephonyManager.NETWORK_TYPE_1xRTT,
TelephonyManager.NETWORK_TYPE_EVDO_0, TelephonyManager.NETWORK_TYPE_EVDO_A,
@@ -10404,6 +10425,51 @@
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 4ba538e..8faedeb 100644
--- a/tests/FlickerTests/Android.bp
+++ b/tests/FlickerTests/Android.bp
@@ -23,14 +23,54 @@
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: "FlickerTestsNotification-src",
+ srcs: ["src/**/notification/*.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 +82,110 @@
"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: "FlickerTestsNotification",
+ defaults: ["FlickerTestsDefault"],
+ additional_manifests: ["manifests/AndroidManifestNotification.xml"],
+ package_name: "com.android.server.wm.flicker.notification",
+ instrumentation_target_package: "com.android.server.wm.flicker.notification",
+ srcs: [
+ ":FlickerTestsBase-src",
+ ":FlickerTestsNotification-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 32ff243..0000000
--- 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 0000000..1ede943
--- /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
similarity index 68%
rename from tests/FlickerTests/AndroidManifest.xml
rename to tests/FlickerTests/manifests/AndroidManifest.xml
index 462f91b..1a34d9e 100644
--- a/tests/FlickerTests/AndroidManifest.xml
+++ b/tests/FlickerTests/manifests/AndroidManifest.xml
@@ -1,20 +1,22 @@
<?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"
+ xmlns:tools="http://schemas.android.com/tools"
package="com.android.server.wm.flicker">
<uses-sdk android:minSdkVersion="29" android:targetSdkVersion="29"/>
@@ -46,10 +48,11 @@
<application android:requestLegacyExternalStorage="true" android:largeHeap="true">
<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>
+ <!-- (b/197936012) Remove startup provider due to test timeout issue -->
+ <provider
+ android:name="androidx.startup.InitializationProvider"
+ android:authorities="${applicationId}.androidx-startup"
+ tools:node="remove" />
+ </application>
</manifest>
diff --git a/tests/FlickerTests/manifests/AndroidManifestAppClose.xml b/tests/FlickerTests/manifests/AndroidManifestAppClose.xml
new file mode 100644
index 0000000..4cdcb90
--- /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 0000000..659a745
--- /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 0000000..abd03af
--- /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/AndroidManifestNotification.xml b/tests/FlickerTests/manifests/AndroidManifestNotification.xml
new file mode 100644
index 0000000..ad33dee
--- /dev/null
+++ b/tests/FlickerTests/manifests/AndroidManifestNotification.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.notification"
+ 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 0000000..47749b8
--- /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 0000000..203035d
--- /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 0000000..2852cf2
--- /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/BaseTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/BaseTest.kt
index efd9b00..7c9c05d 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/BaseTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/BaseTest.kt
@@ -21,7 +21,7 @@
import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.flicker.junit.FlickerBuilderProvider
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.util.Log
import androidx.test.platform.app.InstrumentationRegistry
import com.android.launcher3.tapl.LauncherInstrumentation
@@ -37,7 +37,7 @@
abstract class BaseTest
@JvmOverloads
constructor(
- protected val flicker: FlickerTest,
+ protected val flicker: LegacyFlickerTest,
protected val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation(),
protected val tapl: LauncherInstrumentation = LauncherInstrumentation()
) {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
index ed9e14f..1fdbe7f 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
@@ -23,14 +23,14 @@
import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.common.traces.component.IComponentNameMatcher
import android.tools.common.traces.wm.WindowManagerTrace
-import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.helpers.WindowUtils
/**
* Checks that [ComponentNameMatcher.STATUS_BAR] window is visible and above the app windows in all
* WM trace entries
*/
-fun FlickerTest.statusBarWindowIsAlwaysVisible() {
+fun LegacyFlickerTest.statusBarWindowIsAlwaysVisible() {
assertWm { this.isAboveAppWindowVisible(ComponentNameMatcher.STATUS_BAR) }
}
@@ -38,7 +38,7 @@
* Checks that [ComponentNameMatcher.NAV_BAR] window is visible and above the app windows in all WM
* trace entries
*/
-fun FlickerTest.navBarWindowIsAlwaysVisible() {
+fun LegacyFlickerTest.navBarWindowIsAlwaysVisible() {
assertWm { this.isAboveAppWindowVisible(ComponentNameMatcher.NAV_BAR) }
}
@@ -46,7 +46,7 @@
* Checks that [ComponentNameMatcher.NAV_BAR] window is visible and above the app windows at the
* start and end of the WM trace
*/
-fun FlickerTest.navBarWindowIsVisibleAtStartAndEnd() {
+fun LegacyFlickerTest.navBarWindowIsVisibleAtStartAndEnd() {
this.navBarWindowIsVisibleAtStart()
this.navBarWindowIsVisibleAtEnd()
}
@@ -55,7 +55,7 @@
* Checks that [ComponentNameMatcher.NAV_BAR] window is visible and above the app windows at the
* start of the WM trace
*/
-fun FlickerTest.navBarWindowIsVisibleAtStart() {
+fun LegacyFlickerTest.navBarWindowIsVisibleAtStart() {
assertWmStart { this.isAboveAppWindowVisible(ComponentNameMatcher.NAV_BAR) }
}
@@ -63,7 +63,7 @@
* Checks that [ComponentNameMatcher.NAV_BAR] window is visible and above the app windows at the end
* of the WM trace
*/
-fun FlickerTest.navBarWindowIsVisibleAtEnd() {
+fun LegacyFlickerTest.navBarWindowIsVisibleAtEnd() {
assertWmEnd { this.isAboveAppWindowVisible(ComponentNameMatcher.NAV_BAR) }
}
@@ -71,7 +71,7 @@
* Checks that [ComponentNameMatcher.TASK_BAR] window is visible and above the app windows in all WM
* trace entries
*/
-fun FlickerTest.taskBarWindowIsAlwaysVisible() {
+fun LegacyFlickerTest.taskBarWindowIsAlwaysVisible() {
assertWm { this.isAboveAppWindowVisible(ComponentNameMatcher.TASK_BAR) }
}
@@ -79,7 +79,7 @@
* Checks that [ComponentNameMatcher.TASK_BAR] window is visible and above the app windows in all WM
* trace entries
*/
-fun FlickerTest.taskBarWindowIsVisibleAtEnd() {
+fun LegacyFlickerTest.taskBarWindowIsVisibleAtEnd() {
assertWmEnd { this.isAboveAppWindowVisible(ComponentNameMatcher.TASK_BAR) }
}
@@ -93,43 +93,45 @@
* @param allStates if all states should be checked, othersie, just initial and final
*/
@JvmOverloads
-fun FlickerTest.entireScreenCovered(allStates: Boolean = true) {
+fun LegacyFlickerTest.entireScreenCovered(allStates: Boolean = true) {
if (allStates) {
assertLayers {
this.invoke("entireScreenCovered") { entry ->
- entry.entry.displays.filter { it.isOn }.forEach { display ->
- entry.visibleRegion().coversAtLeast(display.layerStackSpace)
- }
+ entry.entry.displays
+ .filter { it.isOn }
+ .forEach { display ->
+ entry.visibleRegion().coversAtLeast(display.layerStackSpace)
+ }
}
}
} else {
assertLayersStart {
- this.entry.displays.filter { it.isOn }.forEach { display ->
- this.visibleRegion().coversAtLeast(display.layerStackSpace)
- }
+ this.entry.displays
+ .filter { it.isOn }
+ .forEach { display -> this.visibleRegion().coversAtLeast(display.layerStackSpace) }
}
assertLayersEnd {
- this.entry.displays.filter { it.isOn }.forEach { display ->
- this.visibleRegion().coversAtLeast(display.layerStackSpace)
- }
+ this.entry.displays
+ .filter { it.isOn }
+ .forEach { display -> this.visibleRegion().coversAtLeast(display.layerStackSpace) }
}
}
}
/** Checks that [ComponentNameMatcher.NAV_BAR] layer is visible at the start of the SF trace */
-fun FlickerTest.navBarLayerIsVisibleAtStart() {
+fun LegacyFlickerTest.navBarLayerIsVisibleAtStart() {
assertLayersStart { this.isVisible(ComponentNameMatcher.NAV_BAR) }
}
/** Checks that [ComponentNameMatcher.NAV_BAR] layer is visible at the end of the SF trace */
-fun FlickerTest.navBarLayerIsVisibleAtEnd() {
+fun LegacyFlickerTest.navBarLayerIsVisibleAtEnd() {
assertLayersEnd { this.isVisible(ComponentNameMatcher.NAV_BAR) }
}
/**
* Checks that [ComponentNameMatcher.NAV_BAR] layer is visible at the start and end of the SF trace
*/
-fun FlickerTest.navBarLayerIsVisibleAtStartAndEnd() {
+fun LegacyFlickerTest.navBarLayerIsVisibleAtStartAndEnd() {
this.navBarLayerIsVisibleAtStart()
this.navBarLayerIsVisibleAtEnd()
}
@@ -137,18 +139,18 @@
/**
* Checks that [ComponentNameMatcher.TASK_BAR] layer is visible at the start and end of the SF trace
*/
-fun FlickerTest.taskBarLayerIsVisibleAtStartAndEnd() {
+fun LegacyFlickerTest.taskBarLayerIsVisibleAtStartAndEnd() {
this.taskBarLayerIsVisibleAtStart()
this.taskBarLayerIsVisibleAtEnd()
}
/** Checks that [ComponentNameMatcher.TASK_BAR] layer is visible at the start of the SF trace */
-fun FlickerTest.taskBarLayerIsVisibleAtStart() {
+fun LegacyFlickerTest.taskBarLayerIsVisibleAtStart() {
assertLayersStart { this.isVisible(ComponentNameMatcher.TASK_BAR) }
}
/** Checks that [ComponentNameMatcher.TASK_BAR] layer is visible at the end of the SF trace */
-fun FlickerTest.taskBarLayerIsVisibleAtEnd() {
+fun LegacyFlickerTest.taskBarLayerIsVisibleAtEnd() {
assertLayersEnd { this.isVisible(ComponentNameMatcher.TASK_BAR) }
}
@@ -156,7 +158,7 @@
* Checks that [ComponentNameMatcher.STATUS_BAR] layer is visible at the start and end of the SF
* trace
*/
-fun FlickerTest.statusBarLayerIsVisibleAtStartAndEnd() {
+fun LegacyFlickerTest.statusBarLayerIsVisibleAtStartAndEnd() {
assertLayersStart { this.isVisible(ComponentNameMatcher.STATUS_BAR) }
assertLayersEnd { this.isVisible(ComponentNameMatcher.STATUS_BAR) }
}
@@ -165,7 +167,7 @@
* Asserts that the [ComponentNameMatcher.NAV_BAR] layer is at the correct position at the start of
* the SF trace
*/
-fun FlickerTest.navBarLayerPositionAtStart() {
+fun LegacyFlickerTest.navBarLayerPositionAtStart() {
assertLayersStart {
val display =
this.entry.displays.firstOrNull { !it.isVirtual } ?: error("There is no display!")
@@ -180,7 +182,7 @@
* Asserts that the [ComponentNameMatcher.NAV_BAR] layer is at the correct position at the end of
* the SF trace
*/
-fun FlickerTest.navBarLayerPositionAtEnd() {
+fun LegacyFlickerTest.navBarLayerPositionAtEnd() {
assertLayersEnd {
val display =
this.entry.displays.minByOrNull { it.id }
@@ -196,7 +198,7 @@
* Asserts that the [ComponentNameMatcher.NAV_BAR] layer is at the correct position at the start and
* end of the SF trace
*/
-fun FlickerTest.navBarLayerPositionAtStartAndEnd() {
+fun LegacyFlickerTest.navBarLayerPositionAtStartAndEnd() {
navBarLayerPositionAtStart()
navBarLayerPositionAtEnd()
}
@@ -205,7 +207,7 @@
* Asserts that the [ComponentNameMatcher.STATUS_BAR] layer is at the correct position at the start
* of the SF trace
*/
-fun FlickerTest.statusBarLayerPositionAtStart(
+fun LegacyFlickerTest.statusBarLayerPositionAtStart(
wmTrace: WindowManagerTrace? = this.reader.readWmTrace()
) {
// collect navbar position for the equivalent WM state
@@ -221,7 +223,7 @@
* Asserts that the [ComponentNameMatcher.STATUS_BAR] layer is at the correct position at the end of
* the SF trace
*/
-fun FlickerTest.statusBarLayerPositionAtEnd(
+fun LegacyFlickerTest.statusBarLayerPositionAtEnd(
wmTrace: WindowManagerTrace? = this.reader.readWmTrace()
) {
// collect navbar position for the equivalent WM state
@@ -237,7 +239,7 @@
* Asserts that the [ComponentNameMatcher.STATUS_BAR] layer is at the correct position at the start
* and end of the SF trace
*/
-fun FlickerTest.statusBarLayerPositionAtStartAndEnd() {
+fun LegacyFlickerTest.statusBarLayerPositionAtStartAndEnd() {
statusBarLayerPositionAtStart()
statusBarLayerPositionAtEnd()
}
@@ -246,7 +248,9 @@
* Asserts that the visibleRegion of the [ComponentNameMatcher.SNAPSHOT] layer can cover the
* visibleRegion of the given app component exactly
*/
-fun FlickerTest.snapshotStartingWindowLayerCoversExactlyOnApp(component: IComponentNameMatcher) {
+fun LegacyFlickerTest.snapshotStartingWindowLayerCoversExactlyOnApp(
+ component: IComponentNameMatcher
+) {
assertLayers {
invoke("snapshotStartingWindowLayerCoversExactlyOnApp") {
val snapshotLayers =
@@ -307,7 +311,7 @@
* otherwise we won't and the layer must appear immediately.
* ```
*/
-fun FlickerTest.replacesLayer(
+fun LegacyFlickerTest.replacesLayer(
originalLayer: IComponentNameMatcher,
newLayer: IComponentNameMatcher,
ignoreEntriesWithRotationLayer: Boolean = false,
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/ActivityEmbeddingTestBase.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/ActivityEmbeddingTestBase.kt
index 7ef4d93..45cd65d 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/ActivityEmbeddingTestBase.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/ActivityEmbeddingTestBase.kt
@@ -16,12 +16,12 @@
package com.android.server.wm.flicker.activityembedding
-import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTest
import com.android.server.wm.flicker.BaseTest
import com.android.server.wm.flicker.helpers.ActivityEmbeddingAppHelper
import org.junit.Before
-abstract class ActivityEmbeddingTestBase(flicker: FlickerTest) : BaseTest(flicker) {
+abstract class ActivityEmbeddingTestBase(flicker: LegacyFlickerTest) : BaseTest(flicker) {
val testApp = ActivityEmbeddingAppHelper(instrumentation)
@Before
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/RTLStartSecondaryWithPlaceholderTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/RTLStartSecondaryWithPlaceholderTest.kt
new file mode 100644
index 0000000..4bc17ed
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/RTLStartSecondaryWithPlaceholderTest.kt
@@ -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.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.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
+import androidx.test.filters.RequiresDevice
+import com.android.server.wm.flicker.helpers.ActivityEmbeddingAppHelper
+import org.junit.FixMethodOrder
+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: LegacyFlickerTest) :
+ 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 [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and
+ * navigation modes.
+ */
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams() = LegacyFlickerTestFactory.nonRotationTests()
+ }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/close/CloseSecondaryActivityInSplitTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/close/CloseSecondaryActivityInSplitTest.kt
new file mode 100644
index 0000000..7a582f7
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/close/CloseSecondaryActivityInSplitTest.kt
@@ -0,0 +1,135 @@
+/*
+ * 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.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
+import androidx.test.filters.RequiresDevice
+import com.android.server.wm.flicker.helpers.ActivityEmbeddingAppHelper
+import org.junit.FixMethodOrder
+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: LegacyFlickerTest) :
+ ActivityEmbeddingTestBase(flicker) {
+
+ override val transition: FlickerBuilder.() -> Unit = {
+ setup {
+ tapl.setExpectedRotationCheckEnabled(false)
+ // Launches fullscreen A.
+ testApp.launchViaIntent(wmHelper)
+ // Launches a split A|B and waits for both activities to show.
+ testApp.launchSecondaryActivity(wmHelper)
+ // Get fullscreen bounds
+ startDisplayBounds =
+ wmHelper.currentState.layerState.physicalDisplayBounds
+ ?: error("Can't get display bounds")
+ }
+ 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 [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and
+ * navigation modes.
+ */
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams() = LegacyFlickerTestFactory.nonRotationTests()
+ }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/MainActivityStartsSecondaryWithAlwaysExpandTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/MainActivityStartsSecondaryWithAlwaysExpandTest.kt
new file mode 100644
index 0000000..48aaebd
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/MainActivityStartsSecondaryWithAlwaysExpandTest.kt
@@ -0,0 +1,151 @@
+/*
+ * 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.FlakyTest
+import android.platform.test.annotations.Presubmit
+import android.tools.common.datatypes.Rect
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
+import androidx.test.filters.RequiresDevice
+import com.android.server.wm.flicker.helpers.ActivityEmbeddingAppHelper
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * 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: LegacyFlickerTest) :
+ ActivityEmbeddingTestBase(flicker) {
+
+ /** {@inheritDoc} */
+ override val transition: FlickerBuilder.() -> Unit = {
+ setup {
+ tapl.setExpectedRotationCheckEnabled(false)
+ // Launch a split
+ testApp.launchViaIntent(wmHelper)
+ testApp.launchSecondaryActivity(wmHelper)
+ startDisplayBounds =
+ wmHelper.currentState.layerState.physicalDisplayBounds ?: error("Display not found")
+ }
+ transitions {
+ // Launch C with alwaysExpand
+ testApp.launchAlwaysExpandActivity(wmHelper)
+ }
+ teardown {
+ tapl.goHome()
+ testApp.exit(wmHelper)
+ }
+ }
+
+ @FlakyTest(bugId = 286952194)
+ @Presubmit
+ @Test
+ override fun navBarWindowIsVisibleAtStartAndEnd() {}
+
+ @FlakyTest(bugId = 286952194)
+ @Presubmit
+ @Test
+ override fun statusBarWindowIsAlwaysVisible() {}
+
+ @FlakyTest(bugId = 286952194)
+ @Presubmit
+ @Test
+ override fun statusBarLayerPositionAtStartAndEnd() {}
+
+ /** 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. */
+ @FlakyTest(bugId = 286952194)
+ @Presubmit
+ @Test
+ fun endsWithAlwaysExpandActivityOnTop() {
+ flicker.assertWmEnd {
+ this.isAppWindowOnTop(ActivityEmbeddingAppHelper.ALWAYS_EXPAND_ACTIVITY_COMPONENT)
+ }
+ }
+
+ companion object {
+ /** {@inheritDoc} */
+ private var startDisplayBounds = Rect.EMPTY
+ /**
+ * Creates the test configurations.
+ *
+ * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and
+ * navigation modes.
+ */
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams() = LegacyFlickerTestFactory.nonRotationTests()
+ }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/OpenActivityEmbeddingPlaceholderSplitTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenActivityEmbeddingPlaceholderSplitTest.kt
similarity index 91%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/OpenActivityEmbeddingPlaceholderSplitTest.kt
rename to tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenActivityEmbeddingPlaceholderSplitTest.kt
index ed17059..27a5bd0 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
@@ -19,8 +19,8 @@
import android.platform.test.annotations.Presubmit
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.helpers.ActivityEmbeddingAppHelper
import org.junit.FixMethodOrder
@@ -39,7 +39,7 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class OpenActivityEmbeddingPlaceholderSplitTest(flicker: FlickerTest) :
+class OpenActivityEmbeddingPlaceholderSplitTest(flicker: LegacyFlickerTest) :
ActivityEmbeddingTestBase(flicker) {
/** {@inheritDoc} */
@@ -117,13 +117,11 @@
/**
* Creates the test configurations.
*
- * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and
+ * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and
* navigation modes.
*/
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): Collection<FlickerTest> {
- return FlickerTestFactory.nonRotationTests()
- }
+ fun getParams() = LegacyFlickerTestFactory.nonRotationTests()
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/OpenActivityEmbeddingSecondaryToSplitTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenActivityEmbeddingSecondaryToSplitTest.kt
similarity index 90%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/OpenActivityEmbeddingSecondaryToSplitTest.kt
rename to tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenActivityEmbeddingSecondaryToSplitTest.kt
index 8638288..16dbfce 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
@@ -20,8 +20,8 @@
import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.helpers.ActivityEmbeddingAppHelper
import org.junit.FixMethodOrder
@@ -39,7 +39,7 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class OpenActivityEmbeddingSecondaryToSplitTest(flicker: FlickerTest) :
+class OpenActivityEmbeddingSecondaryToSplitTest(flicker: LegacyFlickerTest) :
ActivityEmbeddingTestBase(flicker) {
/** {@inheritDoc} */
@@ -110,13 +110,11 @@
/**
* Creates the test configurations.
*
- * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and
+ * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and
* navigation modes.
*/
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): Collection<FlickerTest> {
- return FlickerTestFactory.nonRotationTests()
- }
+ fun getParams() = LegacyFlickerTestFactory.nonRotationTests()
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/rotation/RotateSplitNoChangeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/rotation/RotateSplitNoChangeTest.kt
new file mode 100644
index 0000000..856c9e2
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/rotation/RotateSplitNoChangeTest.kt
@@ -0,0 +1,146 @@
+/*
+ * 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 android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
+import androidx.test.filters.RequiresDevice
+import com.android.server.wm.flicker.helpers.ActivityEmbeddingAppHelper
+import com.android.server.wm.flicker.rotation.RotationTransition
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+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: LegacyFlickerTest) : 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 [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and
+ * navigation modes.
+ */
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams() = LegacyFlickerTestFactory.rotationTests()
+ }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt
index 10b71ff..865d5b4 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt
@@ -20,8 +20,8 @@
import android.tools.device.flicker.annotation.FlickerServiceCompatible
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
import org.junit.FixMethodOrder
import org.junit.Test
@@ -76,7 +76,7 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class CloseAppBackButtonTest(flicker: FlickerTest) : CloseAppTransition(flicker) {
+open class CloseAppBackButtonTest(flicker: LegacyFlickerTest) : CloseAppTransition(flicker) {
/** {@inheritDoc} */
override val transition: FlickerBuilder.() -> Unit
get() = {
@@ -96,13 +96,11 @@
/**
* Creates the test configurations.
*
- * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and
+ * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and
* navigation modes.
*/
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<FlickerTest> {
- return FlickerTestFactory.nonRotationTests()
- }
+ fun getParams() = LegacyFlickerTestFactory.nonRotationTests()
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTestCfArm.kt
index 9fa84019..c108633 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTestCfArm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTestCfArm.kt
@@ -18,8 +18,8 @@
import android.tools.device.flicker.annotation.FlickerServiceCompatible
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@@ -29,18 +29,16 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class CloseAppBackButtonTestCfArm(flicker: FlickerTest) : CloseAppBackButtonTest(flicker) {
+class CloseAppBackButtonTestCfArm(flicker: LegacyFlickerTest) : CloseAppBackButtonTest(flicker) {
companion object {
/**
* Creates the test configurations.
*
- * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and
+ * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and
* navigation modes.
*/
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): List<FlickerTest> {
- return FlickerTestFactory.nonRotationTests()
- }
+ fun getParams() = LegacyFlickerTestFactory.nonRotationTests()
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt
index e5bd350..ea9710c6 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt
@@ -20,8 +20,8 @@
import android.tools.device.flicker.annotation.FlickerServiceCompatible
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
import org.junit.FixMethodOrder
import org.junit.Test
@@ -76,7 +76,7 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class CloseAppHomeButtonTest(flicker: FlickerTest) : CloseAppTransition(flicker) {
+open class CloseAppHomeButtonTest(flicker: LegacyFlickerTest) : CloseAppTransition(flicker) {
/** {@inheritDoc} */
override val transition: FlickerBuilder.() -> Unit
get() = {
@@ -101,8 +101,6 @@
/** Creates the test configurations. */
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): Collection<FlickerTest> {
- return FlickerTestFactory.nonRotationTests()
- }
+ fun getParams() = LegacyFlickerTestFactory.nonRotationTests()
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTestCfArm.kt
index 136995a..d65555a 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTestCfArm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTestCfArm.kt
@@ -18,8 +18,8 @@
import android.tools.device.flicker.annotation.FlickerServiceCompatible
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@@ -29,13 +29,11 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class CloseAppHomeButtonTestCfArm(flicker: FlickerTest) : CloseAppHomeButtonTest(flicker) {
+class CloseAppHomeButtonTestCfArm(flicker: LegacyFlickerTest) : CloseAppHomeButtonTest(flicker) {
companion object {
/** Creates the test configurations. */
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): Collection<FlickerTest> {
- return FlickerTestFactory.nonRotationTests()
- }
+ fun getParams() = LegacyFlickerTestFactory.nonRotationTests()
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt
index 4570fa2..8737edb 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt
@@ -16,11 +16,14 @@
package com.android.server.wm.flicker.close
+import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Presubmit
+import android.tools.common.flicker.subject.layers.LayersTraceSubject.Companion.VISIBLE_FOR_MORE_THAN_ONE_ENTRY_IGNORE_LAYERS
+import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.common.traces.component.ComponentNameMatcher.Companion.LAUNCHER
import android.tools.device.apphelpers.StandardAppHelper
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTest
import com.android.server.wm.flicker.BaseTest
import com.android.server.wm.flicker.helpers.SimpleAppHelper
import com.android.server.wm.flicker.helpers.setRotation
@@ -28,7 +31,7 @@
import org.junit.Test
/** Base test class for transitions that close an app back to the launcher screen */
-abstract class CloseAppTransition(flicker: FlickerTest) : BaseTest(flicker) {
+abstract class CloseAppTransition(flicker: LegacyFlickerTest) : BaseTest(flicker) {
protected open val testApp: StandardAppHelper = SimpleAppHelper(instrumentation)
/** {@inheritDoc} */
@@ -71,4 +74,21 @@
ignoreEntriesWithRotationLayer = flicker.scenario.isLandscapeOrSeascapeAtStart
)
}
+
+ /** {@inheritDoc} */
+ @Presubmit
+ @Test
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() {
+ flicker.assertLayers {
+ this.visibleLayersShownMoreThanOneConsecutiveEntry(
+ VISIBLE_FOR_MORE_THAN_ONE_ENTRY_IGNORE_LAYERS + listOf(ComponentNameMatcher.NAV_BAR)
+ )
+ }
+ }
+
+ @FlakyTest(bugId = 288369951)
+ @Test
+ fun visibleLayersShownMoreThanOneConsecutiveEntryWithNavBar() {
+ flicker.assertLayers { this.visibleLayersShownMoreThanOneConsecutiveEntry() }
+ }
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ActivityEmbeddingAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ActivityEmbeddingAppHelper.kt
index daecfe7..351eb1e 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,66 @@
}
/**
+ * 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 +138,25 @@
.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 +166,9 @@
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/FlickerExtensions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerExtensions.kt
index 9227e07..5c8cbe4 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerExtensions.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerExtensions.kt
@@ -19,7 +19,7 @@
package com.android.server.wm.flicker.helpers
import android.tools.common.Rotation
-import android.tools.device.flicker.legacy.IFlickerTestData
+import android.tools.device.flicker.legacy.FlickerTestData
import android.tools.device.flicker.rules.ChangeDisplayOrientationRule
/**
@@ -27,7 +27,7 @@
*
* @param rotation New device rotation
*/
-fun IFlickerTestData.setRotation(rotation: Rotation) =
+fun FlickerTestData.setRotation(rotation: Rotation) =
ChangeDisplayOrientationRule.setRotation(
rotation,
instrumentation,
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/GestureHelper.java b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/GestureHelper.java
index a8f1b3d..eeee7b4 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 @@
}
/**
+ * 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 a670d68..d83b6d3 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.Rect
+import android.tools.common.datatypes.Region
import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.apphelpers.StandardAppHelper
import android.tools.device.helpers.FIND_TIMEOUT
@@ -36,6 +38,8 @@
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,74 @@
?: 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/ime/CloseImeOnDismissPopupDialogTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnDismissPopupDialogTest.kt
index 7e0632d..98446c1 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnDismissPopupDialogTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnDismissPopupDialogTest.kt
@@ -22,8 +22,8 @@
import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.BaseTest
import com.android.server.wm.flicker.helpers.ImeEditorPopupDialogAppHelper
@@ -37,7 +37,7 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class CloseImeOnDismissPopupDialogTest(flicker: FlickerTest) : BaseTest(flicker) {
+open class CloseImeOnDismissPopupDialogTest(flicker: LegacyFlickerTest) : BaseTest(flicker) {
private val imeTestApp = ImeEditorPopupDialogAppHelper(instrumentation)
/** {@inheritDoc} */
@@ -101,10 +101,9 @@
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): Collection<FlickerTest> {
- return FlickerTestFactory.nonRotationTests(
+ fun getParams() =
+ LegacyFlickerTestFactory.nonRotationTests(
supportedRotations = listOf(Rotation.ROTATION_0)
)
- }
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnDismissPopupDialogTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnDismissPopupDialogTestCfArm.kt
index c355e27..d87a1da 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnDismissPopupDialogTestCfArm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnDismissPopupDialogTestCfArm.kt
@@ -18,8 +18,8 @@
import android.tools.common.Rotation
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@@ -28,15 +28,14 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class CloseImeOnDismissPopupDialogTestCfArm(flicker: FlickerTest) :
+class CloseImeOnDismissPopupDialogTestCfArm(flicker: LegacyFlickerTest) :
CloseImeOnDismissPopupDialogTest(flicker) {
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): Collection<FlickerTest> {
- return FlickerTestFactory.nonRotationTests(
+ fun getParams() =
+ LegacyFlickerTestFactory.nonRotationTests(
supportedRotations = listOf(Rotation.ROTATION_0)
)
- }
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnGoHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnGoHomeTest.kt
index 537238b..b995d3df 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnGoHomeTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnGoHomeTest.kt
@@ -22,8 +22,8 @@
import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.BaseTest
import com.android.server.wm.flicker.helpers.ImeAppHelper
@@ -41,7 +41,7 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class CloseImeOnGoHomeTest(flicker: FlickerTest) : BaseTest(flicker) {
+open class CloseImeOnGoHomeTest(flicker: LegacyFlickerTest) : BaseTest(flicker) {
private val testApp = ImeAppHelper(instrumentation)
/** {@inheritDoc} */
@@ -115,10 +115,9 @@
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): Collection<FlickerTest> {
- return FlickerTestFactory.nonRotationTests(
+ fun getParams() =
+ LegacyFlickerTestFactory.nonRotationTests(
supportedRotations = listOf(Rotation.ROTATION_0)
)
- }
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnGoHomeTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnGoHomeTestCfArm.kt
index 0fe52df..3b5bfa9 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnGoHomeTestCfArm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnGoHomeTestCfArm.kt
@@ -17,7 +17,7 @@
package com.android.server.wm.flicker.ime
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
-import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTest
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@@ -26,4 +26,4 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class CloseImeOnGoHomeTestCfArm(flicker: FlickerTest) : CloseImeOnGoHomeTest(flicker)
+class CloseImeOnGoHomeTestCfArm(flicker: LegacyFlickerTest) : CloseImeOnGoHomeTest(flicker)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartOnGoHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartOnGoHomeTest.kt
index cbe03dc..765bb4c 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartOnGoHomeTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartOnGoHomeTest.kt
@@ -21,8 +21,8 @@
import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.BaseTest
import com.android.server.wm.flicker.helpers.ImeShownOnAppStartHelper
@@ -49,7 +49,7 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class CloseImeShownOnAppStartOnGoHomeTest(flicker: FlickerTest) : BaseTest(flicker) {
+open class CloseImeShownOnAppStartOnGoHomeTest(flicker: LegacyFlickerTest) : BaseTest(flicker) {
private val testApp = ImeShownOnAppStartHelper(instrumentation, flicker.scenario.startRotation)
/** {@inheritDoc} */
@@ -94,11 +94,10 @@
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): Collection<FlickerTest> {
- return FlickerTestFactory.nonRotationTests(
+ fun getParams() =
+ LegacyFlickerTestFactory.nonRotationTests(
// b/190352379 (IME doesn't show on app launch in 90 degrees)
supportedRotations = listOf(Rotation.ROTATION_0)
)
- }
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartOnGoHomeTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartOnGoHomeTestCfArm.kt
index 5aacb30..58411cc 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartOnGoHomeTestCfArm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartOnGoHomeTestCfArm.kt
@@ -17,7 +17,7 @@
package com.android.server.wm.flicker.ime
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
-import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTest
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@@ -26,5 +26,5 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class CloseImeShownOnAppStartOnGoHomeTestCfArm(flicker: FlickerTest) :
+open class CloseImeShownOnAppStartOnGoHomeTestCfArm(flicker: LegacyFlickerTest) :
CloseImeShownOnAppStartOnGoHomeTest(flicker)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartToAppOnPressBackTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartToAppOnPressBackTest.kt
index 82c390b..c87217b 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartToAppOnPressBackTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartToAppOnPressBackTest.kt
@@ -21,8 +21,8 @@
import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.BaseTest
import com.android.server.wm.flicker.helpers.ImeShownOnAppStartHelper
@@ -49,7 +49,8 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class CloseImeShownOnAppStartToAppOnPressBackTest(flicker: FlickerTest) : BaseTest(flicker) {
+open class CloseImeShownOnAppStartToAppOnPressBackTest(flicker: LegacyFlickerTest) :
+ BaseTest(flicker) {
private val testApp = ImeShownOnAppStartHelper(instrumentation, flicker.scenario.startRotation)
/** {@inheritDoc} */
@@ -88,11 +89,10 @@
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): Collection<FlickerTest> {
- return FlickerTestFactory.nonRotationTests(
+ fun getParams() =
+ LegacyFlickerTestFactory.nonRotationTests(
// b/190352379 (IME doesn't show on app launch in 90 degrees)
supportedRotations = listOf(Rotation.ROTATION_0)
)
- }
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartToAppOnPressBackTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartToAppOnPressBackTestCfArm.kt
index eb81aed..41b06c0 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartToAppOnPressBackTestCfArm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartToAppOnPressBackTestCfArm.kt
@@ -17,7 +17,7 @@
package com.android.server.wm.flicker.ime
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
-import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTest
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@@ -26,5 +26,5 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class CloseImeShownOnAppStartToAppOnPressBackTestCfArm(flicker: FlickerTest) :
+class CloseImeShownOnAppStartToAppOnPressBackTestCfArm(flicker: LegacyFlickerTest) :
CloseImeShownOnAppStartToAppOnPressBackTest(flicker)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToAppOnPressBackTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToAppOnPressBackTest.kt
index 8d80759..c74870b 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToAppOnPressBackTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToAppOnPressBackTest.kt
@@ -21,8 +21,8 @@
import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.BaseTest
import com.android.server.wm.flicker.helpers.ImeAppHelper
@@ -42,7 +42,7 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class CloseImeToAppOnPressBackTest(flicker: FlickerTest) : BaseTest(flicker) {
+open class CloseImeToAppOnPressBackTest(flicker: LegacyFlickerTest) : BaseTest(flicker) {
private val testApp = ImeAppHelper(instrumentation)
/** {@inheritDoc} */
@@ -115,8 +115,6 @@
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): Collection<FlickerTest> {
- return FlickerTestFactory.nonRotationTests()
- }
+ fun getParams() = LegacyFlickerTestFactory.nonRotationTests()
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToAppOnPressBackTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToAppOnPressBackTestCfArm.kt
index db1440b..104af22 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToAppOnPressBackTestCfArm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToAppOnPressBackTestCfArm.kt
@@ -17,7 +17,7 @@
package com.android.server.wm.flicker.ime
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
-import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTest
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@@ -26,5 +26,5 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class CloseImeToAppOnPressBackTestCfArm(flicker: FlickerTest) :
+class CloseImeToAppOnPressBackTestCfArm(flicker: LegacyFlickerTest) :
CloseImeToAppOnPressBackTest(flicker)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToHomeOnFinishActivityTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToHomeOnFinishActivityTest.kt
index 1526295..21fd590 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToHomeOnFinishActivityTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToHomeOnFinishActivityTest.kt
@@ -22,8 +22,8 @@
import android.tools.common.Rotation
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.BaseTest
import com.android.server.wm.flicker.helpers.ImeAppHelper
@@ -44,7 +44,7 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class CloseImeToHomeOnFinishActivityTest(flicker: FlickerTest) : BaseTest(flicker) {
+open class CloseImeToHomeOnFinishActivityTest(flicker: LegacyFlickerTest) : BaseTest(flicker) {
private val simpleApp = SimpleAppHelper(instrumentation)
private val testApp = ImeAppHelper(instrumentation)
@@ -90,10 +90,9 @@
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): Collection<FlickerTest> {
- return FlickerTestFactory.nonRotationTests(
+ fun getParams() =
+ LegacyFlickerTestFactory.nonRotationTests(
supportedRotations = listOf(Rotation.ROTATION_0)
)
- }
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToHomeOnFinishActivityTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToHomeOnFinishActivityTestCfArm.kt
index 405ab6b..0e18385 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToHomeOnFinishActivityTestCfArm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToHomeOnFinishActivityTestCfArm.kt
@@ -17,7 +17,7 @@
package com.android.server.wm.flicker.ime
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
-import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTest
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@@ -26,5 +26,5 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class CloseImeToHomeOnFinishActivityTestCfArm(flicker: FlickerTest) :
+open class CloseImeToHomeOnFinishActivityTestCfArm(flicker: LegacyFlickerTest) :
CloseImeToHomeOnFinishActivityTest(flicker)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CommonAssertions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CommonAssertions.kt
index 8e33719..777231e 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CommonAssertions.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CommonAssertions.kt
@@ -19,21 +19,21 @@
package com.android.server.wm.flicker.ime
import android.tools.common.traces.component.ComponentNameMatcher
-import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTest
-fun FlickerTest.imeLayerBecomesVisible() {
+fun LegacyFlickerTest.imeLayerBecomesVisible() {
assertLayers {
this.isInvisible(ComponentNameMatcher.IME).then().isVisible(ComponentNameMatcher.IME)
}
}
-fun FlickerTest.imeLayerBecomesInvisible() {
+fun LegacyFlickerTest.imeLayerBecomesInvisible() {
assertLayers {
this.isVisible(ComponentNameMatcher.IME).then().isInvisible(ComponentNameMatcher.IME)
}
}
-fun FlickerTest.imeWindowIsAlwaysVisible(rotatesScreen: Boolean = false) {
+fun LegacyFlickerTest.imeWindowIsAlwaysVisible(rotatesScreen: Boolean = false) {
if (rotatesScreen) {
assertWm {
this.isNonAppWindowVisible(ComponentNameMatcher.IME)
@@ -47,7 +47,7 @@
}
}
-fun FlickerTest.imeWindowBecomesVisible() {
+fun LegacyFlickerTest.imeWindowBecomesVisible() {
assertWm {
this.isNonAppWindowInvisible(ComponentNameMatcher.IME)
.then()
@@ -55,7 +55,7 @@
}
}
-fun FlickerTest.imeWindowBecomesInvisible() {
+fun LegacyFlickerTest.imeWindowBecomesInvisible() {
assertWm {
this.isNonAppWindowVisible(ComponentNameMatcher.IME)
.then()
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowToFixedPortraitAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowToFixedPortraitAppTest.kt
index 19bbf0c..976ac82 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowToFixedPortraitAppTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowToFixedPortraitAppTest.kt
@@ -22,8 +22,8 @@
import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import android.tools.device.helpers.WindowUtils
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.BaseTest
@@ -42,7 +42,7 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class OpenImeWindowToFixedPortraitAppTest(flicker: FlickerTest) : BaseTest(flicker) {
+class OpenImeWindowToFixedPortraitAppTest(flicker: LegacyFlickerTest) : BaseTest(flicker) {
private val testApp = ImeShownOnAppStartHelper(instrumentation, flicker.scenario.startRotation)
/** {@inheritDoc} */
@@ -99,19 +99,18 @@
/**
* Creates the test configurations.
*
- * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and
+ * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and
* navigation modes.
*/
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): Collection<FlickerTest> {
- return FlickerTestFactory.nonRotationTests(
+ fun getParams() =
+ LegacyFlickerTestFactory.nonRotationTests(
supportedRotations =
listOf(
Rotation.ROTATION_90,
),
supportedNavigationModes = listOf(NavBar.MODE_3BUTTON, NavBar.MODE_GESTURAL)
)
- }
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppCfArmTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppCfArmTest.kt
index 03f21f9..db80001 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppCfArmTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppCfArmTest.kt
@@ -17,7 +17,7 @@
package com.android.server.wm.flicker.ime
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
-import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTest
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@@ -26,5 +26,5 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class ShowImeOnAppStartWhenLaunchingAppCfArmTest(flicker: FlickerTest) :
+class ShowImeOnAppStartWhenLaunchingAppCfArmTest(flicker: LegacyFlickerTest) :
ShowImeOnAppStartWhenLaunchingAppTest(flicker)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest.kt
index 496165a..44bd8c8 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest.kt
@@ -16,17 +16,17 @@
package com.android.server.wm.flicker.ime
+import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import android.platform.test.annotations.Postsubmit
import android.tools.common.Timestamp
-import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.common.flicker.subject.exceptions.ExceptionMessageBuilder
import android.tools.common.flicker.subject.exceptions.InvalidPropertyException
+import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.BaseTest
import com.android.server.wm.flicker.helpers.ImeShownOnAppStartHelper
@@ -47,7 +47,7 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest(flicker: FlickerTest) :
+open class ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest(flicker: LegacyFlickerTest) :
BaseTest(flicker) {
private val imeTestApp =
ImeShownOnAppStartHelper(instrumentation, flicker.scenario.startRotation)
@@ -93,33 +93,35 @@
layerTraceEntries.zipWithNext { prev, next ->
val prevSnapshotLayerVisible =
- ComponentNameMatcher.SNAPSHOT.layerMatchesAnyOf(prev.visibleLayers)
+ ComponentNameMatcher.SNAPSHOT.layerMatchesAnyOf(prev.visibleLayers)
val nextSnapshotLayerVisible =
- ComponentNameMatcher.SNAPSHOT.layerMatchesAnyOf(next.visibleLayers)
+ ComponentNameMatcher.SNAPSHOT.layerMatchesAnyOf(next.visibleLayers)
- if (imeSnapshotRemovedTimestamp == null &&
- (prevSnapshotLayerVisible && !nextSnapshotLayerVisible)) {
+ if (
+ imeSnapshotRemovedTimestamp == null &&
+ (prevSnapshotLayerVisible && !nextSnapshotLayerVisible)
+ ) {
imeSnapshotRemovedTimestamp = next.timestamp
}
}
// if so, make an assertion
imeSnapshotRemovedTimestamp?.let { timestamp ->
- val stateAfterSnapshot = layerTrace?.getEntryAt(timestamp)
- ?: error("State not found for $timestamp")
+ val stateAfterSnapshot =
+ layerTrace?.getEntryAt(timestamp) ?: error("State not found for $timestamp")
- val imeLayers = ComponentNameMatcher.IME
- .filterLayers(stateAfterSnapshot.visibleLayers.toList())
+ val imeLayers =
+ ComponentNameMatcher.IME.filterLayers(stateAfterSnapshot.visibleLayers.toList())
require(imeLayers.isNotEmpty()) { "IME layer not found" }
if (imeLayers.any { it.color.a != 1.0f }) {
- val errorMsgBuilder = ExceptionMessageBuilder()
+ val errorMsgBuilder =
+ ExceptionMessageBuilder()
.setTimestamp(timestamp)
.forInvalidProperty("IME layer alpha")
.setExpected("is 1.0")
.setActual("not 1.0")
- .addExtraDescription("Filter",
- ComponentNameMatcher.IME.toLayerIdentifier())
+ .addExtraDescription("Filter", ComponentNameMatcher.IME.toLayerIdentifier())
throw InvalidPropertyException(errorMsgBuilder)
}
}
@@ -129,15 +131,14 @@
/**
* Creates the test configurations.
*
- * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and
+ * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and
* navigation modes.
*/
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): Collection<FlickerTest> {
- return FlickerTestFactory.nonRotationTests(
+ fun getParams() =
+ LegacyFlickerTestFactory.nonRotationTests(
supportedRotations = listOf(Rotation.ROTATION_90)
)
- }
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTestCfArm.kt
index 3aca2a0..6d9ea22 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTestCfArm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTestCfArm.kt
@@ -17,7 +17,7 @@
package com.android.server.wm.flicker.ime
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
-import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTest
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@@ -26,5 +26,5 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTestCfArm(flicker: FlickerTest) :
+class ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTestCfArm(flicker: LegacyFlickerTest) :
ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest(flicker)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromOverviewTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromOverviewTest.kt
index f64ad53..ae05e37 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromOverviewTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromOverviewTest.kt
@@ -21,8 +21,8 @@
import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import android.tools.device.helpers.reopenAppFromOverview
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.BaseTest
@@ -41,7 +41,7 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class ShowImeOnAppStartWhenLaunchingAppFromOverviewTest(flicker: FlickerTest) :
+open class ShowImeOnAppStartWhenLaunchingAppFromOverviewTest(flicker: LegacyFlickerTest) :
BaseTest(flicker) {
private val testApp = ImeShownOnAppStartHelper(instrumentation, flicker.scenario.startRotation)
@@ -140,10 +140,9 @@
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): Collection<FlickerTest> {
- return FlickerTestFactory.nonRotationTests(
+ fun getParams() =
+ LegacyFlickerTestFactory.nonRotationTests(
supportedRotations = listOf(Rotation.ROTATION_0)
)
- }
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromOverviewTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromOverviewTestCfArm.kt
index e1aa418..92b3968 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromOverviewTestCfArm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromOverviewTestCfArm.kt
@@ -18,8 +18,8 @@
import android.tools.common.Rotation
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@@ -28,16 +28,15 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class ShowImeOnAppStartWhenLaunchingAppFromOverviewTestCfArm(flicker: FlickerTest) :
+class ShowImeOnAppStartWhenLaunchingAppFromOverviewTestCfArm(flicker: LegacyFlickerTest) :
ShowImeOnAppStartWhenLaunchingAppFromOverviewTest(flicker) {
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): Collection<FlickerTest> {
- return FlickerTestFactory.nonRotationTests(
+ fun getParams() =
+ LegacyFlickerTestFactory.nonRotationTests(
supportedRotations = listOf(Rotation.ROTATION_0)
)
- }
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTest.kt
index 11bc7b9..c991651 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTest.kt
@@ -22,8 +22,8 @@
import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.BaseTest
import com.android.server.wm.flicker.helpers.ImeShownOnAppStartHelper
@@ -44,7 +44,7 @@
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@Presubmit
-open class ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTest(flicker: FlickerTest) :
+open class ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTest(flicker: LegacyFlickerTest) :
BaseTest(flicker) {
private val testApp = SimpleAppHelper(instrumentation)
private val imeTestApp =
@@ -121,12 +121,11 @@
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): Collection<FlickerTest> {
- return FlickerTestFactory.nonRotationTests(
+ fun getParams() =
+ LegacyFlickerTestFactory.nonRotationTests(
supportedNavigationModes = listOf(NavBar.MODE_GESTURAL),
supportedRotations = listOf(Rotation.ROTATION_0)
)
- }
private const val TAG_IME_VISIBLE = "imeVisible"
private const val TAG_IME_INVISIBLE = "imeInVisible"
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTestCfArm.kt
index 0f57467..09bfacc 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTestCfArm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTestCfArm.kt
@@ -18,7 +18,7 @@
import android.platform.test.annotations.Presubmit
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
-import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTest
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@@ -28,5 +28,5 @@
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@Presubmit
-open class ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTestCfArm(flicker: FlickerTest) :
+open class ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTestCfArm(flicker: LegacyFlickerTest) :
ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTest(flicker)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppTest.kt
index 46967db..9763521 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppTest.kt
@@ -21,8 +21,8 @@
import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.BaseTest
import com.android.server.wm.flicker.helpers.ImeShownOnAppStartHelper
@@ -79,7 +79,7 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class ShowImeOnAppStartWhenLaunchingAppTest(flicker: FlickerTest) : BaseTest(flicker) {
+open class ShowImeOnAppStartWhenLaunchingAppTest(flicker: LegacyFlickerTest) : BaseTest(flicker) {
private val testApp = ImeShownOnAppStartHelper(instrumentation, flicker.scenario.startRotation)
private val initializeApp = ImeStateInitializeHelper(instrumentation)
@@ -123,15 +123,14 @@
/**
* Creates the test configurations.
*
- * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and
+ * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and
* navigation modes.
*/
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): Collection<FlickerTest> {
- return FlickerTestFactory.nonRotationTests(
+ fun getParams() =
+ LegacyFlickerTestFactory.nonRotationTests(
supportedRotations = listOf(Rotation.ROTATION_0)
)
- }
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhenFocusingOnInputFieldTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhenFocusingOnInputFieldTest.kt
index 827110d..7bd5825 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhenFocusingOnInputFieldTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhenFocusingOnInputFieldTest.kt
@@ -21,8 +21,8 @@
import android.tools.common.Rotation
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.BaseTest
import com.android.server.wm.flicker.helpers.ImeAppHelper
@@ -37,7 +37,7 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class ShowImeWhenFocusingOnInputFieldTest(flicker: FlickerTest) : BaseTest(flicker) {
+open class ShowImeWhenFocusingOnInputFieldTest(flicker: LegacyFlickerTest) : BaseTest(flicker) {
private val testApp = ImeAppHelper(instrumentation)
/** {@inheritDoc} */
@@ -79,10 +79,9 @@
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): Collection<FlickerTest> {
- return FlickerTestFactory.nonRotationTests(
+ fun getParams() =
+ LegacyFlickerTestFactory.nonRotationTests(
supportedRotations = listOf(Rotation.ROTATION_0)
)
- }
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhenFocusingOnInputFieldTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhenFocusingOnInputFieldTestCfArm.kt
index f5b2294..f8c8149 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhenFocusingOnInputFieldTestCfArm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhenFocusingOnInputFieldTestCfArm.kt
@@ -17,7 +17,7 @@
package com.android.server.wm.flicker.ime
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
-import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTest
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@@ -26,5 +26,5 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class ShowImeWhenFocusingOnInputFieldTestCfArm(flicker: FlickerTest) :
+class ShowImeWhenFocusingOnInputFieldTestCfArm(flicker: LegacyFlickerTest) :
ShowImeWhenFocusingOnInputFieldTest(flicker)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileDismissingThemedPopupDialogTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileDismissingThemedPopupDialogTest.kt
index 277b9155..0b168ba 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileDismissingThemedPopupDialogTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileDismissingThemedPopupDialogTest.kt
@@ -21,8 +21,8 @@
import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import android.view.WindowInsets.Type.ime
import android.view.WindowInsets.Type.navigationBars
import android.view.WindowInsets.Type.statusBars
@@ -45,7 +45,8 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class ShowImeWhileDismissingThemedPopupDialogTest(flicker: FlickerTest) : BaseTest(flicker) {
+open class ShowImeWhileDismissingThemedPopupDialogTest(flicker: LegacyFlickerTest) :
+ BaseTest(flicker) {
private val testApp = ImeShownOnAppStartHelper(instrumentation, flicker.scenario.startRotation)
/** {@inheritDoc} */
@@ -84,15 +85,14 @@
/**
* Creates the test configurations.
*
- * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and
+ * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and
* navigation modes.
*/
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): Collection<FlickerTest> {
- return FlickerTestFactory.nonRotationTests(
+ fun getParams() =
+ LegacyFlickerTestFactory.nonRotationTests(
supportedRotations = listOf(Rotation.ROTATION_0)
)
- }
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileDismissingThemedPopupDialogTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileDismissingThemedPopupDialogTestCfArm.kt
index 8891d26..ad41b81 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileDismissingThemedPopupDialogTestCfArm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileDismissingThemedPopupDialogTestCfArm.kt
@@ -18,8 +18,8 @@
import android.tools.common.Rotation
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@@ -28,21 +28,20 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class ShowImeWhileDismissingThemedPopupDialogTestCfArm(flicker: FlickerTest) :
+class ShowImeWhileDismissingThemedPopupDialogTestCfArm(flicker: LegacyFlickerTest) :
ShowImeWhileDismissingThemedPopupDialogTest(flicker) {
companion object {
/**
* Creates the test configurations.
*
- * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and
+ * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and
* navigation modes.
*/
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): Collection<FlickerTest> {
- return FlickerTestFactory.nonRotationTests(
+ fun getParams() =
+ LegacyFlickerTestFactory.nonRotationTests(
supportedRotations = listOf(Rotation.ROTATION_0)
)
- }
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileEnteringOverviewTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileEnteringOverviewTest.kt
index 9275d6a..7722c93 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileEnteringOverviewTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileEnteringOverviewTest.kt
@@ -21,8 +21,8 @@
import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import android.tools.device.traces.parsers.WindowManagerStateHelper
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.BaseTest
@@ -45,7 +45,7 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class ShowImeWhileEnteringOverviewTest(flicker: FlickerTest) : BaseTest(flicker) {
+open class ShowImeWhileEnteringOverviewTest(flicker: LegacyFlickerTest) : BaseTest(flicker) {
private val imeTestApp =
ImeShownOnAppStartHelper(instrumentation, flicker.scenario.startRotation)
@@ -205,13 +205,11 @@
/**
* Creates the test configurations.
*
- * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and
+ * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and
* navigation modes.
*/
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): Collection<FlickerTest> {
- return FlickerTestFactory.nonRotationTests()
- }
+ fun getParams() = LegacyFlickerTestFactory.nonRotationTests()
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileEnteringOverviewTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileEnteringOverviewTestCfArm.kt
index fc39713..90fbbf9 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileEnteringOverviewTestCfArm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileEnteringOverviewTestCfArm.kt
@@ -17,7 +17,7 @@
package com.android.server.wm.flicker.ime
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
-import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTest
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@@ -26,5 +26,5 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class ShowImeWhileEnteringOverviewTestCfArm(flicker: FlickerTest) :
+class ShowImeWhileEnteringOverviewTestCfArm(flicker: LegacyFlickerTest) :
ShowImeWhileEnteringOverviewTest(flicker)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivityTransitionTest.kt
similarity index 91%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTest.kt
rename to tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivityTransitionTest.kt
index e6594c9..3461907 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
@@ -20,8 +20,8 @@
import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import android.tools.device.traces.parsers.toFlickerComponent
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.BaseTest
@@ -57,7 +57,7 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class ActivitiesTransitionTest(flicker: FlickerTest) : BaseTest(flicker) {
+open class ActivityTransitionTest(flicker: LegacyFlickerTest) : BaseTest(flicker) {
private val testApp: TwoActivitiesAppHelper = TwoActivitiesAppHelper(instrumentation)
/** {@inheritDoc} */
@@ -119,13 +119,11 @@
/**
* Creates the test configurations.
*
- * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and
+ * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and
* navigation modes.
*/
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): Collection<FlickerTest> {
- return FlickerTestFactory.nonRotationTests()
- }
+ fun getParams() = LegacyFlickerTestFactory.nonRotationTests()
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivityTransitionTestCfArm.kt
similarity index 74%
copy from tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTestCfArm.kt
copy to tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivityTransitionTestCfArm.kt
index 8b89a8b..6bbf40e 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTestCfArm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivityTransitionTestCfArm.kt
@@ -17,8 +17,8 @@
package com.android.server.wm.flicker.launch
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@@ -27,18 +27,16 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class ActivitiesTransitionTestCfArm(flicker: FlickerTest) : ActivitiesTransitionTest(flicker) {
+class ActivityTransitionTestCfArm(flicker: LegacyFlickerTest) : ActivityTransitionTest(flicker) {
companion object {
/**
* Creates the test configurations.
*
- * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and
+ * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and
* navigation modes.
*/
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): Collection<FlickerTest> {
- return FlickerTestFactory.nonRotationTests()
- }
+ fun getParams() = LegacyFlickerTestFactory.nonRotationTests()
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppAfterCameraTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppAfterCameraTestCfArm.kt
deleted file mode 100644
index ac05c76..0000000
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppAfterCameraTestCfArm.kt
+++ /dev/null
@@ -1,44 +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.server.wm.flicker.launch
-
-import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
-import org.junit.FixMethodOrder
-import org.junit.runner.RunWith
-import org.junit.runners.MethodSorters
-import org.junit.runners.Parameterized
-
-@RunWith(Parameterized::class)
-@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class OpenAppAfterCameraTestCfArm(flicker: FlickerTest) : OpenAppAfterCameraTest(flicker) {
- 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/launch/OpenAppColdFromIcon.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIconColdTest.kt
similarity index 93%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdFromIcon.kt
rename to tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIconColdTest.kt
index 3a80c66..ae1f78a 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
@@ -20,8 +20,8 @@
import android.tools.common.Rotation
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import android.tools.device.flicker.rules.RemoveAllTasksButHomeRule
import androidx.test.filters.RequiresDevice
import org.junit.FixMethodOrder
@@ -55,7 +55,8 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class OpenAppColdFromIcon(flicker: FlickerTest) : OpenAppFromLauncherTransition(flicker) {
+open class OpenAppFromIconColdTest(flicker: LegacyFlickerTest) :
+ OpenAppFromLauncherTransition(flicker) {
/** {@inheritDoc} */
override val transition: FlickerBuilder.() -> Unit
get() = {
@@ -207,13 +208,11 @@
/**
* Creates the test configurations.
*
- * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and
+ * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and
* navigation modes.
*/
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): Collection<FlickerTest> {
- return FlickerTestFactory.nonRotationTests()
- }
+ fun getParams() = LegacyFlickerTestFactory.nonRotationTests()
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdFromIconCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIconColdTestCfArm.kt
similarity index 79%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdFromIconCfArm.kt
rename to tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIconColdTestCfArm.kt
index d33a272..2563bfb 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
@@ -19,8 +19,8 @@
import android.platform.test.annotations.FlakyTest
import android.tools.device.flicker.annotation.FlickerServiceCompatible
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -32,7 +32,7 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class OpenAppColdFromIconCfArm(flicker: FlickerTest) : OpenAppColdFromIcon(flicker) {
+class OpenAppFromIconColdTestCfArm(flicker: LegacyFlickerTest) : OpenAppFromIconColdTest(flicker) {
@Test
@FlakyTest
override fun visibleLayersShownMoreThanOneConsecutiveEntry() {
@@ -43,13 +43,11 @@
/**
* Creates the test configurations.
*
- * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and
+ * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and
* navigation modes.
*/
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): Collection<FlickerTest> {
- return FlickerTestFactory.nonRotationTests()
- }
+ fun getParams() = LegacyFlickerTestFactory.nonRotationTests()
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppAfterCameraTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdAfterCameraTest.kt
similarity index 84%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppAfterCameraTest.kt
rename to tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdAfterCameraTest.kt
index 549183f..c95c548 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
@@ -19,8 +19,8 @@
import android.tools.device.apphelpers.CameraAppHelper
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
@@ -38,7 +38,8 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class OpenAppAfterCameraTest(flicker: FlickerTest) : OpenAppFromLauncherTransition(flicker) {
+open class OpenAppFromIntentColdAfterCameraTest(flicker: LegacyFlickerTest) :
+ OpenAppFromLauncherTransition(flicker) {
private val cameraApp = CameraAppHelper(instrumentation)
/** {@inheritDoc} */
override val transition: FlickerBuilder.() -> Unit
@@ -61,13 +62,11 @@
/**
* Creates the test configurations.
*
- * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and
+ * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and
* navigation modes.
*/
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): Collection<FlickerTest> {
- return FlickerTestFactory.nonRotationTests()
- }
+ fun getParams() = LegacyFlickerTestFactory.nonRotationTests()
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdAfterCameraTestCfArm.kt
similarity index 74%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTestCfArm.kt
rename to tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdAfterCameraTestCfArm.kt
index 8b89a8b..117cff2 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
@@ -17,8 +17,8 @@
package com.android.server.wm.flicker.launch
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@@ -27,18 +27,17 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class ActivitiesTransitionTestCfArm(flicker: FlickerTest) : ActivitiesTransitionTest(flicker) {
+class OpenAppFromIntentColdAfterCameraTestCfArm(flicker: LegacyFlickerTest) :
+ OpenAppFromIntentColdAfterCameraTest(flicker) {
companion object {
/**
* Creates the test configurations.
*
- * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and
+ * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and
* navigation modes.
*/
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): Collection<FlickerTest> {
- return FlickerTestFactory.nonRotationTests()
- }
+ fun getParams() = LegacyFlickerTestFactory.nonRotationTests()
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdTest.kt
similarity index 87%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
rename to tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdTest.kt
index 26f88d2..33302fa 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
@@ -21,8 +21,8 @@
import android.tools.device.flicker.annotation.FlickerServiceCompatible
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import android.tools.device.flicker.rules.RemoveAllTasksButHomeRule.Companion.removeAllTasksButHome
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.helpers.setRotation
@@ -58,7 +58,8 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class OpenAppColdTest(flicker: FlickerTest) : OpenAppFromLauncherTransition(flicker) {
+open class OpenAppFromIntentColdTest(flicker: LegacyFlickerTest) :
+ OpenAppFromLauncherTransition(flicker) {
/** {@inheritDoc} */
override val transition: FlickerBuilder.() -> Unit
get() = {
@@ -82,13 +83,11 @@
/**
* Creates the test configurations.
*
- * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and
+ * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and
* navigation modes.
*/
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): Collection<FlickerTest> {
- return FlickerTestFactory.nonRotationTests()
- }
+ fun getParams() = LegacyFlickerTestFactory.nonRotationTests()
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdTestCfArm.kt
similarity index 79%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTestCfArm.kt
rename to tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdTestCfArm.kt
index d9a99da..45fb453 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
@@ -19,8 +19,8 @@
import android.platform.test.annotations.FlakyTest
import android.tools.device.flicker.annotation.FlickerServiceCompatible
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -31,7 +31,8 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class OpenAppColdTestCfArm(flicker: FlickerTest) : OpenAppColdTest(flicker) {
+class OpenAppFromIntentColdTestCfArm(flicker: LegacyFlickerTest) :
+ OpenAppFromIntentColdTest(flicker) {
@FlakyTest(bugId = 273696733)
@Test
override fun appLayerReplacesLauncher() = super.appLayerReplacesLauncher()
@@ -40,13 +41,11 @@
/**
* Creates the test configurations.
*
- * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and
+ * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and
* navigation modes.
*/
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): Collection<FlickerTest> {
- return FlickerTestFactory.nonRotationTests()
- }
+ fun getParams() = LegacyFlickerTestFactory.nonRotationTests()
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentWarmTest.kt
similarity index 88%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt
rename to tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentWarmTest.kt
index 3385830..12ee7d0 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
@@ -21,8 +21,8 @@
import android.tools.device.flicker.annotation.FlickerServiceCompatible
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.helpers.setRotation
import org.junit.FixMethodOrder
@@ -58,7 +58,8 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class OpenAppWarmTest(flicker: FlickerTest) : OpenAppFromLauncherTransition(flicker) {
+open class OpenAppFromIntentWarmTest(flicker: LegacyFlickerTest) :
+ OpenAppFromLauncherTransition(flicker) {
/** Defines the transition used to run the test */
override val transition: FlickerBuilder.() -> Unit
get() = {
@@ -93,13 +94,11 @@
/**
* Creates the test configurations.
*
- * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and
+ * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and
* navigation modes.
*/
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): Collection<FlickerTest> {
- return FlickerTestFactory.nonRotationTests()
- }
+ fun getParams() = LegacyFlickerTestFactory.nonRotationTests()
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentWarmTestCfArm.kt
similarity index 76%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTestCfArm.kt
rename to tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentWarmTestCfArm.kt
index d8b38b3..1371fd7 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
@@ -18,8 +18,8 @@
import android.tools.device.flicker.annotation.FlickerServiceCompatible
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@@ -29,18 +29,17 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class OpenAppWarmTestCfArm(flicker: FlickerTest) : OpenAppWarmTest(flicker) {
+class OpenAppFromIntentWarmTestCfArm(flicker: LegacyFlickerTest) :
+ OpenAppFromIntentWarmTest(flicker) {
companion object {
/**
* Creates the test configurations.
*
- * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and
+ * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and
* navigation modes.
*/
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): Collection<FlickerTest> {
- return FlickerTestFactory.nonRotationTests()
- }
+ fun getParams() = LegacyFlickerTestFactory.nonRotationTests()
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLauncherTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLauncherTransition.kt
index 3d5a8e3..62fb570 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLauncherTransition.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLauncherTransition.kt
@@ -18,12 +18,13 @@
import android.platform.test.annotations.Presubmit
import android.tools.common.traces.component.ComponentNameMatcher
-import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTest
import com.android.server.wm.flicker.replacesLayer
import org.junit.Test
/** Base class for app launch tests */
-abstract class OpenAppFromLauncherTransition(flicker: FlickerTest) : OpenAppTransition(flicker) {
+abstract class OpenAppFromLauncherTransition(flicker: LegacyFlickerTest) :
+ OpenAppTransition(flicker) {
/** Checks that the focus changes from the [ComponentNameMatcher.LAUNCHER] to [testApp] */
@Presubmit
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenTransition.kt
similarity index 96%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockTransition.kt
rename to tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenTransition.kt
index eae9ca1..687bc19 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
@@ -20,7 +20,7 @@
import android.platform.test.annotations.Presubmit
import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTest
import com.android.server.wm.flicker.navBarLayerPositionAtEnd
import com.android.server.wm.flicker.statusBarLayerPositionAtEnd
import org.junit.Assume
@@ -28,7 +28,8 @@
import org.junit.Test
/** Base class for app launch tests from lock screen */
-abstract class OpenAppFromLockTransition(flicker: FlickerTest) : OpenAppTransition(flicker) {
+abstract class OpenAppFromLockscreenTransition(flicker: LegacyFlickerTest) :
+ OpenAppTransition(flicker) {
/** Defines the transition used to run the test */
override val transition: FlickerBuilder.() -> Unit = {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenViaIntentTest.kt
similarity index 94%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt
rename to tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenViaIntentTest.kt
index 1383ae3..46eb257 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
@@ -23,8 +23,8 @@
import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.flicker.annotation.FlickerServiceCompatible
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.helpers.NonResizeableAppHelper
import org.junit.Assume
@@ -63,7 +63,8 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class OpenAppNonResizeableTest(flicker: FlickerTest) : OpenAppFromLockTransition(flicker) {
+open class OpenAppFromLockscreenViaIntentTest(flicker: LegacyFlickerTest) :
+ OpenAppFromLockscreenTransition(flicker) {
override val testApp = NonResizeableAppHelper(instrumentation)
/**
@@ -209,16 +210,15 @@
/**
* Creates the test configurations.
*
- * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and
+ * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and
* navigation modes.
*/
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): Collection<FlickerTest> {
- return FlickerTestFactory.nonRotationTests(
+ fun getParams() =
+ LegacyFlickerTestFactory.nonRotationTests(
supportedNavigationModes = listOf(NavBar.MODE_GESTURAL),
supportedRotations = listOf(Rotation.ROTATION_0)
)
- }
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarmCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarmCfArm.kt
deleted file mode 100644
index 43d28fa..0000000
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarmCfArm.kt
+++ /dev/null
@@ -1,45 +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.server.wm.flicker.launch
-
-import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
-import org.junit.FixMethodOrder
-import org.junit.runner.RunWith
-import org.junit.runners.MethodSorters
-import org.junit.runners.Parameterized
-
-@RunWith(Parameterized::class)
-@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class OpenAppFromNotificationWarmCfArm(flicker: FlickerTest) :
- OpenAppFromNotificationWarm(flicker) {
- 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/launch/OpenAppFromOverviewTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
index 8e1b059..1497e50 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
@@ -22,8 +22,8 @@
import android.tools.device.flicker.annotation.FlickerServiceCompatible
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.helpers.setRotation
import org.junit.FixMethodOrder
@@ -60,7 +60,8 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class OpenAppFromOverviewTest(flicker: FlickerTest) : OpenAppFromLauncherTransition(flicker) {
+open class OpenAppFromOverviewTest(flicker: LegacyFlickerTest) :
+ OpenAppFromLauncherTransition(flicker) {
/** Defines the transition used to run the test */
override val transition: FlickerBuilder.() -> Unit
@@ -106,13 +107,11 @@
/**
* Creates the test configurations.
*
- * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and
+ * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and
* navigation modes.
*/
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): Collection<FlickerTest> {
- return FlickerTestFactory.nonRotationTests()
- }
+ fun getParams() = LegacyFlickerTestFactory.nonRotationTests()
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTestCfArm.kt
index ff24190..9b6c136 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTestCfArm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTestCfArm.kt
@@ -18,8 +18,8 @@
import android.tools.device.flicker.annotation.FlickerServiceCompatible
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@@ -30,18 +30,17 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class OpenAppFromOverviewTestCfArm(flicker: FlickerTest) : OpenAppFromOverviewTest(flicker) {
+open class OpenAppFromOverviewTestCfArm(flicker: LegacyFlickerTest) :
+ OpenAppFromOverviewTest(flicker) {
companion object {
/**
* Creates the test configurations.
*
- * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and
+ * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and
* navigation modes.
*/
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): Collection<FlickerTest> {
- return FlickerTestFactory.nonRotationTests()
- }
+ fun getParams() = LegacyFlickerTestFactory.nonRotationTests()
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt
index 87a14c6..bb11be5 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt
@@ -20,7 +20,7 @@
import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.apphelpers.StandardAppHelper
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.helpers.wakeUpAndGoToHomeScreen
import com.android.server.wm.flicker.BaseTest
import com.android.server.wm.flicker.helpers.SimpleAppHelper
@@ -28,7 +28,7 @@
import org.junit.Test
/** Base class for app launch tests */
-abstract class OpenAppTransition(flicker: FlickerTest) : BaseTest(flicker) {
+abstract class OpenAppTransition(flicker: LegacyFlickerTest) : BaseTest(flicker) {
protected open val testApp: StandardAppHelper = SimpleAppHelper(instrumentation)
/** {@inheritDoc} */
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenCameraOnDoubleClickPowerButton.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenCameraFromHomeOnDoubleClickPowerButtonTest.kt
similarity index 93%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenCameraOnDoubleClickPowerButton.kt
rename to tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenCameraFromHomeOnDoubleClickPowerButtonTest.kt
index ae9ca80..4a1bd7e 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
@@ -22,8 +22,8 @@
import android.tools.device.apphelpers.StandardAppHelper
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import android.tools.device.flicker.rules.RemoveAllTasksButHomeRule
import android.view.KeyEvent
import androidx.test.filters.RequiresDevice
@@ -60,7 +60,7 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class OpenCameraOnDoubleClickPowerButton(flicker: FlickerTest) :
+class OpenCameraFromHomeOnDoubleClickPowerButtonTest(flicker: LegacyFlickerTest) :
OpenAppFromLauncherTransition(flicker) {
private val cameraApp = CameraAppHelper(instrumentation)
override val testApp: StandardAppHelper
@@ -155,13 +155,11 @@
/**
* Creates the test configurations.
*
- * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and
+ * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and
* navigation modes.
*/
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): Collection<FlickerTest> {
- return FlickerTestFactory.nonRotationTests()
- }
+ fun getParams() = LegacyFlickerTestFactory.nonRotationTests()
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OverrideTaskTransitionTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OverrideTaskTransitionTest.kt
index b48611e..98e3646 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OverrideTaskTransitionTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OverrideTaskTransitionTest.kt
@@ -25,8 +25,8 @@
import android.tools.device.flicker.junit.FlickerBuilderProvider
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import android.tools.device.flicker.rules.RemoveAllTasksButHomeRule
import android.tools.device.helpers.wakeUpAndGoToHomeScreen
import androidx.test.filters.RequiresDevice
@@ -54,7 +54,7 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class OverrideTaskTransitionTest(val flicker: FlickerTest) {
+class OverrideTaskTransitionTest(val flicker: LegacyFlickerTest) {
private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
private val testApp = SimpleAppHelper(instrumentation)
@@ -121,8 +121,6 @@
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): Collection<FlickerTest> {
- return FlickerTestFactory.nonRotationTests()
- }
+ fun getParams() = LegacyFlickerTestFactory.nonRotationTests()
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt
index d0fd732..39c8ca0 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt
@@ -28,8 +28,8 @@
import android.tools.common.traces.component.IComponentMatcher
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import android.tools.device.helpers.WindowUtils
import android.tools.device.traces.parsers.toFlickerComponent
import androidx.test.filters.RequiresDevice
@@ -58,7 +58,7 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class TaskTransitionTest(flicker: FlickerTest) : BaseTest(flicker) {
+class TaskTransitionTest(flicker: LegacyFlickerTest) : BaseTest(flicker) {
private val launchNewTaskApp = NewTasksAppHelper(instrumentation)
private val simpleApp = SimpleAppHelper(instrumentation)
private val wallpaper by lazy { getWallpaperPackage(instrumentation) }
@@ -214,8 +214,6 @@
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): Collection<FlickerTest> {
- return FlickerTestFactory.nonRotationTests()
- }
+ fun getParams() = LegacyFlickerTestFactory.nonRotationTests()
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationCold.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationColdTest.kt
similarity index 88%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationCold.kt
rename to tests/FlickerTests/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationColdTest.kt
index b21777b..6f5daeb 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationCold.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationColdTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,15 +14,15 @@
* limitations under the License.
*/
-package com.android.server.wm.flicker.launch
+package com.android.server.wm.flicker.notification
import android.platform.test.annotations.Postsubmit
import android.platform.test.rule.SettingOverrideRule
import android.provider.Settings
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
import org.junit.ClassRule
import org.junit.FixMethodOrder
@@ -44,8 +44,8 @@
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@Postsubmit
-open class OpenAppFromLockNotificationCold(flicker: FlickerTest) :
- OpenAppFromNotificationCold(flicker) {
+open class OpenAppFromLockscreenNotificationColdTest(flicker: LegacyFlickerTest) :
+ OpenAppFromNotificationColdTest(flicker) {
override val openingNotificationsFromLockScreen = true
@@ -111,14 +111,12 @@
/**
* Creates the test configurations.
*
- * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and
+ * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and
* navigation modes.
*/
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): Collection<FlickerTest> {
- return FlickerTestFactory.nonRotationTests()
- }
+ fun getParams() = LegacyFlickerTestFactory.nonRotationTests()
/**
* Ensures that posted notifications will be visible on the lockscreen and not suppressed
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWarm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationWarmTest.kt
similarity index 90%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWarm.kt
rename to tests/FlickerTests/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationWarmTest.kt
index ec92ca6..483caa7 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWarm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationWarmTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.wm.flicker.launch
+package com.android.server.wm.flicker.notification
import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Presubmit
@@ -23,8 +23,8 @@
import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.statusBarLayerPositionAtEnd
import org.junit.ClassRule
@@ -46,7 +46,8 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class OpenAppFromLockNotificationWarm(flicker: FlickerTest) : OpenAppFromNotificationWarm(flicker) {
+class OpenAppFromLockscreenNotificationWarmTest(flicker: LegacyFlickerTest) :
+ OpenAppFromNotificationWarmTest(flicker) {
override val openingNotificationsFromLockScreen = true
@@ -142,14 +143,12 @@
/**
* Creates the test configurations.
*
- * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and
+ * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and
* navigation modes.
*/
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): Collection<FlickerTest> {
- return FlickerTestFactory.nonRotationTests()
- }
+ fun getParams() = LegacyFlickerTestFactory.nonRotationTests()
/**
* Ensures that posted notifications will be visible on the lockscreen and not suppressed
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWithLockOverlayApp.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationWithOverlayAppTest.kt
similarity index 87%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWithLockOverlayApp.kt
rename to tests/FlickerTests/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationWithOverlayAppTest.kt
index 009d617..c336399 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWithLockOverlayApp.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationWithOverlayAppTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.wm.flicker.launch
+package com.android.server.wm.flicker.notification
import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Postsubmit
@@ -22,8 +22,8 @@
import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import android.tools.device.helpers.wakeUpAndGoToHomeScreen
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.helpers.ShowWhenLockedAppHelper
@@ -37,6 +37,8 @@
* 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 @@
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@Postsubmit
-class OpenAppFromLockNotificationWithLockOverlayApp(flicker: FlickerTest) :
- OpenAppFromLockNotificationCold(flicker) {
+class OpenAppFromLockscreenNotificationWithOverlayAppTest(flicker: LegacyFlickerTest) :
+ OpenAppFromLockscreenNotificationColdTest(flicker) {
private val showWhenLockedApp = ShowWhenLockedAppHelper(instrumentation)
// Although we are technically still locked here, the overlay app means we should open the
@@ -124,13 +126,11 @@
/**
* Creates the test configurations.
*
- * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and
+ * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and
* navigation modes.
*/
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): Collection<FlickerTest> {
- return FlickerTestFactory.nonRotationTests()
- }
+ fun getParams() = LegacyFlickerTestFactory.nonRotationTests()
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationCold.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationColdTest.kt
similarity index 85%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationCold.kt
rename to tests/FlickerTests/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationColdTest.kt
index 7bcb910..50dec3b 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationCold.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationColdTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,15 +14,15 @@
* limitations under the License.
*/
-package com.android.server.wm.flicker.launch
+package com.android.server.wm.flicker.notification
import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.statusBarLayerPositionAtEnd
import org.junit.FixMethodOrder
@@ -35,8 +35,6 @@
/**
* 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 @@
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@Postsubmit
-open class OpenAppFromNotificationCold(flicker: FlickerTest) :
- OpenAppFromNotificationWarm(flicker) {
+open class OpenAppFromNotificationColdTest(flicker: LegacyFlickerTest) :
+ OpenAppFromNotificationWarmTest(flicker) {
/** {@inheritDoc} */
override val transition: FlickerBuilder.() -> Unit
get() = {
@@ -101,13 +99,11 @@
/**
* Creates the test configurations.
*
- * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and
+ * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and
* navigation modes.
*/
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): Collection<FlickerTest> {
- return FlickerTestFactory.nonRotationTests()
- }
+ fun getParams() = LegacyFlickerTestFactory.nonRotationTests()
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationColdCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationColdTestCfArm.kt
similarity index 72%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationColdCfArm.kt
rename to tests/FlickerTests/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationColdTestCfArm.kt
index 8b4a613..a147171 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationColdCfArm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationColdTestCfArm.kt
@@ -14,12 +14,12 @@
* limitations under the License.
*/
-package com.android.server.wm.flicker.launch
+package com.android.server.wm.flicker.notification
import android.platform.test.annotations.Postsubmit
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@@ -29,19 +29,17 @@
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@Postsubmit
-class OpenAppFromNotificationColdCfArm(flicker: FlickerTest) :
- OpenAppFromNotificationCold(flicker) {
+class OpenAppFromNotificationColdTestCfArm(flicker: LegacyFlickerTest) :
+ OpenAppFromNotificationColdTest(flicker) {
companion object {
/**
* Creates the test configurations.
*
- * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and
+ * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and
* navigation modes.
*/
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): Collection<FlickerTest> {
- return FlickerTestFactory.nonRotationTests()
- }
+ fun getParams() = LegacyFlickerTestFactory.nonRotationTests()
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationWarmTest.kt
similarity index 91%
rename from tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarm.kt
rename to tests/FlickerTests/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationWarmTest.kt
index 425e674d..19a070b 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationWarmTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,15 +14,15 @@
* limitations under the License.
*/
-package com.android.server.wm.flicker.launch
+package com.android.server.wm.flicker.notification
import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import android.tools.device.helpers.wakeUpAndGoToHomeScreen
import android.view.WindowInsets
import android.view.WindowManager
@@ -47,15 +47,14 @@
/**
* 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: LegacyFlickerTest) :
+ OpenAppTransition(flicker) {
override val testApp: NotificationAppHelper = NotificationAppHelper(instrumentation)
open val openingNotificationsFromLockScreen = false
@@ -195,13 +194,11 @@
/**
* Creates the test configurations.
*
- * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and
+ * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and
* navigation modes.
*/
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): Collection<FlickerTest> {
- return FlickerTestFactory.nonRotationTests()
- }
+ fun getParams() = LegacyFlickerTestFactory.nonRotationTests()
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationWarmTestCfArm.kt
similarity index 71%
copy from tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTestCfArm.kt
copy to tests/FlickerTests/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationWarmTestCfArm.kt
index 8b89a8b..98356d7 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTestCfArm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationWarmTestCfArm.kt
@@ -14,11 +14,11 @@
* limitations under the License.
*/
-package com.android.server.wm.flicker.launch
+package com.android.server.wm.flicker.notification
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@@ -27,18 +27,17 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class ActivitiesTransitionTestCfArm(flicker: FlickerTest) : ActivitiesTransitionTest(flicker) {
+class OpenAppFromNotificationWarmTestCfArm(flicker: LegacyFlickerTest) :
+ OpenAppFromNotificationWarmTest(flicker) {
companion object {
/**
* Creates the test configurations.
*
- * See [FlickerTestFactory.nonRotationTests] for configuring screen orientation and
+ * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and
* navigation modes.
*/
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): Collection<FlickerTest> {
- return FlickerTestFactory.nonRotationTests()
- }
+ fun getParams() = LegacyFlickerTestFactory.nonRotationTests()
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/notification/OpenAppTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/notification/OpenAppTransition.kt
new file mode 100644
index 0000000..684b4b7
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/notification/OpenAppTransition.kt
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker.notification
+
+import android.platform.test.annotations.Presubmit
+import android.tools.common.traces.component.ComponentNameMatcher
+import android.tools.device.apphelpers.StandardAppHelper
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.helpers.wakeUpAndGoToHomeScreen
+import com.android.server.wm.flicker.BaseTest
+import com.android.server.wm.flicker.helpers.SimpleAppHelper
+import com.android.server.wm.flicker.helpers.setRotation
+import org.junit.Test
+
+/** Base class for app launch tests */
+abstract class OpenAppTransition(flicker: LegacyFlickerTest) : BaseTest(flicker) {
+ protected open val testApp: StandardAppHelper = SimpleAppHelper(instrumentation)
+
+ /** {@inheritDoc} */
+ override val transition: FlickerBuilder.() -> Unit = {
+ setup {
+ tapl.setExpectedRotation(flicker.scenario.startRotation.value)
+ device.wakeUpAndGoToHomeScreen()
+ this.setRotation(flicker.scenario.startRotation)
+ }
+ teardown { testApp.exit(wmHelper) }
+ }
+
+ /**
+ * Checks that the [testApp] layer doesn't exist or is invisible at the start of the transition,
+ * but is created and/or becomes visible during the transition.
+ */
+ @Presubmit
+ @Test
+ open fun appLayerBecomesVisible() {
+ appLayerBecomesVisible_coldStart()
+ }
+
+ protected fun appLayerBecomesVisible_coldStart() {
+ flicker.assertLayers {
+ this.notContains(testApp)
+ .then()
+ .isInvisible(testApp, isOptional = true)
+ .then()
+ .isVisible(ComponentNameMatcher.SNAPSHOT, isOptional = true)
+ .then()
+ .isVisible(ComponentNameMatcher.SPLASH_SCREEN, isOptional = true)
+ .then()
+ .isVisible(testApp)
+ }
+ }
+
+ protected fun appLayerBecomesVisible_warmStart() {
+ flicker.assertLayers {
+ this.isInvisible(testApp)
+ .then()
+ .isVisible(ComponentNameMatcher.SNAPSHOT, isOptional = true)
+ .then()
+ .isVisible(ComponentNameMatcher.SPLASH_SCREEN, isOptional = true)
+ .then()
+ .isVisible(testApp)
+ }
+ }
+
+ /**
+ * Checks that the [testApp] window doesn't exist at the start of the transition, that it is
+ * created (invisible - optional) and becomes visible during the transition
+ *
+ * The `isAppWindowInvisible` step is optional because we log once per frame, upon logging, the
+ * window may be visible or not depending on what was processed until that moment.
+ */
+ @Presubmit @Test open fun appWindowBecomesVisible() = appWindowBecomesVisible_coldStart()
+
+ protected fun appWindowBecomesVisible_coldStart() {
+ flicker.assertWm {
+ this.notContains(testApp)
+ .then()
+ .isAppWindowInvisible(testApp, isOptional = true)
+ .then()
+ .isAppWindowVisible(testApp)
+ }
+ }
+
+ protected fun appWindowBecomesVisible_warmStart() {
+ flicker.assertWm {
+ this.isAppWindowInvisible(testApp)
+ .then()
+ .isAppWindowVisible(ComponentNameMatcher.SNAPSHOT, isOptional = true)
+ .then()
+ .isAppWindowVisible(ComponentNameMatcher.SPLASH_SCREEN, isOptional = true)
+ .then()
+ .isAppWindowVisible(testApp)
+ }
+ }
+
+ /**
+ * Checks that [testApp] window is not on top at the start of the transition, and then becomes
+ * the top visible window until the end of the transition.
+ */
+ @Presubmit
+ @Test
+ open fun appWindowBecomesTopWindow() {
+ flicker.assertWm {
+ this.isAppWindowNotOnTop(testApp)
+ .then()
+ .isAppWindowOnTop(
+ testApp.or(ComponentNameMatcher.SNAPSHOT).or(ComponentNameMatcher.SPLASH_SCREEN)
+ )
+ }
+ }
+
+ /**
+ * Checks that [testApp] window is not on top at the start of the transition, and then becomes
+ * the top visible window until the end of the transition.
+ */
+ @Presubmit
+ @Test
+ open fun appWindowIsTopWindowAtEnd() {
+ flicker.assertWmEnd { this.isAppWindowOnTop(testApp) }
+ }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt
index a8b80ad..7883910 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt
@@ -23,8 +23,8 @@
import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.BaseTest
import com.android.server.wm.flicker.helpers.NonResizeableAppHelper
@@ -52,7 +52,7 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class QuickSwitchBetweenTwoAppsBackTest(flicker: FlickerTest) : BaseTest(flicker) {
+open class QuickSwitchBetweenTwoAppsBackTest(flicker: LegacyFlickerTest) : BaseTest(flicker) {
private val testApp1 = SimpleAppHelper(instrumentation)
private val testApp2 = NonResizeableAppHelper(instrumentation)
@@ -243,10 +243,9 @@
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): Collection<FlickerTest> {
- return FlickerTestFactory.nonRotationTests(
+ fun getParams() =
+ LegacyFlickerTestFactory.nonRotationTests(
supportedNavigationModes = listOf(NavBar.MODE_GESTURAL)
)
- }
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTestCfArm.kt
index f970a79..f68cd5c 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTestCfArm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTestCfArm.kt
@@ -18,8 +18,8 @@
import android.tools.common.NavBar
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@@ -28,15 +28,14 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class QuickSwitchBetweenTwoAppsBackTestCfArm(flicker: FlickerTest) :
+class QuickSwitchBetweenTwoAppsBackTestCfArm(flicker: LegacyFlickerTest) :
QuickSwitchBetweenTwoAppsBackTest(flicker) {
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): Collection<FlickerTest> {
- return FlickerTestFactory.nonRotationTests(
+ fun getParams() =
+ LegacyFlickerTestFactory.nonRotationTests(
supportedNavigationModes = listOf(NavBar.MODE_GESTURAL)
)
- }
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt
index 96cd8ff..1c4c7cd 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt
@@ -23,8 +23,8 @@
import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.BaseTest
import com.android.server.wm.flicker.helpers.NonResizeableAppHelper
@@ -53,7 +53,7 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class QuickSwitchBetweenTwoAppsForwardTest(flicker: FlickerTest) : BaseTest(flicker) {
+open class QuickSwitchBetweenTwoAppsForwardTest(flicker: LegacyFlickerTest) : BaseTest(flicker) {
private val testApp1 = SimpleAppHelper(instrumentation)
private val testApp2 = NonResizeableAppHelper(instrumentation)
@@ -261,10 +261,9 @@
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): Collection<FlickerTest> {
- return FlickerTestFactory.nonRotationTests(
+ fun getParams() =
+ LegacyFlickerTestFactory.nonRotationTests(
supportedNavigationModes = listOf(NavBar.MODE_GESTURAL)
)
- }
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTestCfArm.kt
index 9f48cda..3de58ac 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTestCfArm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTestCfArm.kt
@@ -18,8 +18,8 @@
import android.tools.common.NavBar
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@@ -28,15 +28,14 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class QuickSwitchBetweenTwoAppsForwardTestCfArm(flicker: FlickerTest) :
+class QuickSwitchBetweenTwoAppsForwardTestCfArm(flicker: LegacyFlickerTest) :
QuickSwitchBetweenTwoAppsForwardTest(flicker) {
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): Collection<FlickerTest> {
- return FlickerTestFactory.nonRotationTests(
+ fun getParams() =
+ LegacyFlickerTestFactory.nonRotationTests(
supportedNavigationModes = listOf(NavBar.MODE_GESTURAL)
)
- }
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt
index 7e935f0..6417456 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt
@@ -24,8 +24,8 @@
import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.BaseTest
import com.android.server.wm.flicker.helpers.SimpleAppHelper
@@ -52,7 +52,7 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class QuickSwitchFromLauncherTest(flicker: FlickerTest) : BaseTest(flicker) {
+open class QuickSwitchFromLauncherTest(flicker: LegacyFlickerTest) : BaseTest(flicker) {
private val testApp = SimpleAppHelper(instrumentation)
/** {@inheritDoc} */
@@ -272,12 +272,11 @@
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): Collection<FlickerTest> {
- return FlickerTestFactory.nonRotationTests(
+ fun getParams() =
+ LegacyFlickerTestFactory.nonRotationTests(
supportedNavigationModes = listOf(NavBar.MODE_GESTURAL),
// TODO: Test with 90 rotation
supportedRotations = listOf(Rotation.ROTATION_0)
)
- }
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTestCfArm.kt
index af671df..84fc754 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTestCfArm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTestCfArm.kt
@@ -19,8 +19,8 @@
import android.tools.common.NavBar
import android.tools.common.Rotation
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@@ -29,17 +29,16 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class QuickSwitchFromLauncherTestCfArm(flicker: FlickerTest) :
+open class QuickSwitchFromLauncherTestCfArm(flicker: LegacyFlickerTest) :
QuickSwitchFromLauncherTest(flicker) {
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): Collection<FlickerTest> {
- return FlickerTestFactory.nonRotationTests(
+ fun getParams() =
+ LegacyFlickerTestFactory.nonRotationTests(
supportedNavigationModes = listOf(NavBar.MODE_GESTURAL),
// TODO: Test with 90 rotation
supportedRotations = listOf(Rotation.ROTATION_0)
)
- }
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
index ef75c61..842ece3 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
@@ -21,8 +21,8 @@
import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.helpers.SimpleAppHelper
import org.junit.FixMethodOrder
@@ -34,7 +34,7 @@
/**
* Test opening an app and cycling through app rotations
*
- * Currently runs:
+ * Currently, runs:
* ```
* 0 -> 90 degrees
* 90 -> 0 degrees
@@ -85,7 +85,7 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class ChangeAppRotationTest(flicker: FlickerTest) : RotationTransition(flicker) {
+open class ChangeAppRotationTest(flicker: LegacyFlickerTest) : RotationTransition(flicker) {
override val testApp = SimpleAppHelper(instrumentation)
override val transition: FlickerBuilder.() -> Unit
get() = {
@@ -140,13 +140,11 @@
/**
* Creates the test configurations.
*
- * See [FlickerTestFactory.rotationTests] for configuring screen orientation and navigation
- * modes.
+ * See [LegacyFlickerTestFactory.rotationTests] for configuring screen orientation and
+ * navigation modes.
*/
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): Collection<FlickerTest> {
- return FlickerTestFactory.rotationTests()
- }
+ fun getParams() = LegacyFlickerTestFactory.rotationTests()
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTestCfArm.kt
index 0e6b20f..1ab5c5a 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTestCfArm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTestCfArm.kt
@@ -17,8 +17,8 @@
package com.android.server.wm.flicker.rotation
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
@@ -27,18 +27,16 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class ChangeAppRotationTestCfArm(flicker: FlickerTest) : ChangeAppRotationTest(flicker) {
+class ChangeAppRotationTestCfArm(flicker: LegacyFlickerTest) : ChangeAppRotationTest(flicker) {
companion object {
/**
* Creates the test configurations.
*
- * See [FlickerTestFactory.rotationTests] for configuring screen orientation and navigation
- * modes.
+ * See [LegacyFlickerTestFactory.rotationTests] for configuring screen orientation and
+ * navigation modes.
*/
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): Collection<FlickerTest> {
- return FlickerTestFactory.rotationTests()
- }
+ fun getParams() = LegacyFlickerTestFactory.rotationTests()
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt
index fe9da33..b0ca4d2 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt
@@ -20,13 +20,13 @@
import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.apphelpers.StandardAppHelper
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTest
import com.android.server.wm.flicker.BaseTest
import com.android.server.wm.flicker.helpers.setRotation
import org.junit.Test
/** Base class for app rotation tests */
-abstract class RotationTransition(flicker: FlickerTest) : BaseTest(flicker) {
+abstract class RotationTransition(flicker: LegacyFlickerTest) : BaseTest(flicker) {
protected abstract val testApp: StandardAppHelper
/** {@inheritDoc} */
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
index f654bdd4..b6ad3cc7 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
@@ -19,11 +19,12 @@
import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.ScenarioBuilder
+import android.tools.common.ScenarioImpl
import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import android.view.WindowManager
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.helpers.SeamlessRotationAppHelper
@@ -91,7 +92,7 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class SeamlessAppRotationTest(flicker: FlickerTest) : RotationTransition(flicker) {
+open class SeamlessAppRotationTest(flicker: LegacyFlickerTest) : RotationTransition(flicker) {
override val testApp = SeamlessRotationAppHelper(instrumentation)
/** {@inheritDoc} */
@@ -236,43 +237,50 @@
}
companion object {
- private val FlickerTest.starveUiThread
+ private val LegacyFlickerTest.starveUiThread
get() =
- getConfigValue<Boolean>(ActivityOptions.SeamlessRotation.EXTRA_STARVE_UI_THREAD)
+ scenario.getConfigValue<Boolean>(
+ ActivityOptions.SeamlessRotation.EXTRA_STARVE_UI_THREAD
+ )
?: false
@JvmStatic
protected fun createConfig(
- sourceConfig: FlickerTest,
+ sourceConfig: LegacyFlickerTest,
starveUiThread: Boolean
- ): FlickerTest {
- val originalScenario = sourceConfig.initialize("createConfig")
+ ): LegacyFlickerTest {
+ val originalScenario = sourceConfig.initialize("createConfig") as ScenarioImpl
val nameExt = if (starveUiThread) "_BUSY_UI_THREAD" else ""
val newConfig =
ScenarioBuilder()
- .fromScenario(originalScenario)
+ .forClass(originalScenario.testClass)
+ .withStartRotation(originalScenario.startRotation)
+ .withEndRotation(originalScenario.endRotation)
+ .withNavBarMode(originalScenario.navBarMode)
+ .withExtraConfigs(originalScenario.extraConfig)
+ .withDescriptionOverride(originalScenario.description)
.withExtraConfig(
ActivityOptions.SeamlessRotation.EXTRA_STARVE_UI_THREAD,
starveUiThread
)
.withDescriptionOverride("${originalScenario.description}$nameExt")
- return FlickerTest(newConfig)
+ return LegacyFlickerTest(newConfig)
}
/**
* Creates the test configurations for seamless rotation based on the default rotation tests
- * from [FlickerTestFactory.rotationTests], but adding a flag (
+ * from [LegacyFlickerTestFactory.rotationTests], but adding a flag (
* [ActivityOptions.SeamlessRotation.EXTRA_STARVE_UI_THREAD]) to indicate if the app should
* starve the UI thread of not
*/
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): Collection<FlickerTest> {
- return FlickerTestFactory.rotationTests().flatMap { sourceConfig ->
- val defaultRun = createConfig(sourceConfig, starveUiThread = false)
- val busyUiRun = createConfig(sourceConfig, starveUiThread = true)
+ fun getParams() =
+ LegacyFlickerTestFactory.rotationTests().flatMap { sourceCfg ->
+ val legacyCfg = sourceCfg as LegacyFlickerTest
+ val defaultRun = createConfig(legacyCfg, starveUiThread = false)
+ val busyUiRun = createConfig(legacyCfg, starveUiThread = true)
listOf(defaultRun, busyUiRun)
}
- }
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTestCfArm.kt
index b236d87..592be05 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTestCfArm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTestCfArm.kt
@@ -17,8 +17,8 @@
package com.android.server.wm.flicker.rotation
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
-import android.tools.device.flicker.legacy.FlickerTest
-import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import com.android.server.wm.flicker.testapp.ActivityOptions
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
@@ -29,22 +29,23 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-open class SeamlessAppRotationTestCfArm(flicker: FlickerTest) : SeamlessAppRotationTest(flicker) {
+open class SeamlessAppRotationTestCfArm(flicker: LegacyFlickerTest) :
+ SeamlessAppRotationTest(flicker) {
companion object {
/**
* Creates the test configurations for seamless rotation based on the default rotation tests
- * from [FlickerTestFactory.rotationTests], but adding a flag (
+ * from [LegacyFlickerTestFactory.rotationTests], but adding a flag (
* [ActivityOptions.SeamlessRotation.EXTRA_STARVE_UI_THREAD]) to indicate if the app should
* starve the UI thread of not
*/
@Parameterized.Parameters(name = "{0}")
@JvmStatic
- fun getParams(): Collection<FlickerTest> {
- return FlickerTestFactory.rotationTests().flatMap { sourceConfig ->
- val defaultRun = createConfig(sourceConfig, starveUiThread = false)
- val busyUiRun = createConfig(sourceConfig, starveUiThread = true)
+ fun getParams() =
+ LegacyFlickerTestFactory.rotationTests().flatMap { sourceCfg ->
+ val legacyCfg = sourceCfg as LegacyFlickerTest
+ val defaultRun = createConfig(legacyCfg, starveUiThread = false)
+ val busyUiRun = createConfig(legacyCfg, starveUiThread = true)
listOf(defaultRun, busyUiRun)
}
- }
}
}
diff --git a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
index 1ec9ec9..6430283 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 3a02cad..f0dfdfc 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 d78b9a8..b9d789b 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 0000000..239aba5
--- /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 0000000..d9b24ed
--- /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 6a7a2cc..817c79c 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;
+ 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 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();
+ 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 static LayoutDirection parseLayoutDirection(@NonNull String layoutDirectionStr) {
+ if (layoutDirectionStr.equals(LayoutDirection.LEFT_TO_RIGHT.toString())) {
+ return LayoutDirection.LEFT_TO_RIGHT;
}
- embeddingComponent.setEmbeddingRules(rules);
- }
-
- 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)
- .build();
- rules.add(rule);
- return rules;
- }
-
- 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;
+ 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 00f4c25..6e78750 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 9c3226b..5210618 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 @@
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,
@@ -180,6 +186,8 @@
public static final String LABEL = "SplitScreenPrimaryActivity";
public static final ComponentName COMPONENT = new ComponentName(FLICKER_APP_PACKAGE,
FLICKER_APP_PACKAGE + ".SplitScreenActivity");
+
+ public static final String EXTRA_LAUNCH_ADJACENT = "launch_adjacent";
}
public static class Secondary {
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/SplitScreenActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/SplitScreenActivity.java
index 70196ae..8a27252 100644
--- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/SplitScreenActivity.java
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/SplitScreenActivity.java
@@ -16,9 +16,14 @@
package com.android.server.wm.flicker.testapp;
+import static com.android.server.wm.flicker.testapp.ActivityOptions.SplitScreen.Primary.EXTRA_LAUNCH_ADJACENT;
+
import android.app.Activity;
+import android.content.Intent;
import android.os.Bundle;
+import androidx.annotation.Nullable;
+
public class SplitScreenActivity extends Activity {
@Override
@@ -26,4 +31,17 @@
super.onCreate(icicle);
setContentView(R.layout.activity_splitscreen);
}
+
+ @Override
+ protected void onPostCreate(@Nullable Bundle savedInstanceState) {
+ super.onPostCreate(savedInstanceState);
+ final boolean startSecondaryActivity = getIntent().hasExtra(EXTRA_LAUNCH_ADJACENT);
+ if (startSecondaryActivity) {
+ final Intent intent = new Intent(this, SplitScreenSecondaryActivity.class);
+ intent.addCategory(Intent.CATEGORY_LAUNCHER);
+ intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT | Intent.FLAG_ACTIVITY_NEW_TASK
+ | Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
+ startActivity(intent);
+ }
+ }
}
diff --git a/tests/FlickerTests/trace_config/trace_config.textproto b/tests/FlickerTests/trace_config/trace_config.textproto
new file mode 100644
index 0000000..c9a35ac
--- /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 039bb4e..46745e9 100644
--- a/tools/protologtool/Android.bp
+++ b/tools/protologtool/Android.bp
@@ -11,7 +11,7 @@
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 433e88c..0000000
--- 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 ca12c4c..2a199d2 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 @@
// 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 @@
/** @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 WifiNl80211Manager(Context context) {
mAlarmManager = context.getSystemService(AlarmManager.class);
mEventHandler = new Handler(context.getMainLooper());
- mContext = context;
}
/**
@@ -443,7 +434,6 @@
if (mWificond == null) {
Log.e(TAG, "Failed to get reference to wificond");
}
- mContext = context;
}
/** @hide */
@@ -451,7 +441,6 @@
public WifiNl80211Manager(Context context, IWificond wificond) {
this(context);
mWificond = wificond;
- mContext = context;
}
/** @hide */
@@ -755,9 +744,6 @@
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 @@
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 @@
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 ebff0e2..0000000
--- 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 f12818f..362eb14 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 @@
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.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.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 @@
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 @@
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 @@
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 @@
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 @@
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> {